@data-client/core 0.14.24 → 0.15.0-beta-20251006024044-92bd01c4976f2921993b8c9f1e4dbb87af87ba7b

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 (59) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +100 -97
  3. package/dist/index.umd.min.js +1 -1
  4. package/legacy/controller/Controller.js +31 -46
  5. package/legacy/index.js +1 -1
  6. package/legacy/manager/NetworkManager.js +44 -33
  7. package/legacy/state/GCPolicy.js +20 -12
  8. package/legacy/state/reducer/createReducer.js +4 -4
  9. package/legacy/state/reducer/setReducer.js +3 -3
  10. package/legacy/state/reducer/setResponseReducer.js +4 -4
  11. package/legacy/types.js +1 -1
  12. package/lib/controller/Controller.d.ts +3 -3
  13. package/lib/controller/Controller.d.ts.map +1 -1
  14. package/lib/controller/Controller.js +31 -46
  15. package/lib/index.d.ts +2 -1
  16. package/lib/index.d.ts.map +1 -1
  17. package/lib/index.js +1 -1
  18. package/lib/manager/NetworkManager.d.ts +7 -12
  19. package/lib/manager/NetworkManager.d.ts.map +1 -1
  20. package/lib/manager/NetworkManager.js +44 -34
  21. package/lib/state/GCPolicy.d.ts.map +1 -1
  22. package/lib/state/GCPolicy.js +20 -12
  23. package/lib/state/reducer/createReducer.js +4 -4
  24. package/lib/state/reducer/expireReducer.d.ts +1 -1
  25. package/lib/state/reducer/invalidateReducer.d.ts +1 -1
  26. package/lib/state/reducer/setReducer.d.ts +1 -1
  27. package/lib/state/reducer/setReducer.js +3 -3
  28. package/lib/state/reducer/setResponseReducer.d.ts +1 -1
  29. package/lib/state/reducer/setResponseReducer.js +4 -4
  30. package/lib/types.d.ts +1 -1
  31. package/lib/types.d.ts.map +1 -1
  32. package/lib/types.js +1 -1
  33. package/package.json +3 -3
  34. package/src/controller/Controller.ts +38 -80
  35. package/src/controller/__tests__/Controller.ts +2 -2
  36. package/src/controller/__tests__/__snapshots__/get.ts.snap +34 -8
  37. package/src/controller/__tests__/__snapshots__/getResponse.ts.snap +1 -1
  38. package/src/controller/__tests__/get.ts +163 -22
  39. package/src/controller/__tests__/getResponse.ts +1 -1
  40. package/src/index.ts +4 -0
  41. package/src/manager/NetworkManager.ts +46 -32
  42. package/src/manager/__tests__/__snapshots__/pollingSubscription.ts.snap +1 -1
  43. package/src/manager/__tests__/pollingSubscription.ts +9 -5
  44. package/src/state/GCPolicy.ts +12 -10
  45. package/src/state/__tests__/GCPolicy.test.ts +6 -6
  46. package/src/state/__tests__/__snapshots__/reducer.ts.snap +3 -3
  47. package/src/state/__tests__/reducer.ts +17 -9
  48. package/src/state/reducer/createReducer.ts +2 -2
  49. package/src/state/reducer/setReducer.ts +2 -2
  50. package/src/state/reducer/setResponseReducer.ts +3 -3
  51. package/src/types.ts +1 -1
  52. package/ts3.4/controller/Controller.d.ts +3 -3
  53. package/ts3.4/index.d.ts +2 -1
  54. package/ts3.4/manager/NetworkManager.d.ts +7 -12
  55. package/ts3.4/state/reducer/expireReducer.d.ts +1 -1
  56. package/ts3.4/state/reducer/invalidateReducer.d.ts +1 -1
  57. package/ts3.4/state/reducer/setReducer.d.ts +1 -1
  58. package/ts3.4/state/reducer/setResponseReducer.d.ts +1 -1
  59. package/ts3.4/types.d.ts +1 -1
@@ -5,7 +5,6 @@ import type {
5
5
  FetchAction,
6
6
  Manager,
7
7
  ActionTypes,
8
- MiddlewareAPI,
9
8
  Middleware,
10
9
  SetResponseAction,
11
10
  } from '../types.js';
