@data-client/core 0.14.18 → 0.14.20

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