1 | import { __assign, __rest } from "tslib";
|
2 | import { invariant, newInvariantError } from "../../utilities/globals/index.js";
|
3 | import { storeKeyNameFromField, argumentsObjectFromField, isReference, getStoreKeyName, isNonNullObject, stringifyForDisplay, } from "../../utilities/index.js";
|
4 | import { hasOwn, fieldNameFromStoreName, storeValueIsStoreObject, selectionSetMatchesResult, TypeOrFieldNameRegExp, defaultDataIdFromObject, isArray, } from "./helpers.js";
|
5 | import { cacheSlot } from "./reactiveVars.js";
|
6 | import { keyArgsFnFromSpecifier, keyFieldsFnFromSpecifier, } from "./key-extractor.js";
|
7 | function argsFromFieldSpecifier(spec) {
|
8 | return (spec.args !== void 0 ? spec.args
|
9 | : spec.field ? argumentsObjectFromField(spec.field, spec.variables)
|
10 | : null);
|
11 | }
|
12 | var nullKeyFieldsFn = function () { return void 0; };
|
13 | var simpleKeyArgsFn = function (_args, context) { return context.fieldName; };
|
14 | // These merge functions can be selected by specifying merge:true or
|
15 | // merge:false in a field policy.
|
16 | var mergeTrueFn = function (existing, incoming, _a) {
|
17 | var mergeObjects = _a.mergeObjects;
|
18 | return mergeObjects(existing, incoming);
|
19 | };
|
20 | var mergeFalseFn = function (_, incoming) { return incoming; };
|
21 | var Policies = /** @class */ (function () {
|
22 | function Policies(config) {
|
23 | this.config = config;
|
24 | this.typePolicies = Object.create(null);
|
25 | this.toBeAdded = Object.create(null);
|
26 | // Map from subtype names to sets of supertype names. Note that this
|
27 | // representation inverts the structure of possibleTypes (whose keys are
|
28 | // supertypes and whose values are arrays of subtypes) because it tends
|
29 | // to be much more efficient to search upwards than downwards.
|
30 | this.supertypeMap = new Map();
|
31 | // Any fuzzy subtypes specified by possibleTypes will be converted to
|
32 | // RegExp objects and recorded here. Every key of this map can also be
|
33 | // found in supertypeMap. In many cases this Map will be empty, which
|
34 | // means no fuzzy subtype checking will happen in fragmentMatches.
|
35 | this.fuzzySubtypes = new Map();
|
36 | this.rootIdsByTypename = Object.create(null);
|
37 | this.rootTypenamesById = Object.create(null);
|
38 | this.usingPossibleTypes = false;
|
39 | this.config = __assign({ dataIdFromObject: defaultDataIdFromObject }, config);
|
40 | this.cache = this.config.cache;
|
41 | this.setRootTypename("Query");
|
42 | this.setRootTypename("Mutation");
|
43 | this.setRootTypename("Subscription");
|
44 | if (config.possibleTypes) {
|
45 | this.addPossibleTypes(config.possibleTypes);
|
46 | }
|
47 | if (config.typePolicies) {
|
48 | this.addTypePolicies(config.typePolicies);
|
49 | }
|
50 | }
|
51 | Policies.prototype.identify = function (object, partialContext) {
|
52 | var _a;
|
53 | var policies = this;
|
54 | var typename = (partialContext &&
|
55 | (partialContext.typename || ((_a = partialContext.storeObject) === null || _a === void 0 ? void 0 : _a.__typename))) ||
|
56 | object.__typename;
|
57 | // It should be possible to write root Query fields with writeFragment,
|
58 | // using { __typename: "Query", ... } as the data, but it does not make
|
59 | // sense to allow the same identification behavior for the Mutation and
|
60 | // Subscription types, since application code should never be writing
|
61 | // directly to (or reading directly from) those root objects.
|
62 | if (typename === this.rootTypenamesById.ROOT_QUERY) {
|
63 | return ["ROOT_QUERY"];
|
64 | }
|
65 | // Default context.storeObject to object if not otherwise provided.
|
66 | var storeObject = (partialContext && partialContext.storeObject) || object;
|
67 | var context = __assign(__assign({}, partialContext), { typename: typename, storeObject: storeObject, readField: (partialContext && partialContext.readField) ||
|
68 | function () {
|
69 | var options = normalizeReadFieldOptions(arguments, storeObject);
|
70 | return policies.readField(options, {
|
71 | store: policies.cache["data"],
|
72 | variables: options.variables,
|
73 | });
|
74 | } });
|
75 | var id;
|
76 | var policy = typename && this.getTypePolicy(typename);
|
77 | var keyFn = (policy && policy.keyFn) || this.config.dataIdFromObject;
|
78 | while (keyFn) {
|
79 | var specifierOrId = keyFn(__assign(__assign({}, object), storeObject), context);
|
80 | if (isArray(specifierOrId)) {
|
81 | keyFn = keyFieldsFnFromSpecifier(specifierOrId);
|
82 | }
|
83 | else {
|
84 | id = specifierOrId;
|
85 | break;
|
86 | }
|
87 | }
|
88 | id = id ? String(id) : void 0;
|
89 | return context.keyObject ? [id, context.keyObject] : [id];
|
90 | };
|
91 | Policies.prototype.addTypePolicies = function (typePolicies) {
|
92 | var _this = this;
|
93 | Object.keys(typePolicies).forEach(function (typename) {
|
94 | var _a = typePolicies[typename], queryType = _a.queryType, mutationType = _a.mutationType, subscriptionType = _a.subscriptionType, incoming = __rest(_a, ["queryType", "mutationType", "subscriptionType"]);
|
95 | // Though {query,mutation,subscription}Type configurations are rare,
|
96 | // it's important to call setRootTypename as early as possible,
|
97 | // since these configurations should apply consistently for the
|
98 | // entire lifetime of the cache. Also, since only one __typename can
|
99 | // qualify as one of these root types, these three properties cannot
|
100 | // be inherited, unlike the rest of the incoming properties. That
|
101 | // restriction is convenient, because the purpose of this.toBeAdded
|
102 | // is to delay the processing of type/field policies until the first
|
103 | // time they're used, allowing policies to be added in any order as
|
104 | // long as all relevant policies (including policies for supertypes)
|
105 | // have been added by the time a given policy is used for the first
|
106 | // time. In other words, since inheritance doesn't matter for these
|
107 | // properties, there's also no need to delay their processing using
|
108 | // the this.toBeAdded queue.
|
109 | if (queryType)
|
110 | _this.setRootTypename("Query", typename);
|
111 | if (mutationType)
|
112 | _this.setRootTypename("Mutation", typename);
|
113 | if (subscriptionType)
|
114 | _this.setRootTypename("Subscription", typename);
|
115 | if (hasOwn.call(_this.toBeAdded, typename)) {
|
116 | _this.toBeAdded[typename].push(incoming);
|
117 | }
|
118 | else {
|
119 | _this.toBeAdded[typename] = [incoming];
|
120 | }
|
121 | });
|
122 | };
|
123 | Policies.prototype.updateTypePolicy = function (typename, incoming) {
|
124 | var _this = this;
|
125 | var existing = this.getTypePolicy(typename);
|
126 | var keyFields = incoming.keyFields, fields = incoming.fields;
|
127 | function setMerge(existing, merge) {
|
128 | existing.merge =
|
129 | typeof merge === "function" ? merge
|
130 | // Pass merge:true as a shorthand for a merge implementation
|
131 | // that returns options.mergeObjects(existing, incoming).
|
132 | : merge === true ? mergeTrueFn
|
133 | // Pass merge:false to make incoming always replace existing
|
134 | // without any warnings about data clobbering.
|
135 | : merge === false ? mergeFalseFn
|
136 | : existing.merge;
|
137 | }
|
138 | // Type policies can define merge functions, as an alternative to
|
139 | // using field policies to merge child objects.
|
140 | setMerge(existing, incoming.merge);
|
141 | existing.keyFn =
|
142 | // Pass false to disable normalization for this typename.
|
143 | keyFields === false ? nullKeyFieldsFn
|
144 | // Pass an array of strings to use those fields to compute a
|
145 | // composite ID for objects of this typename.
|
146 | : isArray(keyFields) ? keyFieldsFnFromSpecifier(keyFields)
|
147 | // Pass a function to take full control over identification.
|
148 | : typeof keyFields === "function" ? keyFields
|
149 | // Leave existing.keyFn unchanged if above cases fail.
|
150 | : existing.keyFn;
|
151 | if (fields) {
|
152 | Object.keys(fields).forEach(function (fieldName) {
|
153 | var existing = _this.getFieldPolicy(typename, fieldName, true);
|
154 | var incoming = fields[fieldName];
|
155 | if (typeof incoming === "function") {
|
156 | existing.read = incoming;
|
157 | }
|
158 | else {
|
159 | var keyArgs = incoming.keyArgs, read = incoming.read, merge = incoming.merge;
|
160 | existing.keyFn =
|
161 | // Pass false to disable argument-based differentiation of
|
162 | // field identities.
|
163 | keyArgs === false ? simpleKeyArgsFn
|
164 | // Pass an array of strings to use named arguments to
|
165 | // compute a composite identity for the field.
|
166 | : isArray(keyArgs) ? keyArgsFnFromSpecifier(keyArgs)
|
167 | // Pass a function to take full control over field identity.
|
168 | : typeof keyArgs === "function" ? keyArgs
|
169 | // Leave existing.keyFn unchanged if above cases fail.
|
170 | : existing.keyFn;
|
171 | if (typeof read === "function") {
|
172 | existing.read = read;
|
173 | }
|
174 | setMerge(existing, merge);
|
175 | }
|
176 | if (existing.read && existing.merge) {
|
177 | // If we have both a read and a merge function, assume
|
178 | // keyArgs:false, because read and merge together can take
|
179 | // responsibility for interpreting arguments in and out. This
|
180 | // default assumption can always be overridden by specifying
|
181 | // keyArgs explicitly in the FieldPolicy.
|
182 | existing.keyFn = existing.keyFn || simpleKeyArgsFn;
|
183 | }
|
184 | });
|
185 | }
|
186 | };
|
187 | Policies.prototype.setRootTypename = function (which, typename) {
|
188 | if (typename === void 0) { typename = which; }
|
189 | var rootId = "ROOT_" + which.toUpperCase();
|
190 | var old = this.rootTypenamesById[rootId];
|
191 | if (typename !== old) {
|
192 | invariant(!old || old === which, 5, which);
|
193 | // First, delete any old __typename associated with this rootId from
|
194 | // rootIdsByTypename.
|
195 | if (old)
|
196 | delete this.rootIdsByTypename[old];
|
197 | // Now make this the only __typename that maps to this rootId.
|
198 | this.rootIdsByTypename[typename] = rootId;
|
199 | // Finally, update the __typename associated with this rootId.
|
200 | this.rootTypenamesById[rootId] = typename;
|
201 | }
|
202 | };
|
203 | Policies.prototype.addPossibleTypes = function (possibleTypes) {
|
204 | var _this = this;
|
205 | this.usingPossibleTypes = true;
|
206 | Object.keys(possibleTypes).forEach(function (supertype) {
|
207 | // Make sure all types have an entry in this.supertypeMap, even if
|
208 | // their supertype set is empty, so we can return false immediately
|
209 | // from policies.fragmentMatches for unknown supertypes.
|
210 | _this.getSupertypeSet(supertype, true);
|
211 | possibleTypes[supertype].forEach(function (subtype) {
|
212 | _this.getSupertypeSet(subtype, true).add(supertype);
|
213 | var match = subtype.match(TypeOrFieldNameRegExp);
|
214 | if (!match || match[0] !== subtype) {
|
215 | // TODO Don't interpret just any invalid typename as a RegExp.
|
216 | _this.fuzzySubtypes.set(subtype, new RegExp(subtype));
|
217 | }
|
218 | });
|
219 | });
|
220 | };
|
221 | Policies.prototype.getTypePolicy = function (typename) {
|
222 | var _this = this;
|
223 | if (!hasOwn.call(this.typePolicies, typename)) {
|
224 | var policy_1 = (this.typePolicies[typename] = Object.create(null));
|
225 | policy_1.fields = Object.create(null);
|
226 | // When the TypePolicy for typename is first accessed, instead of
|
227 | // starting with an empty policy object, inherit any properties or
|
228 | // fields from the type policies of the supertypes of typename.
|
229 | //
|
230 | // Any properties or fields defined explicitly within the TypePolicy
|
231 | // for typename will take precedence, and if there are multiple
|
232 | // supertypes, the properties of policies whose types were added
|
233 | // later via addPossibleTypes will take precedence over those of
|
234 | // earlier supertypes. TODO Perhaps we should warn about these
|
235 | // conflicts in development, and recommend defining the property
|
236 | // explicitly in the subtype policy?
|
237 | //
|
238 | // Field policy inheritance is atomic/shallow: you can't inherit a
|
239 | // field policy and then override just its read function, since read
|
240 | // and merge functions often need to cooperate, so changing only one
|
241 | // of them would be a recipe for inconsistency.
|
242 | //
|
243 | // Once the TypePolicy for typename has been accessed, its properties can
|
244 | // still be updated directly using addTypePolicies, but future changes to
|
245 | // inherited supertype policies will not be reflected in this subtype
|
246 | // policy, because this code runs at most once per typename.
|
247 | var supertypes_1 = this.supertypeMap.get(typename);
|
248 | if (!supertypes_1 && this.fuzzySubtypes.size) {
|
249 | // To make the inheritance logic work for unknown typename strings that
|
250 | // may have fuzzy supertypes, we give this typename an empty supertype
|
251 | // set and then populate it with any fuzzy supertypes that match.
|
252 | supertypes_1 = this.getSupertypeSet(typename, true);
|
253 | // This only works for typenames that are directly matched by a fuzzy
|
254 | // supertype. What if there is an intermediate chain of supertypes?
|
255 | // While possible, that situation can only be solved effectively by
|
256 | // specifying the intermediate relationships via possibleTypes, manually
|
257 | // and in a non-fuzzy way.
|
258 | this.fuzzySubtypes.forEach(function (regExp, fuzzy) {
|
259 | if (regExp.test(typename)) {
|
260 | // The fuzzy parameter is just the original string version of regExp
|
261 | // (not a valid __typename string), but we can look up the
|
262 | // associated supertype(s) in this.supertypeMap.
|
263 | var fuzzySupertypes = _this.supertypeMap.get(fuzzy);
|
264 | if (fuzzySupertypes) {
|
265 | fuzzySupertypes.forEach(function (supertype) {
|
266 | return supertypes_1.add(supertype);
|
267 | });
|
268 | }
|
269 | }
|
270 | });
|
271 | }
|
272 | if (supertypes_1 && supertypes_1.size) {
|
273 | supertypes_1.forEach(function (supertype) {
|
274 | var _a = _this.getTypePolicy(supertype), fields = _a.fields, rest = __rest(_a, ["fields"]);
|
275 | Object.assign(policy_1, rest);
|
276 | Object.assign(policy_1.fields, fields);
|
277 | });
|
278 | }
|
279 | }
|
280 | var inbox = this.toBeAdded[typename];
|
281 | if (inbox && inbox.length) {
|
282 | // Merge the pending policies into this.typePolicies, in the order they
|
283 | // were originally passed to addTypePolicy.
|
284 | inbox.splice(0).forEach(function (policy) {
|
285 | _this.updateTypePolicy(typename, policy);
|
286 | });
|
287 | }
|
288 | return this.typePolicies[typename];
|
289 | };
|
290 | Policies.prototype.getFieldPolicy = function (typename, fieldName, createIfMissing) {
|
291 | if (typename) {
|
292 | var fieldPolicies = this.getTypePolicy(typename).fields;
|
293 | return (fieldPolicies[fieldName] ||
|
294 | (createIfMissing && (fieldPolicies[fieldName] = Object.create(null))));
|
295 | }
|
296 | };
|
297 | Policies.prototype.getSupertypeSet = function (subtype, createIfMissing) {
|
298 | var supertypeSet = this.supertypeMap.get(subtype);
|
299 | if (!supertypeSet && createIfMissing) {
|
300 | this.supertypeMap.set(subtype, (supertypeSet = new Set()));
|
301 | }
|
302 | return supertypeSet;
|
303 | };
|
304 | Policies.prototype.fragmentMatches = function (fragment, typename, result, variables) {
|
305 | var _this = this;
|
306 | if (!fragment.typeCondition)
|
307 | return true;
|
308 | // If the fragment has a type condition but the object we're matching
|
309 | // against does not have a __typename, the fragment cannot match.
|
310 | if (!typename)
|
311 | return false;
|
312 | var supertype = fragment.typeCondition.name.value;
|
313 | // Common case: fragment type condition and __typename are the same.
|
314 | if (typename === supertype)
|
315 | return true;
|
316 | if (this.usingPossibleTypes && this.supertypeMap.has(supertype)) {
|
317 | var typenameSupertypeSet = this.getSupertypeSet(typename, true);
|
318 | var workQueue_1 = [typenameSupertypeSet];
|
319 | var maybeEnqueue_1 = function (subtype) {
|
320 | var supertypeSet = _this.getSupertypeSet(subtype, false);
|
321 | if (supertypeSet &&
|
322 | supertypeSet.size &&
|
323 | workQueue_1.indexOf(supertypeSet) < 0) {
|
324 | workQueue_1.push(supertypeSet);
|
325 | }
|
326 | };
|
327 | // We need to check fuzzy subtypes only if we encountered fuzzy
|
328 | // subtype strings in addPossibleTypes, and only while writing to
|
329 | // the cache, since that's when selectionSetMatchesResult gives a
|
330 | // strong signal of fragment matching. The StoreReader class calls
|
331 | // policies.fragmentMatches without passing a result object, so
|
332 | // needToCheckFuzzySubtypes is always false while reading.
|
333 | var needToCheckFuzzySubtypes = !!(result && this.fuzzySubtypes.size);
|
334 | var checkingFuzzySubtypes = false;
|
335 | // It's important to keep evaluating workQueue.length each time through
|
336 | // the loop, because the queue can grow while we're iterating over it.
|
337 | for (var i = 0; i < workQueue_1.length; ++i) {
|
338 | var supertypeSet = workQueue_1[i];
|
339 | if (supertypeSet.has(supertype)) {
|
340 | if (!typenameSupertypeSet.has(supertype)) {
|
341 | if (checkingFuzzySubtypes) {
|
342 | globalThis.__DEV__ !== false && invariant.warn(6, typename, supertype);
|
343 | }
|
344 | // Record positive results for faster future lookup.
|
345 | // Unfortunately, we cannot safely cache negative results,
|
346 | // because new possibleTypes data could always be added to the
|
347 | // Policies class.
|
348 | typenameSupertypeSet.add(supertype);
|
349 | }
|
350 | return true;
|
351 | }
|
352 | supertypeSet.forEach(maybeEnqueue_1);
|
353 | if (needToCheckFuzzySubtypes &&
|
354 | // Start checking fuzzy subtypes only after exhausting all
|
355 | // non-fuzzy subtypes (after the final iteration of the loop).
|
356 | i === workQueue_1.length - 1 &&
|
357 | // We could wait to compare fragment.selectionSet to result
|
358 | // after we verify the supertype, but this check is often less
|
359 | // expensive than that search, and we will have to do the
|
360 | // comparison anyway whenever we find a potential match.
|
361 | selectionSetMatchesResult(fragment.selectionSet, result, variables)) {
|
362 | // We don't always need to check fuzzy subtypes (if no result
|
363 | // was provided, or !this.fuzzySubtypes.size), but, when we do,
|
364 | // we only want to check them once.
|
365 | needToCheckFuzzySubtypes = false;
|
366 | checkingFuzzySubtypes = true;
|
367 | // If we find any fuzzy subtypes that match typename, extend the
|
368 | // workQueue to search through the supertypes of those fuzzy
|
369 | // subtypes. Otherwise the for-loop will terminate and we'll
|
370 | // return false below.
|
371 | this.fuzzySubtypes.forEach(function (regExp, fuzzyString) {
|
372 | var match = typename.match(regExp);
|
373 | if (match && match[0] === typename) {
|
374 | maybeEnqueue_1(fuzzyString);
|
375 | }
|
376 | });
|
377 | }
|
378 | }
|
379 | }
|
380 | return false;
|
381 | };
|
382 | Policies.prototype.hasKeyArgs = function (typename, fieldName) {
|
383 | var policy = this.getFieldPolicy(typename, fieldName, false);
|
384 | return !!(policy && policy.keyFn);
|
385 | };
|
386 | Policies.prototype.getStoreFieldName = function (fieldSpec) {
|
387 | var typename = fieldSpec.typename, fieldName = fieldSpec.fieldName;
|
388 | var policy = this.getFieldPolicy(typename, fieldName, false);
|
389 | var storeFieldName;
|
390 | var keyFn = policy && policy.keyFn;
|
391 | if (keyFn && typename) {
|
392 | var context = {
|
393 | typename: typename,
|
394 | fieldName: fieldName,
|
395 | field: fieldSpec.field || null,
|
396 | variables: fieldSpec.variables,
|
397 | };
|
398 | var args = argsFromFieldSpecifier(fieldSpec);
|
399 | while (keyFn) {
|
400 | var specifierOrString = keyFn(args, context);
|
401 | if (isArray(specifierOrString)) {
|
402 | keyFn = keyArgsFnFromSpecifier(specifierOrString);
|
403 | }
|
404 | else {
|
405 | // If the custom keyFn returns a falsy value, fall back to
|
406 | // fieldName instead.
|
407 | storeFieldName = specifierOrString || fieldName;
|
408 | break;
|
409 | }
|
410 | }
|
411 | }
|
412 | if (storeFieldName === void 0) {
|
413 | storeFieldName =
|
414 | fieldSpec.field ?
|
415 | storeKeyNameFromField(fieldSpec.field, fieldSpec.variables)
|
416 | : getStoreKeyName(fieldName, argsFromFieldSpecifier(fieldSpec));
|
417 | }
|
418 | // Returning false from a keyArgs function is like configuring
|
419 | // keyArgs: false, but more dynamic.
|
420 | if (storeFieldName === false) {
|
421 | return fieldName;
|
422 | }
|
423 | // Make sure custom field names start with the actual field.name.value
|
424 | // of the field, so we can always figure out which properties of a
|
425 | // StoreObject correspond to which original field names.
|
426 | return fieldName === fieldNameFromStoreName(storeFieldName) ? storeFieldName
|
427 | : fieldName + ":" + storeFieldName;
|
428 | };
|
429 | Policies.prototype.readField = function (options, context) {
|
430 | var objectOrReference = options.from;
|
431 | if (!objectOrReference)
|
432 | return;
|
433 | var nameOrField = options.field || options.fieldName;
|
434 | if (!nameOrField)
|
435 | return;
|
436 | if (options.typename === void 0) {
|
437 | var typename = context.store.getFieldValue(objectOrReference, "__typename");
|
438 | if (typename)
|
439 | options.typename = typename;
|
440 | }
|
441 | var storeFieldName = this.getStoreFieldName(options);
|
442 | var fieldName = fieldNameFromStoreName(storeFieldName);
|
443 | var existing = context.store.getFieldValue(objectOrReference, storeFieldName);
|
444 | var policy = this.getFieldPolicy(options.typename, fieldName, false);
|
445 | var read = policy && policy.read;
|
446 | if (read) {
|
447 | var readOptions = makeFieldFunctionOptions(this, objectOrReference, options, context, context.store.getStorage(isReference(objectOrReference) ?
|
448 | objectOrReference.__ref
|
449 | : objectOrReference, storeFieldName));
|
450 | // Call read(existing, readOptions) with cacheSlot holding this.cache.
|
451 | return cacheSlot.withValue(this.cache, read, [
|
452 | existing,
|
453 | readOptions,
|
454 | ]);
|
455 | }
|
456 | return existing;
|
457 | };
|
458 | Policies.prototype.getReadFunction = function (typename, fieldName) {
|
459 | var policy = this.getFieldPolicy(typename, fieldName, false);
|
460 | return policy && policy.read;
|
461 | };
|
462 | Policies.prototype.getMergeFunction = function (parentTypename, fieldName, childTypename) {
|
463 | var policy = this.getFieldPolicy(parentTypename, fieldName, false);
|
464 | var merge = policy && policy.merge;
|
465 | if (!merge && childTypename) {
|
466 | policy = this.getTypePolicy(childTypename);
|
467 | merge = policy && policy.merge;
|
468 | }
|
469 | return merge;
|
470 | };
|
471 | Policies.prototype.runMergeFunction = function (existing, incoming, _a, context, storage) {
|
472 | var field = _a.field, typename = _a.typename, merge = _a.merge;
|
473 | if (merge === mergeTrueFn) {
|
474 | // Instead of going to the trouble of creating a full
|
475 | // FieldFunctionOptions object and calling mergeTrueFn, we can
|
476 | // simply call mergeObjects, as mergeTrueFn would.
|
477 | return makeMergeObjectsFunction(context.store)(existing, incoming);
|
478 | }
|
479 | if (merge === mergeFalseFn) {
|
480 | // Likewise for mergeFalseFn, whose implementation is even simpler.
|
481 | return incoming;
|
482 | }
|
483 | // If cache.writeQuery or cache.writeFragment was called with
|
484 | // options.overwrite set to true, we still call merge functions, but
|
485 | // the existing data is always undefined, so the merge function will
|
486 | // not attempt to combine the incoming data with the existing data.
|
487 | if (context.overwrite) {
|
488 | existing = void 0;
|
489 | }
|
490 | return merge(existing, incoming, makeFieldFunctionOptions(this,
|
491 | // Unlike options.readField for read functions, we do not fall
|
492 | // back to the current object if no foreignObjOrRef is provided,
|
493 | // because it's not clear what the current object should be for
|
494 | // merge functions: the (possibly undefined) existing object, or
|
495 | // the incoming object? If you think your merge function needs
|
496 | // to read sibling fields in order to produce a new value for
|
497 | // the current field, you might want to rethink your strategy,
|
498 | // because that's a recipe for making merge behavior sensitive
|
499 | // to the order in which fields are written into the cache.
|
500 | // However, readField(name, ref) is useful for merge functions
|
501 | // that need to deduplicate child objects and references.
|
502 | void 0, {
|
503 | typename: typename,
|
504 | fieldName: field.name.value,
|
505 | field: field,
|
506 | variables: context.variables,
|
507 | }, context, storage || Object.create(null)));
|
508 | };
|
509 | return Policies;
|
510 | }());
|
511 | export { Policies };
|
512 | function makeFieldFunctionOptions(policies, objectOrReference, fieldSpec, context, storage) {
|
513 | var storeFieldName = policies.getStoreFieldName(fieldSpec);
|
514 | var fieldName = fieldNameFromStoreName(storeFieldName);
|
515 | var variables = fieldSpec.variables || context.variables;
|
516 | var _a = context.store, toReference = _a.toReference, canRead = _a.canRead;
|
517 | return {
|
518 | args: argsFromFieldSpecifier(fieldSpec),
|
519 | field: fieldSpec.field || null,
|
520 | fieldName: fieldName,
|
521 | storeFieldName: storeFieldName,
|
522 | variables: variables,
|
523 | isReference: isReference,
|
524 | toReference: toReference,
|
525 | storage: storage,
|
526 | cache: policies.cache,
|
527 | canRead: canRead,
|
528 | readField: function () {
|
529 | return policies.readField(normalizeReadFieldOptions(arguments, objectOrReference, variables), context);
|
530 | },
|
531 | mergeObjects: makeMergeObjectsFunction(context.store),
|
532 | };
|
533 | }
|
534 | export function normalizeReadFieldOptions(readFieldArgs, objectOrReference, variables) {
|
535 | var fieldNameOrOptions = readFieldArgs[0], from = readFieldArgs[1], argc = readFieldArgs.length;
|
536 | var options;
|
537 | if (typeof fieldNameOrOptions === "string") {
|
538 | options = {
|
539 | fieldName: fieldNameOrOptions,
|
540 | // Default to objectOrReference only when no second argument was
|
541 | // passed for the from parameter, not when undefined is explicitly
|
542 | // passed as the second argument.
|
543 | from: argc > 1 ? from : objectOrReference,
|
544 | };
|
545 | }
|
546 | else {
|
547 | options = __assign({}, fieldNameOrOptions);
|
548 | // Default to objectOrReference only when fieldNameOrOptions.from is
|
549 | // actually omitted, rather than just undefined.
|
550 | if (!hasOwn.call(options, "from")) {
|
551 | options.from = objectOrReference;
|
552 | }
|
553 | }
|
554 | if (globalThis.__DEV__ !== false && options.from === void 0) {
|
555 | globalThis.__DEV__ !== false && invariant.warn(7, stringifyForDisplay(Array.from(readFieldArgs)));
|
556 | }
|
557 | if (void 0 === options.variables) {
|
558 | options.variables = variables;
|
559 | }
|
560 | return options;
|
561 | }
|
562 | function makeMergeObjectsFunction(store) {
|
563 | return function mergeObjects(existing, incoming) {
|
564 | if (isArray(existing) || isArray(incoming)) {
|
565 | throw newInvariantError(8);
|
566 | }
|
567 | // These dynamic checks are necessary because the parameters of a
|
568 | // custom merge function can easily have the any type, so the type
|
569 | // system cannot always enforce the StoreObject | Reference parameter
|
570 | // types of options.mergeObjects.
|
571 | if (isNonNullObject(existing) && isNonNullObject(incoming)) {
|
572 | var eType = store.getFieldValue(existing, "__typename");
|
573 | var iType = store.getFieldValue(incoming, "__typename");
|
574 | var typesDiffer = eType && iType && eType !== iType;
|
575 | if (typesDiffer) {
|
576 | return incoming;
|
577 | }
|
578 | if (isReference(existing) && storeValueIsStoreObject(incoming)) {
|
579 | // Update the normalized EntityStore for the entity identified by
|
580 | // existing.__ref, preferring/overwriting any fields contributed by the
|
581 | // newer incoming StoreObject.
|
582 | store.merge(existing.__ref, incoming);
|
583 | return existing;
|
584 | }
|
585 | if (storeValueIsStoreObject(existing) && isReference(incoming)) {
|
586 | // Update the normalized EntityStore for the entity identified by
|
587 | // incoming.__ref, taking fields from the older existing object only if
|
588 | // those fields are not already present in the newer StoreObject
|
589 | // identified by incoming.__ref.
|
590 | store.merge(existing, incoming.__ref);
|
591 | return incoming;
|
592 | }
|
593 | if (storeValueIsStoreObject(existing) &&
|
594 | storeValueIsStoreObject(incoming)) {
|
595 | return __assign(__assign({}, existing), incoming);
|
596 | }
|
597 | }
|
598 | return incoming;
|
599 | };
|
600 | }
|
601 | //# sourceMappingURL=policies.js.map |
\ | No newline at end of file |