@nodescript/core
Version:
Visual programming language for Browser and Node
142 lines • 4.16 kB
JavaScript
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