@data-client/core 0.14.18 → 0.14.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/dist/index.js +553 -277
  2. package/dist/index.umd.min.js +1 -1
  3. package/legacy/actions.js +1 -1
  4. package/legacy/controller/Controller.js +100 -11
  5. package/legacy/controller/actions/createFetch.js +3 -2
  6. package/legacy/controller/ensurePojo.js +3 -2
  7. package/legacy/index.js +3 -4
  8. package/legacy/manager/DevtoolsManager.js +14 -7
  9. package/legacy/manager/NetworkManager.js +8 -5
  10. package/legacy/manager/PollingSubscription.js +6 -3
  11. package/legacy/manager/SubscriptionManager.js +4 -3
  12. package/legacy/manager/applyManager.js +3 -7
  13. package/legacy/manager/initManager.js +15 -0
  14. package/legacy/state/GCPolicy.js +153 -0
  15. package/legacy/state/reducer/createReducer.js +7 -3
  16. package/legacy/state/reducer/expireReducer.js +5 -4
  17. package/legacy/state/reducer/fetchReducer.js +3 -2
  18. package/legacy/state/reducer/invalidateReducer.js +6 -5
  19. package/legacy/state/reducer/setResponseReducer.js +10 -7
  20. package/legacy/types.js +1 -1
  21. package/lib/actions.d.ts +2 -2
  22. package/lib/actions.d.ts.map +1 -1
  23. package/lib/actions.js +1 -1
  24. package/lib/controller/Controller.d.ts +108 -5
  25. package/lib/controller/Controller.d.ts.map +1 -1
  26. package/lib/controller/Controller.js +96 -8
  27. package/lib/index.d.ts +2 -0
  28. package/lib/index.d.ts.map +1 -1
  29. package/lib/index.js +3 -4
  30. package/lib/manager/DevtoolsManager.js +7 -3
  31. package/lib/manager/NetworkManager.js +3 -2
  32. package/lib/manager/SubscriptionManager.d.ts.map +1 -1
  33. package/lib/manager/SubscriptionManager.js +1 -1
  34. package/lib/manager/applyManager.d.ts.map +1 -1
  35. package/lib/manager/applyManager.js +3 -7
  36. package/lib/manager/initManager.d.ts +4 -0
  37. package/lib/manager/initManager.d.ts.map +1 -0
  38. package/lib/manager/initManager.js +15 -0
  39. package/lib/state/GCPolicy.d.ts +55 -0
  40. package/lib/state/GCPolicy.d.ts.map +1 -0
  41. package/lib/state/GCPolicy.js +153 -0
  42. package/lib/state/reducer/createReducer.js +5 -2
  43. package/lib/state/reducer/expireReducer.d.ts +1 -0
  44. package/lib/state/reducer/expireReducer.d.ts.map +1 -1
  45. package/lib/state/reducer/invalidateReducer.d.ts +1 -0
  46. package/lib/state/reducer/invalidateReducer.d.ts.map +1 -1
  47. package/lib/state/reducer/setReducer.d.ts +3 -2
  48. package/lib/state/reducer/setReducer.d.ts.map +1 -1
  49. package/lib/state/reducer/setResponseReducer.d.ts +4 -2
  50. package/lib/state/reducer/setResponseReducer.d.ts.map +1 -1
  51. package/lib/state/reducer/setResponseReducer.js +4 -2
  52. package/lib/types.d.ts +1 -0
  53. package/lib/types.d.ts.map +1 -1
  54. package/lib/types.js +1 -1
  55. package/package.json +8 -3
  56. package/src/actions.ts +2 -1
  57. package/src/controller/Controller.ts +205 -8
  58. package/src/controller/__tests__/Controller.ts +8 -4
  59. package/src/controller/__tests__/__snapshots__/get.ts.snap +7 -0
  60. package/src/controller/__tests__/__snapshots__/getResponse.ts.snap +15 -0
  61. package/src/controller/__tests__/get.ts +45 -17
  62. package/src/controller/__tests__/getResponse.ts +46 -0
  63. package/src/index.ts +2 -6
  64. package/src/manager/SubscriptionManager.ts +0 -1
  65. package/src/manager/applyManager.ts +3 -4
  66. package/src/manager/initManager.ts +21 -0
  67. package/src/state/GCPolicy.ts +197 -0
  68. package/src/state/__tests__/GCPolicy.test.ts +258 -0
  69. package/src/state/__tests__/__snapshots__/reducer.ts.snap +2 -0
  70. package/src/state/__tests__/reducer.ts +4 -4
  71. package/src/state/reducer/createReducer.ts +1 -1
  72. package/src/state/reducer/setResponseReducer.ts +3 -1
  73. package/src/types.ts +1 -0
  74. package/ts3.4/actions.d.ts +2 -5
  75. package/ts3.4/controller/Controller.d.ts +141 -5
  76. package/ts3.4/index.d.ts +2 -0
  77. package/ts3.4/manager/initManager.d.ts +4 -0
  78. package/ts3.4/state/GCPolicy.d.ts +55 -0
  79. package/ts3.4/state/reducer/expireReducer.d.ts +1 -0
  80. package/ts3.4/state/reducer/invalidateReducer.d.ts +1 -0
  81. package/ts3.4/state/reducer/setReducer.d.ts +3 -2
  82. package/ts3.4/state/reducer/setResponseReducer.d.ts +4 -2
  83. package/ts3.4/types.d.ts +1 -0
