@data-client/core 0.14.18 → 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.
Files changed (83) hide show
  1. package/dist/index.js +553 -277
  2. package/dist/index.umd.min.js +1 -1
  3. package/legacy/actions.js +1 -1
  4. package/legacy/controller/Controller.js +100 -11
  5. package/legacy/controller/actions/createFetch.js +3 -2
  6. package/legacy/controller/ensurePojo.js +3 -2
  7. package/legacy/index.js +3 -4
  8. package/legacy/manager/DevtoolsManager.js +14 -7
  9. package/legacy/manager/NetworkManager.js +8 -5
  10. package/legacy/manager/PollingSubscription.js +6 -3
  11. package/legacy/manager/SubscriptionManager.js +4 -3
  12. package/legacy/manager/applyManager.js +3 -7
  13. package/legacy/manager/initManager.js +15 -0
  14. package/legacy/state/GCPolicy.js +153 -0
  15. package/legacy/state/reducer/createReducer.js +7 -3
  16. package/legacy/state/reducer/expireReducer.js +5 -4
  17. package/legacy/state/reducer/fetchReducer.js +3 -2
  18. package/legacy/state/reducer/invalidateReducer.js +6 -5
  19. package/legacy/state/reducer/setResponseReducer.js +10 -7
  20. package/legacy/types.js +1 -1
  21. package/lib/actions.d.ts +2 -2
  22. package/lib/actions.d.ts.map +1 -1
  23. package/lib/actions.js +1 -1
  24. package/lib/controller/Controller.d.ts +108 -5
  25. package/lib/controller/Controller.d.ts.map +1 -1
  26. package/lib/controller/Controller.js +96 -8
  27. package/lib/index.d.ts +2 -0
  28. package/lib/index.d.ts.map +1 -1
  29. package/lib/index.js +3 -4
  30. package/lib/manager/DevtoolsManager.js +7 -3
  31. package/lib/manager/NetworkManager.js +3 -2
  32. package/lib/manager/SubscriptionManager.d.ts.map +1 -1
  33. package/lib/manager/SubscriptionManager.js +1 -1
  34. package/lib/manager/applyManager.d.ts.map +1 -1
  35. package/lib/manager/applyManager.js +3 -7
  36. package/lib/manager/initManager.d.ts +4 -0
  37. package/lib/manager/initManager.d.ts.map +1 -0
  38. package/lib/manager/initManager.js +15 -0
  39. package/lib/state/GCPolicy.d.ts +55 -0
  40. package/lib/state/GCPolicy.d.ts.map +1 -0
  41. package/lib/state/GCPolicy.js +153 -0
  42. package/lib/state/reducer/createReducer.js +5 -2
  43. package/lib/state/reducer/expireReducer.d.ts +1 -0
  44. package/lib/state/reducer/expireReducer.d.ts.map +1 -1
  45. package/lib/state/reducer/invalidateReducer.d.ts +1 -0
  46. package/lib/state/reducer/invalidateReducer.d.ts.map +1 -1
  47. package/lib/state/reducer/setReducer.d.ts +3 -2
  48. package/lib/state/reducer/setReducer.d.ts.map +1 -1
  49. package/lib/state/reducer/setResponseReducer.d.ts +4 -2
  50. package/lib/state/reducer/setResponseReducer.d.ts.map +1 -1
  51. package/lib/state/reducer/setResponseReducer.js +4 -2
  52. package/lib/types.d.ts +1 -0
  53. package/lib/types.d.ts.map +1 -1
  54. package/lib/types.js +1 -1
  55. package/package.json +8 -3
  56. package/src/actions.ts +2 -1
  57. package/src/controller/Controller.ts +205 -8
  58. package/src/controller/__tests__/Controller.ts +8 -4
  59. package/src/controller/__tests__/__snapshots__/get.ts.snap +7 -0
  60. package/src/controller/__tests__/__snapshots__/getResponse.ts.snap +15 -0
  61. package/src/controller/__tests__/get.ts +45 -17
  62. package/src/controller/__tests__/getResponse.ts +46 -0
  63. package/src/index.ts +2 -6
  64. package/src/manager/SubscriptionManager.ts +0 -1
  65. package/src/manager/applyManager.ts +3 -4
  66. package/src/manager/initManager.ts +21 -0
  67. package/src/state/GCPolicy.ts +197 -0
  68. package/src/state/__tests__/GCPolicy.test.ts +258 -0
  69. package/src/state/__tests__/__snapshots__/reducer.ts.snap +2 -0
  70. package/src/state/__tests__/reducer.ts +4 -4
  71. package/src/state/reducer/createReducer.ts +1 -1
  72. package/src/state/reducer/setResponseReducer.ts +3 -1
  73. package/src/types.ts +1 -0
  74. package/ts3.4/actions.d.ts +2 -5
  75. package/ts3.4/controller/Controller.d.ts +141 -5
  76. package/ts3.4/index.d.ts +2 -0
  77. package/ts3.4/manager/initManager.d.ts +4 -0
  78. package/ts3.4/state/GCPolicy.d.ts +55 -0
  79. package/ts3.4/state/reducer/expireReducer.d.ts +1 -0
  80. package/ts3.4/state/reducer/invalidateReducer.d.ts +1 -0
  81. package/ts3.4/state/reducer/setReducer.d.ts +3 -2
  82. package/ts3.4/state/reducer/setResponseReducer.d.ts +4 -2
  83. 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
