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