1 | {"version":3,"file":"key-extractor.js","sourceRoot":"","sources":["../../../src/cache/inmemory/key-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import { invariant } from \"../../utilities/globals/index.js\";\n\nimport {\n argumentsObjectFromField,\n DeepMerger,\n isNonEmptyArray,\n isNonNullObject,\n} from \"../../utilities/index.js\";\n\nimport { hasOwn, isArray } from \"./helpers.js\";\nimport type {\n KeySpecifier,\n KeyFieldsFunction,\n KeyArgsFunction,\n} from \"./policies.js\";\n\n// Mapping from JSON-encoded KeySpecifier strings to associated information.\nconst specifierInfoCache: Record<\n string,\n {\n paths?: string[][];\n keyFieldsFn?: KeyFieldsFunction;\n keyArgsFn?: KeyArgsFunction;\n }\n> = Object.create(null);\n\nfunction lookupSpecifierInfo(spec: KeySpecifier) {\n // It's safe to encode KeySpecifier arrays with JSON.stringify, since they're\n // just arrays of strings or nested KeySpecifier arrays, and the order of the\n // array elements is important (and suitably preserved by JSON.stringify).\n const cacheKey = JSON.stringify(spec);\n return (\n specifierInfoCache[cacheKey] ||\n (specifierInfoCache[cacheKey] = Object.create(null))\n );\n}\n\nexport function keyFieldsFnFromSpecifier(\n specifier: KeySpecifier\n): KeyFieldsFunction {\n const info = lookupSpecifierInfo(specifier);\n\n return (\n info.keyFieldsFn ||\n (info.keyFieldsFn = (object, context) => {\n const extract: typeof extractKey = (from, key) =>\n context.readField(key, from);\n\n const keyObject = (context.keyObject = collectSpecifierPaths(\n specifier,\n (schemaKeyPath) => {\n let extracted = extractKeyPath(\n context.storeObject,\n schemaKeyPath,\n // Using context.readField to extract paths from context.storeObject\n // allows the extraction to see through Reference objects and respect\n // custom read functions.\n extract\n );\n\n if (\n extracted === void 0 &&\n object !== context.storeObject &&\n hasOwn.call(object, schemaKeyPath[0])\n ) {\n // If context.storeObject fails to provide a value for the requested\n // path, fall back to the raw result object, if it has a top-level key\n // matching the first key in the path (schemaKeyPath[0]). This allows\n // key fields included in the written data to be saved in the cache\n // even if they are not selected explicitly in context.selectionSet.\n // Not being mentioned by context.selectionSet is convenient here,\n // since it means these extra fields cannot be affected by field\n // aliasing, which is why we can use extractKey instead of\n // context.readField for this extraction.\n extracted = extractKeyPath(object, schemaKeyPath, extractKey);\n }\n\n invariant(\n extracted !== void 0,\n `Missing field '%s' while extracting keyFields from %s`,\n schemaKeyPath.join(\".\"),\n object\n );\n\n return extracted;\n }\n ));\n\n return `${context.typename}:${JSON.stringify(keyObject)}`;\n })\n );\n}\n\n// The keyArgs extraction process is roughly analogous to keyFields extraction,\n// but there are no aliases involved, missing fields are tolerated (by merely\n// omitting them from the key), and drawing from field.directives or variables\n// is allowed (in addition to drawing from the field's arguments object).\n// Concretely, these differences mean passing a different key path extractor\n// function to collectSpecifierPaths, reusing the shared extractKeyPath helper\n// wherever possible.\nexport function keyArgsFnFromSpecifier(\n specifier: KeySpecifier\n): KeyArgsFunction {\n const info = lookupSpecifierInfo(specifier);\n\n return (\n info.keyArgsFn ||\n (info.keyArgsFn = (args, { field, variables, fieldName }) => {\n const collected = collectSpecifierPaths(specifier, (keyPath) => {\n const firstKey = keyPath[0];\n const firstChar = firstKey.charAt(0);\n\n if (firstChar === \"@\") {\n if (field && isNonEmptyArray(field.directives)) {\n const directiveName = firstKey.slice(1);\n // If the directive appears multiple times, only the first\n // occurrence's arguments will be used. TODO Allow repetition?\n // TODO Cache this work somehow, a la aliasMap?\n const d = field.directives.find(\n (d) => d.name.value === directiveName\n );\n // Fortunately argumentsObjectFromField works for DirectiveNode!\n const directiveArgs = d && argumentsObjectFromField(d, variables);\n // For directives without arguments (d defined, but directiveArgs ===\n // null), the presence or absence of the directive still counts as\n // part of the field key, so we return null in those cases. If no\n // directive with this name was found for this field (d undefined and\n // thus directiveArgs undefined), we return undefined, which causes\n // this value to be omitted from the key object returned by\n // collectSpecifierPaths.\n return (\n directiveArgs &&\n extractKeyPath(\n directiveArgs,\n // If keyPath.length === 1, this code calls extractKeyPath with an\n // empty path, which works because it uses directiveArgs as the\n // extracted value.\n keyPath.slice(1)\n )\n );\n }\n // If the key started with @ but there was no corresponding directive,\n // we want to omit this value from the key object, not fall through to\n // treating @whatever as a normal argument name.\n return;\n }\n\n if (firstChar === \"$\") {\n const variableName = firstKey.slice(1);\n if (variables && hasOwn.call(variables, variableName)) {\n const varKeyPath = keyPath.slice(0);\n varKeyPath[0] = variableName;\n return extractKeyPath(variables, varKeyPath);\n }\n // If the key started with $ but there was no corresponding variable, we\n // want to omit this value from the key object, not fall through to\n // treating $whatever as a normal argument name.\n return;\n }\n\n if (args) {\n return extractKeyPath(args, keyPath);\n }\n });\n\n const suffix = JSON.stringify(collected);\n\n // If no arguments were passed to this field, and it didn't have any other\n // field key contributions from directives or variables, hide the empty\n // :{} suffix from the field key. However, a field passed no arguments can\n // still end up with a non-empty :{...} suffix if its key configuration\n // refers to directives or variables.\n if (args || suffix !== \"{}\") {\n fieldName += \":\" + suffix;\n }\n\n return fieldName;\n })\n );\n}\n\nexport function collectSpecifierPaths(\n specifier: KeySpecifier,\n extractor: (path: string[]) => any\n): Record<string, any> {\n // For each path specified by specifier, invoke the extractor, and repeatedly\n // merge the results together, with appropriate ancestor context.\n const merger = new DeepMerger();\n return getSpecifierPaths(specifier).reduce((collected, path) => {\n let toMerge = extractor(path);\n if (toMerge !== void 0) {\n // This path is not expected to contain array indexes, so the toMerge\n // reconstruction will not contain arrays. TODO Fix this?\n for (let i = path.length - 1; i >= 0; --i) {\n toMerge = { [path[i]]: toMerge };\n }\n collected = merger.merge(collected, toMerge);\n }\n return collected;\n }, Object.create(null));\n}\n\nexport function getSpecifierPaths(spec: KeySpecifier): string[][] {\n const info = lookupSpecifierInfo(spec);\n\n if (!info.paths) {\n const paths: string[][] = (info.paths = []);\n const currentPath: string[] = [];\n\n spec.forEach((s, i) => {\n if (isArray(s)) {\n getSpecifierPaths(s).forEach((p) => paths.push(currentPath.concat(p)));\n currentPath.length = 0;\n } else {\n currentPath.push(s);\n if (!isArray(spec[i + 1])) {\n paths.push(currentPath.slice(0));\n currentPath.length = 0;\n }\n }\n });\n }\n\n return info.paths!;\n}\n\nfunction extractKey<TObj extends Record<string, any>, TKey extends string>(\n object: TObj,\n key: TKey\n): TObj[TKey] | undefined {\n return object[key];\n}\n\nexport function extractKeyPath(\n object: Record<string, any>,\n path: string[],\n extract?: typeof extractKey\n): any {\n // For each key in path, extract the corresponding child property from obj,\n // flattening arrays if encountered (uncommon for keyFields and keyArgs, but\n // possible). The final result of path.reduce is normalized so unexpected leaf\n // objects have their keys safely sorted. That final result is difficult to\n // type as anything other than any. You're welcome to try to improve the\n // return type, but keep in mind extractKeyPath is not a public function\n // (exported only for testing), so the effort may not be worthwhile unless the\n // limited set of actual callers (see above) pass arguments that TypeScript\n // can statically type. If we know only that path is some array of strings\n // (and not, say, a specific tuple of statically known strings), any (or\n // possibly unknown) is the honest answer.\n extract = extract || extractKey;\n return normalize(\n path.reduce(function reducer(obj, key): any {\n return isArray(obj) ?\n obj.map((child) => reducer(child, key))\n : obj && extract!(obj, key);\n }, object)\n );\n}\n\nfunction normalize<T>(value: T): T {\n // Usually the extracted value will be a scalar value, since most primary\n // key fields are scalar, but just in case we get an object or an array, we\n // need to do some normalization of the order of (nested) keys.\n if (isNonNullObject(value)) {\n if (isArray(value)) {\n return value.map(normalize) as any;\n }\n return collectSpecifierPaths(Object.keys(value).sort(), (path) =>\n extractKeyPath(value, path)\n ) as T;\n }\n return value;\n}\n"]} |