@@ -0,0 +1,197 @@
1
+ import type { EntityPath } from '@data-client/normalizr';
2
+
3
+ import { GC } from '../actionTypes.js';
4
+ import Controller from '../controller/Controller.js';
5
+
6
+ export class GCPolicy implements GCInterface {
7
+ protected endpointCount = new Map<string, number>();
8
+ protected entityCount = new Map<string, Map<string, number>>();
9
+ protected endpointsQ = new Set<string>();
10
+ protected entitiesQ: EntityPath[] = [];
11
+
12
+ declare protected intervalId: ReturnType<typeof setInterval>;
13
+ declare protected controller: Controller;
14
+ declare protected options: Required<Omit<GCOptions, 'expiresAt'>>;
15
+
16
+ constructor({
17
+ // every 5 min
18
+ intervalMS = 60 * 1000 * 5,
19
+ expiryMultiplier = 2,
20
+ expiresAt,
21
+ }: GCOptions = {}) {
22
+ if (expiresAt) {
23
+ this.expiresAt = expiresAt.bind(this);
24
+ }
25
+ this.options = {
26
+ intervalMS,
27
+ expiryMultiplier,
28
+ };
29
+ }
30
+
31
+ init(controller: Controller) {
32
+ this.controller = controller;
33
+
34
+ this.intervalId = setInterval(() => {
35
+ this.idleCallback(() => this.runSweep(), { timeout: 1000 });
36
+ }, this.options.intervalMS);
37
+ }
38
+
39
+ cleanup() {
40
+ clearInterval(this.intervalId);
41
+ }
42
+
43
+ createCountRef({ key, paths = [] }: { key?: string; paths?: EntityPath[] }) {
44
+ // increment
45
+ return () => {
46
+ if (key)
47
+ this.endpointCount.set(key, (this.endpointCount.get(key) ?? 0) + 1);
48
+ paths.forEach(path => {
49
+ if (!this.entityCount.has(path.key)) {
50
+ this.entityCount.set(path.key, new Map<string, number>());
51
+ }
52
+ const instanceCount = this.entityCount.get(path.key)!;
53
+ instanceCount.set(path.pk, (instanceCount.get(path.pk) ?? 0) + 1);
54
+ });
55
+
56
+ // decrement
57
+ return () => {
58
+ if (key) {
59
+ const currentCount = this.endpointCount.get(key)!;
60
+ if (currentCount !== undefined) {
61
+ if (currentCount <= 1) {
62
+ this.endpointCount.delete(key);
63
+ // queue for cleanup
64
+ this.endpointsQ.add(key);
65
+ } else {
66
+ this.endpointCount.set(key, currentCount - 1);
67
+ }
68
+ }
69
+ }
70
+ paths.forEach(path => {
71
+ if (!this.entityCount.has(path.key)) {
72
+ return;
73
+ }
74
+ const instanceCount = this.entityCount.get(path.key)!;
75
+ const entityCount = instanceCount.get(path.pk)!;
76
+ if (entityCount !== undefined) {
77
+ if (entityCount <= 1) {
78
+ instanceCount.delete(path.pk);
79
+ // queue for cleanup
80
+ this.entitiesQ.push(path);
81
+ } else {
82
+ instanceCount.set(path.pk, entityCount - 1);
83
+ }
84
+ }
85
+ });
86
+ };
87
+ };
88
+ }
89
+
90
+ protected expiresAt({
91
+ fetchedAt,
92
+ expiresAt,
93
+ }: {
94
+ expiresAt: number;
95
+ date: number;
96
+ fetchedAt: number;
97
+ }): number {
98
+ return (
99
+ Math.max(
100
+ (expiresAt - fetchedAt) * this.options.expiryMultiplier,
101
+ 120000,
102
+ ) + fetchedAt
103
+ );
104
+ }
105
+
106
+ protected runSweep() {
107
+ const state = this.controller.getState();
108
+ const entities: EntityPath[] = [];
109
+ const endpoints: string[] = [];
110
+ const now = Date.now();
111
+
112
+ const nextEndpointsQ = new Set<string>();
113
+ for (const key of this.endpointsQ) {
114
+ if (
115
+ !this.endpointCount.has(key) &&
116
+ this.expiresAt(
117
+ state.meta[key] ?? {
118
+ fetchedAt: 0,
119
+ date: 0,
120
+ expiresAt: 0,
121
+ },
122
+ ) < now
123
+ ) {
124
+ endpoints.push(key);
125
+ } else {
126
+ nextEndpointsQ.add(key);
127
+ }
128
+ }
129
+ this.endpointsQ = nextEndpointsQ;
130
+
131
+ const nextEntitiesQ: EntityPath[] = [];
132
+ for (const path of this.entitiesQ) {
133
+ if (
134
+ !this.entityCount.get(path.key)?.has(path.pk) &&
135
+ this.expiresAt(
136
+ state.entityMeta[path.key]?.[path.pk] ?? {
137
+ fetchedAt: 0,
138
+ date: 0,
139
+ expiresAt: 0,
140
+ },
141
+ ) < now
142
+ ) {
143
+ entities.push(path);
144
+ } else {
145
+ nextEntitiesQ.push(path);
146
+ }
147
+ }
148
+ this.entitiesQ = nextEntitiesQ;
149
+
150
+ if (entities.length || endpoints.length) {
151
+ this.controller.dispatch({ type: GC, entities, endpoints });
152
+ }
153
+ }
154
+
155
+ /** Calls the callback when client is not 'busy' with high priority interaction tasks
156
+ *
157
+ * Override for platform-specific implementations
158
+ */
159
+ protected idleCallback(
160
+ callback: (...args: any[]) => void,
161
+ options?: IdleRequestOptions,
162
+ ) {
163
+ if (typeof requestIdleCallback === 'function') {
164
+ requestIdleCallback(callback, options);
165
+ } else {
166
+ callback();
167
+ }
168
+ }
169
+ }
170
+
171
+ export class ImmortalGCPolicy implements GCInterface {
172
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
173
+ init() {}
174
+ // eslint-disable-next-line @typescript-eslint/no-empty-function
175
+ cleanup() {}
176
+ createCountRef() {
177
+ return () => () => undefined;
178
+ }
179
+ }
180
+
181
+ export interface GCOptions {
182
+ intervalMS?: number;
183
+ expiryMultiplier?: number;
184
+ expiresAt?: (meta: {
185
+ expiresAt: number;
186
+ date: number;
187
+ fetchedAt: number;
188
+ }) => number;
189
+ }
190
+ export interface CreateCountRef {
191
+ ({ key, paths }: { key?: string; paths?: EntityPath[] }): () => () => void;
192
+ }
193
+ export interface GCInterface {
194
+ createCountRef: CreateCountRef;
195
+ init(controller: Controller): void;
196
+ cleanup(): void;
197
+ }
@@ -0,0 +1,258 @@
1
+ import type { EntityPath } from '@data-client/normalizr';
2
+ import { jest } from '@jest/globals';
3
+ import { has } from 'benchmark';
4
+
5
+ import { GC } from '../../actionTypes';
6
+ import Controller from '../../controller/Controller';
7
+ import { GCPolicy } from '../GCPolicy';
8
+
9
+ describe('GCPolicy', () => {
10
+ let gcPolicy: GCPolicy;
11
+ let controller: Controller;
12
+
13
+ beforeEach(() => {
14
+ controller = {
15
+ getState: jest.fn(),
16
+ dispatch: jest.fn(),
17
+ } as unknown as Controller;
18
+ gcPolicy = new GCPolicy();
19
+ gcPolicy.init(controller);
20
+ });
21
+
22
+ afterEach(() => {
23
+ gcPolicy.cleanup();
24
+ });
25
+
26
+ it('should increment and decrement endpoint and entity counts', () => {
27
+ const key = 'testEndpoint';
28
+ const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
29
+ const countRef = gcPolicy.createCountRef({ key, paths });
30
+
31
+ const decrement = countRef();
32
+ expect(gcPolicy['endpointCount'].get(key)).toBe(1);
33
+ expect(gcPolicy['entityCount'].get('testEntity')?.get('1')).toBe(1);
34
+
35
+ decrement();
36
+ expect(gcPolicy['endpointCount'].get(key)).toBeUndefined();
37
+ expect(gcPolicy['entityCount'].get('testEntity')?.get('1')).toBeUndefined();
38
+ });
39
+
40
+ it('should dispatch GC action once no ref counts and is expired', () => {
41
+ const key = 'testEndpoint';
42
+ const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
43
+ const state = {
44
+ meta: {
45
+ testEndpoint: {
46
+ date: 0,
47
+ fetchedAt: 0,
48
+ expiresAt: 0,
49
+ },
50
+ },
51
+ entityMeta: {
52
+ testEntity: {
53
+ '1': {
54
+ date: 0,
55
+ fetchedAt: 0,
56
+ expiresAt: 0,
57
+ },
58
+ },
59
+ },
60
+ };
61
+ (controller.getState as jest.Mock).mockReturnValue(state);
62
+
63
+ const countRef = gcPolicy.createCountRef({ key, paths });
64
+
65
+ const decrement = countRef();
66
+ countRef(); // Increment again
67
+ gcPolicy['runSweep']();
68
+ expect(controller.dispatch).not.toHaveBeenCalled();
69
+ decrement();
70
+ gcPolicy['runSweep']();
71
+ expect(controller.dispatch).not.toHaveBeenCalled();
72
+ decrement(); // Decrement twice
73
+
74
+ gcPolicy['runSweep']();
75
+ expect(controller.dispatch).toHaveBeenCalledWith({
76
+ type: GC,
77
+ entities: [{ key: 'testEntity', pk: '1' }],
78
+ endpoints: ['testEndpoint'],
79
+ });
80
+ });
81
+
82
+ it('should dispatch GC action once no ref counts and is expired with extra decrements', () => {
83
+ const key = 'testEndpoint';
84
+ const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
85
+ const state = {
86
+ meta: {
87
+ testEndpoint: {
88
+ date: 0,
89
+ fetchedAt: 0,
90
+ expiresAt: 0,
91
+ },
92
+ },
93
+ entityMeta: {
94
+ testEntity: {
95
+ '1': {
96
+ date: 0,
97
+ fetchedAt: 0,
98
+ expiresAt: 0,
99
+ },
100
+ },
101
+ },
102
+ };
103
+ (controller.getState as jest.Mock).mockReturnValue(state);
104
+
105
+ const countRef = gcPolicy.createCountRef({ key, paths });
106
+
107
+ const decrement = countRef();
108
+ countRef(); // Increment again
109
+ gcPolicy['runSweep']();
110
+ expect(controller.dispatch).not.toHaveBeenCalled();
111
+ decrement();
112
+ gcPolicy['runSweep']();
113
+ expect(controller.dispatch).not.toHaveBeenCalled();
114
+ decrement(); // Decrement twice
115
+ decrement(); // Decrement extra time
116
+
117
+ gcPolicy['runSweep']();
118
+ expect(controller.dispatch).toHaveBeenCalledWith({
119
+ type: GC,
120
+ entities: [{ key: 'testEntity', pk: '1' }],
121
+ endpoints: ['testEndpoint'],
122
+ });
123
+ });
124
+
125
+ it('should dispatch GC action once no ref counts and no expiry state', () => {
126
+ const key = 'testEndpoint';
127
+ const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
128
+ const state = {
129
+ meta: {},
130
+ entityMeta: {},
131
+ };
132
+ (controller.getState as jest.Mock).mockReturnValue(state);
133
+
134
+ const countRef = gcPolicy.createCountRef({ key, paths });
135
+
136
+ const decrement = countRef();
137
+ countRef(); // Increment again
138
+ gcPolicy['runSweep']();
139
+ expect(controller.dispatch).not.toHaveBeenCalled();
140
+ decrement();
141
+ gcPolicy['runSweep']();
142
+ expect(controller.dispatch).not.toHaveBeenCalled();
143
+ decrement(); // Decrement twice
144
+
145
+ gcPolicy['runSweep']();
146
+ expect(controller.dispatch).toHaveBeenCalledWith({
147
+ type: GC,
148
+ entities: [{ key: 'testEntity', pk: '1' }],
149
+ endpoints: ['testEndpoint'],
150
+ });
151
+ });
152
+
153
+ it('should not dispatch GC action if expiresAt has not passed, but dispatch later when it has', () => {
154
+ jest.useFakeTimers();
155
+ const key = 'testEndpoint';
156
+ const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
157
+ const futureTime = Date.now() + 1000;
158
+ const state = {
159
+ meta: {
160
+ testEndpoint: {
161
+ date: futureTime - 100,
162
+ fetchAt: futureTime - 100,
163
+ expiresAt: futureTime,
164
+ },
165
+ },
166
+ entityMeta: {
167
+ testEntity: {
168
+ '1': {
169
+ date: futureTime - 100,
170
+ fetchAt: futureTime - 100,
171
+ expiresAt: futureTime,
172
+ },
173
+ },
174
+ },
175
+ };
176
+ (controller.getState as jest.Mock).mockReturnValue(state);
177
+
178
+ const countRef = gcPolicy.createCountRef({ key, paths });
179
+
180
+ const decrement = countRef();
181
+ countRef(); // Increment again
182
+ decrement();
183
+ decrement(); // Decrement twice
184
+
185
+ gcPolicy['runSweep']();
186
+
187
+ expect(controller.dispatch).not.toHaveBeenCalled();
188
+
189
+ // Fast forward time to past the futureTime
190
+ jest.advanceTimersByTime(2000);
191
+ (controller.getState as jest.Mock).mockReturnValue({
192
+ meta: {
193
+ testEndpoint: {
194
+ date: 0,
195
+ fetchedAt: 0,
196
+ expiresAt: 0,
197
+ },
198
+ },
199
+ entityMeta: {
200
+ testEntity: {
201
+ '1': {
202
+ date: 0,
203
+ fetchedAt: 0,
204
+ expiresAt: 0,
205
+ },
206
+ },
207
+ },
208
+ });
209
+
210
+ gcPolicy['runSweep']();
211
+
212
+ expect(controller.dispatch).toHaveBeenCalledWith({
213
+ type: GC,
214
+ entities: [{ key: 'testEntity', pk: '1' }],
215
+ endpoints: ['testEndpoint'],
216
+ });
217
+
218
+ jest.useRealTimers();
219
+ });
220
+
221
+ it('should support custom hasExpired', () => {
222
+ jest.useFakeTimers();
223
+ gcPolicy = new GCPolicy({ expiresAt: () => 0 });
224
+ gcPolicy.init(controller);
225
+ const key = 'testEndpoint';
226
+ const paths: EntityPath[] = [{ key: 'testEntity', pk: '1' }];
227
+ const futureTime = Date.now() + 1000;
228
+ const state = {
229
+ meta: {
230
+ testEndpoint: {
231
+ date: futureTime - 100,
232
+ fetchAt: futureTime - 100,
233
+ expiresAt: futureTime,
234
+ },
235
+ },
236
+ entityMeta: {
237
+ testEntity: {
238
+ '1': {
239
+ date: futureTime - 100,
240
+ fetchAt: futureTime - 100,
241
+ expiresAt: futureTime,
242
+ },
243
+ },
244
+ },
245
+ };
246
+ (controller.getState as jest.Mock).mockReturnValue(state);
247
+
248
+ const countRef = gcPolicy.createCountRef({ key, paths });
249
+
250
+ const decrement = countRef();
251
+ countRef(); // Increment again
252
+ decrement();
253
+ decrement(); // Decrement twice
254
+
255
+ gcPolicy['runSweep']();
256
+ expect(controller.dispatch).toHaveBeenCalled();
257
+ });
258
+ });
@@ -13,6 +13,7 @@ exports[`reducer should set error in meta for "set" 1`] = `
13
13
  "error": [Error: hi],
14
14
  "errorPolicy": undefined,
15
15
  "expiresAt": 5000500000,
16
+ "fetchedAt": 5000000000,
16
17
  },
17
18
  },
18
19
  "optimistic": [],
@@ -48,6 +49,7 @@ exports[`reducer singles should update state correctly 1`] = `
48
49
  "http://test.com/article/20": {
49
50
  "date": 5000000000,
50
51
  "expiresAt": 5000500000,
52
+ "fetchedAt": 5000000000,
51
53
  "prevExpiresAt": undefined,
52
54
  },