@@ -18,6 +17,13 @@ export class ResetError extends Error {
18
17
  }
19
18
  }
20
19
 
20
+ export interface FetchingMeta {
21
+ promise: Promise<any>;
22
+ resolve: (value?: any) => void;
23
+ reject: (value?: any) => void;
24
+ fetchedAt: number;
25
+ }
26
+
21
27
  /** Handles all async network dispatches
22
28
  *
23
29
  * Dedupes concurrent requests by keeping track of all fetches in flight
@@ -28,10 +34,7 @@ export class ResetError extends Error {
28
34
  * @see https://dataclient.io/docs/api/NetworkManager
29
35
  */
30
36
  export default class NetworkManager implements Manager {
31
- protected fetched: { [k: string]: Promise<any> } = Object.create(null);
32
- protected resolvers: { [k: string]: (value?: any) => void } = {};
33
- protected rejectors: { [k: string]: (value?: any) => void } = {};
34
- protected fetchedAt: { [k: string]: number } = {};
37
+ protected fetching: Map<string, FetchingMeta> = new Map();
35
38
  declare readonly dataExpiryLength: number;
36
39
  declare readonly errorExpiryLength: number;
37
40
  protected controller: Controller = new Controller();
@@ -61,7 +64,7 @@ export default class NetworkManager implements Manager {
61
64
  case SET_RESPONSE:
62
65
  // only set after new state is computed
63
66
  return next(action).then(() => {
64
- if (action.key in this.fetched) {
67
+ if (this.fetching.has(action.key)) {
65
68
  // Note: meta *must* be set by reducer so this should be safe
66
69
  const error = controller.getState().meta[action.key]?.error;
67
70
  // processing errors result in state meta having error, so we should reject the promise
@@ -80,14 +83,16 @@ export default class NetworkManager implements Manager {
80
83
  }
81
84
  });
82
85
  case RESET: {
83
- const rejectors = { ...this.rejectors };
86
+ // take snapshot of rejectors at this point in time
87
+ // we must use Array.from since iteration does not freeze state at this point in time
88
+ const fetches = Array.from(this.fetching.values());
84
89
 
85
90
  this.clearAll();
86
91
  return next(action).then(() => {
87
92
  // there could be external listeners to the promise
88
93
  // 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
+ for (const { reject } of fetches) {
95
+ reject(new ResetError());
91
96
  }
92
97
  });
93
98
  }
@@ -112,28 +117,29 @@ export default class NetworkManager implements Manager {
112
117
  /** Used by DevtoolsManager to determine whether to log an action */
113
118
  skipLogging(action: ActionTypes) {
114
119
  /* istanbul ignore next */
115
- return action.type === FETCH && action.key in this.fetched;
120
+ return action.type === FETCH && this.fetching.has(action.key);
116
121
  }
117
122
 
118
123
  allSettled() {
119
- const fetches = Object.values(this.fetched);
120
- if (fetches.length) return Promise.allSettled(fetches);
124
+ if (this.fetching.size)
125
+ return Promise.allSettled(
126
+ this.fetching.values().map(({ promise }) => promise),
127
+ );
121
128
  }
122
129
 
123
130
  /** Clear all promise state */
124
131
  protected clearAll() {
125
- for (const k in this.rejectors) {
132
+ for (const k of this.fetching.keys()) {
126
133
  this.clear(k);
127
134
  }
128
135
  }
129
136
 
130
137
  /** Clear promise state for a given key */
131
138
  protected clear(key: string) {
132
- this.fetched[key].catch(() => {});
133
- delete this.resolvers[key];
134
- delete this.rejectors[key];
135
- delete this.fetched[key];
136
- delete this.fetchedAt[key];
139
+ if (this.fetching.has(key)) {
140
+ (this.fetching.get(key) as FetchingMeta).promise.catch(() => {});
141
+ this.fetching.delete(key);
142
+ }
137
143
  }
138
144
 
139
145
  protected getLastReset() {
@@ -226,14 +232,14 @@ export default class NetworkManager implements Manager {
226
232
  */
227
233
  protected handleSet(action: SetResponseAction) {
228
234
  // this can still turn out to be untrue since this is async
229
- if (action.key in this.fetched) {
230
- let promiseHandler: (value?: any) => void;
235
+ if (this.fetching.has(action.key)) {
236
+ const { reject, resolve } = this.fetching.get(action.key) as FetchingMeta;
231
237
  if (action.error) {
232
- promiseHandler = this.rejectors[action.key];
238
+ reject(action.response);
233
239
  } else {
234
- promiseHandler = this.resolvers[action.key];
240
+ resolve(action.response);
235
241
  }
236
- promiseHandler(action.response);
242
+
237
243
  // since we're resolved we no longer need to keep track of this promise
238
244
  this.clear(action.key);
239
245
  }
@@ -253,19 +259,18 @@ export default class NetworkManager implements Manager {
253
259
  key: string,
254
260
  fetch: () => Promise<any>,
255
261
  fetchedAt: number,
256
- ) {
262
+ ): Promise<any> {
257
263
  const lastReset = this.getLastReset();
264
+ let fetchMeta = this.fetching.get(key);
265
+
258
266
  // we're already fetching so reuse the promise
259
267
  // fetches after reset do not count
260
- if (key in this.fetched && this.fetchedAt[key] > lastReset) {
261
- return this.fetched[key];
268
+ if (fetchMeta && fetchMeta.fetchedAt > lastReset) {
269
+ return fetchMeta.promise;
262
270
  }
263
271
 
264
- this.fetched[key] = new Promise((resolve, reject) => {
265
- this.resolvers[key] = resolve;
266
- this.rejectors[key] = reject;
267
- });
268
- this.fetchedAt[key] = fetchedAt;
272
+ fetchMeta = newFetchMeta(fetchedAt);
273
+ this.fetching.set(key, fetchMeta);
269
274
 
270
275
  this.idleCallback(
271
276
  () => {
@@ -277,7 +282,7 @@ export default class NetworkManager implements Manager {
277
282
  { timeout: 500 },
278
283
  );
279
284
 
280
- return this.fetched[key];
285
+ return fetchMeta.promise;
281
286
  }
282
287
 
283
288
  /** Calls the callback when client is not 'busy' with high priority interaction tasks
@@ -291,3 +296,12 @@ export default class NetworkManager implements Manager {
291
296
  callback();
292
297
  }
293
298
  }
299
+
300
+ function newFetchMeta(fetchedAt: number): FetchingMeta {
301
+ const fetchMeta = { fetchedAt } as FetchingMeta;
302
+ fetchMeta.promise = new Promise((resolve, reject) => {
303
+ fetchMeta.resolve = resolve;
304
+ fetchMeta.reject = reject;
305
+ });
306
+ return fetchMeta;
307
+ }
@@ -1,4 +1,4 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
1
+ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
2
 
3
3
  exports[`PollingSubscription fresh data cleanup() should not run even if interval not cancelled 1`] = `
4
4
  [
@@ -1,5 +1,5 @@
1
1
  import { Endpoint } from '@data-client/endpoint';
2
- import { Article, PollingArticleResource } from '__tests__/new';
2
+ import { Article } from '__tests__/new';
3
3
 
4
4
  import { createSubscription } from '../../controller/actions/createSubscription';
5
5
  import Controller from '../../controller/Controller';
@@ -284,13 +284,13 @@ describe('PollingSubscription', () => {
284
284
  });
285
285
 
286
286
  describe('cleanup()', () => {
287
- let warnSpy: jest.SpyInstance;
287
+ let warnSpy: jest.Spied<typeof console.warn>;
288
288
  afterEach(() => {
289
289
  warnSpy.mockRestore();
290
290
  });
291
- beforeEach(() =>
292
- (warnSpy = jest.spyOn(console, 'warn')).mockImplementation(() => {}),
293
- );
291
+ beforeEach(() => {
292
+ (warnSpy = jest.spyOn(console, 'warn')).mockImplementation(() => {});
293
+ });
294
294
 
295
295
  it('should stop all timers', () => {
296
296
  dispatch.mockClear();
@@ -394,6 +394,8 @@ describe('PollingSubscription', () => {
394
394
  });
395
395
 
396
396
  it('should not run when timeoutId is deleted after coming online', () => {
397
+ // Silence console.warn for this test
398
+ const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
397
399
  const listener = new MockConnectionListener(false);
398
400
  const { dispatch, pollingSubscription } = createMocks(listener);
399
401
  expect(dispatch.mock.calls.length).toBe(0);
@@ -406,6 +408,8 @@ describe('PollingSubscription', () => {
406
408
  expect(dispatch.mock.calls.length).toBe(0);
407
409
  expect(listener.offlineHandlers.length).toBe(1);
408
410
  expect(listener.onlineHandlers.length).toBe(0);
411
+ expect(warnSpy.mock.calls.length).toBe(1);
412
+ warnSpy.mockRestore();
409
413
  });
410
414
 
411
415
  it('should stop dispatching when offline again', () => {
@@ -46,11 +46,12 @@ export class GCPolicy implements GCInterface {
46
46
  if (key)
47
47
  this.endpointCount.set(key, (this.endpointCount.get(key) ?? 0) + 1);
48
48
  paths.forEach(path => {
49
- if (!this.entityCount.has(path.key)) {
50
- this.entityCount.set(path.key, new Map<string, number>());
49
+ const { key, pk } = path;
50
+ if (!this.entityCount.has(key)) {
51
+ this.entityCount.set(key, new Map<string, number>());
51
52
  }
52
- const instanceCount = this.entityCount.get(path.key)!;
53
- instanceCount.set(path.pk, (instanceCount.get(path.pk) ?? 0) + 1);
53
+ const instanceCount = this.entityCount.get(key)!;
54
+ instanceCount.set(pk, (instanceCount.get(pk) ?? 0) + 1);
54
55
  });
55
56
 
56
57
  // decrement
@@ -68,18 +69,19 @@ export class GCPolicy implements GCInterface {
68
69
  }
69
70
  }
70
71
  paths.forEach(path => {
71
- if (!this.entityCount.has(path.key)) {
72
+ const { key, pk } = path;
73
+ if (!this.entityCount.has(key)) {
72
74
  return;
73
75
  }
74
- const instanceCount = this.entityCount.get(path.key)!;
75
- const entityCount = instanceCount.get(path.pk)!;
76
+ const instanceCount = this.entityCount.get(key)!;
77
+ const entityCount = instanceCount.get(pk)!;
76
78
  if (entityCount !== undefined) {
77
79
  if (entityCount <= 1) {
78
- instanceCount.delete(path.pk);
80
+ instanceCount.delete(pk);
79
81
  // queue for cleanup
80
82
  this.entitiesQ.push(path);
81
83
  } else {
82
- instanceCount.set(path.pk, entityCount - 1);
84
+ instanceCount.set(pk, entityCount - 1);
83
85
  }
84
86
  }
85
87
  });
@@ -133,7 +135,7 @@ export class GCPolicy implements GCInterface {
133
135
  if (
134
136
  !this.entityCount.get(path.key)?.has(path.pk) &&
135
137
  this.expiresAt(
136
- state.entityMeta[path.key]?.[path.pk] ?? {
138
+ state.entitiesMeta[path.key]?.[path.pk] ?? {
137
139
  fetchedAt: 0,
138
140
  date: 0,
139
141
  expiresAt: 0,
@@ -48,7 +48,7 @@ describe('GCPolicy', () => {
48
48
  expiresAt: 0,
49
49
  },
50
50
  },
51
- entityMeta: {
51
+ entitiesMeta: {
52
52
  testEntity: {
53
53
  '1': {
54
54
  date: 0,
@@ -90,7 +90,7 @@ describe('GCPolicy', () => {
90
90
  expiresAt: 0,
91
91
  },
92
92
  },
93
- entityMeta: {
93
+ entitiesMeta: {
94
94
  testEntity: {
95
95
  '1': {
96
96
  date: 0,
@@ -127,7 +127,7 @@ describe('GCPolicy', () => {
127
127
  const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
128
128
  const state = {
129
129
  meta: {},
130
- entityMeta: {},
130
+ entitiesMeta: {},
131
131
  };
132
132
  (controller.getState as jest.Mock).mockReturnValue(state);
133
133
 
@@ -163,7 +163,7 @@ describe('GCPolicy', () => {
163
163
  expiresAt: futureTime,
164
164
  },
165
165
  },
166
- entityMeta: {
166
+ entitiesMeta: {
167
167
  testEntity: {
168
168
  '1': {
169
169
  date: futureTime - 100,
@@ -196,7 +196,7 @@ describe('GCPolicy', () => {
196
196
  expiresAt: 0,
197
197
  },
198
198
  },
199
- entityMeta: {
199
+ entitiesMeta: {
200
200
  testEntity: {
201
201
  '1': {
202
202
  date: 0,
@@ -233,7 +233,7 @@ describe('GCPolicy', () => {
233
233
  expiresAt: futureTime,
234
234
  },
235
235
  },
236
- entityMeta: {
236
+ entitiesMeta: {
237
237
  testEntity: {
238
238
  '1': {
239
239
  date: futureTime - 100,
@@ -1,10 +1,10 @@
1
- // Jest Snapshot v1, https://goo.gl/fbAQLP
1
+ // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
2
 
3
3
  exports[`reducer should set error in meta for "set" 1`] = `
4
4
  {
5
5
  "endpoints": {},
6
6
  "entities": {},
7
- "entityMeta": {},
7
+ "entitiesMeta": {},
8
8
  "indexes": {},
9
9
  "lastReset": 0,
10
10
  "meta": {
@@ -34,7 +34,7 @@ exports[`reducer singles should update state correctly 1`] = `
34
34
  },
35
35
  },
36
36
  },
37
- "entityMeta": {
37
+ "entitiesMeta": {
38
38
  "Article": {
39
39
  "20": {
40
40
  "date": 5000000000,
@@ -1,4 +1,5 @@
1
- import { INVALID, Entity } from '@data-client/endpoint';
1
+ import { Entity } from '@data-client/endpoint';
2
+ import { INVALID } from '@data-client/normalizr';
2
3
  import { ArticleResource, Article, PaginatedArticle } from '__tests__/new';
3
4
 
4
5
  import { Controller } from '../..';
@@ -84,10 +85,10 @@ describe('reducer', () => {
84
85
  expect(nextEntity.content).not.toBe(undefined);
85
86
 
86
87
  expect(
87
- nextState.entityMeta[Article.key][`${Article.pk(action.response)}`],
88
+ nextState.entitiesMeta[Article.key][`${Article.pk(action.response)}`],
88
89
  ).toBeDefined();
89
90
  expect(
90
- nextState.entityMeta[Article.key][`${Article.pk(action.response)}`]
91
+ nextState.entitiesMeta[Article.key][`${Article.pk(action.response)}`]
91
92
  .date,
92
93
  ).toBe(action.meta.date);
93
94
  });
@@ -102,7 +103,7 @@ describe('reducer', () => {
102
103
  },
103
104
  };
104
105
  const getMeta = (state: any): { expiresAt: number } =>
105
- state.entityMeta[Article.key][`${Article.pk(action.response)}`];
106
+ state.entitiesMeta[Article.key][`${Article.pk(action.response)}`];
106
107
  const prevMeta = getMeta(newState);
107
108
  expect(prevMeta).toBeDefined();
108
109
  const nextState = reducer(newState, localAction);
@@ -123,7 +124,7 @@ describe('reducer', () => {
123
124
  },
124
125
  };
125
126
  const getMeta = (state: any): { date: number } =>
126
- state.entityMeta[Article.key][`${Article.pk(action.response)}`];
127
+ state.entitiesMeta[Article.key][`${Article.pk(action.response)}`];
127
128
  const getEntity = (state: any): Article =>
128
129
  state.entities[Article.key][`${Article.pk(action.response)}`];
129
130
  const prevEntity = getEntity(newState);
@@ -182,7 +183,9 @@ describe('reducer', () => {
182
183
  },
183
184
  };
184
185
  const getMeta = (state: any): { date: number; expiresAt: number } =>
185
- state.entityMeta[ExpiresSoon.key][`${ExpiresSoon.pk(action.response)}`];
186
+ state.entitiesMeta[ExpiresSoon.key][
187
+ `${ExpiresSoon.pk(action.response)}`
188
+ ];
186
189
  const getEntity = (state: any): ExpiresSoon =>
187
190
  state.entities[ExpiresSoon.key][`${ExpiresSoon.pk(action.response)}`];
188
191
 
@@ -259,6 +262,11 @@ describe('reducer', () => {
259
262
  [id]: { id, counter: 5 },
260
263
  },
261
264
  },
265
+ entitiesMeta: {
266
+ [Counter.key]: {
267
+ [id]: { date: 0, fetchedAt: 0, expiresAt: 0 },
268
+ },
269
+ },
262
270
  };
263
271
  const newState = reducer(state, action);
264
272
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -630,7 +638,7 @@ describe('reducer', () => {
630
638
  expect(newState).toBe(iniState);
631
639
  });
632
640
  describe('RESET', () => {
633
- let warnspy: jest.SpyInstance;
641
+ let warnspy: jest.Spied<any>;
634
642
  beforeEach(() => {
635
643
  warnspy = jest.spyOn(global.console, 'warn').mockImplementation(() => {});
636
644
  });
@@ -683,7 +691,7 @@ describe('reducer', () => {
683
691
  },
684
692
  '5': undefined,
685
693
  },
686
- entityMeta: {
694
+ entitiesMeta: {
687
695
  [Article.key]: {
688
696
  '10': { date: 0, expiresAt: 10000, fetchedAt: 0 },
689
697
  '20': { date: 0, expiresAt: 10000, fetchedAt: 0 },
@@ -721,7 +729,7 @@ describe('reducer', () => {
721
729
  const newState = reducer(iniState, action);
722
730
  expect(newState).toBe(iniState);
723
731
  expect(Object.keys(newState.entities[Article.key] ?? {}).length).toBe(2);
724
- expect(Object.keys(newState.entityMeta[Article.key] ?? {}).length).toBe(
732
+ expect(Object.keys(newState.entitiesMeta[Article.key] ?? {}).length).toBe(
725
733
  2,
726
734
  );
727
735
  expect(Object.keys(newState.endpoints).length).toBe(0);
@@ -28,7 +28,7 @@ export default function createReducer(controller: Controller): ReducerType {
28
28
  // inline deletes are fine as these should have 0 refcounts
29
29
  action.entities.forEach(({ key, pk }) => {
30
30
  delete (state as any).entities[key]?.[pk];
31
- delete (state as any).entityMeta[key]?.[pk];
31
+ delete (state as any).entitiesMeta[key]?.[pk];
32
32
  });
33
33
  action.endpoints.forEach(fetchKey => {
34
34
  delete (state as any).endpoints[fetchKey];
@@ -69,7 +69,7 @@ export const initialState: State<unknown> = {
69
69
  endpoints: {},
70
70
  indexes: {},
71
71
  meta: {},
72
- entityMeta: {},
72
+ entitiesMeta: {},
73
73
  optimistic: [],
74
74
  lastReset: 0,
75
75
  };
@@ -17,7 +17,7 @@ export function setReducer(
17
17
  value = action.value;
18
18
  }
19
19
  try {
20
- const { entities, indexes, entityMeta } = normalize(
20
+ const { entities, indexes, entitiesMeta } = normalize(
21
21
  action.schema,
22
22
  value,
23
23
  action.args,
@@ -29,7 +29,7 @@ export function setReducer(
29
29
  endpoints: state.endpoints,
30
30
  indexes,
31
31
  meta: state.meta,
32
- entityMeta,
32
+ entitiesMeta,
33
33
  optimistic: state.optimistic,
34
34
  lastReset: state.lastReset,
35
35
  };
@@ -41,7 +41,7 @@ export function setResponseReducer(
41
41
  } else {
42
42
  response = action.response;
43
43
  }
44
- const { result, entities, indexes, entityMeta } = normalize(
44
+ const { result, entities, indexes, entitiesMeta } = normalize(
45
45
  action.endpoint.schema,
46
46
  response,
47
47
  action.args,
@@ -80,7 +80,7 @@ export function setResponseReducer(
80
80
  prevExpiresAt: state.meta[action.key]?.expiresAt,
81
81
  },
82
82
  },
83
- entityMeta,
83
+ entitiesMeta,
84
84
  optimistic: filterOptimistic(state, action),
85
85
  lastReset: state.lastReset,
86
86
  };
@@ -115,7 +115,7 @@ function reduceError(
115
115
  if (error.name === 'AbortError') {
116
116
  // In case we abort simply undo the optimistic update and act like no fetch even occured
117
117
  // We still want those watching promises from fetch directly to observed the abort, but we don't want to
118
- // Trigger errors in this case. This means theoretically improperly built abortes useResource() could suspend forever.
118
+ // Trigger errors in this case. This means theoretically improperly built aborts useSuspense() could suspend forever.
119
119
  return {
120
120
  ...state,
121
121
  optimistic: filterOptimistic(state, action),
package/src/types.ts CHANGED
@@ -41,7 +41,7 @@ export interface State<T> {
41
41
  readonly errorPolicy?: 'hard' | 'soft' | undefined;
42
42
  };
43
43
  };
44
- readonly entityMeta: {
44
+ readonly entitiesMeta: {
45
45
  readonly [entityKey: string]: {
46
46
  readonly [pk: string]: {
47
47
  readonly fetchedAt: number;
@@ -254,7 +254,7 @@ export default class Controller<D extends GenericDispatch = DataClientDispatch>
254
254
  */
255
255
  get<S extends Queryable>(schema: S, ...rest: readonly [
256
256
  ...SchemaArgs<S>,
257
- Pick<State<unknown>, 'entities' | 'entityMeta'>
257
+ Pick<State<unknown>, 'entities' | 'indexes'>
258
258
  ]): DenormalizeNullable<S> | undefined;
259
259
  /**
260
260
  * Queries the store for a Querable schema; providing related metadata
@@ -262,12 +262,12 @@ export default class Controller<D extends GenericDispatch = DataClientDispatch>
262
262
  */
263
263
  getQueryMeta<S extends Queryable>(schema: S, ...rest: readonly [
264
264
  ...SchemaArgs<S>,
265
- Pick<State<unknown>, 'entities' | 'entityMeta'>
265
+ Pick<State<unknown>, 'entities' | 'indexes'>
266
266
  ]): {
267
267
  data: DenormalizeNullable<S> | undefined;
268
268
  countRef: () => () => void;
269
269
  };
270
- private getSchemaResponse;
270
+ private getExpiryStatus;
271
271
  }
272
272
  export { ErrorTypes };
273
273
  declare class Snapshot<T = unknown> implements SnapshotInterface {
package/ts3.4/index.d.ts CHANGED
@@ -1,8 +1,9 @@
1
1
  import * as __INTERNAL__1 from './internal.js';
2
2
  export { __INTERNAL__1 as __INTERNAL__ };
3
- export { NetworkError, UnknownError, ErrorTypes, Schema, EndpointInterface, EntityInterface, SchemaClass, ResolveType, DenormalizeNullable, Denormalize, Normalize, NormalizeNullable, FetchFunction, EndpointExtraOptions, Queryable, SchemaArgs, NI, } from '@data-client/normalizr';
3
+ export { NetworkError, UnknownError, ErrorTypes, Schema, EndpointInterface, EntityInterface, SchemaClass, ResolveType, DenormalizeNullable, Denormalize, Normalize, NormalizeNullable, FetchFunction, EndpointExtraOptions, Queryable, SchemaArgs, Mergeable, IQueryDelegate, INormalizeDelegate, NI, } from '@data-client/normalizr';
4
4
  export { ExpiryStatus } from '@data-client/normalizr';
5
5
  export { default as NetworkManager, ResetError, } from './manager/NetworkManager.js';
6
+ export { FetchingMeta } from './manager/NetworkManager.js';
6
7
  export * from './state/GCPolicy.js';
7
8
  export { default as createReducer, initialState, } from './state/reducer/createReducer.js';
8
9
  export { default as applyManager } from './manager/applyManager.js';
@@ -4,6 +4,12 @@ export declare class ResetError extends Error {
4
4
  name: string;
5
5
  constructor();
6
6
  }
7
+ export interface FetchingMeta {
8
+ promise: Promise<any>;
9
+ resolve: (value?: any) => void;
10
+ reject: (value?: any) => void;
11
+ fetchedAt: number;
12
+ }
7
13
  /** Handles all async network dispatches
8
14
  *
9
15
  * Dedupes concurrent requests by keeping track of all fetches in flight
@@ -14,18 +20,7 @@ export declare class ResetError extends Error {
14
20
  * @see https://dataclient.io/docs/api/NetworkManager
15
21
  */
16
22
  export default class NetworkManager implements Manager {
17
- protected fetched: {
18
- [k: string]: Promise<any>;
19
- };
20
- protected resolvers: {
21
- [k: string]: (value?: any) => void;
22
- };
23
- protected rejectors: {
24
- [k: string]: (value?: any) => void;
25
- };
26
- protected fetchedAt: {
27
- [k: string]: number;
28
- };
23
+ protected fetching: Map<string, FetchingMeta>;
29
24
  readonly dataExpiryLength: number;
30
25
  readonly errorExpiryLength: number;
31
26
  protected controller: Controller;
@@ -20,7 +20,7 @@ export declare function expireReducer(state: State<unknown>, action: ExpireAllAc
20
20
  readonly [key: string]: unknown | import("../../types.js").PK[] | import("../../types.js").PK | undefined;
21
21
  };
22
22
  indexes: import("packages/normalizr/lib/interface.js").NormalizedIndex;
23
- entityMeta: {
23
+ entitiesMeta: {
24
24
  readonly [entityKey: string]: {
25
25
  readonly [pk: string]: {
26
26
  readonly fetchedAt: number;
@@ -20,7 +20,7 @@ export declare function invalidateReducer(state: State<unknown>, action: Invalid
20
20
  } | undefined;
21
21
  };
22
22
  indexes: import("packages/normalizr/lib/interface.js").NormalizedIndex;
23
- entityMeta: {
23
+ entitiesMeta: {
24
24
  readonly [entityKey: string]: {
25
25
  readonly [pk: string]: {
26
26
  readonly fetchedAt: number;
@@ -19,7 +19,7 @@ export declare function setReducer(state: State<unknown>, action: SetAction, con
19
19
  readonly errorPolicy?: "hard" | "soft" | undefined;
20
20
  };
21
21
  };
22
- entityMeta: import("packages/normalizr/lib/types.js").EntitiesToMeta<{
22
+ entitiesMeta: import("packages/normalizr/lib/types.js").EntitiesToMeta<{
23
23
  [x: string]: any;
24
24
  }>;
25
25
  optimistic: (import("../../actions.js").OptimisticAction<import("@data-client/normalizr").EndpointInterface<import("@data-client/normalizr").FetchFunction, import("@data-client/normalizr").Schema | undefined, boolean | undefined> & {
@@ -22,7 +22,7 @@ export declare function setResponseReducer(state: State<unknown>, action: Optimi
22
22
  prevExpiresAt: number;
23
23
  };
24
24
  };
25
- entityMeta: import("packages/normalizr/lib/types.js").EntitiesToMeta<{
25
+ entitiesMeta: import("packages/normalizr/lib/types.js").EntitiesToMeta<{
26
26
  [x: string]: any;
27
27
  }>;
28
28
  optimistic: (OptimisticAction<import("@data-client/normalizr").EndpointInterface<import("@data-client/normalizr").FetchFunction, import("@data-client/normalizr").Schema | undefined, boolean | undefined> & {
package/ts3.4/types.d.ts CHANGED
@@ -31,7 +31,7 @@ export interface State<T> {
31
31
  readonly errorPolicy?: 'hard' | 'soft' | undefined;
32
32
  };
33
33
  };
34
- readonly entityMeta: {
34
+ readonly entitiesMeta: {
35
35
  readonly [entityKey: string]: {
36
36
  readonly [pk: string]: {
37
37
  readonly fetchedAt: number;