UNPKG

@apollo/client

Version:

A fully-featured caching GraphQL client.

416 lines 18.9 kB
import { __assign, __rest } from "tslib"; import { invariant, InvariantError } from "../../utilities/globals/index.js"; import { storeKeyNameFromField, argumentsObjectFromField, isReference, getStoreKeyName, isNonNullObject, stringifyForDisplay, } from "../../utilities/index.js"; import { hasOwn, fieldNameFromStoreName, storeValueIsStoreObject, selectionSetMatchesResult, TypeOrFieldNameRegExp, defaultDataIdFromObject, isArray, } from "./helpers.js"; import { cacheSlot } from "./reactiveVars.js"; import { canonicalStringify } from "./object-canon.js"; import { keyArgsFnFromSpecifier, keyFieldsFnFromSpecifier } from "./key-extractor.js"; getStoreKeyName.setStringify(canonicalStringify); function argsFromFieldSpecifier(spec) { return spec.args !== void 0 ? spec.args : spec.field ? argumentsObjectFromField(spec.field, spec.variables) : null; } var nullKeyFieldsFn = function () { return void 0; }; var simpleKeyArgsFn = function (_args, context) { return context.fieldName; }; var mergeTrueFn = function (existing, incoming, _a) { var mergeObjects = _a.mergeObjects; return mergeObjects(existing, incoming); }; var mergeFalseFn = function (_, incoming) { return incoming; }; var Policies = (function () { function Policies(config) { this.config = config; this.typePolicies = Object.create(null); this.toBeAdded = Object.create(null); this.supertypeMap = new Map(); this.fuzzySubtypes = new Map(); this.rootIdsByTypename = Object.create(null); this.rootTypenamesById = Object.create(null); this.usingPossibleTypes = false; this.config = __assign({ dataIdFromObject: defaultDataIdFromObject }, config); this.cache = this.config.cache; this.setRootTypename("Query"); this.setRootTypename("Mutation"); this.setRootTypename("Subscription"); if (config.possibleTypes) { this.addPossibleTypes(config.possibleTypes); } if (config.typePolicies) { this.addTypePolicies(config.typePolicies); } } Policies.prototype.identify = function (object, partialContext) { var _a; var policies = this; var typename = partialContext && (partialContext.typename || ((_a = partialContext.storeObject) === null || _a === void 0 ? void 0 : _a.__typename)) || object.__typename; if (typename === this.rootTypenamesById.ROOT_QUERY) { return ["ROOT_QUERY"]; } var storeObject = partialContext && partialContext.storeObject || object; var context = __assign(__assign({}, partialContext), { typename: typename, storeObject: storeObject, readField: partialContext && partialContext.readField || function () { var options = normalizeReadFieldOptions(arguments, storeObject); return policies.readField(options, { store: policies.cache["data"], variables: options.variables, }); } }); var id; var policy = typename && this.getTypePolicy(typename); var keyFn = policy && policy.keyFn || this.config.dataIdFromObject; while (keyFn) { var specifierOrId = keyFn(object, context); if (isArray(specifierOrId)) { keyFn = keyFieldsFnFromSpecifier(specifierOrId); } else { id = specifierOrId; break; } } id = id ? String(id) : void 0; return context.keyObject ? [id, context.keyObject] : [id]; }; Policies.prototype.addTypePolicies = function (typePolicies) { var _this = this; Object.keys(typePolicies).forEach(function (typename) { var _a = typePolicies[typename], queryType = _a.queryType, mutationType = _a.mutationType, subscriptionType = _a.subscriptionType, incoming = __rest(_a, ["queryType", "mutationType", "subscriptionType"]); if (queryType) _this.setRootTypename("Query", typename); if (mutationType) _this.setRootTypename("Mutation", typename); if (subscriptionType) _this.setRootTypename("Subscription", typename); if (hasOwn.call(_this.toBeAdded, typename)) { _this.toBeAdded[typename].push(incoming); } else { _this.toBeAdded[typename] = [incoming]; } }); }; Policies.prototype.updateTypePolicy = function (typename, incoming) { var _this = this; var existing = this.getTypePolicy(typename); var keyFields = incoming.keyFields, fields = incoming.fields; function setMerge(existing, merge) { existing.merge = typeof merge === "function" ? merge : merge === true ? mergeTrueFn : merge === false ? mergeFalseFn : existing.merge; } setMerge(existing, incoming.merge); existing.keyFn = keyFields === false ? nullKeyFieldsFn : isArray(keyFields) ? keyFieldsFnFromSpecifier(keyFields) : typeof keyFields === "function" ? keyFields : existing.keyFn; if (fields) { Object.keys(fields).forEach(function (fieldName) { var existing = _this.getFieldPolicy(typename, fieldName, true); var incoming = fields[fieldName]; if (typeof incoming === "function") { existing.read = incoming; } else { var keyArgs = incoming.keyArgs, read = incoming.read, merge = incoming.merge; existing.keyFn = keyArgs === false ? simpleKeyArgsFn : isArray(keyArgs) ? keyArgsFnFromSpecifier(keyArgs) : typeof keyArgs === "function" ? keyArgs : existing.keyFn; if (typeof read === "function") { existing.read = read; } setMerge(existing, merge); } if (existing.read && existing.merge) { existing.keyFn = existing.keyFn || simpleKeyArgsFn; } }); } }; Policies.prototype.setRootTypename = function (which, typename) { if (typename === void 0) { typename = which; } var rootId = "ROOT_" + which.toUpperCase(); var old = this.rootTypenamesById[rootId]; if (typename !== old) { __DEV__ ? invariant(!old || old === which, "Cannot change root ".concat(which, " __typename more than once")) : invariant(!old || old === which, 3); if (old) delete this.rootIdsByTypename[old]; this.rootIdsByTypename[typename] = rootId; this.rootTypenamesById[rootId] = typename; } }; Policies.prototype.addPossibleTypes = function (possibleTypes) { var _this = this; this.usingPossibleTypes = true; Object.keys(possibleTypes).forEach(function (supertype) { _this.getSupertypeSet(supertype, true); possibleTypes[supertype].forEach(function (subtype) { _this.getSupertypeSet(subtype, true).add(supertype); var match = subtype.match(TypeOrFieldNameRegExp); if (!match || match[0] !== subtype) { _this.fuzzySubtypes.set(subtype, new RegExp(subtype)); } }); }); }; Policies.prototype.getTypePolicy = function (typename) { var _this = this; if (!hasOwn.call(this.typePolicies, typename)) { var policy_1 = this.typePolicies[typename] = Object.create(null); policy_1.fields = Object.create(null); var supertypes = this.supertypeMap.get(typename); if (supertypes && supertypes.size) { supertypes.forEach(function (supertype) { var _a = _this.getTypePolicy(supertype), fields = _a.fields, rest = __rest(_a, ["fields"]); Object.assign(policy_1, rest); Object.assign(policy_1.fields, fields); }); } } var inbox = this.toBeAdded[typename]; if (inbox && inbox.length) { inbox.splice(0).forEach(function (policy) { _this.updateTypePolicy(typename, policy); }); } return this.typePolicies[typename]; }; Policies.prototype.getFieldPolicy = function (typename, fieldName, createIfMissing) { if (typename) { var fieldPolicies = this.getTypePolicy(typename).fields; return fieldPolicies[fieldName] || (createIfMissing && (fieldPolicies[fieldName] = Object.create(null))); } }; Policies.prototype.getSupertypeSet = function (subtype, createIfMissing) { var supertypeSet = this.supertypeMap.get(subtype); if (!supertypeSet && createIfMissing) { this.supertypeMap.set(subtype, supertypeSet = new Set()); } return supertypeSet; }; Policies.prototype.fragmentMatches = function (fragment, typename, result, variables) { var _this = this; if (!fragment.typeCondition) return true; if (!typename) return false; var supertype = fragment.typeCondition.name.value; if (typename === supertype) return true; if (this.usingPossibleTypes && this.supertypeMap.has(supertype)) { var typenameSupertypeSet = this.getSupertypeSet(typename, true); var workQueue_1 = [typenameSupertypeSet]; var maybeEnqueue_1 = function (subtype) { var supertypeSet = _this.getSupertypeSet(subtype, false); if (supertypeSet && supertypeSet.size && workQueue_1.indexOf(supertypeSet) < 0) { workQueue_1.push(supertypeSet); } }; var needToCheckFuzzySubtypes = !!(result && this.fuzzySubtypes.size); var checkingFuzzySubtypes = false; for (var i = 0; i < workQueue_1.length; ++i) { var supertypeSet = workQueue_1[i]; if (supertypeSet.has(supertype)) { if (!typenameSupertypeSet.has(supertype)) { if (checkingFuzzySubtypes) { __DEV__ && invariant.warn("Inferring subtype ".concat(typename, " of supertype ").concat(supertype)); } typenameSupertypeSet.add(supertype); } return true; } supertypeSet.forEach(maybeEnqueue_1); if (needToCheckFuzzySubtypes && i === workQueue_1.length - 1 && selectionSetMatchesResult(fragment.selectionSet, result, variables)) { needToCheckFuzzySubtypes = false; checkingFuzzySubtypes = true; this.fuzzySubtypes.forEach(function (regExp, fuzzyString) { var match = typename.match(regExp); if (match && match[0] === typename) { maybeEnqueue_1(fuzzyString); } }); } } } return false; }; Policies.prototype.hasKeyArgs = function (typename, fieldName) { var policy = this.getFieldPolicy(typename, fieldName, false); return !!(policy && policy.keyFn); }; Policies.prototype.getStoreFieldName = function (fieldSpec) { var typename = fieldSpec.typename, fieldName = fieldSpec.fieldName; var policy = this.getFieldPolicy(typename, fieldName, false); var storeFieldName; var keyFn = policy && policy.keyFn; if (keyFn && typename) { var context = { typename: typename, fieldName: fieldName, field: fieldSpec.field || null, variables: fieldSpec.variables, }; var args = argsFromFieldSpecifier(fieldSpec); while (keyFn) { var specifierOrString = keyFn(args, context); if (isArray(specifierOrString)) { keyFn = keyArgsFnFromSpecifier(specifierOrString); } else { storeFieldName = specifierOrString || fieldName; break; } } } if (storeFieldName === void 0) { storeFieldName = fieldSpec.field ? storeKeyNameFromField(fieldSpec.field, fieldSpec.variables) : getStoreKeyName(fieldName, argsFromFieldSpecifier(fieldSpec)); } if (storeFieldName === false) { return fieldName; } return fieldName === fieldNameFromStoreName(storeFieldName) ? storeFieldName : fieldName + ":" + storeFieldName; }; Policies.prototype.readField = function (options, context) { var objectOrReference = options.from; if (!objectOrReference) return; var nameOrField = options.field || options.fieldName; if (!nameOrField) return; if (options.typename === void 0) { var typename = context.store.getFieldValue(objectOrReference, "__typename"); if (typename) options.typename = typename; } var storeFieldName = this.getStoreFieldName(options); var fieldName = fieldNameFromStoreName(storeFieldName); var existing = context.store.getFieldValue(objectOrReference, storeFieldName); var policy = this.getFieldPolicy(options.typename, fieldName, false); var read = policy && policy.read; if (read) { var readOptions = makeFieldFunctionOptions(this, objectOrReference, options, context, context.store.getStorage(isReference(objectOrReference) ? objectOrReference.__ref : objectOrReference, storeFieldName)); return cacheSlot.withValue(this.cache, read, [existing, readOptions]); } return existing; }; Policies.prototype.getReadFunction = function (typename, fieldName) { var policy = this.getFieldPolicy(typename, fieldName, false); return policy && policy.read; }; Policies.prototype.getMergeFunction = function (parentTypename, fieldName, childTypename) { var policy = this.getFieldPolicy(parentTypename, fieldName, false); var merge = policy && policy.merge; if (!merge && childTypename) { policy = this.getTypePolicy(childTypename); merge = policy && policy.merge; } return merge; }; Policies.prototype.runMergeFunction = function (existing, incoming, _a, context, storage) { var field = _a.field, typename = _a.typename, merge = _a.merge; if (merge === mergeTrueFn) { return makeMergeObjectsFunction(context.store)(existing, incoming); } if (merge === mergeFalseFn) { return incoming; } if (context.overwrite) { existing = void 0; } return merge(existing, incoming, makeFieldFunctionOptions(this, void 0, { typename: typename, fieldName: field.name.value, field: field, variables: context.variables }, context, storage || Object.create(null))); }; return Policies; }()); export { Policies }; function makeFieldFunctionOptions(policies, objectOrReference, fieldSpec, context, storage) { var storeFieldName = policies.getStoreFieldName(fieldSpec); var fieldName = fieldNameFromStoreName(storeFieldName); var variables = fieldSpec.variables || context.variables; var _a = context.store, toReference = _a.toReference, canRead = _a.canRead; return { args: argsFromFieldSpecifier(fieldSpec), field: fieldSpec.field || null, fieldName: fieldName, storeFieldName: storeFieldName, variables: variables, isReference: isReference, toReference: toReference, storage: storage, cache: policies.cache, canRead: canRead, readField: function () { return policies.readField(normalizeReadFieldOptions(arguments, objectOrReference, variables), context); }, mergeObjects: makeMergeObjectsFunction(context.store), }; } export function normalizeReadFieldOptions(readFieldArgs, objectOrReference, variables) { var fieldNameOrOptions = readFieldArgs[0], from = readFieldArgs[1], argc = readFieldArgs.length; var options; if (typeof fieldNameOrOptions === "string") { options = { fieldName: fieldNameOrOptions, from: argc > 1 ? from : objectOrReference, }; } else { options = __assign({}, fieldNameOrOptions); if (!hasOwn.call(options, "from")) { options.from = objectOrReference; } } if (__DEV__ && options.from === void 0) { __DEV__ && invariant.warn("Undefined 'from' passed to readField with arguments ".concat(stringifyForDisplay(Array.from(readFieldArgs)))); } if (void 0 === options.variables) { options.variables = variables; } return options; } function makeMergeObjectsFunction(store) { return function mergeObjects(existing, incoming) { if (isArray(existing) || isArray(incoming)) { throw __DEV__ ? new InvariantError("Cannot automatically merge arrays") : new InvariantError(4); } if (isNonNullObject(existing) && isNonNullObject(incoming)) { var eType = store.getFieldValue(existing, "__typename"); var iType = store.getFieldValue(incoming, "__typename"); var typesDiffer = eType && iType && eType !== iType; if (typesDiffer) { return incoming; } if (isReference(existing) && storeValueIsStoreObject(incoming)) { store.merge(existing.__ref, incoming); return existing; } if (storeValueIsStoreObject(existing) && isReference(incoming)) { store.merge(existing, incoming.__ref); return incoming; } if (storeValueIsStoreObject(existing) && storeValueIsStoreObject(incoming)) { return __assign(__assign({}, existing), incoming); } } return incoming; }; } //# sourceMappingURL=policies.js.map