UNPKG

gqty

Version:

The No-GraphQL Client for TypeScript

397 lines (392 loc) • 12.6 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); const crawl = require('../Cache/crawl.js'); const utils = require('../Cache/utils.js'); const index = require('../Error/index.js'); const Schema = require('../Schema.js'); require('../Utils/hash.js'); const object = require('../Utils/object.js'); const meta = require('./meta.js'); const skeleton = require('./skeleton.js'); const verbose = process.env.NODE_ENV !== "production"; const resolve = (accessor, selection, __type) => { var _a, _b; const meta$1 = meta.$meta(accessor); if (!meta$1) return; const { context } = meta$1; const { alias, key, isUnion, cacheKeys } = selection; const isNumericSelection = +key === +key; if (cacheKeys.length >= context.depthLimit) { if (verbose) { throw new index.GQtyError( `Depth limit reached at ${cacheKeys.join( "." )}, ignoring further selections.` ); } return; } const cache = selection.parent === selection.root && key !== "__typename" ? ( // The way we structure the cache for SWR requires special treatment // for 2nd level selections, e.g. query.hello, mutation.update()... etc. ensureCache(cacheKeys[0], cacheKeys[1], meta$1) ) : Object.defineProperties( { data: void 0, expiresAt: Infinity }, Object.getOwnPropertyDescriptors(meta$1.cache) ); let data = cache.data; const { pureType, isArray, isNullable } = Schema.parseSchemaType(__type); const type = context.schema[pureType]; if (!isUnion && selection.root !== selection && (selection.root !== selection.parent || key === "__typename") && data !== null) { if (Array.isArray(data)) { if (verbose && !isNumericSelection) { console.warn(`[GQty] Accessing arrays with non-numeric key "${key}".`); } data = data[+key]; } else if (typeof data === "object") { data = data[alias != null ? alias : key]; } } if (data === null) { if (!isNullable) { throw new index.GQtyError(`Cached null for non-nullable type ${pureType}.`); } if (context.scalars[pureType]) { context.select(selection, cache); } else { context.select(selection); } return null; } if (!type) { if (context.scalars[pureType]) { if (cache.expiresAt === -Infinity) { data = (_a = context.cache.get( selection.cacheKeys.join("."), context.cacheOptions )) == null ? void 0 : _a.data; } context.select(selection, { ...cache, data }); return isArray ? Array.isArray(data) ? data : [void 0] : data; } const unions = (_b = context.schema[Schema.SchemaUnionsKey]) == null ? void 0 : _b[pureType.slice(1)]; if (unions == null ? void 0 : unions.length) { return createUnionAccessor({ context, cache: meta$1.cache, possibleTypes: unions, selection }); } throw new index.GQtyError(`GraphQL type not found: ${pureType}`); } if (data === void 0) { data = skeleton.createSkeleton(() => ({})); if (isArray && !isNumericSelection) { data = skeleton.createSkeleton(() => [data]); } if (cacheKeys.length === 2) { const [type2, field] = cacheKeys; context.cache.set({ [type2]: { [field]: data } }); } } if (cacheKeys.length > 2 && cache.data && typeof cache.data === "object" && !Array.isArray(cache.data) && !isNumericSelection) { cache.data[alias != null ? alias : key] = data; } cache.data = data; if (isArray && !isNumericSelection) { return createArrayAccessor({ cache, context, selection, type: { __type: pureType } }); } else { return createObjectAccessor({ cache, context, selection, type: { __type } }); } }; const createUnionAccessor = ({ context, cache, possibleTypes, selection }) => { return new Proxy( Object.fromEntries(possibleTypes.map((__typename) => [__typename, true])), { get(_, __typename) { if (typeof __typename !== "string") return; if (!possibleTypes.includes(__typename)) return; const data = cache.data; if (!skeleton.isSkeleton(data) && (!utils.isCacheObject(data) || data.__typename !== __typename)) return; const type = context.schema[__typename]; if (!type) return; return createObjectAccessor({ context, cache, selection: selection.getChild(__typename, { isUnion: true }), type: { __type: __typename } }); } } ); }; const objectProxyHandler = { get(currentType, key, proxy) { if (typeof key !== "string") return; if (key === "toJSON") { return () => { var _a; const data = (_a = meta.$meta(proxy)) == null ? void 0 : _a.cache.data; if (typeof data !== "object" || data === null) { return data; } return Object.entries(data).reduce( (prev, [key2, value]) => { if (!skeleton.isSkeleton(value)) { prev[key2] = value; } return prev; }, {} ); }; } const meta$1 = meta.$meta(proxy); if (!meta$1) return; if ( // Skip Query, Mutation and Subscription meta$1.selection.parent !== void 0 && // Prevent infinite recursions !getIdentityFields(meta$1).includes(key) ) { selectIdentityFields(proxy, currentType); } const targetType = currentType[key]; if (!targetType || typeof targetType !== "object") return; const { __args, __type } = targetType; if (__args) { return (args) => resolve( proxy, meta$1.selection.getChild( key, args ? { input: { types: __args, values: args } } : {} ), __type ); } return resolve(proxy, meta$1.selection.getChild(key), __type); }, set(_, key, value, proxy) { var _a, _b, _c; const meta$1 = meta.$meta(proxy); if (typeof key !== "string" || !meta$1) return false; const { cache, context, selection } = meta$1; value = (_a = deepMetadata(value)) != null ? _a : value; if (selection.ancestry.length <= 2) { const [type, field] = selection.cacheKeys; if (field) { const data = (_c = (_b = context.cache.get(`${type}.${field}`, context.cacheOptions)) == null ? void 0 : _b.data) != null ? _c : {}; if (!object.isPlainObject(data)) return false; data[key] = value; context.cache.set({ [type]: { [field]: data } }); } else { context.cache.set({ [type]: { [key]: value } }); } } let result = false; if (utils.isCacheObject(cache.data)) { result = Reflect.set(cache.data, key, value); } for (const [keys, scalar] of crawl.flattenObject(value)) { let currentSelection = selection.getChild(key); for (const key2 of keys) { if (!isNaN(Number(key2))) continue; currentSelection = currentSelection.getChild(key2); } context.select(currentSelection, { ...cache, data: scalar }); } return result; } }; const createObjectAccessor = (meta$1) => { const { cache: { data }, context: { schema }, type: { __type } } = meta$1; if (data !== void 0 && !utils.isCacheObject(data)) { throw new index.GQtyError( `Invalid type ${data === null ? "null" : typeof data} for object accessors.` ); } const createAccessor = () => { const type = schema[Schema.parseSchemaType(__type).pureType]; if (!type) throw new index.GQtyError(`Invalid schema type ${__type}.`); const proxy = new Proxy( // `type` here for ownKeys proxy trap type, data ? Object.assign({}, objectProxyHandler, { getOwnPropertyDescriptor: (target, key) => { var _a; return (_a = Reflect.getOwnPropertyDescriptor(data, key)) != null ? _a : Reflect.getOwnPropertyDescriptor(target, key); } }) : objectProxyHandler ); meta.$meta.set(proxy, meta$1); return proxy; }; return createAccessor(); }; const deepMetadata = (input) => { const data = metadata(input); const stack = /* @__PURE__ */ new Set([data]); const seen = /* @__PURE__ */ new Set(); for (const it of stack) { if (seen.has(it)) continue; seen.add(it); if (Array.isArray(it)) { for (const [k, v] of it.entries()) { if (isObject(v)) { stack.add(it[k] = metadata(v)); } else { stack.add(v); } } } else if (isObject(it)) { for (const [k, v] of Object.entries(it)) { if (isObject(v)) { stack.add(it[k] = metadata(v)); } else { stack.add(v); } } } } return data; function metadata(it) { var _a, _b; return (_b = (_a = meta.$meta(it)) == null ? void 0 : _a.cache.data) != null ? _b : it; } function isObject(it) { return typeof it === "object" && it !== null; } }; const ensureCache = (type, field, { context: { cache, cacheOptions } }) => { if (!cache.get(`${type}.${field}`, cacheOptions)) { cache.set({ [type]: { [field]: void 0 } }); } const { data } = cache.get(`${type}.${field}`, cacheOptions); return { data, get expiresAt() { var _a, _b; return (_b = (_a = cache.get(`${type}.${field}`, cacheOptions)) == null ? void 0 : _a.expiresAt) != null ? _b : -Infinity; }, get swrBefore() { var _a, _b; return (_b = (_a = cache.get(`${type}.${field}`, cacheOptions)) == null ? void 0 : _a.swrBefore) != null ? _b : -Infinity; } }; }; const getIdentityFields = ({ context: { typeKeys }, type: { __type } }) => { var _a; const { pureType } = Schema.parseSchemaType(__type); return (_a = typeKeys == null ? void 0 : typeKeys[pureType]) != null ? _a : ["__typename", "id", "_id"]; }; const selectIdentityFields = (accessor, type) => { var _a; if (accessor == null) return; const meta$1 = meta.$meta(accessor); if (!meta$1) return; const { selection: { parent, isUnion } } = meta$1; if ((parent == null ? void 0 : parent.key) !== "$on") { accessor.__typename; } const keys = getIdentityFields(meta$1); for (const key of keys) { if (!type[key]) continue; if (isUnion && ((_a = parent == null ? void 0 : parent.parent) == null ? void 0 : _a.children.has(key))) continue; accessor[key]; } }; const arrayProxyHandler = { get(_, key, proxy) { const meta$1 = meta.$meta(proxy); if (!meta$1) return; if (key === "toJSON" && !skeleton.isSkeleton(meta$1.cache.data)) { return () => { var _a; return (_a = meta.$meta(proxy)) == null ? void 0 : _a.cache.data; }; } const { cache: { data }, selection } = meta$1; if (!Array.isArray(data)) return; if (typeof key === "string") { if (!Array.isArray(data)) { throw new index.GQtyError(`Cache data must be an array.`); } if (key === "length") proxy[0]; const numKey = +key; if (!isNaN(numKey) && numKey < data.length) { return resolve(proxy, selection.getChild(numKey), meta$1.type.__type); } } const value = Reflect.get(data, key); if (typeof value === "function") { return value.bind(proxy); } return value; }, set(_, key, value, proxy) { var _a, _b; if (typeof key === "symbol" || key !== "length" && +key !== +key) { throw new index.GQtyError(`Invalid array assignment.`); } const meta$1 = meta.$meta(proxy); if (!meta$1) return false; const { cache: { data } } = meta$1; if (!Array.isArray(data)) return false; value = (_b = (_a = meta.$meta(value)) == null ? void 0 : _a.cache.data) != null ? _b : value; return Reflect.set(data, key, value); } }; const createArrayAccessor = (meta$1) => { const { cache, context, selection } = meta$1; if (!Array.isArray(cache.data)) { if (verbose) { console.warn( "Invalid cache for an array accessor, monkey-patch by wrapping it with an array.", meta$1, meta$1.context.cache.toJSON() ); } cache.data = [cache.data]; } if (cache.data.length === 0) { context.select(selection); } const proxy = new Proxy(cache.data, arrayProxyHandler); meta.$meta.set(proxy, meta$1); return proxy; }; exports.createArrayAccessor = createArrayAccessor; exports.createObjectAccessor = createObjectAccessor; exports.createUnionAccessor = createUnionAccessor; exports.deepMetadata = deepMetadata; exports.resolve = resolve;