UNPKG

grafast

Version:

Cutting edge GraphQL planning and execution engine

192 lines 6.78 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.nextTick = void 0; exports.makeAccessMap = makeAccessMap; exports.ioEquivalenceMatches = ioEquivalenceMatches; exports.paramSig = paramSig; exports.executeBatches = executeBatches; exports.executeLoad = executeLoad; const deferred_1 = require("../deferred"); const step_js_1 = require("../step.js"); const utils_js_1 = require("../utils.js"); const access_js_1 = require("./access.js"); exports.nextTick = typeof process !== "undefined" && typeof process.nextTick === "function" ? (cb) => process.nextTick(cb) : (cb) => setTimeout(cb, 0); function makeAccessMap($spec, ioEquivalence) { const map = Object.create(null); if (ioEquivalence == null) { return map; } else if (typeof ioEquivalence === "string") { map[ioEquivalence] = $spec; return map; } else if ((0, utils_js_1.isTuple)(ioEquivalence)) { for (let i = 0, l = ioEquivalence.length; i < l; i++) { const key = ioEquivalence[i]; map[key] = (0, step_js_1.isListLikeStep)($spec) ? $spec.at(i) : (0, access_js_1.access)($spec, [i]); } return map; } else if (typeof ioEquivalence === "object") { for (const key of Object.keys(ioEquivalence)) { const attr = ioEquivalence[key]; if (attr != null) { map[attr] = (0, step_js_1.isObjectLikeStep)($spec) ? $spec.get(key) : (0, access_js_1.access)($spec, [key]); } } return map; } else { throw new Error(`ioEquivalence passed to loadOne() call not understood`); } } function ioEquivalenceMatches(io1, io2) { if (io1 === io2) return true; if (io1 == null) return false; if (io2 == null) return false; if (typeof io1 === "string") return false; if (typeof io2 === "string") return false; if (Array.isArray(io1)) { if (!Array.isArray(io2)) return false; return (0, utils_js_1.arraysMatch)(io1, io2); } else { if (Array.isArray(io2)) return false; return (0, utils_js_1.recordsMatch)(io1, io2); } } function paramSig(paramDepIdByKey, depIdToStepId) { // No more params allowed! Object.freeze(paramDepIdByKey); return JSON.stringify(Object.fromEntries(Object.entries(paramDepIdByKey) .map(([key, depId]) => [key, depIdToStepId(depId)]) .sort(utils_js_1.stableStringSortFirstTupleEntry))); } async function executeBatches(loadBatches, loadInfo, load) { try { const numberOfBatches = loadBatches.length; if (numberOfBatches === 1) { const [loadBatch] = loadBatches; loadBatch.deferred.resolve(load(loadBatch.batchSpecs, loadInfo)); return; } else { // Do some tick-batching! const indexStarts = []; const allBatchSpecs = []; for (let i = 0; i < numberOfBatches; i++) { const loadBatch = loadBatches[i]; indexStarts[i] = allBatchSpecs.length; for (const batchSpec of loadBatch.batchSpecs) { allBatchSpecs.push(batchSpec); } } const results = await load(allBatchSpecs, loadInfo); for (let i = 0; i < numberOfBatches; i++) { const loadBatch = loadBatches[i]; const start = indexStarts[i]; const stop = indexStarts[i + 1] ?? allBatchSpecs.length; const entries = results.slice(start, stop); loadBatch.deferred.resolve(entries); } } } catch (e) { for (const loadBatch of loadBatches) { loadBatch.deferred.reject(e); } } } function executeLoad(details, sharedDepId, paramDepIdByKey, baseLoadInfo, load) { const { count, extra, values } = details; const values0 = values[0]; const shared = sharedDepId != null ? values[sharedDepId].unaryValue() : undefined; const meta = extra.meta; let cache = meta.cache; if (!cache) { cache = new Map(); meta.cache = cache; } const batch = new Map(); const params = Object.fromEntries(Object.entries(paramDepIdByKey).map(([key, depId]) => [ key, values[depId].unaryValue(), ])); const loadInfo = { ...baseLoadInfo, params, shared, unary: shared, }; const results = []; for (let i = 0; i < count; i++) { const spec = values0.at(i); if (cache.has(spec)) { results.push(cache.get(spec)); } else { // We'll fill this in in a minute const index = results.push(null) - 1; const existingIdx = batch.get(spec); if (existingIdx !== undefined) { existingIdx.push(index); } else { batch.set(spec, [index]); } } } const pendingCount = batch.size; if (pendingCount > 0) { const deferred = (0, deferred_1.defer)(); const batchSpecs = [...batch.keys()]; const loadBatch = { deferred, batchSpecs }; if (!meta.loadBatchesByLoad) { meta.loadBatchesByLoad = new Map(); } let loadBatches = meta.loadBatchesByLoad.get(load); if (loadBatches) { // Add to existing batch load loadBatches.push(loadBatch); } else { // Create new batch load loadBatches = [loadBatch]; meta.loadBatchesByLoad.set(load, loadBatches); // Guaranteed by the metaKey to be equivalent for all entries sharing the same `meta`. Note equivalent is not identical; key order may change. (0, exports.nextTick)(() => { // Don't allow adding anything else to the batch meta.loadBatchesByLoad.delete(load); executeBatches(loadBatches, loadInfo, load); }); } return (async () => { const loadResults = await deferred; for (let pendingIndex = 0; pendingIndex < pendingCount; pendingIndex++) { const spec = batchSpecs[pendingIndex]; const targetIndexes = batch.get(spec); const loadResult = loadResults[pendingIndex]; cache.set(spec, loadResult); for (const targetIndex of targetIndexes) { results[targetIndex] = loadResult; } } return results; })(); } return results; } //# sourceMappingURL=_loadCommon.js.map