UNPKG

@nodescript/core

Version:

Visual programming language for Browser and Node

142 lines 4.16 kB
import { Schema } from 'airtight'; import { Event } from 'nanoevent'; import { convertAuto, runtimeLib } from '../util/index.js'; export const SYM_DEFERRED = Symbol.for('NodeScript:Deferred'); export const SYM_PENDING = Symbol.for('NodeScript:Pending'); export const SYM_SKIPPED = Symbol.for('NodeScript:Skipped'); /** * GraphEvalContext provides runtime tools for graph computation, * node caching, introspection, etc. */ export class GraphEvalContext { constructor(parent = null) { this.parent = parent; this.lib = runtimeLib; // Each context maintains its own cache. Subscopes have separate caches // and do not delegate to parent contexts. this.cache = new Map(); // Locals are stored per-context. Lookups delegate up the hierarchy. this.locals = new Map(); // Scope data is maintained by each context separately. // Compiler populates it via setScopeData when emitting nodes with subgraphs. this.scopeData = undefined; this.nodeEvaluated = parent ? parent.nodeEvaluated : new Event(); this.scopeCaptured = parent ? parent.scopeCaptured : new Event(); this.pendingNodeUids = parent ? parent.pendingNodeUids : new Set(); } clear() { this.cache.clear(); this.locals.clear(); this.pendingNodeUids.clear(); } async finalize() { } get depth() { return this.parent ? this.parent.depth + 1 : 0; } getLocal(key, defaultValue) { const val = this.locals.get(key); if (val === undefined) { if (this.parent) { return this.parent.getLocal(key); } return defaultValue ?? undefined; } return val; } setLocal(key, value) { if (value === undefined) { this.locals.delete(key); } else { this.locals.set(key, value); } } newScope() { return new GraphEvalContext(this); } getScopeData() { return this.scopeData; } setScopeData(data) { this.scopeData = data; return this; } convertType(value, schema) { return new Schema(schema).decode(value); } get(object, keyish) { return this.lib.get(object, keyish); } set(object, keyish, value) { return this.lib.set(object, keyish, value); } toArray(value) { return Array.isArray(value) ? value : [value]; } convertAuto(value, targetSchema = { type: 'any' }) { return convertAuto(value, targetSchema); } checkPendingNode(nodeUid) { if (this.pendingNodeUids.has(nodeUid)) { throw new NodePendingError(); } } skipEvaluation(message, token, status) { throw new EvaluationSkippedError(message ?? 'Evaluation skipped', token, status); } isControlException(error) { if (!error) { return false; } return error[SYM_PENDING] || error[SYM_SKIPPED]; } deferred(fn) { return new Deferred(fn); } isDeferred(value) { return value?.[SYM_DEFERRED]; } resolveDeferred(value) { if (this.isDeferred(value)) { const { resolve } = value; return resolve(); } return value; } /** * @deprecated kept for backwards compatibility */ span() { } } export class NodePendingError extends Error { constructor() { super(...arguments); this.name = this.constructor.name; this.code = 'EPENDING'; } get [SYM_PENDING]() { return true; } } export class EvaluationSkippedError extends Error { constructor(message, token, status) { super(message); this.code = 'ESKIPPED'; this.status = 204; this.token = ''; this.token = token ?? ''; this.status = status ?? 204; } get [SYM_SKIPPED]() { return true; } } export class Deferred { constructor(resolve) { this.resolve = resolve; } get [SYM_DEFERRED]() { return true; } } //# sourceMappingURL=GraphEvalContext.js.map