@data-client/core 0.14.2 → 0.14.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/dist/index.js +73 -101
  2. package/dist/index.umd.min.js +1 -1
  3. package/legacy/index.js +1 -1
  4. package/legacy/manager/DevtoolsManager.js +50 -52
  5. package/legacy/manager/LogoutManager.js +3 -8
  6. package/legacy/manager/NetworkManager.js +13 -22
  7. package/legacy/manager/SubscriptionManager.js +2 -15
  8. package/legacy/manager/applyManager.js +5 -3
  9. package/legacy/middlewareTypes.js +1 -1
  10. package/legacy/state/reducer/createReducer.js +2 -2
  11. package/legacy/state/reducer/setReducer.js +3 -3
  12. package/legacy/state/reducer/setResponseReducer.js +3 -3
  13. package/legacy/types.js +13 -1
  14. package/lib/index.d.ts +1 -1
  15. package/lib/index.d.ts.map +1 -1
  16. package/lib/index.js +1 -1
  17. package/lib/manager/DevtoolsManager.d.ts +4 -7
  18. package/lib/manager/DevtoolsManager.d.ts.map +1 -1
  19. package/lib/manager/DevtoolsManager.js +50 -52
  20. package/lib/manager/LogoutManager.d.ts +5 -8
  21. package/lib/manager/LogoutManager.d.ts.map +1 -1
  22. package/lib/manager/LogoutManager.js +3 -8
  23. package/lib/manager/NetworkManager.d.ts +7 -12
  24. package/lib/manager/NetworkManager.d.ts.map +1 -1
  25. package/lib/manager/NetworkManager.js +13 -22
  26. package/lib/manager/SubscriptionManager.d.ts +2 -11
  27. package/lib/manager/SubscriptionManager.d.ts.map +1 -1
  28. package/lib/manager/SubscriptionManager.js +2 -15
  29. package/lib/manager/applyManager.d.ts +8 -5
  30. package/lib/manager/applyManager.d.ts.map +1 -1
  31. package/lib/manager/applyManager.js +5 -3
  32. package/lib/middlewareTypes.d.ts +6 -10
  33. package/lib/middlewareTypes.d.ts.map +1 -1
  34. package/lib/middlewareTypes.js +1 -1
  35. package/lib/state/reducer/createReducer.js +2 -2
  36. package/lib/state/reducer/expireReducer.d.ts +1 -1
  37. package/lib/state/reducer/setReducer.d.ts +4 -4
  38. package/lib/state/reducer/setReducer.js +3 -3
  39. package/lib/state/reducer/setResponseReducer.d.ts +4 -4
  40. package/lib/state/reducer/setResponseReducer.js +3 -3
  41. package/lib/types.d.ts +17 -2
  42. package/lib/types.d.ts.map +1 -1
  43. package/lib/types.js +13 -1
  44. package/package.json +1 -1
  45. package/src/index.ts +1 -0
  46. package/src/manager/DevtoolsManager.ts +37 -40
  47. package/src/manager/LogoutManager.ts +15 -27
  48. package/src/manager/NetworkManager.ts +58 -71
  49. package/src/manager/SubscriptionManager.ts +21 -34
  50. package/src/manager/__tests__/applyManager.ts +61 -1
  51. package/src/manager/__tests__/logoutManager.ts +8 -10
  52. package/src/manager/__tests__/manager.ts +2 -3
  53. package/src/manager/__tests__/networkManager.ts +16 -19
  54. package/src/manager/__tests__/subscriptionManager.ts +14 -16
  55. package/src/manager/applyManager.ts +20 -8
  56. package/src/middlewareTypes.ts +7 -17
  57. package/src/state/reducer/createReducer.ts +1 -1
  58. package/src/state/reducer/setReducer.ts +2 -2
  59. package/src/state/reducer/setResponseReducer.ts +2 -2
  60. package/src/types.ts +17 -2
  61. package/ts3.4/index.d.ts +1 -1
  62. package/ts3.4/manager/DevtoolsManager.d.ts +3 -6
  63. package/ts3.4/manager/LogoutManager.d.ts +4 -7
  64. package/ts3.4/manager/NetworkManager.d.ts +7 -12
  65. package/ts3.4/manager/SubscriptionManager.d.ts +2 -11
  66. package/ts3.4/manager/applyManager.d.ts +8 -5
  67. package/ts3.4/middlewareTypes.d.ts +5 -9
  68. package/ts3.4/state/reducer/expireReducer.d.ts +1 -1
  69. package/ts3.4/state/reducer/setReducer.d.ts +4 -4
  70. package/ts3.4/state/reducer/setResponseReducer.d.ts +4 -4
  71. package/ts3.4/types.d.ts +17 -2
