ar_sync 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,75 +1,80 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- class ConnectionManager {
4
- constructor(adapter) {
3
+ var ConnectionManager = /** @class */ (function () {
4
+ function ConnectionManager(adapter) {
5
+ var _this = this;
5
6
  this.subscriptions = {};
6
7
  this.adapter = adapter;
7
8
  this.networkListeners = {};
8
9
  this.networkListenerSerial = 0;
9
10
  this.networkStatus = true;
10
- adapter.ondisconnect = () => {
11
- this.unsubscribeAll();
12
- this.triggerNetworkChange(false);
11
+ adapter.ondisconnect = function () {
12
+ _this.unsubscribeAll();
13
+ _this.triggerNetworkChange(false);
13
14
  };
14
- adapter.onreconnect = () => this.triggerNetworkChange(true);
15
+ adapter.onreconnect = function () { return _this.triggerNetworkChange(true); };
15
16
  }
16
- triggerNetworkChange(status) {
17
+ ConnectionManager.prototype.triggerNetworkChange = function (status) {
17
18
  if (this.networkStatus == status)
18
19
  return;
19
20
  this.networkStatus = status;
20
- for (const id in this.networkListeners)
21
+ for (var id in this.networkListeners)
21
22
  this.networkListeners[id](status);
22
- }
23
- unsubscribeAll() {
24
- for (const id in this.subscriptions) {
25
- const subscription = this.subscriptions[id];
23
+ };
24
+ ConnectionManager.prototype.unsubscribeAll = function () {
25
+ for (var id in this.subscriptions) {
26
+ var subscription = this.subscriptions[id];
26
27
  subscription.listeners = {};
27
28
  subscription.connection.unsubscribe();
28
29
  }
29
30
  this.subscriptions = {};
30
- }
31
- subscribeNetwork(func) {
32
- const id = this.networkListenerSerial++;
31
+ };
32
+ ConnectionManager.prototype.subscribeNetwork = function (func) {
33
+ var _this = this;
34
+ var id = this.networkListenerSerial++;
33
35
  this.networkListeners[id] = func;
34
- const unsubscribe = () => {
35
- delete this.networkListeners[id];
36
+ var unsubscribe = function () {
37
+ delete _this.networkListeners[id];
36
38
  };
37
- return { unsubscribe };
38
- }
39
- subscribe(key, func) {
40
- const subscription = this.connect(key);
41
- const id = subscription.serial++;
39
+ return { unsubscribe: unsubscribe };
40
+ };
41
+ ConnectionManager.prototype.subscribe = function (key, func) {
42
+ var _this = this;
43
+ var subscription = this.connect(key);
44
+ var id = subscription.serial++;
42
45
  subscription.ref++;
43
46
  subscription.listeners[id] = func;
44
- const unsubscribe = () => {
47
+ var unsubscribe = function () {
45
48
  if (!subscription.listeners[id])
46
49
  return;
47
50
  delete subscription.listeners[id];
48
51
  subscription.ref--;
49
52
  if (subscription.ref === 0)
50
- this.disconnect(key);
53
+ _this.disconnect(key);
51
54
  };
52
- return { unsubscribe };
53
- }
54
- connect(key) {
55
+ return { unsubscribe: unsubscribe };
56
+ };
57
+ ConnectionManager.prototype.connect = function (key) {
58
+ var _this = this;
55
59
  if (this.subscriptions[key])
56
60
  return this.subscriptions[key];
57
- const connection = this.adapter.subscribe(key, data => this.received(key, data));
58
- return this.subscriptions[key] = { connection, listeners: {}, ref: 0, serial: 0 };
59
- }
60
- disconnect(key) {
61
- const subscription = this.subscriptions[key];
61
+ var connection = this.adapter.subscribe(key, function (data) { return _this.received(key, data); });
62
+ return this.subscriptions[key] = { connection: connection, listeners: {}, ref: 0, serial: 0 };
63
+ };
64
+ ConnectionManager.prototype.disconnect = function (key) {
65
+ var subscription = this.subscriptions[key];
62
66
  if (!subscription || subscription.ref !== 0)
63
67
  return;
64
68
  delete this.subscriptions[key];
65
69
  subscription.connection.unsubscribe();
66
- }
67
- received(key, data) {
68
- const subscription = this.subscriptions[key];
70
+ };
71
+ ConnectionManager.prototype.received = function (key, data) {
72
+ var subscription = this.subscriptions[key];
69
73
  if (!subscription)
70
74
  return;
71
- for (const id in subscription.listeners)
75
+ for (var id in subscription.listeners)
72
76
  subscription.listeners[id](data);
73
- }
74
- }
77
+ };
78
+ return ConnectionManager;
79
+ }());
75
80
  exports.default = ConnectionManager;
@@ -1,10 +1,14 @@
1
1
  declare let useState: <T>(t: T | (() => T)) => [T, (t: T | ((t: T) => T)) => void];
2
2
  declare let useEffect: (f: (() => void) | (() => (() => void)), deps: any[]) => void;
3
3
  declare let useMemo: <T>(f: () => T, deps: any[]) => T;
4
+ declare let useRef: <T>(value: T) => {
5
+ current: T;
6
+ };
4
7
  declare type InitializeHooksParams = {
5
8
  useState: typeof useState;
6
9
  useEffect: typeof useEffect;
7
10
  useMemo: typeof useMemo;
11
+ useRef: typeof useRef;
8
12
  };
9
13
  export declare function initializeHooks(hooks: InitializeHooksParams): void;
10
14
  interface ModelStatus {
@@ -1,37 +1,43 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- const ArSyncApi_1 = require("./ArSyncApi");
4
- const ArSyncModel_1 = require("./ArSyncModel");
5
- let useState;
6
- let useEffect;
7
- let useMemo;
3
+ var ArSyncApi_1 = require("./ArSyncApi");
4
+ var ArSyncModel_1 = require("./ArSyncModel");
5
+ var useState;
6
+ var useEffect;
7
+ var useMemo;
8
+ var useRef;
8
9
  function initializeHooks(hooks) {
9
10
  useState = hooks.useState;
10
11
  useEffect = hooks.useEffect;
11
12
  useMemo = hooks.useMemo;
13
+ useRef = hooks.useRef;
12
14
  }
13
15
  exports.initializeHooks = initializeHooks;
14
16
  function checkHooks() {
15
17
  if (!useState)
16
- throw 'uninitialized. needs `initializeHooks({ useState, useEffect, useMemo })`';
18
+ throw 'uninitialized. needs `initializeHooks({ useState, useEffect, useMemo, useRef })`';
17
19
  }
18
- const initialResult = [null, { complete: false, notfound: undefined, connected: true }];
20
+ var initialResult = [null, { complete: false, notfound: undefined, connected: true }];
19
21
  function useArSyncModel(request) {
20
22
  checkHooks();
21
- const [result, setResult] = useState(initialResult);
22
- const requestString = JSON.stringify(request && request.params);
23
- useEffect(() => {
23
+ var _a = useState(initialResult), result = _a[0], setResult = _a[1];
24
+ var requestString = JSON.stringify(request && request.params);
25
+ var prevRequestStringRef = useRef(requestString);
26
+ useEffect(function () {
27
+ prevRequestStringRef.current = requestString;
24
28
  if (!request) {
25
29
  setResult(initialResult);
26
- return () => { };
30
+ return function () { };
27
31
  }
28
- const model = new ArSyncModel_1.default(request, { immutable: true });
32
+ var model = new ArSyncModel_1.default(request, { immutable: true });
29
33
  function update() {
30
- const { complete, notfound, connected, data } = model;
31
- setResult(resultWas => {
32
- const [, statusWas] = resultWas;
33
- const statusPersisted = statusWas.complete === complete && statusWas.notfound === notfound && statusWas.connected === connected;
34
- const status = statusPersisted ? statusWas : { complete, notfound, connected };
34
+ var complete = model.complete, notfound = model.notfound, connected = model.connected, data = model.data;
35
+ setResult(function (resultWas) {
36
+ var dataWas = resultWas[0], statusWas = resultWas[1];
37
+ var statusPersisted = statusWas.complete === complete && statusWas.notfound === notfound && statusWas.connected === connected;
38
+ if (dataWas === data && statusPersisted)
39
+ return resultWas;
40
+ var status = statusPersisted ? statusWas : { complete: complete, notfound: notfound, connected: connected };
35
41
  return [data, status];
36
42
  });
37
43
  }
@@ -41,21 +47,38 @@ function useArSyncModel(request) {
41
47
  else {
42
48
  setResult(initialResult);
43
49
  }
50
+ model.subscribe('load', update);
44
51
  model.subscribe('change', update);
45
52
  model.subscribe('connection', update);
46
- return () => model.release();
53
+ return function () { return model.release(); };
47
54
  }, [requestString]);
48
- return result;
55
+ return prevRequestStringRef.current === requestString ? result : initialResult;
49
56
  }
50
57
  exports.useArSyncModel = useArSyncModel;
51
- const initialFetchState = { data: null, status: { complete: false, notfound: undefined } };
58
+ var initialFetchState = { data: null, status: { complete: false, notfound: undefined } };
59
+ function extractParams(query, output) {
60
+ if (output === void 0) { output = []; }
61
+ if (typeof (query) !== 'object' || query == null || Array.isArray(query))
62
+ return output;
63
+ if ('params' in query)
64
+ output.push(query.params);
65
+ for (var key in query) {
66
+ extractParams(query[key], output);
67
+ }
68
+ return output;
69
+ }
52
70
  function useArSyncFetch(request) {
53
71
  checkHooks();
54
- const [state, setState] = useState(initialFetchState);
55
- const requestString = JSON.stringify(request && request.params);
56
- const loader = useMemo(() => {
57
- let lastLoadId = 0;
58
- let timer = null;
72
+ var _a = useState(initialFetchState), state = _a[0], setState = _a[1];
73
+ var query = request && request.query;
74
+ var params = request && request.params;
75
+ var requestString = useMemo(function () {
76
+ return JSON.stringify(extractParams(query, [params]));
77
+ }, [query, params]);
78
+ var prevRequestStringRef = useRef(requestString);
79
+ var loader = useMemo(function () {
80
+ var lastLoadId = 0;
81
+ var timer = null;
59
82
  function cancel() {
60
83
  if (timer)
61
84
  clearTimeout(timer);
@@ -64,28 +87,28 @@ function useArSyncFetch(request) {
64
87
  }
65
88
  function fetch(request, retryCount) {
66
89
  cancel();
67
- const currentLoadingId = lastLoadId;
68
- ArSyncApi_1.default.fetch(request).then((response) => {
90
+ var currentLoadingId = lastLoadId;
91
+ ArSyncApi_1.default.fetch(request).then(function (response) {
69
92
  if (currentLoadingId !== lastLoadId)
70
93
  return;
71
94
  setState({ data: response, status: { complete: true, notfound: false } });
72
- }).catch(e => {
95
+ }).catch(function (e) {
73
96
  if (currentLoadingId !== lastLoadId)
74
97
  return;
75
98
  if (!e.retry) {
76
99
  setState({ data: null, status: { complete: true, notfound: true } });
77
100
  return;
78
101
  }
79
- timer = setTimeout(() => fetch(request, retryCount + 1), 1000 * Math.min(4 ** retryCount, 30));
102
+ timer = setTimeout(function () { return fetch(request, retryCount + 1); }, 1000 * Math.min(Math.pow(4, retryCount), 30));
80
103
  });
81
104
  }
82
105
  function update() {
83
106
  if (request) {
84
- setState(state => {
85
- const { data, status } = state;
107
+ setState(function (state) {
108
+ var data = state.data, status = state.status;
86
109
  if (!status.complete && status.notfound === undefined)
87
110
  return state;
88
- return { data, status: { complete: false, notfound: undefined } };
111
+ return { data: data, status: { complete: false, notfound: undefined } };
89
112
  });
90
113
  fetch(request, 0);
91
114
  }
@@ -93,13 +116,15 @@ function useArSyncFetch(request) {
93
116
  setState(initialFetchState);
94
117
  }
95
118
  }
96
- return { update, cancel };
119
+ return { update: update, cancel: cancel };
97
120
  }, [requestString]);
98
- useEffect(() => {
121
+ useEffect(function () {
122
+ prevRequestStringRef.current = requestString;
99
123
  setState(initialFetchState);
100
124
  loader.update();
101
- return () => loader.cancel();
125
+ return function () { return loader.cancel(); };
102
126
  }, [requestString]);
103
- return [state.data, state.status, loader.update];
127
+ var responseState = prevRequestStringRef.current === requestString ? state : initialFetchState;
128
+ return [responseState.data, responseState.status, loader.update];
104
129
  }
105
130
  exports.useArSyncFetch = useArSyncFetch;
@@ -75,23 +75,24 @@ module ArSync::TypeScript
75
75
  <<~CODE
76
76
  import { TypeRequest, ApiNameRequests } from './types'
77
77
  import { DataTypeFromRequest as DataTypeFromRequestPair } from 'ar_sync/core/DataType'
78
- type DataTypeFromRequest<R extends TypeRequest> = DataTypeFromRequestPair<ApiNameRequests[R['api']], R>
78
+ export type NeverMatchArgument = { __nevermatch: never }
79
+ type DataTypeFromRequest<R extends TypeRequest | NeverMatchArgument> = NeverMatchArgument extends R ? never : R extends TypeRequest ? DataTypeFromRequestPair<ApiNameRequests[R['api']], R> : never
79
80
  export default DataTypeFromRequest
80
81
  CODE
81
82
  end
82
83
 
83
84
  def self.generate_hooks_script
84
85
  <<~CODE
85
- import { useState, useEffect, useMemo } from 'react'
86
+ import { useState, useEffect, useMemo, useRef } from 'react'
86
87
  import { TypeRequest } from './types'
87
- import DataTypeFromRequest from './DataTypeFromRequest'
88
+ import DataTypeFromRequest, { NeverMatchArgument } from './DataTypeFromRequest'
88
89
  import { initializeHooks, useArSyncModel as useArSyncModelBase, useArSyncFetch as useArSyncFetchBase } from 'ar_sync/core/hooks'
89
- initializeHooks({ useState, useEffect, useMemo })
90
- export function useArSyncModel<R extends TypeRequest>(request: R | null) {
91
- return useArSyncModelBase<DataTypeFromRequest<R>>(request)
90
+ initializeHooks({ useState, useEffect, useMemo, useRef })
91
+ export function useArSyncModel<R extends TypeRequest | NeverMatchArgument>(request: R | null) {
92
+ return useArSyncModelBase<DataTypeFromRequest<R>>(request as TypeRequest)
92
93
  }
93
- export function useArSyncFetch<R extends TypeRequest>(request: R | null) {
94
- return useArSyncFetchBase<DataTypeFromRequest<R>>(request)
94
+ export function useArSyncFetch<R extends TypeRequest | NeverMatchArgument>(request: R | null) {
95
+ return useArSyncFetchBase<DataTypeFromRequest<R>>(request as TypeRequest)
95
96
  }
96
97
  CODE
97
98
  end
@@ -1,3 +1,3 @@
1
1
  module ArSync
2
- VERSION = '1.0.4'
2
+ VERSION = '1.0.5'
3
3
  end
@@ -83,4 +83,4 @@ type DataTypeBaseFromRequestType<R extends RequestBase, ID> = R extends { _meta?
83
83
  : never
84
84
  export type DataTypeFromRequest<Req extends RequestBase, R extends RequestBase> = ValidateDataTypeExtraFileds<
85
85
  DataTypeFromQuery<DataTypeBaseFromRequestType<Req, R['id']>, R['query']>
86
- >
86
+ >
@@ -4,18 +4,21 @@ import ArSyncModel from './ArSyncModel'
4
4
  let useState: <T>(t: T | (() => T)) => [T, (t: T | ((t: T) => T)) => void]
5
5
  let useEffect: (f: (() => void) | (() => (() => void)), deps: any[]) => void
6
6
  let useMemo: <T>(f: () => T, deps: any[]) => T
7
+ let useRef: <T>(value: T) => { current: T }
7
8
  type InitializeHooksParams = {
8
9
  useState: typeof useState
9
10
  useEffect: typeof useEffect
10
11
  useMemo: typeof useMemo
12
+ useRef: typeof useRef
11
13
  }
12
14
  export function initializeHooks(hooks: InitializeHooksParams) {
13
15
  useState = hooks.useState
14
16
  useEffect = hooks.useEffect
15
17
  useMemo = hooks.useMemo
18
+ useRef = hooks.useRef
16
19
  }
17
20
  function checkHooks() {
18
- if (!useState) throw 'uninitialized. needs `initializeHooks({ useState, useEffect, useMemo })`'
21
+ if (!useState) throw 'uninitialized. needs `initializeHooks({ useState, useEffect, useMemo, useRef })`'
19
22
  }
20
23
 
21
24
  interface ModelStatus { complete: boolean; notfound?: boolean; connected: boolean }
@@ -27,7 +30,9 @@ export function useArSyncModel<T>(request: Request | null): DataAndStatus<T> {
27
30
  checkHooks()
28
31
  const [result, setResult] = useState<DataAndStatus<T>>(initialResult)
29
32
  const requestString = JSON.stringify(request && request.params)
33
+ const prevRequestStringRef = useRef(requestString)
30
34
  useEffect(() => {
35
+ prevRequestStringRef.current = requestString
31
36
  if (!request) {
32
37
  setResult(initialResult)
33
38
  return () => {}
@@ -36,8 +41,9 @@ export function useArSyncModel<T>(request: Request | null): DataAndStatus<T> {
36
41
  function update() {
37
42
  const { complete, notfound, connected, data } = model
38
43
  setResult(resultWas => {
39
- const [, statusWas] = resultWas
44
+ const [dataWas, statusWas] = resultWas
40
45
  const statusPersisted = statusWas.complete === complete && statusWas.notfound === notfound && statusWas.connected === connected
46
+ if (dataWas === data && statusPersisted) return resultWas
41
47
  const status = statusPersisted ? statusWas : { complete, notfound, connected }
42
48
  return [data, status]
43
49
  })
@@ -47,21 +53,37 @@ export function useArSyncModel<T>(request: Request | null): DataAndStatus<T> {
47
53
  } else {
48
54
  setResult(initialResult)
49
55
  }
56
+ model.subscribe('load', update)
50
57
  model.subscribe('change', update)
51
58
  model.subscribe('connection', update)
52
59
  return () => model.release()
53
60
  }, [requestString])
54
- return result
61
+ return prevRequestStringRef.current === requestString ? result : initialResult
55
62
  }
56
63
 
57
64
  interface FetchStatus { complete: boolean; notfound?: boolean }
58
65
  type DataStatusUpdate<T> = [T | null, FetchStatus, () => void]
59
66
  type FetchState<T> = { data: T | null; status: FetchStatus }
60
67
  const initialFetchState: FetchState<any> = { data: null, status: { complete: false, notfound: undefined } }
68
+
69
+ function extractParams(query: unknown, output: any[] = []): any[] {
70
+ if (typeof(query) !== 'object' || query == null || Array.isArray(query)) return output
71
+ if ('params' in query) output.push((query as { params: any }).params)
72
+ for (const key in query) {
73
+ extractParams(query[key], output)
74
+ }
75
+ return output
76
+ }
77
+
61
78
  export function useArSyncFetch<T>(request: Request | null): DataStatusUpdate<T> {
62
79
  checkHooks()
63
80
  const [state, setState] = useState<FetchState<T>>(initialFetchState)
64
- const requestString = JSON.stringify(request && request.params)
81
+ const query = request && request.query
82
+ const params = request && request.params
83
+ const requestString = useMemo(() => {
84
+ return JSON.stringify(extractParams(query, [params]))
85
+ }, [query, params])
86
+ const prevRequestStringRef = useRef(requestString)
65
87
  const loader = useMemo(() => {
66
88
  let lastLoadId = 0
67
89
  let timer: null | number = null
@@ -100,9 +122,11 @@ export function useArSyncFetch<T>(request: Request | null): DataStatusUpdate<T>
100
122
  return { update, cancel }
101
123
  }, [requestString])
102
124
  useEffect(() => {
125
+ prevRequestStringRef.current = requestString
103
126
  setState(initialFetchState)
104
127
  loader.update()
105
128
  return () => loader.cancel()
106
129
  }, [requestString])
107
- return [state.data, state.status, loader.update]
130
+ const responseState = prevRequestStringRef.current === requestString ? state : initialFetchState
131
+ return [responseState.data, responseState.status, loader.update]
108
132
  }
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "compilerOptions": {
3
- "incremental": true,
4
3
  "outDir": ".",
5
4
  "rootDir": "./src",
6
5
  "module": "commonjs",
7
- "target": "es2017",
6
+ "target": "es5",
7
+ "lib": ["es2017", "dom"],
8
8
  "strictNullChecks": true,
9
9
  "noUnusedLocals": true,
10
10
  "noUnusedParameters": true,