diff --git a/src/main/runtime/GraphEvalContext.ts b/src/main/runtime/GraphEvalContext.ts index 2bf94e2..c7bda48 100644 --- a/src/main/runtime/GraphEvalContext.ts +++ b/src/main/runtime/GraphEvalContext.ts @@ -24,6 +24,7 @@ export class GraphEvalContext implements t.GraphEvalContext { cache = new Map(); // Locals are stored per-context. Lookups delegate up the hierarchy. locals = new Map(); + _currentScope: any = null; constructor( readonly parent: GraphEvalContext | null = null, @@ -115,6 +116,16 @@ export class GraphEvalContext implements t.GraphEvalContext { */ span() {} + get currentScope(): any { + return this._currentScope ?? this.parent?.currentScope; + } + + withScope(scope: any): t.GraphEvalContext { + const ctx = new GraphEvalContext(this); + ctx._currentScope = scope; + return ctx; + } + } export class NodePendingError extends Error { diff --git a/src/main/runtime/ModuleLoader.ts b/src/main/runtime/ModuleLoader.ts index 3b0d4d5..323c4de 100644 --- a/src/main/runtime/ModuleLoader.ts +++ b/src/main/runtime/ModuleLoader.ts @@ -25,6 +25,7 @@ export abstract class GenericModuleLoader implements ModuleLoader { this.addModule('@system/Output', systemModules.Output); this.addModule('@system/Param', systemModules.Param); this.addModule('@system/Result', systemModules.Output); + this.addModule('@system/Scope', systemModules.Scope); } abstract resolveComputeUrl(ref: string): string; diff --git a/src/main/runtime/NodeView.ts b/src/main/runtime/NodeView.ts index 4abe915..55f7cfb 100644 --- a/src/main/runtime/NodeView.ts +++ b/src/main/runtime/NodeView.ts @@ -95,12 +95,15 @@ export class NodeView { supportsSubgraph() { const { subgraph } = this.getModuleSpec(); + if (this.isScopeNode() && this.graph.isSubgraph()) { + return false; + } return !!subgraph; } getSubgraph(): GraphView | null { const { subgraph } = this.getModuleSpec(); - if (!subgraph) { + if (!subgraph || (this.isScopeNode() && this.graph.isSubgraph())) { return null; } const { nodes = {}, rootNodeId = '', metadata = {} } = this.nodeSpec.subgraph ?? {}; @@ -303,6 +306,10 @@ export class NodeView { return this.canDock() && !!this.metadata.docked; } + isScopeNode() { + return this.ref === '@system/Scope'; + } + } export interface NodeLink { diff --git a/src/main/system/Scope.ts b/src/main/system/Scope.ts new file mode 100644 index 0000000..7ab938a --- /dev/null +++ b/src/main/system/Scope.ts @@ -0,0 +1,17 @@ +import { ModuleSpecSchema } from '../schema/ModuleSpec.js'; + +export const Scope = ModuleSpecSchema.create({ + moduleName: 'Scope', + version: '1.0.0', + keywords: ['system'], + description: 'Returns the current scope or an empty object', + deprecated: '', + params: {}, + result: { + schema: { type: 'object' }, + async: false, + }, + newScope: false, + cacheMode: 'auto', + evalMode: 'auto' +}); diff --git a/src/main/system/index.ts b/src/main/system/index.ts index c3a7a4e..0ec507e 100644 --- a/src/main/system/index.ts +++ b/src/main/system/index.ts @@ -9,3 +9,4 @@ export * from './Frame.js'; export * from './Input.js'; export * from './Output.js'; export * from './Param.js'; +export * from './Scope.js'; diff --git a/src/main/types/ctx.ts b/src/main/types/ctx.ts index 9a7dfe3..6eccb6c 100644 --- a/src/main/types/ctx.ts +++ b/src/main/types/ctx.ts @@ -15,6 +15,7 @@ export interface GraphEvalContext { locals: Map; nodeEvaluated: Event; scopeCaptured: Event; + _currentScope: any; clear(): void; finalize(): Promise;