@apollo/client
Version:
A fully-featured caching GraphQL client.
416 lines • 18.9 kB
JavaScript
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