@data-client/core 0.14.16 → 0.14.19
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.
- package/dist/index.js +556 -280
- package/dist/index.umd.min.js +1 -1
- package/legacy/actions.js +1 -1
- package/legacy/controller/Controller.js +101 -12
- package/legacy/controller/actions/createFetch.js +3 -2
- package/legacy/controller/actions/createSubscription.js +3 -3
- package/legacy/controller/ensurePojo.js +3 -2
- package/legacy/index.js +3 -4
- package/legacy/manager/DevtoolsManager.js +14 -9
- package/legacy/manager/NetworkManager.js +8 -5
- package/legacy/manager/PollingSubscription.js +6 -3
- package/legacy/manager/SubscriptionManager.js +4 -3
- package/legacy/manager/applyManager.js +3 -7
- package/legacy/manager/initManager.js +15 -0
- package/legacy/state/GCPolicy.js +153 -0
- package/legacy/state/reducer/createReducer.js +7 -3
- package/legacy/state/reducer/expireReducer.js +5 -4
- package/legacy/state/reducer/fetchReducer.js +3 -2
- package/legacy/state/reducer/invalidateReducer.js +6 -5
- package/legacy/state/reducer/setResponseReducer.js +10 -7
- package/legacy/types.js +1 -1
- package/lib/actionTypes.d.ts.map +1 -1
- package/lib/actions.d.ts +2 -2
- package/lib/actions.d.ts.map +1 -1
- package/lib/actions.js +1 -1
- package/lib/controller/Controller.d.ts +108 -5
- package/lib/controller/Controller.d.ts.map +1 -1
- package/lib/controller/Controller.js +97 -9
- package/lib/controller/actions/createSubscription.js +3 -3
- package/lib/index.d.ts +2 -0
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +3 -4
- package/lib/manager/DevtoolsManager.d.ts.map +1 -1
- package/lib/manager/DevtoolsManager.js +7 -5
- package/lib/manager/NetworkManager.js +3 -2
- package/lib/manager/PollingSubscription.js +1 -1
- package/lib/manager/SubscriptionManager.d.ts.map +1 -1
- package/lib/manager/SubscriptionManager.js +1 -1
- package/lib/manager/applyManager.d.ts.map +1 -1
- package/lib/manager/applyManager.js +3 -7
- package/lib/manager/initManager.d.ts +4 -0
- package/lib/manager/initManager.d.ts.map +1 -0
- package/lib/manager/initManager.js +15 -0
- package/lib/state/GCPolicy.d.ts +55 -0
- package/lib/state/GCPolicy.d.ts.map +1 -0
- package/lib/state/GCPolicy.js +153 -0
- package/lib/state/reducer/createReducer.js +5 -2
- package/lib/state/reducer/expireReducer.d.ts +1 -0
- package/lib/state/reducer/expireReducer.d.ts.map +1 -1
- package/lib/state/reducer/invalidateReducer.d.ts +1 -0
- package/lib/state/reducer/invalidateReducer.d.ts.map +1 -1
- package/lib/state/reducer/setReducer.d.ts +5 -4
- package/lib/state/reducer/setReducer.d.ts.map +1 -1
- package/lib/state/reducer/setResponseReducer.d.ts +6 -4
- package/lib/state/reducer/setResponseReducer.d.ts.map +1 -1
- package/lib/state/reducer/setResponseReducer.js +4 -2
- package/lib/types.d.ts +1 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js +1 -1
- package/package.json +12 -4
- package/src/actions.ts +2 -1
- package/src/controller/Controller.ts +206 -9
- package/src/controller/__tests__/Controller.ts +8 -4
- package/src/controller/__tests__/__snapshots__/get.ts.snap +7 -0
- package/src/controller/__tests__/__snapshots__/getResponse.ts.snap +15 -0
- package/src/controller/__tests__/get.ts +45 -17
- package/src/controller/__tests__/getResponse.ts +46 -0
- package/src/controller/actions/createSubscription.ts +2 -2
- package/src/index.ts +2 -6
- package/src/manager/DevtoolsManager.ts +2 -3
- package/src/manager/PollingSubscription.ts +9 -9
- package/src/manager/SubscriptionManager.ts +1 -2
- package/src/manager/applyManager.ts +3 -4
- package/src/manager/initManager.ts +21 -0
- package/src/state/GCPolicy.ts +197 -0
- package/src/state/__tests__/GCPolicy.test.ts +258 -0
- package/src/state/__tests__/__snapshots__/reducer.ts.snap +2 -0
- package/src/state/__tests__/reducer.ts +4 -4
- package/src/state/reducer/createReducer.ts +1 -1
- package/src/state/reducer/setResponseReducer.ts +3 -1
- package/src/types.ts +1 -0
- package/ts3.4/actions.d.ts +2 -5
- package/ts3.4/controller/Controller.d.ts +141 -5
- package/ts3.4/index.d.ts +2 -0
- package/ts3.4/manager/initManager.d.ts +4 -0
- package/ts3.4/state/GCPolicy.d.ts +55 -0
- package/ts3.4/state/reducer/expireReducer.d.ts +1 -0
- package/ts3.4/state/reducer/invalidateReducer.d.ts +1 -0
- package/ts3.4/state/reducer/setReducer.d.ts +3 -2
- package/ts3.4/state/reducer/setResponseReducer.d.ts +4 -2
- package/ts3.4/types.d.ts +1 -0
package/dist/index.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
-
|
|
5
3
|
var normalizr = require('@data-client/normalizr');
|
|
4
|
+
var _pushInstanceProperty = require('core-js-pure/stable/instance/push.js');
|
|
6
5
|
|
|
7
6
|
function expireReducer(state, action) {
|
|
8
7
|
const meta = {
|
|
@@ -57,28 +56,28 @@ const GC_TYPE = GC;
|
|
|
57
56
|
|
|
58
57
|
var actionTypes = /*#__PURE__*/Object.freeze({
|
|
59
58
|
__proto__: null,
|
|
59
|
+
EXPIREALL: EXPIREALL,
|
|
60
|
+
EXPIREALL_TYPE: EXPIREALL_TYPE,
|
|
60
61
|
FETCH: FETCH,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
RESET: RESET,
|
|
65
|
-
SUBSCRIBE: SUBSCRIBE,
|
|
66
|
-
UNSUBSCRIBE: UNSUBSCRIBE,
|
|
62
|
+
FETCH_TYPE: FETCH_TYPE,
|
|
63
|
+
GC: GC,
|
|
64
|
+
GC_TYPE: GC_TYPE,
|
|
67
65
|
INVALIDATE: INVALIDATE,
|
|
68
66
|
INVALIDATEALL: INVALIDATEALL,
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
SET_TYPE: SET_TYPE,
|
|
73
|
-
SET_RESPONSE_TYPE: SET_RESPONSE_TYPE,
|
|
67
|
+
INVALIDATEALL_TYPE: INVALIDATEALL_TYPE,
|
|
68
|
+
INVALIDATE_TYPE: INVALIDATE_TYPE,
|
|
69
|
+
OPTIMISTIC: OPTIMISTIC,
|
|
74
70
|
OPTIMISTIC_TYPE: OPTIMISTIC_TYPE,
|
|
71
|
+
RESET: RESET,
|
|
75
72
|
RESET_TYPE: RESET_TYPE,
|
|
73
|
+
SET: SET,
|
|
74
|
+
SET_RESPONSE: SET_RESPONSE,
|
|
75
|
+
SET_RESPONSE_TYPE: SET_RESPONSE_TYPE,
|
|
76
|
+
SET_TYPE: SET_TYPE,
|
|
77
|
+
SUBSCRIBE: SUBSCRIBE,
|
|
76
78
|
SUBSCRIBE_TYPE: SUBSCRIBE_TYPE,
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
INVALIDATEALL_TYPE: INVALIDATEALL_TYPE,
|
|
80
|
-
EXPIREALL_TYPE: EXPIREALL_TYPE,
|
|
81
|
-
GC_TYPE: GC_TYPE
|
|
79
|
+
UNSUBSCRIBE: UNSUBSCRIBE,
|
|
80
|
+
UNSUBSCRIBE_TYPE: UNSUBSCRIBE_TYPE
|
|
82
81
|
});
|
|
83
82
|
|
|
84
83
|
function createOptimistic(endpoint, args, fetchedAt) {
|
|
@@ -233,6 +232,7 @@ function setResponseReducer(state, action, controller) {
|
|
|
233
232
|
...state.meta,
|
|
234
233
|
[action.key]: {
|
|
235
234
|
date: action.meta.date,
|
|
235
|
+
fetchedAt: action.meta.fetchedAt,
|
|
236
236
|
expiresAt: action.meta.expiresAt,
|
|
237
237
|
prevExpiresAt: (_state$meta$action$ke = state.meta[action.key]) == null ? void 0 : _state$meta$action$ke.expiresAt
|
|
238
238
|
}
|
|
@@ -273,8 +273,9 @@ function reduceError(state, action, error) {
|
|
|
273
273
|
...state.meta,
|
|
274
274
|
[action.key]: {
|
|
275
275
|
date: action.meta.date,
|
|
276
|
-
|
|
276
|
+
fetchedAt: action.meta.fetchedAt,
|
|
277
277
|
expiresAt: action.meta.expiresAt,
|
|
278
|
+
error,
|
|
278
279
|
errorPolicy: action.endpoint.errorPolicy == null ? void 0 : action.endpoint.errorPolicy(error)
|
|
279
280
|
}
|
|
280
281
|
},
|
|
@@ -292,7 +293,10 @@ function createReducer(controller) {
|
|
|
292
293
|
switch (action.type) {
|
|
293
294
|
case GC:
|
|
294
295
|
// inline deletes are fine as these should have 0 refcounts
|
|
295
|
-
action.entities.forEach((
|
|
296
|
+
action.entities.forEach(({
|
|
297
|
+
key,
|
|
298
|
+
pk
|
|
299
|
+
}) => {
|
|
296
300
|
var _entities$key, _entityMeta$key;
|
|
297
301
|
(_entities$key = state.entities[key]) == null || delete _entities$key[pk];
|
|
298
302
|
(_entityMeta$key = state.entityMeta[key]) == null || delete _entityMeta$key[pk];
|
|
@@ -339,8 +343,8 @@ const initialState = {
|
|
|
339
343
|
|
|
340
344
|
var internal = /*#__PURE__*/Object.freeze({
|
|
341
345
|
__proto__: null,
|
|
342
|
-
MemoCache: normalizr.MemoCache,
|
|
343
346
|
INVALID: normalizr.INVALID,
|
|
347
|
+
MemoCache: normalizr.MemoCache,
|
|
344
348
|
initialState: initialState
|
|
345
349
|
});
|
|
346
350
|
|
|
@@ -349,9 +353,9 @@ function createSubscription(endpoint, {
|
|
|
349
353
|
}) {
|
|
350
354
|
return {
|
|
351
355
|
type: SUBSCRIBE,
|
|
352
|
-
endpoint,
|
|
356
|
+
key: endpoint.key(...args),
|
|
353
357
|
args,
|
|
354
|
-
|
|
358
|
+
endpoint
|
|
355
359
|
};
|
|
356
360
|
}
|
|
357
361
|
function createUnsubscription(endpoint, {
|
|
@@ -369,7 +373,6 @@ const ensurePojo =
|
|
|
369
373
|
// FormData doesn't exist in node
|
|
370
374
|
/* istanbul ignore else we don't run coverage when we test node*/
|
|
371
375
|
typeof FormData !== 'undefined' ? body => body instanceof FormData ? Object.fromEntries(body.entries()) : body : /* istanbul ignore next */body => body;
|
|
372
|
-
var ensurePojo$1 = ensurePojo;
|
|
373
376
|
|
|
374
377
|
function createSetResponse(endpoint, {
|
|
375
378
|
args,
|
|
@@ -387,7 +390,7 @@ function createSetResponse(endpoint, {
|
|
|
387
390
|
type: SET_RESPONSE,
|
|
388
391
|
key: endpoint.key(...args),
|
|
389
392
|
response,
|
|
390
|
-
args: args.map(ensurePojo
|
|
393
|
+
args: args.map(ensurePojo),
|
|
391
394
|
endpoint,
|
|
392
395
|
meta: createMeta(expiryLength, fetchedAt),
|
|
393
396
|
error
|
|
@@ -402,7 +405,7 @@ function createSet(schema, {
|
|
|
402
405
|
return {
|
|
403
406
|
type: SET,
|
|
404
407
|
value,
|
|
405
|
-
args: args.map(ensurePojo
|
|
408
|
+
args: args.map(ensurePojo),
|
|
406
409
|
schema,
|
|
407
410
|
meta: createMeta(60000, fetchedAt)
|
|
408
411
|
};
|
|
@@ -466,19 +469,170 @@ function createExpireAll(testKey) {
|
|
|
466
469
|
|
|
467
470
|
var index = /*#__PURE__*/Object.freeze({
|
|
468
471
|
__proto__: null,
|
|
469
|
-
|
|
470
|
-
createUnsubscription: createUnsubscription,
|
|
471
|
-
createSetResponse: createSetResponse,
|
|
472
|
-
createSet: createSet,
|
|
473
|
-
createReset: createReset,
|
|
474
|
-
createOptimistic: createOptimistic,
|
|
475
|
-
createMeta: createMeta,
|
|
476
|
-
createInvalidateAll: createInvalidateAll,
|
|
477
|
-
createInvalidate: createInvalidate,
|
|
472
|
+
createExpireAll: createExpireAll,
|
|
478
473
|
createFetch: createFetch,
|
|
479
|
-
|
|
474
|
+
createInvalidate: createInvalidate,
|
|
475
|
+
createInvalidateAll: createInvalidateAll,
|
|
476
|
+
createMeta: createMeta,
|
|
477
|
+
createOptimistic: createOptimistic,
|
|
478
|
+
createReset: createReset,
|
|
479
|
+
createSet: createSet,
|
|
480
|
+
createSetResponse: createSetResponse,
|
|
481
|
+
createSubscription: createSubscription,
|
|
482
|
+
createUnsubscription: createUnsubscription
|
|
480
483
|
});
|
|
481
484
|
|
|
485
|
+
class GCPolicy {
|
|
486
|
+
endpointCount = new Map();
|
|
487
|
+
entityCount = new Map();
|
|
488
|
+
endpointsQ = new Set();
|
|
489
|
+
entitiesQ = [];
|
|
490
|
+
constructor({
|
|
491
|
+
// every 5 min
|
|
492
|
+
intervalMS = 60 * 1000 * 5,
|
|
493
|
+
expiryMultiplier = 2,
|
|
494
|
+
expiresAt
|
|
495
|
+
} = {}) {
|
|
496
|
+
if (expiresAt) {
|
|
497
|
+
this.expiresAt = expiresAt.bind(this);
|
|
498
|
+
}
|
|
499
|
+
this.options = {
|
|
500
|
+
intervalMS,
|
|
501
|
+
expiryMultiplier
|
|
502
|
+
};
|
|
503
|
+
}
|
|
504
|
+
init(controller) {
|
|
505
|
+
this.controller = controller;
|
|
506
|
+
this.intervalId = setInterval(() => {
|
|
507
|
+
this.idleCallback(() => this.runSweep(), {
|
|
508
|
+
timeout: 1000
|
|
509
|
+
});
|
|
510
|
+
}, this.options.intervalMS);
|
|
511
|
+
}
|
|
512
|
+
cleanup() {
|
|
513
|
+
clearInterval(this.intervalId);
|
|
514
|
+
}
|
|
515
|
+
createCountRef({
|
|
516
|
+
key,
|
|
517
|
+
paths = []
|
|
518
|
+
}) {
|
|
519
|
+
// increment
|
|
520
|
+
return () => {
|
|
521
|
+
var _this$endpointCount$g;
|
|
522
|
+
if (key) this.endpointCount.set(key, ((_this$endpointCount$g = this.endpointCount.get(key)) != null ? _this$endpointCount$g : 0) + 1);
|
|
523
|
+
paths.forEach(path => {
|
|
524
|
+
var _instanceCount$get;
|
|
525
|
+
if (!this.entityCount.has(path.key)) {
|
|
526
|
+
this.entityCount.set(path.key, new Map());
|
|
527
|
+
}
|
|
528
|
+
const instanceCount = this.entityCount.get(path.key);
|
|
529
|
+
instanceCount.set(path.pk, ((_instanceCount$get = instanceCount.get(path.pk)) != null ? _instanceCount$get : 0) + 1);
|
|
530
|
+
});
|
|
531
|
+
|
|
532
|
+
// decrement
|
|
533
|
+
return () => {
|
|
534
|
+
if (key) {
|
|
535
|
+
const currentCount = this.endpointCount.get(key);
|
|
536
|
+
if (currentCount !== undefined) {
|
|
537
|
+
if (currentCount <= 1) {
|
|
538
|
+
this.endpointCount.delete(key);
|
|
539
|
+
// queue for cleanup
|
|
540
|
+
this.endpointsQ.add(key);
|
|
541
|
+
} else {
|
|
542
|
+
this.endpointCount.set(key, currentCount - 1);
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
paths.forEach(path => {
|
|
547
|
+
if (!this.entityCount.has(path.key)) {
|
|
548
|
+
return;
|
|
549
|
+
}
|
|
550
|
+
const instanceCount = this.entityCount.get(path.key);
|
|
551
|
+
const entityCount = instanceCount.get(path.pk);
|
|
552
|
+
if (entityCount !== undefined) {
|
|
553
|
+
if (entityCount <= 1) {
|
|
554
|
+
var _context;
|
|
555
|
+
instanceCount.delete(path.pk);
|
|
556
|
+
// queue for cleanup
|
|
557
|
+
_pushInstanceProperty(_context = this.entitiesQ).call(_context, path);
|
|
558
|
+
} else {
|
|
559
|
+
instanceCount.set(path.pk, entityCount - 1);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
});
|
|
563
|
+
};
|
|
564
|
+
};
|
|
565
|
+
}
|
|
566
|
+
expiresAt({
|
|
567
|
+
fetchedAt,
|
|
568
|
+
expiresAt
|
|
569
|
+
}) {
|
|
570
|
+
return Math.max((expiresAt - fetchedAt) * this.options.expiryMultiplier, 120000) + fetchedAt;
|
|
571
|
+
}
|
|
572
|
+
runSweep() {
|
|
573
|
+
const state = this.controller.getState();
|
|
574
|
+
const entities = [];
|
|
575
|
+
const endpoints = [];
|
|
576
|
+
const now = Date.now();
|
|
577
|
+
const nextEndpointsQ = new Set();
|
|
578
|
+
for (const key of this.endpointsQ) {
|
|
579
|
+
var _state$meta$key;
|
|
580
|
+
if (!this.endpointCount.has(key) && this.expiresAt((_state$meta$key = state.meta[key]) != null ? _state$meta$key : {
|
|
581
|
+
fetchedAt: 0,
|
|
582
|
+
date: 0,
|
|
583
|
+
expiresAt: 0
|
|
584
|
+
}) < now) {
|
|
585
|
+
_pushInstanceProperty(endpoints).call(endpoints, key);
|
|
586
|
+
} else {
|
|
587
|
+
nextEndpointsQ.add(key);
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
this.endpointsQ = nextEndpointsQ;
|
|
591
|
+
const nextEntitiesQ = [];
|
|
592
|
+
for (const path of this.entitiesQ) {
|
|
593
|
+
var _this$entityCount$get, _state$entityMeta$pat, _state$entityMeta$pat2;
|
|
594
|
+
if (!((_this$entityCount$get = this.entityCount.get(path.key)) != null && _this$entityCount$get.has(path.pk)) && this.expiresAt((_state$entityMeta$pat = (_state$entityMeta$pat2 = state.entityMeta[path.key]) == null ? void 0 : _state$entityMeta$pat2[path.pk]) != null ? _state$entityMeta$pat : {
|
|
595
|
+
fetchedAt: 0,
|
|
596
|
+
date: 0,
|
|
597
|
+
expiresAt: 0
|
|
598
|
+
}) < now) {
|
|
599
|
+
_pushInstanceProperty(entities).call(entities, path);
|
|
600
|
+
} else {
|
|
601
|
+
_pushInstanceProperty(nextEntitiesQ).call(nextEntitiesQ, path);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
this.entitiesQ = nextEntitiesQ;
|
|
605
|
+
if (entities.length || endpoints.length) {
|
|
606
|
+
this.controller.dispatch({
|
|
607
|
+
type: GC,
|
|
608
|
+
entities,
|
|
609
|
+
endpoints
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
/** Calls the callback when client is not 'busy' with high priority interaction tasks
|
|
615
|
+
*
|
|
616
|
+
* Override for platform-specific implementations
|
|
617
|
+
*/
|
|
618
|
+
idleCallback(callback, options) {
|
|
619
|
+
if (typeof requestIdleCallback === 'function') {
|
|
620
|
+
requestIdleCallback(callback, options);
|
|
621
|
+
} else {
|
|
622
|
+
callback();
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
class ImmortalGCPolicy {
|
|
627
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
628
|
+
init() {}
|
|
629
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
630
|
+
cleanup() {}
|
|
631
|
+
createCountRef() {
|
|
632
|
+
return () => () => undefined;
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
|
|
482
636
|
function selectMeta(state, fetchKey) {
|
|
483
637
|
return state.meta[fetchKey];
|
|
484
638
|
}
|
|
@@ -515,131 +669,104 @@ class Controller {
|
|
|
515
669
|
* Singleton to maintain referential equality between calls
|
|
516
670
|
*/
|
|
517
671
|
|
|
672
|
+
/**
|
|
673
|
+
* Handles garbage collection
|
|
674
|
+
*/
|
|
675
|
+
|
|
518
676
|
constructor({
|
|
519
677
|
dispatch = unsetDispatch,
|
|
520
678
|
getState = unsetState,
|
|
521
|
-
memo = new normalizr.MemoCache()
|
|
679
|
+
memo = new normalizr.MemoCache(),
|
|
680
|
+
gcPolicy = new ImmortalGCPolicy()
|
|
522
681
|
} = {}) {
|
|
523
|
-
|
|
524
|
-
/**
|
|
525
|
-
* Fetches the endpoint with given args, updating the Reactive Data Client cache with the response or error upon completion.
|
|
526
|
-
* @see https://dataclient.io/docs/api/Controller#fetch
|
|
527
|
-
*/
|
|
528
|
-
this.fetch = (endpoint, ...args) => {
|
|
529
|
-
const action = createFetch(endpoint, {
|
|
530
|
-
args
|
|
531
|
-
});
|
|
532
|
-
this.dispatch(action);
|
|
533
|
-
if (endpoint.schema) {
|
|
534
|
-
return action.meta.promise.then(input => normalizr.denormalize(endpoint.schema, input, {}, args));
|
|
535
|
-
}
|
|
536
|
-
return action.meta.promise;
|
|
537
|
-
};
|
|
538
|
-
/**
|
|
539
|
-
* Fetches only if endpoint is considered 'stale'; otherwise returns undefined
|
|
540
|
-
* @see https://dataclient.io/docs/api/Controller#fetchIfStale
|
|
541
|
-
*/
|
|
542
|
-
this.fetchIfStale = (endpoint, ...args) => {
|
|
543
|
-
const {
|
|
544
|
-
data,
|
|
545
|
-
expiresAt,
|
|
546
|
-
expiryStatus
|
|
547
|
-
} = this.getResponse(endpoint, ...args, this.getState());
|
|
548
|
-
if (expiryStatus !== normalizr.ExpiryStatus.Invalid && Date.now() <= expiresAt) return data;
|
|
549
|
-
return this.fetch(endpoint, ...args);
|
|
550
|
-
};
|
|
551
|
-
/**
|
|
552
|
-
* Forces refetching and suspense on useSuspense with the same Endpoint and parameters.
|
|
553
|
-
* @see https://dataclient.io/docs/api/Controller#invalidate
|
|
554
|
-
*/
|
|
555
|
-
this.invalidate = (endpoint, ...args) => args[0] !== null ? this.dispatch(createInvalidate(endpoint, {
|
|
556
|
-
args: args
|
|
557
|
-
})) : Promise.resolve();
|
|
558
|
-
/**
|
|
559
|
-
* Forces refetching and suspense on useSuspense on all matching endpoint result keys.
|
|
560
|
-
* @see https://dataclient.io/docs/api/Controller#invalidateAll
|
|
561
|
-
* @returns Promise that resolves when invalidation is commited.
|
|
562
|
-
*/
|
|
563
|
-
this.invalidateAll = options => this.dispatch(createInvalidateAll(key => options.testKey(key)));
|
|
564
|
-
/**
|
|
565
|
-
* Sets all matching endpoint result keys to be STALE.
|
|
566
|
-
* @see https://dataclient.io/docs/api/Controller#expireAll
|
|
567
|
-
* @returns Promise that resolves when expiry is commited. *NOT* fetch promise
|
|
568
|
-
*/
|
|
569
|
-
this.expireAll = options => this.dispatch(createExpireAll(key => options.testKey(key)));
|
|
570
|
-
/**
|
|
571
|
-
* Resets the entire Reactive Data Client cache. All inflight requests will not resolve.
|
|
572
|
-
* @see https://dataclient.io/docs/api/Controller#resetEntireStore
|
|
573
|
-
*/
|
|
574
|
-
this.resetEntireStore = () => this.dispatch(createReset());
|
|
575
|
-
/**
|
|
576
|
-
* Sets response for the Endpoint and args.
|
|
577
|
-
* @see https://dataclient.io/docs/api/Controller#setResponse
|
|
578
|
-
*/
|
|
579
|
-
this.setResponse = (endpoint, ...rest) => {
|
|
580
|
-
const response = rest[rest.length - 1];
|
|
581
|
-
const action = createSetResponse(endpoint, {
|
|
582
|
-
args: rest.slice(0, rest.length - 1),
|
|
583
|
-
response
|
|
584
|
-
});
|
|
585
|
-
return this.dispatch(action);
|
|
586
|
-
};
|
|
587
|
-
/**
|
|
588
|
-
* Sets an error response for the Endpoint and args.
|
|
589
|
-
* @see https://dataclient.io/docs/api/Controller#setError
|
|
590
|
-
*/
|
|
591
|
-
this.setError = (endpoint, ...rest) => {
|
|
592
|
-
const response = rest[rest.length - 1];
|
|
593
|
-
const action = createSetResponse(endpoint, {
|
|
594
|
-
args: rest.slice(0, rest.length - 1),
|
|
595
|
-
response,
|
|
596
|
-
error: true
|
|
597
|
-
});
|
|
598
|
-
return this.dispatch(action);
|
|
599
|
-
};
|
|
600
|
-
/**
|
|
601
|
-
* Resolves an inflight fetch.
|
|
602
|
-
* @see https://dataclient.io/docs/api/Controller#resolve
|
|
603
|
-
*/
|
|
604
|
-
this.resolve = (endpoint, meta) => {
|
|
605
|
-
return this.dispatch(createSetResponse(endpoint, meta));
|
|
606
|
-
};
|
|
607
|
-
/**
|
|
608
|
-
* Marks a new subscription to a given Endpoint.
|
|
609
|
-
* @see https://dataclient.io/docs/api/Controller#subscribe
|
|
610
|
-
*/
|
|
611
|
-
this.subscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createSubscription(endpoint, {
|
|
612
|
-
args: args
|
|
613
|
-
})) : Promise.resolve();
|
|
614
|
-
/**
|
|
615
|
-
* Marks completion of subscription to a given Endpoint.
|
|
616
|
-
* @see https://dataclient.io/docs/api/Controller#unsubscribe
|
|
617
|
-
*/
|
|
618
|
-
this.unsubscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createUnsubscription(endpoint, {
|
|
619
|
-
args: args
|
|
620
|
-
})) : Promise.resolve();
|
|
621
|
-
/*************** More ***************/
|
|
622
|
-
/* TODO:
|
|
623
|
-
abort = <E extends EndpointInterface>(
|
|
624
|
-
endpoint: E,
|
|
625
|
-
...args: readonly [...Parameters<E>]
|
|
626
|
-
): Promise<void>
|
|
627
|
-
*/
|
|
628
|
-
/**
|
|
629
|
-
* Gets a snapshot (https://dataclient.io/docs/api/Snapshot)
|
|
630
|
-
* @see https://dataclient.io/docs/api/Controller#snapshot
|
|
631
|
-
*/
|
|
632
|
-
this.snapshot = (state, fetchedAt) => {
|
|
633
|
-
return new Snapshot(this, state, fetchedAt);
|
|
634
|
-
};
|
|
635
|
-
this.dispatch = dispatch;
|
|
682
|
+
this._dispatch = dispatch;
|
|
636
683
|
this.getState = getState;
|
|
637
684
|
this.memo = memo;
|
|
685
|
+
this.gcPolicy = gcPolicy;
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
689
|
+
set dispatch(dispatch) {
|
|
690
|
+
/* istanbul ignore next */
|
|
691
|
+
this._dispatch = dispatch;
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// TODO: drop when drop support for destructuring (0.14 and below)
|
|
695
|
+
get dispatch() {
|
|
696
|
+
return this._dispatch;
|
|
638
697
|
}
|
|
698
|
+
bindMiddleware({
|
|
699
|
+
dispatch,
|
|
700
|
+
getState
|
|
701
|
+
}) {
|
|
702
|
+
this._dispatch = dispatch;
|
|
703
|
+
this.getState = getState;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
/*************** Action Dispatchers ***************/
|
|
707
|
+
|
|
708
|
+
/**
|
|
709
|
+
* Fetches the endpoint with given args, updating the Reactive Data Client cache with the response or error upon completion.
|
|
710
|
+
* @see https://dataclient.io/docs/api/Controller#fetch
|
|
711
|
+
*/
|
|
712
|
+
fetch = (endpoint, ...args) => {
|
|
713
|
+
const action = createFetch(endpoint, {
|
|
714
|
+
args
|
|
715
|
+
});
|
|
716
|
+
this.dispatch(action);
|
|
717
|
+
if (endpoint.schema) {
|
|
718
|
+
return action.meta.promise.then(input => normalizr.denormalize(endpoint.schema, input, {}, args));
|
|
719
|
+
}
|
|
720
|
+
return action.meta.promise;
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
/**
|
|
724
|
+
* Fetches only if endpoint is considered 'stale'; otherwise returns undefined
|
|
725
|
+
* @see https://dataclient.io/docs/api/Controller#fetchIfStale
|
|
726
|
+
*/
|
|
727
|
+
fetchIfStale = (endpoint, ...args) => {
|
|
728
|
+
const {
|
|
729
|
+
data,
|
|
730
|
+
expiresAt,
|
|
731
|
+
expiryStatus
|
|
732
|
+
} = this.getResponse(endpoint, ...args, this.getState());
|
|
733
|
+
if (expiryStatus !== normalizr.ExpiryStatus.Invalid && Date.now() <= expiresAt) return data;
|
|
734
|
+
return this.fetch(endpoint, ...args);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
/**
|
|
738
|
+
* Forces refetching and suspense on useSuspense with the same Endpoint and parameters.
|
|
739
|
+
* @see https://dataclient.io/docs/api/Controller#invalidate
|
|
740
|
+
*/
|
|
741
|
+
invalidate = (endpoint, ...args) => args[0] !== null ? this.dispatch(createInvalidate(endpoint, {
|
|
742
|
+
args: args
|
|
743
|
+
})) : Promise.resolve();
|
|
744
|
+
|
|
745
|
+
/**
|
|
746
|
+
* Forces refetching and suspense on useSuspense on all matching endpoint result keys.
|
|
747
|
+
* @see https://dataclient.io/docs/api/Controller#invalidateAll
|
|
748
|
+
* @returns Promise that resolves when invalidation is commited.
|
|
749
|
+
*/
|
|
750
|
+
invalidateAll = options => this.dispatch(createInvalidateAll(key => options.testKey(key)));
|
|
751
|
+
|
|
752
|
+
/**
|
|
753
|
+
* Sets all matching endpoint result keys to be STALE.
|
|
754
|
+
* @see https://dataclient.io/docs/api/Controller#expireAll
|
|
755
|
+
* @returns Promise that resolves when expiry is commited. *NOT* fetch promise
|
|
756
|
+
*/
|
|
757
|
+
expireAll = options => this.dispatch(createExpireAll(key => options.testKey(key)));
|
|
758
|
+
|
|
759
|
+
/**
|
|
760
|
+
* Resets the entire Reactive Data Client cache. All inflight requests will not resolve.
|
|
761
|
+
* @see https://dataclient.io/docs/api/Controller#resetEntireStore
|
|
762
|
+
*/
|
|
763
|
+
resetEntireStore = () => this.dispatch(createReset());
|
|
764
|
+
|
|
639
765
|
/**
|
|
640
766
|
* Sets value for the Queryable and args.
|
|
641
767
|
* @see https://dataclient.io/docs/api/Controller#set
|
|
642
768
|
*/
|
|
769
|
+
|
|
643
770
|
set(schema, ...rest) {
|
|
644
771
|
const value = rest[rest.length - 1];
|
|
645
772
|
const action = createSet(schema, {
|
|
@@ -649,10 +776,80 @@ class Controller {
|
|
|
649
776
|
// TODO: reject with error if this fails in reducer
|
|
650
777
|
return this.dispatch(action);
|
|
651
778
|
}
|
|
779
|
+
|
|
780
|
+
/**
|
|
781
|
+
* Sets response for the Endpoint and args.
|
|
782
|
+
* @see https://dataclient.io/docs/api/Controller#setResponse
|
|
783
|
+
*/
|
|
784
|
+
setResponse = (endpoint, ...rest) => {
|
|
785
|
+
const response = rest[rest.length - 1];
|
|
786
|
+
const action = createSetResponse(endpoint, {
|
|
787
|
+
args: rest.slice(0, rest.length - 1),
|
|
788
|
+
response
|
|
789
|
+
});
|
|
790
|
+
return this.dispatch(action);
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
* Sets an error response for the Endpoint and args.
|
|
795
|
+
* @see https://dataclient.io/docs/api/Controller#setError
|
|
796
|
+
*/
|
|
797
|
+
setError = (endpoint, ...rest) => {
|
|
798
|
+
const response = rest[rest.length - 1];
|
|
799
|
+
const action = createSetResponse(endpoint, {
|
|
800
|
+
args: rest.slice(0, rest.length - 1),
|
|
801
|
+
response,
|
|
802
|
+
error: true
|
|
803
|
+
});
|
|
804
|
+
return this.dispatch(action);
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* Resolves an inflight fetch.
|
|
809
|
+
* @see https://dataclient.io/docs/api/Controller#resolve
|
|
810
|
+
*/
|
|
811
|
+
resolve = (endpoint, meta) => {
|
|
812
|
+
return this.dispatch(createSetResponse(endpoint, meta));
|
|
813
|
+
};
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
* Marks a new subscription to a given Endpoint.
|
|
817
|
+
* @see https://dataclient.io/docs/api/Controller#subscribe
|
|
818
|
+
*/
|
|
819
|
+
subscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createSubscription(endpoint, {
|
|
820
|
+
args: args
|
|
821
|
+
})) : Promise.resolve();
|
|
822
|
+
|
|
823
|
+
/**
|
|
824
|
+
* Marks completion of subscription to a given Endpoint.
|
|
825
|
+
* @see https://dataclient.io/docs/api/Controller#unsubscribe
|
|
826
|
+
*/
|
|
827
|
+
unsubscribe = (endpoint, ...args) => args[0] !== null ? this.dispatch(createUnsubscription(endpoint, {
|
|
828
|
+
args: args
|
|
829
|
+
})) : Promise.resolve();
|
|
830
|
+
|
|
831
|
+
/*************** More ***************/
|
|
832
|
+
|
|
833
|
+
/* TODO:
|
|
834
|
+
abort = <E extends EndpointInterface>(
|
|
835
|
+
endpoint: E,
|
|
836
|
+
...args: readonly [...Parameters<E>]
|
|
837
|
+
): Promise<void>
|
|
838
|
+
*/
|
|
839
|
+
|
|
840
|
+
/**
|
|
841
|
+
* Gets a snapshot (https://dataclient.io/docs/api/Snapshot)
|
|
842
|
+
* @see https://dataclient.io/docs/api/Controller#snapshot
|
|
843
|
+
*/
|
|
844
|
+
snapshot = (state, fetchedAt) => {
|
|
845
|
+
return new Snapshot(this, state, fetchedAt);
|
|
846
|
+
};
|
|
847
|
+
|
|
652
848
|
/**
|
|
653
849
|
* Gets the error, if any, for a given endpoint. Returns undefined for no errors.
|
|
654
850
|
* @see https://dataclient.io/docs/api/Controller#getError
|
|
655
851
|
*/
|
|
852
|
+
|
|
656
853
|
getError(endpoint, ...rest) {
|
|
657
854
|
if (rest[0] === null) return;
|
|
658
855
|
const state = rest[rest.length - 1];
|
|
@@ -671,11 +868,21 @@ class Controller {
|
|
|
671
868
|
*/
|
|
672
869
|
|
|
673
870
|
getResponse(endpoint, ...rest) {
|
|
871
|
+
// TODO: breaking: only return data
|
|
872
|
+
return this.getResponseMeta(endpoint, ...rest);
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* Gets the (globally referentially stable) response for a given endpoint/args pair from state given.
|
|
877
|
+
* @see https://dataclient.io/docs/api/Controller#getResponseMeta
|
|
878
|
+
*/
|
|
879
|
+
|
|
880
|
+
getResponseMeta(endpoint, ...rest) {
|
|
674
881
|
const state = rest[rest.length - 1];
|
|
675
882
|
// this is typescript generics breaking
|
|
676
883
|
const args = rest.slice(0, rest.length - 1)
|
|
677
884
|
// handle FormData
|
|
678
|
-
.map(ensurePojo
|
|
885
|
+
.map(ensurePojo);
|
|
679
886
|
const isActive = args.length !== 1 || args[0] !== null;
|
|
680
887
|
const key = isActive ? endpoint.key(...args) : '';
|
|
681
888
|
const cacheEndpoints = isActive ? state.endpoints[key] : undefined;
|
|
@@ -692,22 +899,27 @@ class Controller {
|
|
|
692
899
|
return {
|
|
693
900
|
data: input,
|
|
694
901
|
expiryStatus: normalizr.ExpiryStatus.Valid,
|
|
695
|
-
expiresAt: Infinity
|
|
902
|
+
expiresAt: Infinity,
|
|
903
|
+
countRef: () => () => undefined
|
|
696
904
|
};
|
|
697
905
|
}
|
|
698
906
|
let isInvalid = false;
|
|
699
907
|
if (shouldQuery) {
|
|
700
908
|
isInvalid = !normalizr.validateQueryKey(input);
|
|
909
|
+
// endpoint without entities
|
|
701
910
|
} else if (!schema || !schemaHasEntity(schema)) {
|
|
702
911
|
return {
|
|
703
912
|
data: cacheEndpoints,
|
|
704
913
|
expiryStatus: meta != null && meta.invalidated ? normalizr.ExpiryStatus.Invalid : cacheEndpoints && !endpoint.invalidIfStale ? normalizr.ExpiryStatus.Valid : normalizr.ExpiryStatus.InvalidIfStale,
|
|
705
|
-
expiresAt: expiresAt || 0
|
|
914
|
+
expiresAt: expiresAt || 0,
|
|
915
|
+
countRef: this.gcPolicy.createCountRef({
|
|
916
|
+
key
|
|
917
|
+
})
|
|
706
918
|
};
|
|
707
919
|
}
|
|
708
920
|
|
|
709
921
|
// second argument is false if any entities are missing
|
|
710
|
-
|
|
922
|
+
|
|
711
923
|
const {
|
|
712
924
|
data,
|
|
713
925
|
paths
|
|
@@ -715,7 +927,7 @@ class Controller {
|
|
|
715
927
|
|
|
716
928
|
// note: isInvalid can only be true if shouldQuery is true
|
|
717
929
|
if (!expiresAt && isInvalid) expiresAt = 1;
|
|
718
|
-
return this.getSchemaResponse(data, paths, state.entityMeta, expiresAt, endpoint.invalidIfStale || isInvalid, meta);
|
|
930
|
+
return this.getSchemaResponse(data, key, paths, state.entityMeta, expiresAt, endpoint.invalidIfStale || isInvalid, meta);
|
|
719
931
|
}
|
|
720
932
|
|
|
721
933
|
/**
|
|
@@ -725,10 +937,40 @@ class Controller {
|
|
|
725
937
|
get(schema, ...rest) {
|
|
726
938
|
const state = rest[rest.length - 1];
|
|
727
939
|
// this is typescript generics breaking
|
|
728
|
-
const args = rest.slice(0, rest.length - 1).map(ensurePojo
|
|
940
|
+
const args = rest.slice(0, rest.length - 1).map(ensurePojo);
|
|
729
941
|
return this.memo.query(schema, args, state.entities, state.indexes);
|
|
730
942
|
}
|
|
731
|
-
|
|
943
|
+
|
|
944
|
+
/**
|
|
945
|
+
* Queries the store for a Querable schema; providing related metadata
|
|
946
|
+
* @see https://dataclient.io/docs/api/Controller#getQueryMeta
|
|
947
|
+
*/
|
|
948
|
+
getQueryMeta(schema, ...rest) {
|
|
949
|
+
const state = rest[rest.length - 1];
|
|
950
|
+
// this is typescript generics breaking
|
|
951
|
+
const args = rest.slice(0, rest.length - 1).map(ensurePojo);
|
|
952
|
+
|
|
953
|
+
// TODO: breaking: Switch back to this.memo.query(schema, args, state.entities as any, state.indexes) to do
|
|
954
|
+
// this logic
|
|
955
|
+
const input = this.memo.buildQueryKey(schema, args, state.entities, state.indexes, JSON.stringify(args));
|
|
956
|
+
if (!input) {
|
|
957
|
+
return {
|
|
958
|
+
data: undefined,
|
|
959
|
+
countRef: () => () => undefined
|
|
960
|
+
};
|
|
961
|
+
}
|
|
962
|
+
const {
|
|
963
|
+
data,
|
|
964
|
+
paths
|
|
965
|
+
} = this.memo.denormalize(schema, input, state.entities, args);
|
|
966
|
+
return {
|
|
967
|
+
data: typeof data === 'symbol' ? undefined : data,
|
|
968
|
+
countRef: this.gcPolicy.createCountRef({
|
|
969
|
+
paths
|
|
970
|
+
})
|
|
971
|
+
};
|
|
972
|
+
}
|
|
973
|
+
getSchemaResponse(data, key, paths, entityMeta, expiresAt, invalidIfStale, meta = {}) {
|
|
732
974
|
const invalidDenormalize = typeof data === 'symbol';
|
|
733
975
|
|
|
734
976
|
// fallback to entity expiry time
|
|
@@ -743,7 +985,11 @@ class Controller {
|
|
|
743
985
|
return {
|
|
744
986
|
data,
|
|
745
987
|
expiryStatus,
|
|
746
|
-
expiresAt
|
|
988
|
+
expiresAt,
|
|
989
|
+
countRef: this.gcPolicy.createCountRef({
|
|
990
|
+
key,
|
|
991
|
+
paths
|
|
992
|
+
})
|
|
747
993
|
};
|
|
748
994
|
}
|
|
749
995
|
}
|
|
@@ -781,11 +1027,12 @@ function schemaHasEntity(schema) {
|
|
|
781
1027
|
return false;
|
|
782
1028
|
}
|
|
783
1029
|
class Snapshot {
|
|
1030
|
+
static abort = new AbortOptimistic();
|
|
1031
|
+
state;
|
|
1032
|
+
controller;
|
|
1033
|
+
fetchedAt;
|
|
1034
|
+
abort = Snapshot.abort;
|
|
784
1035
|
constructor(controller, state, fetchedAt = 0) {
|
|
785
|
-
this.state = void 0;
|
|
786
|
-
this.controller = void 0;
|
|
787
|
-
this.fetchedAt = void 0;
|
|
788
|
-
this.abort = Snapshot.abort;
|
|
789
1036
|
this.state = state;
|
|
790
1037
|
this.controller = controller;
|
|
791
1038
|
this.fetchedAt = fetchedAt;
|
|
@@ -798,6 +1045,12 @@ class Snapshot {
|
|
|
798
1045
|
return this.controller.getResponse(endpoint, ...args, this.state);
|
|
799
1046
|
}
|
|
800
1047
|
|
|
1048
|
+
/** @see https://dataclient.io/docs/api/Snapshot#getResponseMeta */
|
|
1049
|
+
|
|
1050
|
+
getResponseMeta(endpoint, ...args) {
|
|
1051
|
+
return this.controller.getResponseMeta(endpoint, ...args, this.state);
|
|
1052
|
+
}
|
|
1053
|
+
|
|
801
1054
|
/** @see https://dataclient.io/docs/api/Snapshot#getError */
|
|
802
1055
|
|
|
803
1056
|
getError(endpoint, ...args) {
|
|
@@ -811,13 +1064,20 @@ class Snapshot {
|
|
|
811
1064
|
get(schema, ...args) {
|
|
812
1065
|
return this.controller.get(schema, ...args, this.state);
|
|
813
1066
|
}
|
|
1067
|
+
|
|
1068
|
+
/**
|
|
1069
|
+
* Queries the store for a Querable schema; providing related metadata
|
|
1070
|
+
* @see https://dataclient.io/docs/api/Snapshot#getQueryMeta
|
|
1071
|
+
*/
|
|
1072
|
+
getQueryMeta(schema, ...args) {
|
|
1073
|
+
return this.controller.getQueryMeta(schema, ...args, this.state);
|
|
1074
|
+
}
|
|
814
1075
|
}
|
|
815
|
-
Snapshot.abort = new AbortOptimistic();
|
|
816
1076
|
|
|
817
1077
|
class ResetError extends Error {
|
|
1078
|
+
name = 'ResetError';
|
|
818
1079
|
constructor() {
|
|
819
1080
|
super('Aborted due to RESET');
|
|
820
|
-
this.name = 'ResetError';
|
|
821
1081
|
}
|
|
822
1082
|
}
|
|
823
1083
|
|
|
@@ -831,70 +1091,71 @@ class ResetError extends Error {
|
|
|
831
1091
|
* @see https://dataclient.io/docs/api/NetworkManager
|
|
832
1092
|
*/
|
|
833
1093
|
class NetworkManager {
|
|
1094
|
+
fetched = Object.create(null);
|
|
1095
|
+
resolvers = {};
|
|
1096
|
+
rejectors = {};
|
|
1097
|
+
fetchedAt = {};
|
|
1098
|
+
controller = new Controller();
|
|
834
1099
|
constructor({
|
|
835
1100
|
dataExpiryLength = 60000,
|
|
836
1101
|
errorExpiryLength = 1000
|
|
837
1102
|
} = {}) {
|
|
838
|
-
this.
|
|
839
|
-
this.
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
this.controller =
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
case
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
1103
|
+
this.dataExpiryLength = dataExpiryLength;
|
|
1104
|
+
this.errorExpiryLength = errorExpiryLength;
|
|
1105
|
+
}
|
|
1106
|
+
middleware = controller => {
|
|
1107
|
+
this.controller = controller;
|
|
1108
|
+
return next => action => {
|
|
1109
|
+
switch (action.type) {
|
|
1110
|
+
case FETCH:
|
|
1111
|
+
this.handleFetch(action);
|
|
1112
|
+
// This is the only case that causes any state change
|
|
1113
|
+
// It's important to intercept other fetches as we don't want to trigger reducers during
|
|
1114
|
+
// render - so we need to stop 'readonly' fetches which can be triggered in render
|
|
1115
|
+
if (action.endpoint.getOptimisticResponse !== undefined && action.endpoint.sideEffect) {
|
|
1116
|
+
return next(action);
|
|
1117
|
+
}
|
|
1118
|
+
return Promise.resolve();
|
|
1119
|
+
case SET_RESPONSE:
|
|
1120
|
+
// only set after new state is computed
|
|
1121
|
+
return next(action).then(() => {
|
|
1122
|
+
if (action.key in this.fetched) {
|
|
1123
|
+
var _controller$getState$;
|
|
1124
|
+
// Note: meta *must* be set by reducer so this should be safe
|
|
1125
|
+
const error = (_controller$getState$ = controller.getState().meta[action.key]) == null ? void 0 : _controller$getState$.error;
|
|
1126
|
+
// processing errors result in state meta having error, so we should reject the promise
|
|
1127
|
+
if (error) {
|
|
1128
|
+
this.handleSet(createSetResponse(action.endpoint, {
|
|
1129
|
+
args: action.args,
|
|
1130
|
+
response: error,
|
|
1131
|
+
fetchedAt: action.meta.fetchedAt,
|
|
1132
|
+
error: true
|
|
1133
|
+
}));
|
|
1134
|
+
} else {
|
|
1135
|
+
this.handleSet(action);
|
|
1136
|
+
}
|
|
854
1137
|
}
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
1138
|
+
});
|
|
1139
|
+
case RESET:
|
|
1140
|
+
{
|
|
1141
|
+
const rejectors = {
|
|
1142
|
+
...this.rejectors
|
|
1143
|
+
};
|
|
1144
|
+
this.clearAll();
|
|
858
1145
|
return next(action).then(() => {
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
// processing errors result in state meta having error, so we should reject the promise
|
|
864
|
-
if (error) {
|
|
865
|
-
this.handleSet(createSetResponse(action.endpoint, {
|
|
866
|
-
args: action.args,
|
|
867
|
-
response: error,
|
|
868
|
-
fetchedAt: action.meta.fetchedAt,
|
|
869
|
-
error: true
|
|
870
|
-
}));
|
|
871
|
-
} else {
|
|
872
|
-
this.handleSet(action);
|
|
873
|
-
}
|
|
1146
|
+
// there could be external listeners to the promise
|
|
1147
|
+
// this must happen after commit so our own rejector knows not to dispatch an error based on this
|
|
1148
|
+
for (const k in rejectors) {
|
|
1149
|
+
rejectors[k](new ResetError());
|
|
874
1150
|
}
|
|
875
1151
|
});
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
};
|
|
881
|
-
this.clearAll();
|
|
882
|
-
return next(action).then(() => {
|
|
883
|
-
// there could be external listeners to the promise
|
|
884
|
-
// this must happen after commit so our own rejector knows not to dispatch an error based on this
|
|
885
|
-
for (const k in rejectors) {
|
|
886
|
-
rejectors[k](new ResetError());
|
|
887
|
-
}
|
|
888
|
-
});
|
|
889
|
-
}
|
|
890
|
-
default:
|
|
891
|
-
return next(action);
|
|
892
|
-
}
|
|
893
|
-
};
|
|
1152
|
+
}
|
|
1153
|
+
default:
|
|
1154
|
+
return next(action);
|
|
1155
|
+
}
|
|
894
1156
|
};
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
}
|
|
1157
|
+
};
|
|
1158
|
+
|
|
898
1159
|
/** On mount */
|
|
899
1160
|
init() {
|
|
900
1161
|
delete this.cleanupDate;
|
|
@@ -1076,13 +1337,9 @@ function applyManager(managers, controller) {
|
|
|
1076
1337
|
}
|
|
1077
1338
|
return managers.map((manager, i) => {
|
|
1078
1339
|
if (!manager.middleware) manager.middleware = manager.getMiddleware == null ? void 0 : manager.getMiddleware();
|
|
1079
|
-
return
|
|
1080
|
-
dispatch,
|
|
1081
|
-
getState
|
|
1082
|
-
}) => {
|
|
1340
|
+
return api => {
|
|
1083
1341
|
if (i === 0) {
|
|
1084
|
-
controller.
|
|
1085
|
-
controller.getState = getState;
|
|
1342
|
+
controller.bindMiddleware(api);
|
|
1086
1343
|
}
|
|
1087
1344
|
// controller is a superset of the middleware API
|
|
1088
1345
|
return manager.middleware(controller);
|
|
@@ -1094,6 +1351,21 @@ function applyManager(managers, controller) {
|
|
|
1094
1351
|
|
|
1095
1352
|
/* The next are types from React; but we don't want dependencies on it */
|
|
1096
1353
|
|
|
1354
|
+
function initManager(managers, controller, initialState) {
|
|
1355
|
+
return () => {
|
|
1356
|
+
managers.forEach(manager => {
|
|
1357
|
+
manager.init == null || manager.init(initialState);
|
|
1358
|
+
});
|
|
1359
|
+
controller.gcPolicy.init(controller);
|
|
1360
|
+
return () => {
|
|
1361
|
+
managers.forEach(manager => {
|
|
1362
|
+
manager.cleanup();
|
|
1363
|
+
});
|
|
1364
|
+
controller.gcPolicy.cleanup();
|
|
1365
|
+
};
|
|
1366
|
+
};
|
|
1367
|
+
}
|
|
1368
|
+
|
|
1097
1369
|
class BrowserConnectionListener {
|
|
1098
1370
|
isOnline() {
|
|
1099
1371
|
if (navigator.onLine !== undefined) {
|
|
@@ -1142,29 +1414,8 @@ var DefaultConnectionListener$1 = DefaultConnectionListener;
|
|
|
1142
1414
|
* @see https://dataclient.io/docs/api/PollingSubscription
|
|
1143
1415
|
*/
|
|
1144
1416
|
class PollingSubscription {
|
|
1417
|
+
frequencyHistogram = new Map();
|
|
1145
1418
|
constructor(action, controller, connectionListener) {
|
|
1146
|
-
this.frequencyHistogram = new Map();
|
|
1147
|
-
/** What happens when browser goes offline */
|
|
1148
|
-
this.offlineListener = () => {
|
|
1149
|
-
// this clears existing listeners, so no need to clear offline listener
|
|
1150
|
-
this.cleanup();
|
|
1151
|
-
this.connectionListener.addOnlineListener(this.onlineListener);
|
|
1152
|
-
};
|
|
1153
|
-
/** What happens when browser comes online */
|
|
1154
|
-
this.onlineListener = () => {
|
|
1155
|
-
this.connectionListener.removeOnlineListener(this.onlineListener);
|
|
1156
|
-
const now = Date.now();
|
|
1157
|
-
this.startId = setTimeout(() => {
|
|
1158
|
-
if (this.startId) {
|
|
1159
|
-
delete this.startId;
|
|
1160
|
-
this.update();
|
|
1161
|
-
this.run();
|
|
1162
|
-
} else if (process.env.NODE_ENV !== 'production') {
|
|
1163
|
-
console.warn(`Poll setTimeout for ${this.key} still running, but timeoutId deleted`);
|
|
1164
|
-
}
|
|
1165
|
-
}, Math.max(0, this.lastFetchTime() - now + this.frequency));
|
|
1166
|
-
this.connectionListener.addOfflineListener(this.offlineListener);
|
|
1167
|
-
};
|
|
1168
1419
|
if (action.endpoint.pollFrequency === undefined) throw new Error('frequency needed for polling subscription');
|
|
1169
1420
|
this.endpoint = action.endpoint;
|
|
1170
1421
|
this.frequency = action.endpoint.pollFrequency;
|
|
@@ -1256,6 +1507,30 @@ class PollingSubscription {
|
|
|
1256
1507
|
// stop any errors here from bubbling
|
|
1257
1508
|
this.controller.fetch(endpoint, ...this.args).catch(() => null);
|
|
1258
1509
|
}
|
|
1510
|
+
|
|
1511
|
+
/** What happens when browser goes offline */
|
|
1512
|
+
offlineListener = () => {
|
|
1513
|
+
// this clears existing listeners, so no need to clear offline listener
|
|
1514
|
+
this.cleanup();
|
|
1515
|
+
this.connectionListener.addOnlineListener(this.onlineListener);
|
|
1516
|
+
};
|
|
1517
|
+
|
|
1518
|
+
/** What happens when browser comes online */
|
|
1519
|
+
onlineListener = () => {
|
|
1520
|
+
this.connectionListener.removeOnlineListener(this.onlineListener);
|
|
1521
|
+
const now = Date.now();
|
|
1522
|
+
this.startId = setTimeout(() => {
|
|
1523
|
+
if (this.startId) {
|
|
1524
|
+
delete this.startId;
|
|
1525
|
+
this.update();
|
|
1526
|
+
this.run();
|
|
1527
|
+
} else if (process.env.NODE_ENV !== 'production') {
|
|
1528
|
+
console.warn(`Poll setTimeout for ${this.key} still running, but timeoutId deleted`);
|
|
1529
|
+
}
|
|
1530
|
+
}, Math.max(0, this.lastFetchTime() - now + this.frequency));
|
|
1531
|
+
this.connectionListener.addOfflineListener(this.offlineListener);
|
|
1532
|
+
};
|
|
1533
|
+
|
|
1259
1534
|
/** Run polling process with current frequency
|
|
1260
1535
|
*
|
|
1261
1536
|
* Will clean up old poll interval on next run
|
|
@@ -1295,30 +1570,31 @@ class PollingSubscription {
|
|
|
1295
1570
|
* @see https://dataclient.io/docs/api/SubscriptionManager
|
|
1296
1571
|
*/
|
|
1297
1572
|
class SubscriptionManager {
|
|
1573
|
+
subscriptions = {};
|
|
1574
|
+
controller = new Controller();
|
|
1298
1575
|
constructor(Subscription) {
|
|
1299
|
-
this.subscriptions = {};
|
|
1300
|
-
this.controller = new Controller();
|
|
1301
|
-
this.middleware = controller => {
|
|
1302
|
-
this.controller = controller;
|
|
1303
|
-
return next => action => {
|
|
1304
|
-
switch (action.type) {
|
|
1305
|
-
case SUBSCRIBE:
|
|
1306
|
-
try {
|
|
1307
|
-
this.handleSubscribe(action);
|
|
1308
|
-
} catch (e) {
|
|
1309
|
-
console.error(e);
|
|
1310
|
-
}
|
|
1311
|
-
return Promise.resolve();
|
|
1312
|
-
case UNSUBSCRIBE:
|
|
1313
|
-
this.handleUnsubscribe(action);
|
|
1314
|
-
return Promise.resolve();
|
|
1315
|
-
default:
|
|
1316
|
-
return next(action);
|
|
1317
|
-
}
|
|
1318
|
-
};
|
|
1319
|
-
};
|
|
1320
1576
|
this.Subscription = Subscription;
|
|
1321
1577
|
}
|
|
1578
|
+
middleware = controller => {
|
|
1579
|
+
this.controller = controller;
|
|
1580
|
+
return next => action => {
|
|
1581
|
+
switch (action.type) {
|
|
1582
|
+
case SUBSCRIBE:
|
|
1583
|
+
try {
|
|
1584
|
+
this.handleSubscribe(action);
|
|
1585
|
+
} catch (e) {
|
|
1586
|
+
console.error(e);
|
|
1587
|
+
}
|
|
1588
|
+
return Promise.resolve();
|
|
1589
|
+
case UNSUBSCRIBE:
|
|
1590
|
+
this.handleUnsubscribe(action);
|
|
1591
|
+
return Promise.resolve();
|
|
1592
|
+
default:
|
|
1593
|
+
return next(action);
|
|
1594
|
+
}
|
|
1595
|
+
};
|
|
1596
|
+
};
|
|
1597
|
+
|
|
1322
1598
|
/** Ensures all subscriptions are cleaned up. */
|
|
1323
1599
|
cleanup() {
|
|
1324
1600
|
for (const key in this.subscriptions) {
|
|
@@ -1449,11 +1725,12 @@ class DevToolsManager {
|
|
|
1449
1725
|
if (this.started) {
|
|
1450
1726
|
this.devTools.send(action, state);
|
|
1451
1727
|
} else {
|
|
1728
|
+
var _context;
|
|
1452
1729
|
// avoid this getting too big in case this is long running
|
|
1453
1730
|
// we cut in half so we aren't constantly reallocating
|
|
1454
1731
|
if (this.actions.length > this.maxBufferLength) this.actions = this.actions.slice(this.maxBufferLength / 2);
|
|
1455
1732
|
// queue actions
|
|
1456
|
-
this.actions.
|
|
1733
|
+
_pushInstanceProperty(_context = this.actions).call(_context, [action, state]);
|
|
1457
1734
|
}
|
|
1458
1735
|
}
|
|
1459
1736
|
|
|
@@ -1529,15 +1806,15 @@ class LogoutManager {
|
|
|
1529
1806
|
handleLogout,
|
|
1530
1807
|
shouldLogout
|
|
1531
1808
|
} = {}) {
|
|
1532
|
-
this.middleware = controller => next => async action => {
|
|
1533
|
-
await next(action);
|
|
1534
|
-
if (action.type === SET_RESPONSE && action.error && this.shouldLogout(action.response)) {
|
|
1535
|
-
this.handleLogout(controller);
|
|
1536
|
-
}
|
|
1537
|
-
};
|
|
1538
1809
|
if (handleLogout) this.handleLogout = handleLogout;
|
|
1539
1810
|
if (shouldLogout) this.shouldLogout = shouldLogout;
|
|
1540
1811
|
}
|
|
1812
|
+
middleware = controller => next => async action => {
|
|
1813
|
+
await next(action);
|
|
1814
|
+
if (action.type === SET_RESPONSE && action.error && this.shouldLogout(action.response)) {
|
|
1815
|
+
this.handleLogout(controller);
|
|
1816
|
+
}
|
|
1817
|
+
};
|
|
1541
1818
|
cleanup() {}
|
|
1542
1819
|
shouldLogout(error) {
|
|
1543
1820
|
// 401 indicates reauthorization is needed
|
|
@@ -1548,17 +1825,15 @@ class LogoutManager {
|
|
|
1548
1825
|
}
|
|
1549
1826
|
}
|
|
1550
1827
|
|
|
1551
|
-
Object.
|
|
1552
|
-
return Object.prototype.hasOwnProperty.call(it, key);
|
|
1553
|
-
};
|
|
1554
|
-
|
|
1555
|
-
Object.defineProperty(exports, 'ExpiryStatus', {
|
|
1828
|
+
Object.defineProperty(exports, "ExpiryStatus", {
|
|
1556
1829
|
enumerable: true,
|
|
1557
1830
|
get: function () { return normalizr.ExpiryStatus; }
|
|
1558
1831
|
});
|
|
1559
1832
|
exports.Controller = Controller;
|
|
1560
1833
|
exports.DefaultConnectionListener = DefaultConnectionListener$1;
|
|
1561
1834
|
exports.DevToolsManager = DevToolsManager;
|
|
1835
|
+
exports.GCPolicy = GCPolicy;
|
|
1836
|
+
exports.ImmortalGCPolicy = ImmortalGCPolicy;
|
|
1562
1837
|
exports.LogoutManager = LogoutManager;
|
|
1563
1838
|
exports.NetworkManager = NetworkManager;
|
|
1564
1839
|
exports.PollingSubscription = PollingSubscription;
|
|
@@ -1569,4 +1844,5 @@ exports.actionTypes = actionTypes;
|
|
|
1569
1844
|
exports.actions = index;
|
|
1570
1845
|
exports.applyManager = applyManager;
|
|
1571
1846
|
exports.createReducer = createReducer;
|
|
1847
|
+
exports.initManager = initManager;
|
|
1572
1848
|
exports.initialState = initialState;
|