grafast
Version:
Cutting edge GraphQL planning and execution engine
214 lines • 9.59 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.__ListTransformStep = void 0;
exports.listTransform = listTransform;
const tslib_1 = require("tslib");
const assert = tslib_1.__importStar(require("../assert.js"));
const constants_js_1 = require("../constants.js");
const dev_js_1 = require("../dev.js");
const executeBucket_js_1 = require("../engine/executeBucket.js");
const LayerPlan_js_1 = require("../engine/LayerPlan.js");
const withGlobalLayerPlan_js_1 = require("../engine/lib/withGlobalLayerPlan.js");
const step_js_1 = require("../step.js");
const __item_js_1 = require("./__item.js");
const connection_js_1 = require("./connection.js");
/**
* **Experimental.**
*
* A "special" plan that has custom handling in Grafast. Used for turning lists
* into other things (or maybe more lists!).
*
* It's recommended that you don't use this directly, please use one of the
* functions that uses this under the hood such as `filter()`.
*/
class __ListTransformStep extends step_js_1.Step {
static { this.$$export = {
moduleName: "grafast",
exportName: "__ListTransformStep",
}; }
constructor(options) {
super();
// OPTIMIZE: if all the steps in the inner bucket are synchronous then theoretically we can be synchronous too
this.isSyncAndSafe = false;
const { listStep: rawListStep, itemPlanCallback, initialState, reduceCallback, finalizeCallback, listItem, meta, optimize, connectionClone, } = options;
const listStep = (0, connection_js_1.itemsOrStep)(rawListStep);
this.listStepDepId = this.addDependency(listStep);
this.rawListStepRefId = this.addRef(rawListStep);
this.itemPlanCallback = itemPlanCallback;
this.initialState = initialState;
this.reduceCallback = reduceCallback;
this.finalizeCallback = finalizeCallback;
this.listItem = listItem;
this.meta = meta ?? null;
if (optimize !== undefined) {
this.optimize = optimize;
}
this.connectionClone = connectionClone;
// Plan this subroutine
this.subroutineLayer = new LayerPlan_js_1.LayerPlan(this.operationPlan, {
type: "subroutine",
parentLayerPlan: this.layerPlan,
parentStep: this,
});
const itemPlan = (0, withGlobalLayerPlan_js_1.withGlobalLayerPlan)(this.subroutineLayer, listStep.polymorphicPaths, null, null, () => {
// This does NOT use `itemPlanFor` because __ListTransformPlans are special.
const $__listItem = new __item_js_1.__ItemStep(listStep);
$__listItem.transformStepId = this.id;
this.itemStepId = $__listItem.id;
const $listItem = (0, step_js_1.isListCapableStep)(listStep)
? listStep.listItem($__listItem)
: $__listItem;
const $newListItem = this.itemPlanCallback($listItem);
if (this.isSyncAndSafe &&
(!$__listItem.isSyncAndSafe ||
!$listItem.isSyncAndSafe ||
!$newListItem.isSyncAndSafe)) {
this.isSyncAndSafe = false;
}
return $newListItem;
});
this.subroutineLayer.setRootStep(itemPlan);
this.operationPlan.finishSubroutine(this, this.subroutineLayer);
}
toStringMeta() {
return this.meta;
}
getListStep() {
return this.getRef(this.rawListStepRefId);
}
[constants_js_1.$$deepDepSkip]() {
return this.getListStep();
}
dangerouslyGetListPlan() {
return this.getRef(this.rawListStepRefId);
}
deduplicate(peers) {
return peers.filter((peer) => peer.itemPlanCallback === this.itemPlanCallback &&
peer.initialState === this.initialState &&
peer.reduceCallback === this.reduceCallback &&
peer.finalizeCallback === this.finalizeCallback &&
peer.listItem === this.listItem);
}
// ListTransform plans must _NOT_ optimize away. They must persist (unless
// the options overrides this)
optimize() {
return this;
}
async execute({ indexForEach, indexMap, values, extra, }) {
const bucket = extra._bucket;
const childLayerPlan = this.subroutineLayer;
const { copyStepIds, rootStep } = childLayerPlan;
if (rootStep === null) {
throw new Error(`rootStep of ${childLayerPlan} must not be null.`);
}
const store = new Map();
const polymorphicPathList = [];
const iterators = [];
const map = new Map();
let size = 0;
// ENHANCE: do this better!
const itemStep = this.operationPlan.dangerouslyGetStep(this.itemStepId);
const itemStepId = itemStep.id;
if (itemStepId == null) {
throw new Error("GrafastInternalError<b3a2bff9-15c6-47e2-aa82-19c862324f1a>: listItem layer plan has no rootStepId");
}
const listStepValue = values[this.listStepDepId];
if (itemStep._isUnary) {
const list = listStepValue.unaryValue();
store.set(itemStepId, (0, executeBucket_js_1.unaryExecutionValue)(Array.isArray(list) ? list[0] : list));
}
else {
store.set(itemStepId, (0, executeBucket_js_1.batchExecutionValue)([]));
}
for (const stepId of copyStepIds) {
const ev = bucket.store.get(stepId);
if (!ev) {
throw new Error(`GrafastInternalError<2be5c2c6-a7f8-4002-93a0-6ace5a89a962>: unary step '${stepId}' (${this.operationPlan.dangerouslyGetStep(stepId)}) listed in copyStepIds but not available in parent bucket for ${this}`);
}
if (ev.isBatch) {
// Prepare store with an empty list for each copyPlanId
store.set(stepId, (0, executeBucket_js_1.batchExecutionValue)([]));
}
else {
store.set(stepId, ev);
}
}
// We'll typically be creating more listItem bucket entries than we
// have parent buckets, so we must "multiply up" the store entries.
indexForEach((originalIndex) => {
const list = listStepValue.at(originalIndex);
if (Array.isArray(list)) {
const newIndexes = [];
map.set(originalIndex, newIndexes);
for (let j = 0, l = list.length; j < l; j++) {
const newIndex = size++;
newIndexes.push(newIndex);
polymorphicPathList[newIndex] =
bucket.polymorphicPathList[originalIndex];
// Copying across the iterators because we do NOT call outputBucket,
// so we need to ensure any streams are cleaned up.
iterators[newIndex] = bucket.iterators[originalIndex];
const ev = store.get(itemStepId);
if (ev.isBatch) {
ev._setResult(newIndex, list[j], 0);
}
for (const planId of copyStepIds) {
const ev = store.get(planId);
if (ev.isBatch) {
const orig = bucket.store.get(planId);
ev._setResult(newIndex, orig.at(originalIndex), orig._flagsAt(originalIndex));
}
}
}
}
});
if (size > 0) {
const childBucket = (0, executeBucket_js_1.newBucket)(bucket, {
layerPlan: childLayerPlan,
size,
store,
flagUnion: bucket.flagUnion,
polymorphicPathList,
polymorphicType: null,
iterators,
});
await (0, executeBucket_js_1.executeBucket)(childBucket, extra._requestContext);
}
const depResults = store.get(rootStep.id);
return indexMap((originalIndex) => {
const list = listStepValue.at(originalIndex);
if (list == null) {
return list;
}
const indexes = map.get(originalIndex);
if (!Array.isArray(list) || !Array.isArray(indexes)) {
// ERRORS: should this be an error?
console.warn(`Either list or values was not an array when processing ${this}`);
return null;
}
const values = indexes.map((idx) => depResults.at(idx));
if (dev_js_1.isDev) {
assert.strictEqual(list.length, values.length, "GrafastInternalError<c85b6936-d406-4801-9c6b-625a567d32ff>: The list and values length must match for a __ListTransformStep");
}
const initialState = this.initialState();
const reduceResult = list.reduce((memo, entireItemValue, listEntryIndex) => this.reduceCallback(memo, entireItemValue, values[listEntryIndex]), initialState);
const finalResult = this.finalizeCallback
? this.finalizeCallback(reduceResult)
: reduceResult;
return finalResult;
});
}
}
exports.__ListTransformStep = __ListTransformStep;
/**
* **Experimental.**
*
* A "special" plan that has custom handling in Grafast. Used for turning lists
* into other things (or maybe more lists!).
*
* {@page ~grafast/steps/listTransform.md}
*/
function listTransform(options) {
return new __ListTransformStep(options);
}
//# sourceMappingURL=listTransform.js.map