ar_sync 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -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,