UNPKG

grafast

Version:

Cutting edge GraphQL planning and execution engine

216 lines 8.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.Modifier = exports.ApplyInputStep = void 0; exports.inputArgsApply = inputArgsApply; exports.applyInput = applyInput; exports.isModifier = isModifier; exports.assertModifier = assertModifier; exports.isApplyableStep = isApplyableStep; const graphql_1 = require("graphql"); const global_js_1 = require("../global.js"); const step_js_1 = require("../step.js"); const constant_js_1 = require("./constant.js"); let currentModifiers = []; let applyingModifiers = false; let inputArgsApplyDepth = 0; class ApplyInputStep extends step_js_1.UnbatchedStep { static { this.$$export = { moduleName: "grafast", exportName: "ApplyInputStep", }; } constructor(inputType, $value, getTargetFromParent, $scope) { super(); this.inputType = inputType; this.getTargetFromParent = getTargetFromParent; this.isSyncAndSafe = true; this.allowMultipleOptimizations = true; this.valueDepId = this.addUnaryDependency($value); this.scopeDepId = $scope ? this.addUnaryDependency($scope) : null; if (!this._isUnary) { throw new Error(`applyInput() must be unary`); } this._isUnaryLocked = true; } deduplicate(peers) { return peers.filter((p) => p.inputType === this.inputType && p.getTargetFromParent === this.getTargetFromParent); } optimize() { const $value = this.getDep(this.valueDepId); const $scope = this.scopeDepId === null ? null : this.getDep(this.scopeDepId); if ((!$scope || $scope instanceof constant_js_1.ConstantStep) && $value instanceof constant_js_1.ConstantStep) { // Replace myself with a constant! const { operationPlan: { schema }, inputType, getTargetFromParent, } = this; const { data } = $value; const scope = $scope?.data; return (0, constant_js_1.constant)(function applyInputConstant(parent) { inputArgsApply(schema, inputType, parent, data, getTargetFromParent, scope); }, false); } return this; } unbatchedExecute(extra, value, scope) { const { getTargetFromParent } = this; return (parentThing) => inputArgsApply(this.operationPlan.schema, this.inputType, parentThing, value, getTargetFromParent, scope); } } exports.ApplyInputStep = ApplyInputStep; function inputArgsApply(schema, inputType, parent, inputValue, getTargetFromParent, scope) { try { inputArgsApplyDepth++; const target = getTargetFromParent ? getTargetFromParent(parent, inputValue, { scope }) : parent; if (target != null) { _inputArgsApply(schema, inputType, target, inputValue, scope); } } finally { inputArgsApplyDepth--; } let l; if (inputArgsApplyDepth === 0 && (l = currentModifiers.length) > 0) { applyingModifiers = true; try { for (let i = l - 1; i >= 0; i--) { currentModifiers[i].apply(); } } finally { applyingModifiers = false; currentModifiers = []; } } } function applyInput(inputType, $value, getTargetFromParent) { const opPlan = (0, global_js_1.operationPlan)(); const { schema } = opPlan; const namedType = (0, graphql_1.getNamedType)(inputType); return opPlan.withRootLayerPlan(() => { const $scope = namedType.extensions?.grafast?.applyScope?.() ?? null; if ((!$scope || $scope instanceof constant_js_1.ConstantStep) && $value instanceof constant_js_1.ConstantStep) { // Replace us with a constant const { data } = $value; const scope = $scope?.data; return (0, constant_js_1.constant)(function applyInputConstant(parent) { inputArgsApply(schema, inputType, parent, data, getTargetFromParent, scope); }, false); } else { return new ApplyInputStep(inputType, $value, getTargetFromParent, $scope); } }); } /* const defaultInputObjectTypeInputPlanResolver: InputObjectTypeInputPlanResolver = (input, info) => { const fields = info.type.getFields(); const obj: { [key: string]: ExecutableStep } = Object.create(null); for (const fieldName in fields) { obj[fieldName] = input.get(fieldName); } return object(obj); }; */ function _inputArgsApply(schema, inputType, target, inputValue, scope) { // PERF: we should have the plan generate a digest of `inputType` so that we // can jump right to the relevant parts without too much traversal cost. if (inputValue === undefined) { return; } if ((0, graphql_1.isNonNullType)(inputType)) { if (inputValue === null) { throw new Error(`null value found in non-null position`); } _inputArgsApply(schema, inputType.ofType, target, inputValue, scope); } else if ((0, graphql_1.isListType)(inputType)) { if (inputValue == null) return; if (!Array.isArray(inputValue)) { throw new Error(`Expected list in list position`); } for (const item of inputValue) { const itemTarget = typeof target === "function" ? target() : target; _inputArgsApply(schema, inputType.ofType, itemTarget, item, scope); } } else if (typeof target === "function") { throw new Error("Functions may only be used as the target for list types (the function is called once per list item)"); } else if ((0, graphql_1.isInputObjectType)(inputType)) { if (inputValue === null) { return; } const fields = inputType.getFields(); for (const [fieldName, field] of Object.entries(fields)) { const val = inputValue[fieldName]; if (val === undefined) continue; if (field.extensions.grafast?.apply) { const newTarget = field.extensions.grafast.apply(target, val, { schema, field, fieldName, scope, }); if (newTarget != null) { _inputArgsApply(schema, field.type, newTarget, val, scope); } } } } else if ((0, graphql_1.isScalarType)(inputType)) { // if (inputType.extensions.grafast?.apply) { // } } else if ((0, graphql_1.isEnumType)(inputType)) { if (inputValue === null) { return; } const values = inputType.getValues(); const value = values.find((v) => v.value === inputValue); if (value) { if (value.extensions.grafast?.apply) { value.extensions.grafast.apply(target, { scope, value }); } } else { throw new Error(`Couldn't find value in ${inputType} for ${inputValue}`); } } else { const never = inputType; throw new Error(`Input type expected, but found ${never}`); } } /** * Modifiers modify their parent (which may be another modifier or anything * else). First they gather all the requirements from their children (if any) * being applied to them, then they apply themselves to their parent. This * application is done through the `apply()` method. */ class Modifier { constructor(parent) { this.parent = parent; if (applyingModifiers) { throw new Error(`Must not create new modifier whilst modifiers are being applied!`); } currentModifiers.push(this); } } exports.Modifier = Modifier; function isModifier(plan) { return plan instanceof Modifier; } function assertModifier(plan, pathDescription) { if (!isModifier(plan)) { throw new Error(`The plan returned from '${pathDescription}' should be a modifier plan, but it does not implement the 'apply' method.`); } } function isApplyableStep(s) { return typeof s.apply === "function"; } //# sourceMappingURL=applyInput.js.map