UNPKG

grafast

Version:

Cutting edge GraphQL planning and execution engine

173 lines 6.14 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AccessStep = void 0; exports.access = access; const tslib_1 = require("tslib"); const chalk_1 = tslib_1.__importDefault(require("chalk")); const step_js_1 = require("../step.js"); const utils_js_1 = require("../utils.js"); /** * Returns a function that will extract the value at the given path from an * incoming object. Optimized functions for common cases */ function constructDestructureFunction(path, fallback, callback) { const n = path.length; if (n === 0) { if (fallback === undefined) { callback((_meta, value) => value); } else { callback((_meta, value) => value ?? fallback); } } else if (n === 1) { const [p0] = path; if (fallback === undefined) { callback((_meta, value) => value?.[p0]); } else { callback((_meta, value) => value?.[p0] ?? fallback); } } else if (n === 2) { const [p0, p1] = path; if (fallback === undefined) { callback((_meta, value) => value?.[p0]?.[p1]); } else { callback((_meta, value) => value?.[p0]?.[p1] ?? fallback); } } else if (n === 3) { const [p0, p1, p2] = path; if (fallback === undefined) { callback((_meta, value) => value?.[p0]?.[p1]?.[p2]); } else { callback((_meta, value) => value?.[p0]?.[p1]?.[p2] ?? fallback); } } else { callback(function slowlyExtractValueAtPath(_meta, value) { let current = value; for (let i = 0; i < n && current != null; i++) { const pathItem = path[i]; current = current[pathItem]; } return current ?? fallback; }); } } /** * Accesses a (potentially nested) property from the result of a plan. * * NOTE: this could lead to unexpected results (which could introduce security * issues) if it is not used carefully; only use it on JSON-like data, * preferably where the objects have null prototypes, and be sure to adhere to * the naming conventions detailed in assertSafeToAccessViaBraces. */ class AccessStep extends step_js_1.UnbatchedStep { static { this.$$export = { moduleName: "grafast", exportName: "AccessStep", }; } constructor(parentPlan, path, fallback) { super(); this.fallback = fallback; this.isSyncAndSafe = true; this.allowMultipleOptimizations = true; this._isImmutable = parentPlan._isImmutable; this.path = path; this.hasSymbols = this.path.some((k) => typeof k === "symbol"); this.peerKey = (this.fallback === undefined ? "U" : "D") + (this.hasSymbols ? "§" : ".") + (0, utils_js_1.digestKeys)(this.path); this.addStrongDependency(parentPlan); } toStringMeta() { return `${chalk_1.default.bold.yellow(String(this.getDepOptions(0).step.id))}.${this.path.map((p) => String(p)).join(".")}`; } getParentStep() { return this.getDep(0); } /** * Get the named property of an object. */ get(attrName) { if (typeof attrName !== "string") { throw new Error(`AccessStep::get can only be called with string values`); } return access(this.getDep(0), [...this.path, attrName]); } /** * Get the entry at the given index in an array. */ at(index) { if (typeof index !== "number") { throw new Error(`AccessStep::get can only be called with string values`); } return access(this.getDep(0), [...this.path, index]); } // An access of an access can become a single access optimize() { const $dep = this.getDep(0); if (this.fallback === undefined && this.path.length === 0) { // I don't do anything return $dep; } if ($dep instanceof AccessStep && $dep.fallback === undefined) { return access($dep.getDep(0), [...$dep.path, ...this.path], this.fallback); } return this; } finalize() { // Note that `OutputPlan.optimize` depends on this. constructDestructureFunction(this.path, this.fallback, (fn) => { this.unbatchedExecute = fn; }); super.finalize(); } unbatchedExecute(_extra, ..._values) { throw new Error(`${this}: should have had unbatchedExecute method replaced`); } deduplicate(peers) { if (peers.length === 0) { return peers; } else if (!this.hasSymbols && this.fallback === undefined) { // Rely entirely on peerKey return peers; } else if (!this.hasSymbols) { // Rely on peerKey for path, but check fallback const { fallback } = this; return peers.filter((p) => p.fallback === fallback); } else { // Check both fallback and path const { fallback, path } = this; return peers.filter((p) => p.fallback === fallback && (0, utils_js_1.arraysMatch)(p.path, path)); } } } exports.AccessStep = AccessStep; function access(parentPlan, rawPath, fallback) { const path = Array.isArray(rawPath) ? rawPath : rawPath != null ? [rawPath] : []; if (typeof fallback === "undefined" && !path.some((k) => typeof k === "symbol")) { const pathKey = (0, utils_js_1.digestKeys)(path); if (parentPlan._isImmutable) { return parentPlan.operationPlan.withRootLayerPlan(() => parentPlan.operationPlan.cacheStep(parentPlan, "GrafastInternal:access()", pathKey, () => new AccessStep(parentPlan, path))); } else { return parentPlan.operationPlan.cacheStep(parentPlan, "GrafastInternal:access()", pathKey, () => new AccessStep(parentPlan, path)); } } return new AccessStep(parentPlan, path, fallback); } //# sourceMappingURL=access.js.map