Skip to content

Commit dc521da

Browse files
authored
feat(package/gqty): Allow subscription to all cache changes (#2298)
1 parent fed26bf commit dc521da

File tree

4 files changed

+58
-10
lines changed

4 files changed

+58
-10
lines changed

.changeset/pretty-bats-shave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'gqty': major
3+
---
4+
5+
feat(package/gqty): Allow subscription to all cache changes

.vscode/settings.json

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
{
2-
"[javascript][typescript][typescriptreact]": {
2+
"[javascript]": {
3+
"editor.defaultFormatter": "esbenp.prettier-vscode"
4+
},
5+
"[typescript]": {
6+
"editor.defaultFormatter": "esbenp.prettier-vscode"
7+
},
8+
"[typescriptreact]": {
39
"editor.defaultFormatter": "esbenp.prettier-vscode"
410
},
511
"typescript.tsdk": "node_modules/typescript/lib",

packages/gqty/src/Cache/index.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ import { isSkeleton } from '../Accessor/skeleton';
55
import { deepCopy, select } from '../Helpers';
66
import { crawl } from './crawl';
77
import {
8+
type CacheNormalizationHandler,
89
deepNormalizeObject,
910
defaultNormalizationHandler,
10-
type CacheNormalizationHandler,
1111
type NormalizedObjectShell,
1212
} from './normalization';
1313
import {
14+
type CacheSnapshot,
1415
exportCacheSnapshot,
1516
importCacheSnapshot,
16-
type CacheSnapshot,
1717
} from './persistence';
1818
import { isCacheObject } from './utils';
1919

@@ -182,18 +182,34 @@ export class Cache {
182182
/** Subscription paths and it's listener function. */
183183
#subscriptions = new Map<readonly string[], CacheListener>();
184184

185+
/** Subscriptions for all paths. */
186+
#globalSubscriptions = new Set<CacheListener>();
187+
185188
/** Subscription paths that reached a normalized object. */
186189
#normalizedSubscriptions = new MultiDict<CacheObject, CacheListener>();
187190

188191
/** Subscribe to cache changes. */
189-
subscribe(paths: string[], fn: CacheListener) {
190-
const pathsSnapshot = Object.freeze([...paths]);
192+
subscribe(fn: CacheListener): () => void;
193+
subscribe(paths: string[], fn: CacheListener): () => void;
194+
subscribe(arg1: string[] | CacheListener, arg2?: CacheListener) {
195+
if (typeof arg1 === 'function') {
196+
this.#globalSubscriptions.add(arg1);
197+
198+
return () => {
199+
this.#globalSubscriptions.delete(arg1);
200+
};
201+
}
202+
203+
if (!arg2) return () => {};
204+
205+
const fn = arg2!;
206+
const paths = Object.freeze(arg1);
191207

192-
this.#subscriptions.set(pathsSnapshot, fn);
193-
this.#subscribeNormalized(pathsSnapshot, fn);
208+
this.#subscriptions.set(paths, fn);
209+
this.#subscribeNormalized(paths, fn);
194210

195211
return () => {
196-
this.#subscriptions.delete(pathsSnapshot);
212+
this.#subscriptions.delete(paths);
197213
this.#normalizedSubscriptions.delete(fn);
198214
};
199215
}
@@ -230,7 +246,7 @@ export class Cache {
230246
#notifySubscribers = (value: CacheRoot) => {
231247
// Collect all relevant listeners from both path selections and
232248
// normalized objects in a unique Set.
233-
const listeners = new Set<CacheListener>();
249+
const listeners = new Set<CacheListener>(this.#globalSubscriptions);
234250
const subs = this.#subscriptions;
235251
const nsubs = this.#normalizedSubscriptions;
236252
const getId = this.normalizationOptions?.identity;

packages/gqty/test/cache.test.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -404,7 +404,7 @@ describe('Cache#subscribe', () => {
404404
},
405405
});
406406

407-
expect(listener).toHaveBeenCalledWith({
407+
expect(listener).toHaveBeenLastCalledWith({
408408
query: { c: { a: { __typename: 'A', id: 1, a: 1, b: 2 } } },
409409
});
410410

@@ -527,4 +527,25 @@ describe('Cache#subscribe', () => {
527527
}
528528
`);
529529
});
530+
531+
it('should notify global subscribers with delta payloads', () => {
532+
const cache = new Cache();
533+
const listener = jest.fn();
534+
const unsub = cache.subscribe(listener);
535+
536+
cache.set({ query: { a: 1 } });
537+
cache.set({ mutation: { doThing: true } });
538+
cache.set({ subscription: { newEvent: 123 } });
539+
540+
expect(listener).toHaveBeenCalledTimes(3);
541+
expect(listener).toHaveBeenNthCalledWith(1, { query: { a: 1 } });
542+
expect(listener).toHaveBeenNthCalledWith(2, {
543+
mutation: { doThing: true },
544+
});
545+
expect(listener).toHaveBeenNthCalledWith(3, {
546+
subscription: { newEvent: 123 },
547+
});
548+
549+
unsub();
550+
});
530551
});

0 commit comments

Comments
 (0)