Skip to content

Commit 038c2c0

Browse files
authored
feat(graphcache): add possibleTypes config for deterministic fragment matching (#3805)
1 parent b0a0f32 commit 038c2c0

File tree

4 files changed

+57
-10
lines changed

4 files changed

+57
-10
lines changed

.changeset/unlucky-apes-change.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@urql/exchange-graphcache': minor
3+
---
4+
5+
Add possibleTypes config for deterministic fragment matching

exchanges/graphcache/src/operations/shared.ts

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -234,18 +234,24 @@ export class SelectionIterator {
234234
fragment,
235235
this.typename
236236
)
237-
: (currentOperation === 'read' &&
238-
isFragmentMatching(
237+
: this.ctx.store.possibleTypeMap
238+
? isSuperType(
239+
this.ctx.store.possibleTypeMap,
239240
fragment.typeCondition.name.value,
240241
this.typename
241-
)) ||
242-
isFragmentHeuristicallyMatching(
243-
fragment,
244-
this.typename,
245-
this.entityKey,
246-
this.ctx.variables,
247-
this.ctx.store.logger
248-
));
242+
)
243+
: (currentOperation === 'read' &&
244+
isFragmentMatching(
245+
fragment.typeCondition.name.value,
246+
this.typename
247+
)) ||
248+
isFragmentHeuristicallyMatching(
249+
fragment,
250+
this.typename,
251+
this.entityKey,
252+
this.ctx.variables,
253+
this.ctx.store.logger
254+
));
249255
if (
250256
isMatching ||
251257
(currentOperation === 'write' && !this.ctx.store.schema)
@@ -290,6 +296,19 @@ export class SelectionIterator {
290296
}
291297
}
292298

299+
const isSuperType = (
300+
possibleTypeMap: Map<string, Set<string>>,
301+
typeCondition: string,
302+
typename: string | void
303+
) => {
304+
if (!typename) return false;
305+
if (typeCondition === typename) return true;
306+
307+
const concreteTypes = possibleTypeMap.get(typeCondition);
308+
309+
return concreteTypes && concreteTypes.has(typename);
310+
};
311+
293312
const isFragmentMatching = (typeCondition: string, typename: string | void) => {
294313
if (!typename) return false;
295314
if (typeCondition === typename) return true;

exchanges/graphcache/src/store/store.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export class Store<
5757
keys: KeyingConfig;
5858
globalIDs: Set<string> | boolean;
5959
schema?: SchemaIntrospector;
60+
possibleTypeMap?: Map<string, Set<string>>;
6061

6162
rootFields: { query: string; mutation: string; subscription: string };
6263
rootNames: { [name: string]: RootField | void };
@@ -86,6 +87,14 @@ export class Store<
8687
if (schema.types) this.schema = schema;
8788
}
8889

90+
if (!this.schema && opts.possibleTypes) {
91+
this.possibleTypeMap = new Map();
92+
for (const entry of Object.entries(opts.possibleTypes)) {
93+
const [abstractType, concreteTypes] = entry;
94+
this.possibleTypeMap.set(abstractType, new Set(concreteTypes));
95+
}
96+
}
97+
8998
this.updates = opts.updates || {};
9099

91100
this.rootFields = {

exchanges/graphcache/src/types.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -618,6 +618,16 @@ export type CacheExchangeOpts = {
618618
* type names may be passed instead.
619619
*/
620620
globalIDs?: string[] | boolean;
621+
/** Configures abstract to concrete types mapping for GraphQL types.
622+
*
623+
* @remarks
624+
* This will disable heuristic fragment matching, allowing Graphcache to match
625+
* fragment deterministically.
626+
*
627+
* When both `possibleTypes` and `schema` is set, `possibleTypes` value will be
628+
* ignored.
629+
*/
630+
possibleTypes?: PossibleTypesConfig;
621631
/** Configures Graphcache with Schema Introspection data.
622632
*
623633
* @remarks
@@ -932,6 +942,10 @@ export type KeyingConfig = {
932942
[typename: string]: KeyGenerator;
933943
};
934944

945+
export type PossibleTypesConfig = {
946+
[abstractType: string]: string[];
947+
};
948+
935949
/** Serialized normalized caching data. */
936950
export interface SerializedEntries {
937951
[key: string]: string | undefined;

0 commit comments

Comments
 (0)