- SET: SET,
62
- SET_RESPONSE: SET_RESPONSE,
63
- OPTIMISTIC: OPTIMISTIC,
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
- EXPIREALL: EXPIREALL,
70
- GC: GC,
71
- FETCH_TYPE: FETCH_TYPE,
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
- UNSUBSCRIBE_TYPE: UNSUBSCRIBE_TYPE,
78
- INVALIDATE_TYPE: INVALIDATE_TYPE,
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
- error,
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(([key, pk]) => {
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
 
@@ -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$1),
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$1),
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
- createSubscription: createSubscription,
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
- createExpireAll: createExpireAll
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
- /*************** Action Dispatchers ***************/
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;
697
+ }
698
+ bindMiddleware({
699
+ dispatch,
700
+ getState
701
+ }) {
702
+ this._dispatch = dispatch;
703
+ this.getState = getState;
638
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$1);
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,17 +899,22 @@ 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
 
@@ -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$1);
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
- getSchemaResponse(data, paths, entityMeta, expiresAt, invalidIfStale, meta = {}) {
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.fetched = Object.create(null);
839
- this.resolvers = {};
840
- this.rejectors = {};
841
- this.fetchedAt = {};
842
- this.controller = new Controller();
843
- this.middleware = controller => {
844
- this.controller = controller;
845
- return next => action => {
846
- switch (action.type) {
847
- case FETCH:
848
- this.handleFetch(action);
849
- // This is the only case that causes any state change
850
- // It's important to intercept other fetches as we don't want to trigger reducers during
851
- // render - so we need to stop 'readonly' fetches which can be triggered in render
852
- if (action.endpoint.getOptimisticResponse !== undefined && action.endpoint.sideEffect) {
853
- return next(action);
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
- return Promise.resolve();
856
- case SET_RESPONSE:
857
- // only set after new state is computed
1138
+ });
1139
+ case RESET:
1140
+ {
1141
+ const rejectors = {
1142
+ ...this.rejectors
1143
+ };
1144
+ this.clearAll();
858
1145
  return next(action).then(() => {
859
- if (action.key in this.fetched) {
860
- var _controller$getState$;
861
- // Note: meta *must* be set by reducer so this should be safe
862
- const error = (_controller$getState$ = controller.getState().meta[action.key]) == null ? void 0 : _controller$getState$.error;
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
- case RESET:
877
- {
878
- const rejectors = {
879
- ...this.rejectors
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
- this.dataExpiryLength = dataExpiryLength;
896
- this.errorExpiryLength = errorExpiryLength;
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.dispatch = dispatch;
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.push([action, state]);
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.hasOwn = Object.hasOwn || /* istanbul ignore next */function hasOwn(it, key) {
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;