@@ -34,76 +34,68 @@ export default class NetworkManager implements Manager {
34
34
  protected fetchedAt: { [k: string]: number } = {};
35
35
  declare readonly dataExpiryLength: number;
36
36
  declare readonly errorExpiryLength: number;
37
- protected declare middleware: Middleware;
38
37
  protected controller: Controller = new Controller();
39
38
  declare cleanupDate?: number;
40
39
 
41
- constructor(dataExpiryLength = 60000, errorExpiryLength = 1000) {
40
+ constructor({ dataExpiryLength = 60000, errorExpiryLength = 1000 } = {}) {
42
41
  this.dataExpiryLength = dataExpiryLength;
43
42
  this.errorExpiryLength = errorExpiryLength;
43
+ }
44
44
 
45
- this.middleware = <C extends MiddlewareAPI>(controller: C) => {
46
- this.controller = controller;
47
- return (next: C['dispatch']): C['dispatch'] =>
48
- (action): Promise<void> => {
49
- switch (action.type) {
50
- case FETCH_TYPE:
51
- this.handleFetch(action);
52
- // This is the only case that causes any state change
53
- // It's important to intercept other fetches as we don't want to trigger reducers during
54
- // render - so we need to stop 'readonly' fetches which can be triggered in render
55
- if (
56
- action.endpoint.getOptimisticResponse !== undefined &&
57
- action.endpoint.sideEffect
58
- ) {
59
- return next(action);
45
+ middleware: Middleware = controller => {
46
+ this.controller = controller;
47
+ return next => action => {
48
+ switch (action.type) {
49
+ case FETCH_TYPE:
50
+ this.handleFetch(action);
51
+ // This is the only case that causes any state change
52
+ // It's important to intercept other fetches as we don't want to trigger reducers during
53
+ // render - so we need to stop 'readonly' fetches which can be triggered in render
54
+ if (
55
+ action.endpoint.getOptimisticResponse !== undefined &&
56
+ action.endpoint.sideEffect
57
+ ) {
58
+ return next(action);
59
+ }
60
+ return Promise.resolve();
61
+ case SET_RESPONSE_TYPE:
62
+ // only set after new state is computed
63
+ return next(action).then(() => {
64
+ if (action.key in this.fetched) {
65
+ // Note: meta *must* be set by reducer so this should be safe
66
+ const error = controller.getState().meta[action.key]?.error;
67
+ // processing errors result in state meta having error, so we should reject the promise
68
+ if (error) {
69
+ this.handleSet(
70
+ createSetResponse(action.endpoint, {
71
+ args: action.args,
72
+ response: error,
73
+ fetchedAt: action.meta.fetchedAt,
74
+ error: true,
75
+ }),
76
+ );
77
+ } else {
78
+ this.handleSet(action);
60
79
  }
61
- return Promise.resolve();
62
- case SET_RESPONSE_TYPE:
63
- // only set after new state is computed
64
- return next(action).then(() => {
65
- if (action.key in this.fetched) {
66
- // Note: meta *must* be set by reducer so this should be safe
67
- const error = controller.getState().meta[action.key]?.error;
68
- // processing errors result in state meta having error, so we should reject the promise
69
- if (error) {
70
- this.handleSet(
71
- createSetResponse(action.endpoint, {
72
- args: action.args,
73
- response: error,
74
- fetchedAt: action.meta.fetchedAt,
75
- error: true,
76
- }),
77
- );
78
- } else {
79
- this.handleSet(action);
80
- }
81
- }
82
- });
83
- case RESET_TYPE: {
84
- const rejectors = { ...this.rejectors };
80
+ }
81
+ });
82
+ case RESET_TYPE: {
83
+ const rejectors = { ...this.rejectors };
85
84
 
86
- this.clearAll();
87
- return next(action).then(() => {
88
- // there could be external listeners to the promise
89
- // this must happen after commit so our own rejector knows not to dispatch an error based on this
90
- for (const k in rejectors) {
91
- rejectors[k](new ResetError());
92
- }
93
- });
85
+ this.clearAll();
86
+ return next(action).then(() => {
87
+ // there could be external listeners to the promise
88
+ // this must happen after commit so our own rejector knows not to dispatch an error based on this
89
+ for (const k in rejectors) {
90
+ rejectors[k](new ResetError());
94
91
  }
95
- default:
96
- return next(action);
97
- }
98
- };
92
+ });
93
+ }
94
+ default:
95
+ return next(action);
96
+ }
99
97
  };
100
- }
101
-
102
- /** Used by DevtoolsManager to determine whether to log an action */
103
- skipLogging(action: ActionTypes) {
104
- /* istanbul ignore next */
105
- return action.type === FETCH_TYPE && action.key in this.fetched;
106
- }
98
+ };
107
99
 
108
100
  /** On mount */
109
101
  init() {
@@ -117,6 +109,12 @@ export default class NetworkManager implements Manager {
117
109
  this.cleanupDate = Date.now();
118
110
  }
119
111
 
112
+ /** Used by DevtoolsManager to determine whether to log an action */
113
+ skipLogging(action: ActionTypes) {
114
+ /* istanbul ignore next */
115
+ return action.type === FETCH_TYPE && action.key in this.fetched;
116
+ }
117
+
120
118
  allSettled() {
121
119
  const fetches = Object.values(this.fetched);
122
120
  if (fetches.length) return Promise.allSettled(fetches);
@@ -241,17 +239,6 @@ export default class NetworkManager implements Manager {
241
239
  }
242
240
  }
243
241
 
244
- /** Attaches NetworkManager to store
245
- *
246
- * Intercepts 'rdc/fetch' actions to start requests.
247
- *
248
- * Resolve/rejects a request when matching 'rdc/set' event
249
- * is seen.
250
- */
251
- getMiddleware() {
252
- return this.middleware;
253
- }
254
-
255
242
  /** Ensures only one request for a given key is in flight at any time
256
243
  *
257
244
  * Uses key to either retrieve in-flight promise, or if not
@@ -32,41 +32,40 @@ export interface SubscriptionConstructable {
32
32
  *
33
33
  * @see https://dataclient.io/docs/api/SubscriptionManager
34
34
  */
35
- export default class SubscriptionManager<S extends SubscriptionConstructable>
36
- implements Manager<Actions>
35
+ export default class SubscriptionManager<
36
+ S extends SubscriptionConstructable = SubscriptionConstructable,
37
+ > implements Manager<Actions>
37
38
  {
38
39
  protected subscriptions: {
39
40
  [key: string]: InstanceType<S>;
40
41
  } = {};
41
42
 
42
43
  protected declare readonly Subscription: S;
43
- protected declare middleware: Middleware;
44
44
  protected controller: Controller = new Controller();
45
45
 
46
46
  constructor(Subscription: S) {
47
47
  this.Subscription = Subscription;
48
+ }
48
49
 
49
- this.middleware = <C extends MiddlewareAPI>(controller: C) => {
50
- this.controller = controller;
51
- return (next: C['dispatch']): C['dispatch'] =>
52
- action => {
53
- switch (action.type) {
54
- case SUBSCRIBE_TYPE:
55
- try {
56
- this.handleSubscribe(action);
57
- } catch (e) {
58
- console.error(e);
59
- }
60
- return Promise.resolve();
61
- case UNSUBSCRIBE_TYPE:
62
- this.handleUnsubscribe(action);
63
- return Promise.resolve();
64
- default:
65
- return next(action);
50
+ middleware: Middleware = controller => {
51
+ this.controller = controller;
52
+ return next => action => {
53
+ switch (action.type) {
54
+ case SUBSCRIBE_TYPE:
55
+ try {
56
+ this.handleSubscribe(action);
57
+ } catch (e) {
58
+ console.error(e);
66
59
  }
67
- };
60
+ return Promise.resolve();
61
+ case UNSUBSCRIBE_TYPE:
62
+ this.handleUnsubscribe(action);
63
+ return Promise.resolve();
64
+ default:
65
+ return next(action);
66
+ }
68
67
  };
69
- }
68
+ };
70
69
 
71
70
  /** Ensures all subscriptions are cleaned up. */
72
71
  cleanup() {
@@ -109,16 +108,4 @@ export default class SubscriptionManager<S extends SubscriptionConstructable>
109
108
  console.error(`Mismatched unsubscribe: ${key} is not subscribed`);
110
109
  }
111
110
  }
112
-
113
- /** Attaches Manager to store
114
- *
115
- * Intercepts 'rdc/subscribe'/'rest-hordc/ribe' to register resources that
116
- * need to be kept up to date.
117
- *
118
- * Will possibly dispatch 'rdc/fetch' or 'rest-hordc/' to keep resources fresh
119
- *
120
- */
121
- getMiddleware() {
122
- return this.middleware;
123
- }
124
111
  }
@@ -1,5 +1,13 @@
1
+ import { Article } from '__tests__/new';
2
+
3
+ import { createSet } from '../../controller/actions';
1
4
  import Controller from '../../controller/Controller';
2
- import applyManager from '../applyManager';
5
+ import { Dispatch, Middleware } from '../../middlewareTypes';
6
+ import { Manager } from '../../types';
7
+ import applyManager, {
8
+ ReduxMiddleware,
9
+ ReduxMiddlewareAPI,
10
+ } from '../applyManager';
3
11
  import NetworkManager from '../NetworkManager';
4
12
 
5
13
  function onError(e: any) {
@@ -36,3 +44,55 @@ it('applyManagers should not console.warn() when NetworkManager is provided', ()
36
44
  warnspy.mockRestore();
37
45
  }
38
46
  });
47
+ it('applyManagers should handle legacy Manager.getMiddleware()', () => {
48
+ let initCount = 0;
49
+ let actionCount = 0;
50
+ class MyManager implements Manager {
51
+ getMiddleware = (): Middleware => ctrl => {
52
+ initCount++;
53
+ return next => action => {
54
+ actionCount++;
55
+ return next(action);
56
+ };
57
+ };
58
+
59
+ cleanup() {}
60
+ }
61
+ const middlewares = applyManager(
62
+ [new MyManager(), new NetworkManager()],
63
+ new Controller(),
64
+ );
65
+
66
+ const rootDispatch = jest.fn((action: any) => {
67
+ return Promise.resolve();
68
+ });
69
+
70
+ const dispatch = middlewareDispatch(middlewares, rootDispatch);
71
+
72
+ expect(initCount).toBe(1);
73
+ expect(actionCount).toBe(0);
74
+ expect(rootDispatch.mock.calls.length).toBe(0);
75
+ dispatch(
76
+ createSet(Article, { args: [{ id: 1 }], value: { id: 1, title: 'hi' } }),
77
+ );
78
+ expect(initCount).toBe(1);
79
+ expect(actionCount).toBe(1);
80
+ expect(rootDispatch.mock.calls.length).toBe(1);
81
+ });
82
+
83
+ function middlewareDispatch(
84
+ middlewares: ReduxMiddleware[],
85
+ rootDispatch: Dispatch,
86
+ ) {
87
+ const middlewareAPI: ReduxMiddlewareAPI = {
88
+ getState: () => ({}),
89
+ dispatch: action => rootDispatch(action),
90
+ };
91
+ const comp = compose(
92
+ middlewares.map(middleware => middleware(middlewareAPI)),
93
+ );
94
+ const dispatch = comp(rootDispatch);
95
+ return dispatch;
96
+ }
97
+ const compose = (fns: ((...args: any) => any)[]) => (initial: any) =>
98
+ fns.reduceRight((v, f) => f(v), initial);
@@ -21,11 +21,10 @@ describe('LogoutManager', () => {
21
21
  const manager = new LogoutManager();
22
22
  const getState = () => initialState;
23
23
 
24
- describe('getMiddleware()', () => {
24
+ describe('middleware', () => {
25
25
  it('should return the same value every call', () => {
26
- const a = manager.getMiddleware();
27
- expect(a).toBe(manager.getMiddleware());
28
- expect(a).toBe(manager.getMiddleware());
26
+ const a = manager.middleware;
27
+ expect(a).toBe(manager.middleware);
29
28
  });
30
29
  });
31
30
 
@@ -33,7 +32,6 @@ describe('LogoutManager', () => {
33
32
  afterEach(() => {
34
33
  jest.useRealTimers();
35
34
  });
36
- const middleware = manager.getMiddleware();
37
35
  const next = jest.fn();
38
36
  const dispatch = jest.fn(action => Promise.resolve());
39
37
  const controller = new Controller({ dispatch, getState });
@@ -48,7 +46,7 @@ describe('LogoutManager', () => {
48
46
  args: [{ id: 5 }],
49
47
  response: { id: 5, title: 'hi' },
50
48
  });
51
- await middleware(API)(next)(action);
49
+ await manager.middleware(API)(next)(action);
52
50
 
53
51
  expect(dispatch.mock.calls.length).toBe(0);
54
52
  });
@@ -60,7 +58,7 @@ describe('LogoutManager', () => {
60
58
  response: error,
61
59
  error: true,
62
60
  });
63
- await middleware(API)(next)(action);
61
+ await manager.middleware(API)(next)(action);
64
62
 
65
63
  expect(dispatch.mock.calls.length).toBe(0);
66
64
  });
@@ -72,7 +70,7 @@ describe('LogoutManager', () => {
72
70
  response: error,
73
71
  error: true,
74
72
  });
75
- await middleware(API)(next)(action);
73
+ await manager.middleware(API)(next)(action);
76
74
 
77
75
  expect(dispatch.mock.calls.length).toBe(1);
78
76
  expect(dispatch.mock.calls[0][0]?.type).toBe(RESET_TYPE);
@@ -85,7 +83,7 @@ describe('LogoutManager', () => {
85
83
  return error.status === 403;
86
84
  },
87
85
  handleLogout,
88
- }).getMiddleware();
86
+ }).middleware;
89
87
  const error: any = new Error('network failed');
90
88
  error.status = 403;
91
89
  const action = createSetResponse(CoolerArticleResource.get, {
@@ -102,7 +100,7 @@ describe('LogoutManager', () => {
102
100
  const action = { type: FETCH_TYPE };
103
101
  next.mockReset();
104
102
 
105
- await middleware(API)(next)(action as any);
103
+ await manager.middleware(API)(next)(action as any);
106
104
 
107
105
  expect(next.mock.calls.length).toBe(1);
108
106
  });
@@ -1,9 +1,8 @@
1
1
  import Controller from '../../controller/Controller';
2
- import { Middleware } from '../../middlewareTypes';
3
2
  import { ActionTypes } from '../../types';
4
3
  import NetworkManager from '../NetworkManager';
5
4
 
6
- const middleware: Middleware = new NetworkManager().getMiddleware();
5
+ const netMgr = new NetworkManager();
7
6
  it('middlewares should compose with non-data-client middlewares', () => {
8
7
  type AnotherAction = {
9
8
  type: 'BOB';
@@ -30,7 +29,7 @@ it('middlewares should compose with non-data-client middlewares', () => {
30
29
  counter++;
31
30
  };
32
31
 
33
- const [a, b] = [middleware(API), nonRHMiddleware(API)];
32
+ const [a, b] = [netMgr.middleware(API), nonRHMiddleware(API)];
34
33
  const dispA = a(b(dispatch));
35
34
  const dispB = b(a(dispatch));
36
35
  expect(dispatch.mock.calls.length).toBe(0);
@@ -33,18 +33,17 @@ describe('NetworkManager', () => {
33
33
  expect(hacked.getHacked()).toEqual(initialState);
34
34
  });
35
35
 
36
- describe('getMiddleware()', () => {
36
+ describe('middleware', () => {
37
37
  it('should return the same value every call', () => {
38
- const a = manager.getMiddleware();
39
- expect(a).toBe(manager.getMiddleware());
40
- expect(a).toBe(manager.getMiddleware());
38
+ const a = manager.middleware;
39
+ expect(a).toBe(manager.middleware);
41
40
  });
42
41
  it('should return the different value for a different instance', () => {
43
- const a = manager.getMiddleware();
42
+ const a = manager.middleware;
44
43
  const manager2 = new NetworkManager();
45
- const a2 = manager2.getMiddleware();
44
+ const a2 = manager2.middleware;
46
45
  expect(a).not.toBe(a2);
47
- expect(a2).toBe(manager2.getMiddleware());
46
+ expect(a2).toBe(manager2.middleware);
48
47
  manager2.cleanup();
49
48
  });
50
49
  });
@@ -132,10 +131,8 @@ describe('NetworkManager', () => {
132
131
  (fetchRejectAction.meta.promise as any).catch((e: unknown) => {});
133
132
 
134
133
  let NM: NetworkManager;
135
- let middleware: Middleware;
136
134
  beforeEach(() => {
137
- NM = new NetworkManager(42, 7);
138
- middleware = NM.getMiddleware();
135
+ NM = new NetworkManager({ dataExpiryLength: 42, errorExpiryLength: 7 });
139
136
  });
140
137
  afterEach(() => {
141
138
  NM.cleanup();
@@ -152,7 +149,7 @@ describe('NetworkManager', () => {
152
149
  },
153
150
  );
154
151
 
155
- middleware(API)(next)(fetchResolveAction);
152
+ NM.middleware(API)(next)(fetchResolveAction);
156
153
 
157
154
  const response = await fetchResolveAction.endpoint(
158
155
  ...fetchResolveAction.args,
@@ -188,7 +185,7 @@ describe('NetworkManager', () => {
188
185
  },
189
186
  );
190
187
 
191
- middleware(API)(next)(fetchSetWithUpdatersAction);
188
+ NM.middleware(API)(next)(fetchSetWithUpdatersAction);
192
189
 
193
190
  const response = await fetchSetWithUpdatersAction.endpoint(
194
191
  ...fetchSetWithUpdatersAction.args,
@@ -224,7 +221,7 @@ describe('NetworkManager', () => {
224
221
  },
225
222
  );
226
223
 
227
- middleware(API)(next)(fetchRpcWithUpdatersAction);
224
+ NM.middleware(API)(next)(fetchRpcWithUpdatersAction);
228
225
 
229
226
  const response = await fetchRpcWithUpdatersAction.endpoint(
230
227
  ...fetchRpcWithUpdatersAction.args,
@@ -260,7 +257,7 @@ describe('NetworkManager', () => {
260
257
  },
261
258
  );
262
259
 
263
- middleware(API)(next)(fetchRpcWithUpdatersAndOptimisticAction);
260
+ NM.middleware(API)(next)(fetchRpcWithUpdatersAndOptimisticAction);
264
261
 
265
262
  const response = await fetchRpcWithUpdatersAndOptimisticAction.endpoint(
266
263
  ...fetchRpcWithUpdatersAndOptimisticAction.args,
@@ -293,7 +290,7 @@ describe('NetworkManager', () => {
293
290
  },
294
291
  );
295
292
 
296
- middleware(API)(() => Promise.resolve())({
293
+ NM.middleware(API)(() => Promise.resolve())({
297
294
  ...fetchResolveAction,
298
295
  endpoint: detailEndpoint.extend({ dataExpiryLength: 314 }),
299
296
  });
@@ -314,7 +311,7 @@ describe('NetworkManager', () => {
314
311
  },
315
312
  );
316
313
 
317
- middleware(API)(() => Promise.resolve())({
314
+ NM.middleware(API)(() => Promise.resolve())({
318
315
  ...fetchResolveAction,
319
316
  endpoint: detailEndpoint.extend({ dataExpiryLength: undefined }),
320
317
  });
@@ -337,7 +334,7 @@ describe('NetworkManager', () => {
337
334
  );
338
335
 
339
336
  try {
340
- await middleware(API)(next)(fetchRejectAction);
337
+ await NM.middleware(API)(next)(fetchRejectAction);
341
338
  } catch (error) {
342
339
  expect(next).not.toHaveBeenCalled();
343
340
  expect(dispatch).toHaveBeenCalledWith({
@@ -363,7 +360,7 @@ describe('NetworkManager', () => {
363
360
  );
364
361
 
365
362
  try {
366
- await middleware(API)(() => Promise.resolve())({
363
+ await NM.middleware(API)(() => Promise.resolve())({
367
364
  ...fetchRejectAction,
368
365
  meta: {
369
366
  ...fetchRejectAction.meta,
@@ -386,7 +383,7 @@ describe('NetworkManager', () => {
386
383
  );
387
384
 
388
385
  try {
389
- await middleware(API)(() => Promise.resolve())({
386
+ await NM.middleware(API)(() => Promise.resolve())({
390
387
  ...fetchRejectAction,
391
388
  meta: {
392
389
  ...fetchRejectAction.meta,
@@ -27,11 +27,10 @@ describe('SubscriptionManager', () => {
27
27
  const manager = new SubscriptionManager(TestSubscription);
28
28
  const getState = () => initialState;
29
29
 
30
- describe('getMiddleware()', () => {
30
+ describe('middleware', () => {
31
31
  it('should return the same value every call', () => {
32
- const a = manager.getMiddleware();
33
- expect(a).toBe(manager.getMiddleware());
34
- expect(a).toBe(manager.getMiddleware());
32
+ const a = manager.middleware;
33
+ expect(a).toBe(manager.middleware);
35
34
  });
36
35
  });
37
36
 
@@ -74,7 +73,6 @@ describe('SubscriptionManager', () => {
74
73
  }
75
74
 
76
75
  const manager = new SubscriptionManager(TestSubscription);
77
- const middleware = manager.getMiddleware();
78
76
  const next = jest.fn();
79
77
  const dispatch = () => Promise.resolve();
80
78
  const controller = new Controller({ dispatch, getState });
@@ -86,14 +84,14 @@ describe('SubscriptionManager', () => {
86
84
  );
87
85
  it('subscribe should add a subscription', () => {
88
86
  const action = createSubscribeAction({ id: 5 });
89
- middleware(API)(next)(action);
87
+ manager.middleware(API)(next)(action);
90
88
 
91
89
  expect(next).not.toHaveBeenCalled();
92
90
  expect((manager as any).subscriptions[action.key]).toBeDefined();
93
91
  });
94
92
  it('subscribe should add a subscription (no frequency)', () => {
95
93
  const action = createSubscribeAction({ id: 597 });
96
- middleware(API)(next)(action);
94
+ manager.middleware(API)(next)(action);
97
95
 
98
96
  expect(next).not.toHaveBeenCalled();
99
97
  expect((manager as any).subscriptions[action.key]).toBeDefined();
@@ -101,19 +99,19 @@ describe('SubscriptionManager', () => {
101
99
 
102
100
  it('subscribe with same should call subscription.add', () => {
103
101
  const action = createSubscribeAction({ id: 5, title: 'four' });
104
- middleware(API)(next)(action);
102
+ manager.middleware(API)(next)(action);
105
103
 
106
104
  expect(
107
105
  (manager as any).subscriptions[action.key].add.mock.calls.length,
108
106
  ).toBe(1);
109
- middleware(API)(next)(action);
107
+ manager.middleware(API)(next)(action);
110
108
  expect(
111
109
  (manager as any).subscriptions[action.key].add.mock.calls.length,
112
110
  ).toBe(2);
113
111
  });
114
112
  it('subscribe with another should create another', () => {
115
113
  const action = createSubscribeAction({ id: 7, title: 'four cakes' });
116
- middleware(API)(next)(action);
114
+ manager.middleware(API)(next)(action);
117
115
 
118
116
  expect((manager as any).subscriptions[action.key]).toBeDefined();
119
117
  expect(
@@ -134,13 +132,13 @@ describe('SubscriptionManager', () => {
134
132
  () => true,
135
133
  );
136
134
 
137
- middleware(API)(next)(action);
135
+ manager.middleware(API)(next)(action);
138
136
 
139
137
  expect((manager as any).subscriptions[action.key]).not.toBeDefined();
140
138
  });
141
139
 
142
140
  it('unsubscribe should delete when remove returns true (no frequency)', () => {
143
- middleware(API)(next)(
141
+ manager.middleware(API)(next)(
144
142
  createSubscribeAction({ id: 50, title: 'four cakes' }),
145
143
  );
146
144
 
@@ -149,7 +147,7 @@ describe('SubscriptionManager', () => {
149
147
  () => true,
150
148
  );
151
149
 
152
- middleware(API)(next)(action);
150
+ manager.middleware(API)(next)(action);
153
151
 
154
152
  expect((manager as any).subscriptions[action.key]).not.toBeDefined();
155
153
  });
@@ -160,7 +158,7 @@ describe('SubscriptionManager', () => {
160
158
  () => false,
161
159
  );
162
160
 
163
- middleware(API)(next)(action);
161
+ manager.middleware(API)(next)(action);
164
162
 
165
163
  expect((manager as any).subscriptions[action.key]).toBeDefined();
166
164
  expect(
@@ -174,7 +172,7 @@ describe('SubscriptionManager', () => {
174
172
 
175
173
  const action = createUnsubscribeAction({ id: 25 });
176
174
 
177
- middleware(API)(next)(action);
175
+ manager.middleware(API)(next)(action);
178
176
 
179
177
  expect((manager as any).subscriptions[action.key]).not.toBeDefined();
180
178
 
@@ -190,7 +188,7 @@ describe('SubscriptionManager', () => {
190
188
  const action = { type: SET_RESPONSE_TYPE };
191
189
  next.mockReset();
192
190
 
193
- middleware(API)(next)(action as any);
191
+ manager.middleware(API)(next)(action as any);
194
192
 
195
193
  expect(next.mock.calls.length).toBe(1);
196
194
  });
@@ -1,12 +1,11 @@
1
1
  import NetworkManager from './NetworkManager.js';
2
2
  import type Controller from '../controller/Controller.js';
3
- import type { Reducer, Dispatch, ReducerState } from '../middlewareTypes.js';
4
3
  import { Manager } from '../types.js';
5
4
 
6
5
  export default function applyManager(
7
6
  managers: Manager[],
8
7
  controller: Controller,
9
- ): Middleware[] {
8
+ ): ReduxMiddleware[] {
10
9
  /* istanbul ignore next */
11
10
  if (
12
11
  process.env.NODE_ENV !== 'production' &&
@@ -18,25 +17,38 @@ export default function applyManager(
18
17
  );
19
18
  }
20
19
  return managers.map((manager, i) => {
21
- const middleware = manager.getMiddleware();
20
+ if (!manager.middleware) manager.middleware = manager.getMiddleware?.();
22
21
  return ({ dispatch, getState }) => {
23
22
  if (i === 0) {
24
23
  (controller as any).dispatch = dispatch;
25
24
  (controller as any).getState = getState;
26
25
  }
27
26
  // controller is a superset of the middleware API
28
- return middleware(controller as Controller<any>);
27
+ return (manager as Manager & { middleware: ReduxMiddleware }).middleware(
28
+ controller as Controller<any>,
29
+ );
29
30
  };
30
31
  });
31
32
  }
32
33
 
33
34
  /* These should be compatible with redux */
34
- export interface MiddlewareAPI<
35
+ export interface ReduxMiddlewareAPI<
35
36
  R extends Reducer<any, any> = Reducer<any, any>,
36
37
  > {
37
38
  getState: () => ReducerState<R>;
38
- dispatch: Dispatch<R>;
39
+ dispatch: ReactDispatch<R>;
39
40
  }
40
- export type Middleware = <R extends Reducer<any, any>>({
41
+ export type ReduxMiddleware = <R extends Reducer<any, any>>({
41
42
  dispatch,
42
- }: MiddlewareAPI<R>) => (next: Dispatch<R>) => Dispatch<R>;
43
+ }: ReduxMiddlewareAPI<R>) => (next: ReactDispatch<R>) => ReactDispatch<R>;
44
+
45
+ /* The next are types from React; but we don't want dependencies on it */
46
+ export type ReactDispatch<R extends Reducer<any, any>> = (
47
+ action: ReducerAction<R>,
48
+ ) => Promise<void>;
49
+
50
+ export type Reducer<S, A> = (prevState: S, action: A) => S;
51
+ export type ReducerState<R extends Reducer<any, any>> =
52
+ R extends Reducer<infer S, any> ? S : never;
53
+ export type ReducerAction<R extends Reducer<any, any>> =
54
+ R extends Reducer<any, infer A> ? A : never;