grafast
Version:
Cutting edge GraphQL planning and execution engine
192 lines • 6.78 kB
JavaScript
;
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