53
55
  },
@@ -712,8 +712,8 @@ describe('reducer', () => {
712
712
  const action: GCAction = {
713
713
  type: GC,
714
714
  entities: [
715
- [Article.key, '10'],
716
- [Article.key, '250'],
715
+ { key: Article.key, pk: '10' },
716
+ { key: Article.key, pk: '250' },
717
717
  ],
718
718
  endpoints: ['abc'],
719
719
  };
@@ -731,8 +731,8 @@ describe('reducer', () => {
731
731
  const action: GCAction = {
732
732
  type: GC,
733
733
  entities: [
734
- [Article.key, '100000000'],
735
- ['sillythings', '10'],
734
+ { key: Article.key, pk: '100000000' },
735
+ { key: 'sillythings', pk: '10' },
736
736
  ],
737
737
  endpoints: [],
738
738
  };
@@ -26,7 +26,7 @@ export default function createReducer(controller: Controller): ReducerType {
26
26
  switch (action.type) {
27
27
  case GC:
28
28
  // inline deletes are fine as these should have 0 refcounts
29
- action.entities.forEach(([key, pk]) => {
29
+ action.entities.forEach(({ key, pk }) => {
30
30
  delete (state as any).entities[key]?.[pk];
31
31
  delete (state as any).entityMeta[key]?.[pk];
32
32
  });
@@ -75,6 +75,7 @@ export function setResponseReducer(
75
75
  ...state.meta,
76
76
  [action.key]: {
77
77
  date: action.meta.date,
78
+ fetchedAt: action.meta.fetchedAt,
78
79
  expiresAt: action.meta.expiresAt,
79
80
  prevExpiresAt: state.meta[action.key]?.expiresAt,
80
81
  },
@@ -126,8 +127,9 @@ function reduceError(
126
127
  ...state.meta,
127
128
  [action.key]: {
128
129
  date: action.meta.date,
129
- error,
130
+ fetchedAt: action.meta.fetchedAt,
130
131
  expiresAt: action.meta.expiresAt,
132
+ error,
131
133
  errorPolicy: action.endpoint.errorPolicy?.(error),
132
134
  },
133
135
  },
package/src/types.ts CHANGED
@@ -33,6 +33,7 @@ export interface State<T> {
33
33
  readonly meta: {
34
34
  readonly [key: string]: {
35
35
  readonly date: number;
36
+ readonly fetchedAt: number;
36
37
  readonly expiresAt: number;
37
38
  readonly prevExpiresAt?: number;
38
39
  readonly error?: ErrorTypes;
@@ -1,4 +1,4 @@
1
- import { Denormalize, EndpointInterface, Queryable, ResolveType, UnknownError } from '@data-client/normalizr';
1
+ import { Denormalize, EndpointInterface, EntityPath, Queryable, ResolveType, UnknownError } from '@data-client/normalizr';
2
2
  import { SET, RESET, FETCH, SUBSCRIBE, UNSUBSCRIBE, INVALIDATE, GC, OPTIMISTIC, INVALIDATEALL, EXPIREALL, SET_RESPONSE } from './actionTypes.js';
3
3
  import { EndpointUpdateFunction } from './controller/types.js';
4
4
  type EndpointAndUpdate<E extends EndpointInterface> = EndpointInterface & {
@@ -95,10 +95,7 @@ export interface ResetAction {
95
95
  }
96
96
  export interface GCAction {
97
97
  type: typeof GC;
98
- entities: [
99
- string,
100
- string
101
- ][];
98
+ entities: EntityPath[];
102
99
  endpoints: string[];
103
100
  }
104
101
  /** @see https://dataclient.io/docs/api/Actions */