flowcraft 2.8.1 → 2.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -98,19 +98,22 @@ interface AwaitingWorkflow {
98
98
  serializedContext: string;
99
99
  awaitingNodeId: string;
100
100
  wakeUpAt: string;
101
+ functionRegistry?: Map<string, any>;
101
102
  }
102
103
  declare class WorkflowScheduler {
103
104
  private runtime;
104
105
  private activeWorkflows;
106
+ private resumeResults;
105
107
  private intervalId?;
106
108
  private checkIntervalMs;
107
109
  constructor(runtime: FlowRuntime<any, any>, checkIntervalMs?: number);
108
110
  start(): void;
109
111
  stop(): void;
110
- registerAwaitingWorkflow(executionId: string, blueprintId: string, serializedContext: string, awaitingNodeId: string, wakeUpAt: string): void;
112
+ registerAwaitingWorkflow(executionId: string, blueprintId: string, serializedContext: string, awaitingNodeId: string, wakeUpAt: string, functionRegistry?: Map<string, any>): void;
111
113
  unregisterWorkflow(executionId: string): void;
112
114
  private checkAndResumeWorkflows;
113
115
  getActiveWorkflows(): AwaitingWorkflow[];
116
+ getResumeResult(executionId: string): WorkflowResult | undefined;
114
117
  }
115
118
  //#endregion
116
119
  //#region src/runtime/state.d.ts
@@ -261,7 +264,6 @@ interface IRuntime<TContext extends Record<string, any> = Record<string, any>, T
261
264
  edge: EdgeDefinition;
262
265
  }[]>;
263
266
  applyEdgeTransform: (edge: EdgeDefinition, sourceResult: NodeResult, targetNode: NodeDefinition, context: ContextImplementation<TContext>, allPredecessors?: Map<string, Set<string>>, executionId?: string) => Promise<void>;
264
- createForSubflow: (subBlueprint: WorkflowBlueprint, initialSubState: Partial<TContext>, executionId: string, signal?: AbortSignal) => ExecutionContext<TContext, TDependencies>;
265
267
  getExecutorForNode: (nodeId: string, context: ExecutionContext<TContext, TDependencies>) => NodeExecutor<TContext, TDependencies>;
266
268
  }
267
269
  //#endregion
@@ -307,7 +309,6 @@ declare class FlowRuntime<TContext extends Record<string, any>, TDependencies ex
307
309
  _createExecutionRegistry(dynamicRegistry?: Map<string, any>): Map<string, NodeFunction | NodeClass>;
308
310
  executeNode(blueprint: WorkflowBlueprint, nodeId: string, state: WorkflowState<TContext>, _allPredecessors?: Map<string, Set<string>>, functionRegistry?: Map<string, any>, executionId?: string, signal?: AbortSignal): Promise<NodeResult<any, any>>;
309
311
  getExecutorForNode(nodeId: string, context: ExecutionContext<TContext, TDependencies>): any;
310
- createForSubflow(subBlueprint: WorkflowBlueprint, initialSubState: Partial<TContext>, executionId: string, signal?: AbortSignal): ExecutionContext<TContext, TDependencies>;
311
312
  determineNextNodes(blueprint: WorkflowBlueprint, nodeId: string, result: NodeResult<any, any>, context: ContextImplementation<TContext>, executionId?: string): Promise<{
312
313
  node: NodeDefinition;
313
314
  edge: EdgeDefinition;
@@ -1011,6 +1012,27 @@ declare class FlowBuilder<TContext extends Record<string, any> = Record<string,
1011
1012
  setCycleEntryPoint(nodeId: string): this;
1012
1013
  toBlueprint(): WorkflowBlueprint;
1013
1014
  getFunctionRegistry(): Map<string, NodeFunction | NodeClass>;
1015
+ /**
1016
+ * Runs this flow on the given runtime, automatically passing the function registry.
1017
+ * Convenience wrapper around `runtime.run(blueprint, initialState, { functionRegistry })`.
1018
+ */
1019
+ run(runtime: FlowRuntime<TContext, TDependencies>, initialState?: Partial<TContext> | string, options?: {
1020
+ strict?: boolean;
1021
+ signal?: AbortSignal;
1022
+ concurrency?: number;
1023
+ }): Promise<WorkflowResult<TContext>>;
1024
+ /**
1025
+ * Resumes this flow on the given runtime, automatically passing the function registry.
1026
+ * Convenience wrapper around `runtime.resume(blueprint, ...)`.
1027
+ */
1028
+ resume(runtime: FlowRuntime<TContext, TDependencies>, serializedContext: string, resumeData: {
1029
+ output?: any;
1030
+ action?: string;
1031
+ }, nodeId?: string, options?: {
1032
+ strict?: boolean;
1033
+ signal?: AbortSignal;
1034
+ concurrency?: number;
1035
+ }): Promise<WorkflowResult<TContext>>;
1014
1036
  toGraphRepresentation(): UIGraph;
1015
1037
  }
1016
1038
  /**
@@ -1056,6 +1078,40 @@ declare class NullLogger implements ILogger {
1056
1078
  error(_message: string, _meta?: Record<string, any>): void;
1057
1079
  }
1058
1080
  //#endregion
1081
+ //#region src/nodes/batch-gather.d.ts
1082
+ declare class BatchGatherNode extends BaseNode {
1083
+ exec(_prepResult: any, context: NodeContext<any, any, any>): Promise<Omit<NodeResult, 'error'>>;
1084
+ }
1085
+ //#endregion
1086
+ //#region src/nodes/batch-scatter.d.ts
1087
+ declare class BatchScatterNode extends BaseNode {
1088
+ exec(_prepResult: any, context: NodeContext<any, any, any>): Promise<Omit<NodeResult, 'error'>>;
1089
+ }
1090
+ //#endregion
1091
+ //#region src/nodes/sleep.d.ts
1092
+ declare class SleepNode extends BaseNode {
1093
+ exec(prepResult: any, context: NodeContext<Record<string, any>, any, any>): Promise<Omit<NodeResult, 'error'>>;
1094
+ }
1095
+ //#endregion
1096
+ //#region src/nodes/subflow.d.ts
1097
+ declare class SubflowNode extends BaseNode {
1098
+ exec(_prepResult: any, context: NodeContext<Record<string, any>, any, any>): Promise<Omit<NodeResult, 'error'>>;
1099
+ }
1100
+ //#endregion
1101
+ //#region src/nodes/wait.d.ts
1102
+ declare class WaitNode extends BaseNode {
1103
+ exec(_prepResult: any, context: NodeContext<Record<string, any>, any, any>): Promise<Omit<NodeResult, 'error'>>;
1104
+ }
1105
+ //#endregion
1106
+ //#region src/nodes/webhook.d.ts
1107
+ declare class WebhookNode extends BaseNode {
1108
+ prep(context: NodeContext<Record<string, any>, any, any>): Promise<any>;
1109
+ exec(prepResult: {
1110
+ url: string;
1111
+ event: string;
1112
+ }, _context: NodeContext<Record<string, any>, any, any>): Promise<Omit<NodeResult, 'error'>>;
1113
+ }
1114
+ //#endregion
1059
1115
  //#region src/runtime/adapter.d.ts
1060
1116
  /**
1061
1117
  * Defines the contract for an atomic, distributed key-value store required by
@@ -1265,5 +1321,5 @@ declare class JsonSerializer implements ISerializer {
1265
1321
  deserialize(text: string): Record<string, any>;
1266
1322
  }
1267
1323
  //#endregion
1268
- export { NodeResult as $, checkForCycles as A, IEvaluator as B, Context as C, WorkflowState as Ct, BlueprintAnalysis as D, BaseNode as Dt, createDefaultContainer as E, ServiceTokens as Et, PersistentEventBusAdapter as F, Middleware as G, ILogger as H, ContextImplementation as I, NodeContext as J, NodeClass as K, EdgeDefinition as L, generateMermaidForRun as M, IEventStore as N, Cycles as O, isNodeClass as Ot, InMemoryEventStore as P, NodeRegistry as Q, FlowcraftEvent as R, AsyncContextView as S, NodeExecutorConfig as St, ContainerOptions as T, ServiceToken as Tt, ISerializer as U, IEventBus as V, ISyncContext as W, NodeFunction as X, NodeDefinition as Y, NodeImplementation as Z, FlowBuilder as _, ClassNodeExecutor as _t, ReplayOrchestrator as a, WorkflowBlueprint as at, UnsafeEvaluator as b, NodeExecutionResult as bt, BaseDistributedAdapter as c, WorkflowResult as ct, ConsoleLogger as d, ExecutionServices as dt, PatchOperation as et, NullLogger as f, IOrchestrator as ft, lintBlueprint as g, ReadyNode as gt, LinterResult as h, GraphTraverser as ht, processResults as i, UIGraph as it, generateMermaid as j, analyzeBlueprint as k, FlowcraftError as kt, ICoordinationStore as l, WorkflowStatus as lt, LinterIssueCode as m, NodeExecutorFactory as mt, sanitizeBlueprint as n, RuntimeOptions as nt, DefaultOrchestrator as o, WorkflowBlueprintMetadata as ot, LinterIssue as p, IRuntime as pt, NodeConfig as q, executeBatch as r, SourceLocation as rt, AdapterOptions as s, WorkflowError as st, JsonSerializer as t, RuntimeDependencies as tt, JobPayload as u, FlowRuntime as ut, createFlow as v, ExecutionStrategy as vt, TrackedAsyncContext as w, DIContainer as wt, createErrorMapper as x, NodeExecutor as xt, PropertyEvaluator as y, FunctionNodeExecutor as yt, IAsyncContext as z };
1269
- //# sourceMappingURL=index-D3dyjW2G.d.mts.map
1324
+ export { NodeConfig as $, TrackedAsyncContext as A, DIContainer as At, InMemoryEventStore as B, FlowBuilder as C, ClassNodeExecutor as Ct, createErrorMapper as D, NodeExecutor as Dt, UnsafeEvaluator as E, NodeExecutionResult as Et, analyzeBlueprint as F, FlowcraftError as Ft, IAsyncContext as G, ContextImplementation as H, checkForCycles as I, ILogger as J, IEvaluator as K, generateMermaid as L, createDefaultContainer as M, ServiceTokens as Mt, BlueprintAnalysis as N, BaseNode as Nt, AsyncContextView as O, NodeExecutorConfig as Ot, Cycles as P, isNodeClass as Pt, NodeClass as Q, generateMermaidForRun as R, lintBlueprint as S, ReadyNode as St, PropertyEvaluator as T, FunctionNodeExecutor as Tt, EdgeDefinition as U, PersistentEventBusAdapter as V, FlowcraftEvent as W, ISyncContext as X, ISerializer as Y, Middleware as Z, ConsoleLogger as _, ExecutionServices as _t, ReplayOrchestrator as a, NodeResult as at, LinterIssueCode as b, NodeExecutorFactory as bt, BaseDistributedAdapter as c, RuntimeOptions as ct, WebhookNode as d, WorkflowBlueprint as dt, NodeContext as et, WaitNode as f, WorkflowBlueprintMetadata as ft, BatchGatherNode as g, FlowRuntime as gt, BatchScatterNode as h, WorkflowStatus as ht, processResults as i, NodeRegistry as it, ContainerOptions as j, ServiceToken as jt, Context as k, WorkflowState as kt, ICoordinationStore as l, SourceLocation as lt, SleepNode as m, WorkflowResult as mt, sanitizeBlueprint as n, NodeFunction as nt, DefaultOrchestrator as o, PatchOperation as ot, SubflowNode as p, WorkflowError as pt, IEventBus as q, executeBatch as r, NodeImplementation as rt, AdapterOptions as s, RuntimeDependencies as st, JsonSerializer as t, NodeDefinition as tt, JobPayload as u, UIGraph as ut, NullLogger as v, IOrchestrator as vt, createFlow as w, ExecutionStrategy as wt, LinterResult as x, GraphTraverser as xt, LinterIssue as y, IRuntime as yt, IEventStore as z };
1325
+ //# sourceMappingURL=index-BzLn8lCA.d.mts.map
package/dist/index.d.mts CHANGED
@@ -1,2 +1,2 @@
1
- import { $ as NodeResult, A as checkForCycles, B as IEvaluator, C as Context, Ct as WorkflowState, D as BlueprintAnalysis, Dt as BaseNode, E as createDefaultContainer, Et as ServiceTokens, F as PersistentEventBusAdapter, G as Middleware, H as ILogger, I as ContextImplementation, J as NodeContext, K as NodeClass, L as EdgeDefinition, M as generateMermaidForRun, N as IEventStore, O as Cycles, Ot as isNodeClass, P as InMemoryEventStore, Q as NodeRegistry, R as FlowcraftEvent, S as AsyncContextView, St as NodeExecutorConfig, T as ContainerOptions, Tt as ServiceToken, U as ISerializer, V as IEventBus, W as ISyncContext, X as NodeFunction, Y as NodeDefinition, Z as NodeImplementation, _ as FlowBuilder, _t as ClassNodeExecutor, a as ReplayOrchestrator, at as WorkflowBlueprint, b as UnsafeEvaluator, bt as NodeExecutionResult, c as BaseDistributedAdapter, ct as WorkflowResult, d as ConsoleLogger, dt as ExecutionServices, et as PatchOperation, f as NullLogger, ft as IOrchestrator, g as lintBlueprint, gt as ReadyNode, h as LinterResult, ht as GraphTraverser, i as processResults, it as UIGraph, j as generateMermaid, k as analyzeBlueprint, kt as FlowcraftError, l as ICoordinationStore, lt as WorkflowStatus, m as LinterIssueCode, mt as NodeExecutorFactory, n as sanitizeBlueprint, nt as RuntimeOptions, o as DefaultOrchestrator, ot as WorkflowBlueprintMetadata, p as LinterIssue, pt as IRuntime, q as NodeConfig, r as executeBatch, rt as SourceLocation, s as AdapterOptions, st as WorkflowError, t as JsonSerializer, tt as RuntimeDependencies, u as JobPayload, ut as FlowRuntime, v as createFlow, vt as ExecutionStrategy, w as TrackedAsyncContext, wt as DIContainer, x as createErrorMapper, xt as NodeExecutor, y as PropertyEvaluator, yt as FunctionNodeExecutor, z as IAsyncContext } from "./index-D3dyjW2G.mjs";
2
- export { AdapterOptions, AsyncContextView, BaseDistributedAdapter, BaseNode, BlueprintAnalysis, ClassNodeExecutor, ConsoleLogger, ContainerOptions, Context, ContextImplementation, Cycles, DIContainer, DefaultOrchestrator, EdgeDefinition, ExecutionServices, ExecutionStrategy, FlowBuilder, FlowRuntime, FlowcraftError, FlowcraftEvent, FunctionNodeExecutor, GraphTraverser, IAsyncContext, ICoordinationStore, IEvaluator, IEventBus, IEventStore, ILogger, IOrchestrator, IRuntime, ISerializer, ISyncContext, InMemoryEventStore, JobPayload, JsonSerializer, LinterIssue, LinterIssueCode, LinterResult, Middleware, NodeClass, NodeConfig, NodeContext, NodeDefinition, NodeExecutionResult, NodeExecutor, NodeExecutorConfig, NodeExecutorFactory, NodeFunction, NodeImplementation, NodeRegistry, NodeResult, NullLogger, PatchOperation, PersistentEventBusAdapter, PropertyEvaluator, ReadyNode, ReplayOrchestrator, RuntimeDependencies, RuntimeOptions, ServiceToken, ServiceTokens, SourceLocation, TrackedAsyncContext, UIGraph, UnsafeEvaluator, WorkflowBlueprint, WorkflowBlueprintMetadata, WorkflowError, WorkflowResult, WorkflowState, WorkflowStatus, analyzeBlueprint, checkForCycles, createDefaultContainer, createErrorMapper, createFlow, executeBatch, generateMermaid, generateMermaidForRun, isNodeClass, lintBlueprint, processResults, sanitizeBlueprint };
1
+ import { $ as NodeConfig, A as TrackedAsyncContext, At as DIContainer, B as InMemoryEventStore, C as FlowBuilder, Ct as ClassNodeExecutor, D as createErrorMapper, Dt as NodeExecutor, E as UnsafeEvaluator, Et as NodeExecutionResult, F as analyzeBlueprint, Ft as FlowcraftError, G as IAsyncContext, H as ContextImplementation, I as checkForCycles, J as ILogger, K as IEvaluator, L as generateMermaid, M as createDefaultContainer, Mt as ServiceTokens, N as BlueprintAnalysis, Nt as BaseNode, O as AsyncContextView, Ot as NodeExecutorConfig, P as Cycles, Pt as isNodeClass, Q as NodeClass, R as generateMermaidForRun, S as lintBlueprint, St as ReadyNode, T as PropertyEvaluator, Tt as FunctionNodeExecutor, U as EdgeDefinition, V as PersistentEventBusAdapter, W as FlowcraftEvent, X as ISyncContext, Y as ISerializer, Z as Middleware, _ as ConsoleLogger, _t as ExecutionServices, a as ReplayOrchestrator, at as NodeResult, b as LinterIssueCode, bt as NodeExecutorFactory, c as BaseDistributedAdapter, ct as RuntimeOptions, d as WebhookNode, dt as WorkflowBlueprint, et as NodeContext, f as WaitNode, ft as WorkflowBlueprintMetadata, g as BatchGatherNode, gt as FlowRuntime, h as BatchScatterNode, ht as WorkflowStatus, i as processResults, it as NodeRegistry, j as ContainerOptions, jt as ServiceToken, k as Context, kt as WorkflowState, l as ICoordinationStore, lt as SourceLocation, m as SleepNode, mt as WorkflowResult, n as sanitizeBlueprint, nt as NodeFunction, o as DefaultOrchestrator, ot as PatchOperation, p as SubflowNode, pt as WorkflowError, q as IEventBus, r as executeBatch, rt as NodeImplementation, s as AdapterOptions, st as RuntimeDependencies, t as JsonSerializer, tt as NodeDefinition, u as JobPayload, ut as UIGraph, v as NullLogger, vt as IOrchestrator, w as createFlow, wt as ExecutionStrategy, x as LinterResult, xt as GraphTraverser, y as LinterIssue, yt as IRuntime, z as IEventStore } from "./index-BzLn8lCA.mjs";
2
+ export { AdapterOptions, AsyncContextView, BaseDistributedAdapter, BaseNode, BatchGatherNode, BatchScatterNode, BlueprintAnalysis, ClassNodeExecutor, ConsoleLogger, ContainerOptions, Context, ContextImplementation, Cycles, DIContainer, DefaultOrchestrator, EdgeDefinition, ExecutionServices, ExecutionStrategy, FlowBuilder, FlowRuntime, FlowcraftError, FlowcraftEvent, FunctionNodeExecutor, GraphTraverser, IAsyncContext, ICoordinationStore, IEvaluator, IEventBus, IEventStore, ILogger, IOrchestrator, IRuntime, ISerializer, ISyncContext, InMemoryEventStore, JobPayload, JsonSerializer, LinterIssue, LinterIssueCode, LinterResult, Middleware, NodeClass, NodeConfig, NodeContext, NodeDefinition, NodeExecutionResult, NodeExecutor, NodeExecutorConfig, NodeExecutorFactory, NodeFunction, NodeImplementation, NodeRegistry, NodeResult, NullLogger, PatchOperation, PersistentEventBusAdapter, PropertyEvaluator, ReadyNode, ReplayOrchestrator, RuntimeDependencies, RuntimeOptions, ServiceToken, ServiceTokens, SleepNode, SourceLocation, SubflowNode, TrackedAsyncContext, UIGraph, UnsafeEvaluator, WaitNode, WebhookNode, WorkflowBlueprint, WorkflowBlueprintMetadata, WorkflowError, WorkflowResult, WorkflowState, WorkflowStatus, analyzeBlueprint, checkForCycles, createDefaultContainer, createErrorMapper, createFlow, executeBatch, generateMermaid, generateMermaidForRun, isNodeClass, lintBlueprint, processResults, sanitizeBlueprint };
package/dist/index.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { t as ReplayOrchestrator } from "./replay-BB11M6K1.mjs";
2
- import { A as PersistentEventBusAdapter, C as DIContainer, D as generateMermaid, E as checkForCycles, O as generateMermaidForRun, S as UnsafeEvaluator, T as analyzeBlueprint, _ as TrackedAsyncContext, a as sanitizeBlueprint, b as NullLogger, c as isNodeClass, d as executeBatch, f as processResults, g as Context, h as AsyncContextView, i as NodeExecutor, k as InMemoryEventStore, l as JsonSerializer, m as WorkflowState, n as ClassNodeExecutor, o as GraphTraverser, r as FunctionNodeExecutor, s as BaseNode, t as FlowRuntime, u as DefaultOrchestrator, v as FlowcraftError, w as ServiceTokens, x as PropertyEvaluator, y as ConsoleLogger } from "./runtime-CmefIAu_.mjs";
2
+ import { A as ServiceTokens, C as TrackedAsyncContext, D as PropertyEvaluator, E as NullLogger, F as InMemoryEventStore, I as PersistentEventBusAdapter, M as checkForCycles, N as generateMermaid, O as UnsafeEvaluator, P as generateMermaidForRun, S as Context, T as ConsoleLogger, _ as executeBatch, a as sanitizeBlueprint, b as WorkflowState, c as SubflowNode, d as BatchScatterNode, f as BatchGatherNode, g as DefaultOrchestrator, h as JsonSerializer, i as NodeExecutor, j as analyzeBlueprint, k as DIContainer, l as GraphTraverser, m as isNodeClass, n as ClassNodeExecutor, o as WebhookNode, p as BaseNode, r as FunctionNodeExecutor, s as WaitNode, t as FlowRuntime, u as SleepNode, v as processResults, w as FlowcraftError, x as AsyncContextView } from "./runtime-gjjfM1q1.mjs";
3
3
 
4
4
  //#region src/container-factory.ts
5
5
  function createDefaultContainer(options = {}) {
@@ -279,6 +279,26 @@ var FlowBuilder = class {
279
279
  getFunctionRegistry() {
280
280
  return this.functionRegistry;
281
281
  }
282
+ /**
283
+ * Runs this flow on the given runtime, automatically passing the function registry.
284
+ * Convenience wrapper around `runtime.run(blueprint, initialState, { functionRegistry })`.
285
+ */
286
+ async run(runtime, initialState = {}, options) {
287
+ return runtime.run(this.toBlueprint(), initialState, {
288
+ ...options,
289
+ functionRegistry: this.functionRegistry
290
+ });
291
+ }
292
+ /**
293
+ * Resumes this flow on the given runtime, automatically passing the function registry.
294
+ * Convenience wrapper around `runtime.resume(blueprint, ...)`.
295
+ */
296
+ async resume(runtime, serializedContext, resumeData, nodeId, options) {
297
+ return runtime.resume(this.toBlueprint(), serializedContext, resumeData, nodeId, {
298
+ ...options,
299
+ functionRegistry: this.functionRegistry
300
+ });
301
+ }
282
302
  toGraphRepresentation() {
283
303
  const blueprint = this.toBlueprint();
284
304
  const uiNodes = [];
@@ -723,5 +743,5 @@ var BaseDistributedAdapter = class {
723
743
  };
724
744
 
725
745
  //#endregion
726
- export { AsyncContextView, BaseDistributedAdapter, BaseNode, ClassNodeExecutor, ConsoleLogger, Context, DIContainer, DefaultOrchestrator, FlowBuilder, FlowRuntime, FlowcraftError, FunctionNodeExecutor, GraphTraverser, InMemoryEventStore, JsonSerializer, NodeExecutor, NullLogger, PersistentEventBusAdapter, PropertyEvaluator, ReplayOrchestrator, ServiceTokens, TrackedAsyncContext, UnsafeEvaluator, WorkflowState, analyzeBlueprint, checkForCycles, createDefaultContainer, createErrorMapper, createFlow, executeBatch, generateMermaid, generateMermaidForRun, isNodeClass, lintBlueprint, processResults, sanitizeBlueprint };
746
+ export { AsyncContextView, BaseDistributedAdapter, BaseNode, BatchGatherNode, BatchScatterNode, ClassNodeExecutor, ConsoleLogger, Context, DIContainer, DefaultOrchestrator, FlowBuilder, FlowRuntime, FlowcraftError, FunctionNodeExecutor, GraphTraverser, InMemoryEventStore, JsonSerializer, NodeExecutor, NullLogger, PersistentEventBusAdapter, PropertyEvaluator, ReplayOrchestrator, ServiceTokens, SleepNode, SubflowNode, TrackedAsyncContext, UnsafeEvaluator, WaitNode, WebhookNode, WorkflowState, analyzeBlueprint, checkForCycles, createDefaultContainer, createErrorMapper, createFlow, executeBatch, generateMermaid, generateMermaidForRun, isNodeClass, lintBlueprint, processResults, sanitizeBlueprint };
727
747
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/container-factory.ts","../src/error-mapper.ts","../src/flow.ts","../src/linter.ts","../src/runtime/adapter.ts"],"sourcesContent":["import { DIContainer, ServiceTokens } from './container'\nimport { PropertyEvaluator } from './evaluator'\nimport { NullLogger } from './logger'\nimport { DefaultOrchestrator } from './runtime/orchestrator'\nimport { JsonSerializer } from './serializer'\nimport type {\n\tIEvaluator,\n\tIEventBus,\n\tILogger,\n\tISerializer,\n\tMiddleware,\n\tNodeClass,\n\tNodeFunction,\n\tRuntimeDependencies,\n\tWorkflowBlueprint,\n} from './types'\n\nexport interface ContainerOptions<TDependencies extends RuntimeDependencies = RuntimeDependencies> {\n\tlogger?: ILogger\n\tserializer?: ISerializer\n\tevaluator?: IEvaluator\n\teventBus?: IEventBus\n\tmiddleware?: Middleware[]\n\tregistry?: Record<string, NodeFunction | NodeClass>\n\tblueprints?: Record<string, WorkflowBlueprint>\n\tdependencies?: TDependencies\n}\n\nexport function createDefaultContainer<TDependencies extends RuntimeDependencies = RuntimeDependencies>(\n\toptions: ContainerOptions<TDependencies> = {},\n): DIContainer {\n\tconst container = new DIContainer()\n\n\tcontainer.register(ServiceTokens.Logger, options.logger || new NullLogger())\n\tcontainer.register(ServiceTokens.Serializer, options.serializer || new JsonSerializer())\n\tcontainer.register(ServiceTokens.Evaluator, options.evaluator || new PropertyEvaluator())\n\tcontainer.register(ServiceTokens.EventBus, options.eventBus || { emit: async () => {} })\n\tcontainer.register(ServiceTokens.Middleware, options.middleware || [])\n\tcontainer.register(ServiceTokens.NodeRegistry, options.registry || {})\n\tcontainer.register(ServiceTokens.BlueprintRegistry, options.blueprints || {})\n\tcontainer.register(ServiceTokens.Dependencies, options.dependencies || ({} as TDependencies))\n\n\tcontainer.registerFactory(ServiceTokens.Orchestrator, () => new DefaultOrchestrator())\n\n\treturn container\n}\n","import { FlowcraftError } from './errors'\nimport type { SourceLocation, WorkflowBlueprint } from './types'\n\n/**\n * Creates an error mapper function that enhances runtime errors with source location information.\n * The mapper looks up node IDs in the provided manifest blueprints and returns enhanced errors\n * that point to the original TypeScript source code.\n *\n * @param manifestBlueprints - The compiled blueprint manifest containing source location data\n * @returns A function that maps errors to enhanced errors with source location information\n */\nexport function createErrorMapper(manifestBlueprints: Record<string, WorkflowBlueprint>) {\n\tconst locationMap = new Map<string, SourceLocation>()\n\n\t// build a quick lookup map\n\tfor (const blueprint of Object.values(manifestBlueprints)) {\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (node._sourceLocation) {\n\t\t\t\tlocationMap.set(node.id, node._sourceLocation)\n\t\t\t}\n\t\t}\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tif (edge._sourceLocation) {\n\t\t\t\tconst edgeKey = `${edge.source}-${edge.target}`\n\t\t\t\tlocationMap.set(edgeKey, edge._sourceLocation)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn function mapError(error: Error): Error {\n\t\tif (error instanceof FlowcraftError && error.nodeId) {\n\t\t\tconst location = locationMap.get(error.nodeId)\n\t\t\tif (location) {\n\t\t\t\treturn new Error(\n\t\t\t\t\t`Workflow error at ${location.file}:${location.line}:${location.column}. Original error: ${error.message}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// try to extract nodeId from error message\n\t\tconst nodeIdMatch = error.message.match(/nodeId[:\\s]+([^\\s,]+)/i)\n\t\tif (nodeIdMatch) {\n\t\t\tconst nodeId = nodeIdMatch[1]\n\t\t\tconst location = locationMap.get(nodeId)\n\t\t\tif (location) {\n\t\t\t\treturn new Error(\n\t\t\t\t\t`Workflow error at ${location.file}:${location.line}:${location.column}. Original error: ${error.message}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\treturn error\n\t}\n}\n","import { isNodeClass } from './node'\nimport type { EdgeDefinition, NodeClass, NodeDefinition, NodeFunction, UIGraph, WorkflowBlueprint } from './types'\n\n/**\n * Generates a deterministic hash for a function based on its source code and a unique counter.\n */\nlet hashCounter = 0\nfunction _hashFunction(fn: NodeFunction<any, any, any, any, any> | NodeClass<any, any, any, any, any>): string {\n\tconst source = fn.toString()\n\tlet hash = 0\n\tfor (let i = 0; i < source.length; i++) {\n\t\tconst char = source.charCodeAt(i)\n\t\thash = (hash << 5) - hash + char\n\t\thash = hash & hash // Convert to 32-bit integer\n\t}\n\t// Add counter to ensure uniqueness even for identical functions\n\treturn (Math.abs(hash) + hashCounter++).toString(16)\n}\n\n/**\n * A fluent API for programmatically constructing a WorkflowBlueprint.\n */\nexport class FlowBuilder<\n\tTContext extends Record<string, any> = Record<string, any>,\n\tTDependencies extends Record<string, any> = Record<string, any>,\n> {\n\tprivate blueprint: Partial<WorkflowBlueprint>\n\tprivate functionRegistry: Map<string, NodeFunction | NodeClass>\n\tprivate loopDefinitions: Array<{\n\t\tid: string\n\t\tstartNodeId: string\n\t\tendNodeId: string\n\t\tcondition: string\n\t}>\n\tprivate batchDefinitions: Array<{\n\t\tid: string\n\t\tscatterId: string\n\t\tgatherId: string\n\t}>\n\tprivate cycleEntryPoints: Map<string, string>\n\n\tconstructor(id: string) {\n\t\tthis.blueprint = { id, nodes: [], edges: [] }\n\t\tthis.functionRegistry = new Map()\n\t\tthis.loopDefinitions = []\n\t\tthis.batchDefinitions = []\n\t\tthis.cycleEntryPoints = new Map()\n\t}\n\n\tnode<TInput = any, TOutput = any, TAction extends string = string>(\n\t\tid: string,\n\t\timplementation:\n\t\t\t| NodeFunction<TContext, TDependencies, TInput, TOutput, TAction>\n\t\t\t| NodeClass<TContext, TDependencies, TInput, TOutput, TAction>,\n\t\toptions?: Omit<NodeDefinition, 'id' | 'uses'>,\n\t): this {\n\t\tlet usesKey: string\n\n\t\tif (isNodeClass(implementation)) {\n\t\t\tusesKey =\n\t\t\t\timplementation.name && implementation.name !== 'BaseNode'\n\t\t\t\t\t? implementation.name\n\t\t\t\t\t: `class_${_hashFunction(implementation)}`\n\t\t\tthis.functionRegistry.set(usesKey, implementation)\n\t\t} else {\n\t\t\tusesKey = `fn_${_hashFunction(implementation)}`\n\t\t\tthis.functionRegistry.set(usesKey, implementation as unknown as NodeFunction)\n\t\t}\n\n\t\tconst nodeDef: NodeDefinition = { id, uses: usesKey, ...options }\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\tedge(source: string, target: string, options?: Omit<EdgeDefinition, 'source' | 'target'>): this {\n\t\tconst edgeDef: EdgeDefinition = { source, target, ...options }\n\t\tthis.blueprint.edges?.push(edgeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a batch processing pattern.\n\t * It takes an input array, runs a worker node on each item in parallel, and gathers the results.\n\t * This method augments the Flow's TContext with a new key for the output array.\n\t *\n\t * @param id The base ID for this batch operation.\n\t * @param worker The node implementation to run on each item.\n\t * @param options Configuration for the batch operation.\n\t * @returns The Flow instance with an updated context type for chaining.\n\t */\n\tbatch<TWorkerInput, TWorkerOutput, TWorkerAction extends string, TOutputKey extends string>(\n\t\tid: string,\n\t\tworker:\n\t\t\t| NodeFunction<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>\n\t\t\t| NodeClass<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>,\n\t\toptions: {\n\t\t\t/** The key in the context that holds the input array for the batch. */\n\t\t\tinputKey: keyof TContext\n\t\t\t/** The key in the context where the array of results will be stored. */\n\t\t\toutputKey: TOutputKey\n\t\t\t/** The number of items to process in each chunk to limit memory usage. */\n\t\t\tchunkSize?: number\n\t\t},\n\t): FlowBuilder<TContext & { [K in TOutputKey]: TWorkerOutput[] }, TDependencies> {\n\t\tconst { inputKey, outputKey } = options\n\t\tconst scatterId = `${id}_scatter`\n\t\tconst gatherId = `${id}_gather`\n\n\t\tthis.batchDefinitions.push({ id, scatterId, gatherId })\n\n\t\tlet workerUsesKey: string\n\t\tif (isNodeClass(worker)) {\n\t\t\tworkerUsesKey =\n\t\t\t\tworker.name && worker.name !== 'BaseNode' ? worker.name : `class_batch_worker_${_hashFunction(worker)}`\n\t\t\tthis.functionRegistry.set(workerUsesKey, worker)\n\t\t} else {\n\t\t\tworkerUsesKey = `fn_batch_worker_${_hashFunction(worker)}`\n\t\t\tthis.functionRegistry.set(workerUsesKey, worker as unknown as NodeFunction)\n\t\t}\n\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: scatterId,\n\t\t\tuses: 'batch-scatter',\n\t\t\tinputs: inputKey as string,\n\t\t\tparams: { workerUsesKey, outputKey: outputKey as string, gatherNodeId: gatherId, chunkSize: options.chunkSize },\n\t\t})\n\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: gatherId,\n\t\t\tuses: 'batch-gather',\n\t\t\tparams: { outputKey, gatherNodeId: gatherId },\n\t\t\tconfig: { joinStrategy: 'all' },\n\t\t})\n\n\t\tthis.edge(scatterId, gatherId)\n\n\t\treturn this as unknown as FlowBuilder<TContext & { [K in TOutputKey]: TWorkerOutput[] }, TDependencies>\n\t}\n\n\t/**\n\t * Creates a sleep node that pauses workflow execution for a specified duration.\n\t * @param id A unique identifier for the sleep node.\n\t * @param options Configuration for the sleep duration.\n\t */\n\tsleep(\n\t\tid: string,\n\t\toptions: {\n\t\t\t/** The duration to sleep in milliseconds or a string like '5s', '1m', '2h', '1d'. */\n\t\t\tduration: number | string\n\t\t},\n\t): this {\n\t\tconst nodeDef: NodeDefinition = {\n\t\t\tid,\n\t\t\tuses: 'sleep',\n\t\t\tparams: { duration: options.duration },\n\t\t}\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a wait node that pauses workflow execution for external input.\n\t * @param id A unique identifier for the wait node.\n\t * @param options Optional configuration for the wait node.\n\t */\n\twait(id: string, options?: Omit<NodeDefinition, 'id' | 'uses'>): this {\n\t\tconst nodeDef: NodeDefinition = { id, uses: 'wait', ...options }\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a loop pattern in the workflow graph.\n\t * @param id A unique identifier for the loop construct.\n\t * @param options Defines the start, end, and continuation condition of the loop.\n\t * @param options.startNodeId The ID of the first node inside the loop body.\n\t * @param options.endNodeId The ID of the last node inside the loop body.\n\t * @param options.condition An expression that, if true, causes the loop to run again.\n\t */\n\tloop(\n\t\tid: string,\n\t\toptions: {\n\t\t\t/** The ID of the first node inside the loop body. */\n\t\t\tstartNodeId: string\n\t\t\t/** The ID of the last node inside the loop body. */\n\t\t\tendNodeId: string\n\t\t\t/** An expression that, if true, causes the loop to run again. */\n\t\t\tcondition: string\n\t\t},\n\t): this {\n\t\tconst { startNodeId, endNodeId, condition } = options\n\t\tthis.loopDefinitions.push({ id, startNodeId, endNodeId, condition })\n\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid,\n\t\t\tuses: 'loop-controller',\n\t\t\tparams: { condition },\n\t\t\tconfig: { joinStrategy: 'any' },\n\t\t})\n\n\t\tthis.edge(endNodeId, id)\n\t\tthis.edge(id, startNodeId, {\n\t\t\taction: 'continue',\n\t\t\ttransform: `context.${endNodeId}`,\n\t\t})\n\n\t\treturn this\n\t}\n\n\t/**\n\t * Sets the preferred entry point for a cycle in non-DAG workflows.\n\t * This helps remove ambiguity when the runtime needs to choose a starting node for cycles.\n\t * @param nodeId The ID of the node to use as the entry point for cycles containing this node.\n\t */\n\tsetCycleEntryPoint(nodeId: string): this {\n\t\tthis.cycleEntryPoints.set(nodeId, nodeId)\n\t\treturn this\n\t}\n\n\ttoBlueprint(): WorkflowBlueprint {\n\t\tif (!this.blueprint.nodes || this.blueprint.nodes.length === 0) {\n\t\t\tthrow new Error('Cannot build a blueprint with no nodes.')\n\t\t}\n\t\tconst finalEdges: EdgeDefinition[] = []\n\t\tconst processedOriginalEdges = new Set<EdgeDefinition>()\n\t\tconst allOriginalEdges = this.blueprint.edges || []\n\n\t\t// loop edge re-wiring\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst edgesToRewire = allOriginalEdges.filter((e) => e.source === loopDef.id && e.target !== loopDef.startNodeId)\n\t\t\tfor (const edge of edgesToRewire) {\n\t\t\t\tfinalEdges.push({ ...edge, action: edge.action || 'break', transform: `context.${loopDef.endNodeId}` })\n\t\t\t\tprocessedOriginalEdges.add(edge)\n\t\t\t}\n\t\t}\n\n\t\t// batch edge re-wiring\n\t\tfor (const batchDef of this.batchDefinitions) {\n\t\t\tconst incomingEdges = allOriginalEdges.filter((e) => e.target === batchDef.id)\n\t\t\tfor (const edge of incomingEdges) {\n\t\t\t\tfinalEdges.push({ ...edge, target: batchDef.scatterId })\n\t\t\t\tprocessedOriginalEdges.add(edge)\n\t\t\t}\n\n\t\t\tconst outgoingEdges = allOriginalEdges.filter((e) => e.source === batchDef.id)\n\t\t\tfor (const edge of outgoingEdges) {\n\t\t\t\tfinalEdges.push({ ...edge, source: batchDef.gatherId })\n\t\t\t\tprocessedOriginalEdges.add(edge)\n\t\t\t}\n\t\t}\n\n\t\t// all remaining edges\n\t\tfor (const edge of allOriginalEdges) {\n\t\t\tif (!processedOriginalEdges.has(edge)) {\n\t\t\t\tfinalEdges.push(edge)\n\t\t\t}\n\t\t}\n\t\tthis.blueprint.edges = finalEdges\n\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst startNode = this.blueprint.nodes?.find((n) => n.id === loopDef.startNodeId)\n\t\t\tconst endNode = this.blueprint.nodes?.find((n) => n.id === loopDef.endNodeId)\n\n\t\t\tif (!startNode) {\n\t\t\t\tthrow new Error(`Loop '${loopDef.id}' references non-existent start node '${loopDef.startNodeId}'.`)\n\t\t\t}\n\t\t\tif (!endNode) {\n\t\t\t\tthrow new Error(`Loop '${loopDef.id}' references non-existent end node '${loopDef.endNodeId}'.`)\n\t\t\t}\n\t\t}\n\n\t\tif (this.cycleEntryPoints.size > 0) {\n\t\t\tthis.blueprint.metadata = {\n\t\t\t\t...this.blueprint.metadata,\n\t\t\t\tcycleEntryPoints: Array.from(this.cycleEntryPoints.keys()),\n\t\t\t}\n\t\t}\n\n\t\treturn this.blueprint as WorkflowBlueprint\n\t}\n\n\tgetFunctionRegistry() {\n\t\treturn this.functionRegistry\n\t}\n\n\ttoGraphRepresentation(): UIGraph {\n\t\tconst blueprint = this.toBlueprint()\n\t\tconst uiNodes: UIGraph['nodes'] = []\n\t\tconst uiEdges: UIGraph['edges'] = []\n\n\t\tconst ignoredNodeIds = new Set<string>()\n\n\t\t// replace loop-controllers with direct, cyclical edges\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst id = loopDef.id\n\t\t\tignoredNodeIds.add(id)\n\n\t\t\t// direct edge from the end of loop to start\n\t\t\tuiEdges.push({\n\t\t\t\tsource: loopDef.endNodeId,\n\t\t\t\ttarget: loopDef.startNodeId,\n\t\t\t\tdata: {\n\t\t\t\t\tisLoopback: true,\n\t\t\t\t\tcondition: loopDef.condition,\n\t\t\t\t\tlabel: `continue if: ${loopDef.condition}`,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// re-wire any 'break' edges\n\t\t\tconst breakEdges = blueprint.edges.filter((edge) => edge.source === id && edge.action === 'break')\n\t\t\tfor (const breakEdge of breakEdges) {\n\t\t\t\tuiEdges.push({\n\t\t\t\t\t...breakEdge,\n\t\t\t\t\tsource: loopDef.endNodeId,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// re-wire any 'incoming' edges\n\t\t\tconst incomingEdges = blueprint.edges.filter((edge) => edge.target === id && edge.source !== loopDef.endNodeId)\n\t\t\tfor (const incomingEdge of incomingEdges) {\n\t\t\t\tuiEdges.push({\n\t\t\t\t\t...incomingEdge,\n\t\t\t\t\tsource: loopDef.startNodeId,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// replace scatter/gather pairs with a single representative worker node\n\t\tconst scatterNodes = blueprint.nodes.filter((n) => n.uses === 'batch-scatter')\n\t\tfor (const scatterNode of scatterNodes) {\n\t\t\tconst gatherNodeId = scatterNode.params?.gatherNodeId\n\t\t\tif (!gatherNodeId) continue\n\n\t\t\tignoredNodeIds.add(scatterNode.id)\n\t\t\tignoredNodeIds.add(gatherNodeId)\n\n\t\t\t// single node to represent parallel work\n\t\t\tconst batchId = scatterNode.id.replace('_scatter', '')\n\t\t\tconst gatherNode = blueprint.nodes.find((n) => n.id === gatherNodeId)\n\n\t\t\tuiNodes.push({\n\t\t\t\tid: batchId,\n\t\t\t\tuses: scatterNode.params?.workerUsesKey,\n\t\t\t\ttype: 'batch-worker',\n\t\t\t\tdata: {\n\t\t\t\t\tlabel: `Batch: ${batchId}`,\n\t\t\t\t\tisBatchPlaceholder: true,\n\t\t\t\t\tworkerUsesKey: scatterNode.params?.workerUsesKey,\n\t\t\t\t\tinputKey: scatterNode.inputs,\n\t\t\t\t\toutputKey: gatherNode?.params?.outputKey,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// re-wire incoming edges\n\t\t\tconst incomingEdges = blueprint.edges.filter((e) => e.target === scatterNode.id)\n\t\t\tfor (const edge of incomingEdges) {\n\t\t\t\tuiEdges.push({ ...edge, target: batchId })\n\t\t\t}\n\n\t\t\t// re-wire outgoing edges\n\t\t\tconst outgoingEdges = blueprint.edges.filter((e) => e.source === gatherNodeId)\n\t\t\tfor (const edge of outgoingEdges) {\n\t\t\t\tuiEdges.push({ ...edge, source: batchId })\n\t\t\t}\n\t\t}\n\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (!ignoredNodeIds.has(node.id)) {\n\t\t\t\tuiNodes.push(node)\n\t\t\t}\n\t\t}\n\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tif (!ignoredNodeIds.has(edge.source) && !ignoredNodeIds.has(edge.target)) {\n\t\t\t\tconst alreadyAdded = uiEdges.some(\n\t\t\t\t\t(e) => e.source === edge.source && e.target === edge.target && e.action === edge.action,\n\t\t\t\t)\n\t\t\t\tif (!alreadyAdded) {\n\t\t\t\t\tuiEdges.push(edge)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { nodes: uiNodes, edges: uiEdges }\n\t}\n}\n\n/**\n * Helper function to create a new Flow builder instance.\n */\nexport function createFlow<\n\tTContext extends Record<string, any> = Record<string, any>,\n\tTDependencies extends Record<string, any> = Record<string, any>,\n>(id: string): FlowBuilder<TContext, TDependencies> {\n\treturn new FlowBuilder(id)\n}\n","import { analyzeBlueprint } from './analysis'\nimport type { NodeClass, NodeFunction, WorkflowBlueprint } from './types'\n\nexport type LinterIssueCode =\n\t| 'INVALID_EDGE_SOURCE'\n\t| 'INVALID_EDGE_TARGET'\n\t| 'MISSING_NODE_IMPLEMENTATION'\n\t| 'ORPHAN_NODE'\n\t| 'INVALID_BATCH_WORKER_KEY'\n\t| 'INVALID_SUBFLOW_BLUEPRINT_ID'\n\nexport interface LinterIssue {\n\tcode: LinterIssueCode\n\tmessage: string\n\tnodeId?: string\n\trelatedId?: string\n}\n\nexport interface LinterResult {\n\tisValid: boolean\n\tissues: LinterIssue[]\n}\n\n/**\n * Statically analyzes a workflow blueprint against a registry of implementations\n * to find common errors before runtime.\n *\n * @param blueprint The WorkflowBlueprint to analyze.\n * @param registry A map of node implementations (functions or classes) to check against.\n * @returns A LinterResult object containing any issues found.\n */\nexport function lintBlueprint(\n\tblueprint: WorkflowBlueprint,\n\tregistry: Map<string, NodeFunction | NodeClass> | Record<string, NodeFunction | NodeClass>,\n\tblueprints?: Record<string, WorkflowBlueprint>,\n): LinterResult {\n\tconst issues: LinterIssue[] = []\n\tconst nodeIds = new Set(blueprint.nodes.map((n) => n.id))\n\tconst registryKeys = registry instanceof Map ? new Set(registry.keys()) : new Set(Object.keys(registry))\n\n\t// check for missing node implementations\n\tfor (const node of blueprint.nodes) {\n\t\tif (!node.uses.startsWith('batch-') && !node.uses.startsWith('loop-') && !registryKeys.has(node.uses)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'MISSING_NODE_IMPLEMENTATION',\n\t\t\t\tmessage: `Node implementation key '${node.uses}' is not found in the provided registry.`,\n\t\t\t\tnodeId: node.id,\n\t\t\t})\n\t\t}\n\t}\n\n\t// check for dynamic node validation\n\tfor (const node of blueprint.nodes) {\n\t\tif (node.uses.startsWith('batch-') && node.params?.workerUsesKey) {\n\t\t\tif (!registryKeys.has(node.params.workerUsesKey)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'INVALID_BATCH_WORKER_KEY',\n\t\t\t\t\tmessage: `Batch node '${node.id}' references workerUsesKey '${node.params.workerUsesKey}' which is not found in the registry.`,\n\t\t\t\t\tnodeId: node.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (node.uses === 'subflow' && node.params?.blueprintId) {\n\t\t\tif (!blueprints?.[node.params.blueprintId]) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'INVALID_SUBFLOW_BLUEPRINT_ID',\n\t\t\t\t\tmessage: `Subflow node '${node.id}' references blueprintId '${node.params.blueprintId}' which is not found in the blueprints registry.`,\n\t\t\t\t\tnodeId: node.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t// check for graph integrity (edges must point to valid nodes)\n\tfor (const edge of blueprint.edges || []) {\n\t\tif (!nodeIds.has(edge.source)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'INVALID_EDGE_SOURCE',\n\t\t\t\tmessage: `Edge source '${edge.source}' does not correspond to a valid node ID.`,\n\t\t\t\trelatedId: edge.target,\n\t\t\t})\n\t\t}\n\t\tif (!nodeIds.has(edge.target)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'INVALID_EDGE_TARGET',\n\t\t\t\tmessage: `Edge target '${edge.target}' does not correspond to a valid node ID.`,\n\t\t\t\trelatedId: edge.source,\n\t\t\t})\n\t\t}\n\t}\n\n\t// check for orphan nodes (not connected to the main graph)\n\tif (blueprint.nodes.length > 1) {\n\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\tconst connectedNodes = new Set<string>()\n\t\tconst nodesToVisit = [...analysis.startNodeIds]\n\t\tconst visited = new Set<string>()\n\n\t\twhile (nodesToVisit.length > 0) {\n\t\t\tconst currentId = nodesToVisit.pop()\n\t\t\tif (!currentId || visited.has(currentId)) continue\n\n\t\t\tvisited.add(currentId)\n\t\t\tconnectedNodes.add(currentId)\n\n\t\t\tfor (const e of blueprint.edges.filter((e) => e.source === currentId)) {\n\t\t\t\tnodesToVisit.push(e.target)\n\t\t\t}\n\t\t}\n\n\t\tfor (const nodeId of nodeIds) {\n\t\t\tif (!connectedNodes.has(nodeId)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'ORPHAN_NODE',\n\t\t\t\t\tmessage: `Node '${nodeId}' is not reachable from any start node.`,\n\t\t\t\t\tnodeId,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tisValid: issues.length === 0,\n\t\tissues,\n\t}\n}\n","import { analyzeBlueprint } from '../analysis'\nimport { TrackedAsyncContext } from '../context'\nimport { ConsoleLogger } from '../logger'\nimport { JsonSerializer } from '../serializer'\nimport type {\n\tIAsyncContext,\n\tIEventBus,\n\tILogger,\n\tISerializer,\n\tNodeResult,\n\tRuntimeOptions,\n\tWorkflowBlueprint,\n\tWorkflowResult,\n} from '../types'\nimport { FlowRuntime } from './runtime'\nimport { WorkflowState } from './state'\n\n/**\n * Defines the contract for an atomic, distributed key-value store required by\n * the adapter for coordination tasks like fan-in joins and locking.\n */\nexport interface ICoordinationStore {\n\t/** Atomically increments a key and returns the new value. Ideal for 'all' joins. */\n\tincrement: (key: string, ttlSeconds: number) => Promise<number>\n\t/** Sets a key only if it does not already exist. Ideal for 'any' joins (locking). */\n\tsetIfNotExist: (key: string, value: string, ttlSeconds: number) => Promise<boolean>\n\t/** Extends the TTL of an existing key. Used for heartbeat mechanism in long-running jobs. */\n\textendTTL: (key: string, ttlSeconds: number) => Promise<boolean>\n\t/** Deletes a key. Used for cleanup. */\n\tdelete: (key: string) => Promise<void>\n\t/** Gets the value of a key. */\n\tget: (key: string) => Promise<string | undefined>\n}\n\n/** Configuration options for constructing a BaseDistributedAdapter. */\nexport interface AdapterOptions {\n\truntimeOptions: RuntimeOptions<any>\n\tcoordinationStore: ICoordinationStore\n\teventBus?: IEventBus\n}\n\n/** The data payload expected for a job in the queue. */\nexport interface JobPayload {\n\trunId: string\n\tblueprintId: string\n\tnodeId: string\n}\n\n/**\n * The base class for all distributed adapters. It handles the technology-agnostic\n * orchestration logic and leaves queue-specific implementation to subclasses.\n */\nexport abstract class BaseDistributedAdapter {\n\tprotected readonly runtime: FlowRuntime<any, any>\n\tprotected readonly store: ICoordinationStore\n\tprotected readonly serializer: ISerializer\n\tprotected readonly logger: ILogger\n\tprotected readonly eventBus?: IEventBus\n\n\tconstructor(options: AdapterOptions) {\n\t\tconst runtimeOptions = {\n\t\t\t...options.runtimeOptions,\n\t\t\tdependencies: {\n\t\t\t\t...options.runtimeOptions.dependencies,\n\t\t\t\tadapter: this,\n\t\t\t} as any,\n\t\t}\n\t\tthis.runtime = new FlowRuntime(runtimeOptions)\n\t\tthis.store = options.coordinationStore\n\t\tthis.serializer = options.runtimeOptions.serializer || new JsonSerializer()\n\t\tthis.logger = options.runtimeOptions.logger || new ConsoleLogger()\n\t\tthis.eventBus = options.eventBus\n\t\tthis.logger.info('[Adapter] BaseDistributedAdapter initialized.')\n\t}\n\n\t/**\n\t * Starts the worker, which begins listening for and processing jobs from the queue.\n\t */\n\tpublic start(): void {\n\t\tthis.logger.info('[Adapter] Starting worker...')\n\t\tthis.processJobs(this.handleJob.bind(this))\n\t}\n\n\t/**\n\t * Creates a technology-specific distributed context for a given workflow run.\n\t * @param runId The unique ID for the workflow execution.\n\t */\n\tprotected abstract createContext(runId: string): IAsyncContext<Record<string, any>>\n\t/**\n\t * Sets up the listener for the message queue. The implementation should call the\n\t * provided `handler` function for each new job received.\n\t * @param handler The core logic to execute for each job.\n\t */\n\tprotected abstract processJobs(handler: (job: JobPayload) => Promise<void>): void\n\n\t/**\n\t * Enqueues a new job onto the message queue.\n\t * @param job The payload for the job to be enqueued.\n\t */\n\tprotected abstract enqueueJob(job: JobPayload): Promise<void>\n\n\t/**\n\t * Publishes the final result of a completed or failed workflow run.\n\t * @param runId The unique ID of the workflow run.\n\t * @param result The final status and payload of the workflow.\n\t */\n\tprotected abstract publishFinalResult(\n\t\trunId: string,\n\t\tresult: {\n\t\t\tstatus: 'completed' | 'failed'\n\t\t\tpayload?: WorkflowResult\n\t\t\treason?: string\n\t\t},\n\t): Promise<void>\n\n\t/**\n\t * Registers a webhook endpoint for a specific node in a workflow run.\n\t * @param runId The unique ID of the workflow run.\n\t * @param nodeId The ID of the node that will wait for the webhook.\n\t * @returns The URL and event name for the webhook.\n\t */\n\tpublic abstract registerWebhookEndpoint(runId: string, nodeId: string): Promise<{ url: string; event: string }>\n\n\t/**\n\t * Hook called at the start of job processing. Subclasses can override this\n\t * to perform additional setup (e.g., timestamp tracking for reconciliation).\n\t */\n\tprotected async onJobStart(_runId: string, _blueprintId: string, _nodeId: string): Promise<void> {\n\t\t// default implementation does nothing\n\t}\n\n\t/**\n\t * The main handler for processing a single job from the queue.\n\t */\n\tprotected async handleJob(job: JobPayload): Promise<void> {\n\t\tconst { runId, blueprintId, nodeId } = job\n\t\tconst startTime = Date.now()\n\n\t\tawait this.onJobStart(runId, blueprintId, nodeId)\n\n\t\tconst blueprint = this.runtime.options.blueprints?.[blueprintId]\n\t\tif (!blueprint) {\n\t\t\tconst reason = `Blueprint with ID '${blueprintId}' not found in the worker's runtime registry.`\n\t\t\tthis.logger.error(`[Adapter] FATAL: ${reason}`)\n\t\t\tawait this.publishFinalResult(runId, { status: 'failed', reason })\n\t\t\treturn\n\t\t}\n\n\t\tconst context = this.createContext(runId)\n\n\t\tconst storedVersion = await context.get('blueprintVersion' as any)\n\t\tconst currentVersion = blueprint.metadata?.version || null\n\t\tif (storedVersion !== currentVersion) {\n\t\t\tconst reason = `Blueprint version mismatch: stored version '${storedVersion}', current version '${currentVersion}'. Rejecting job to prevent state corruption.`\n\t\t\tthis.logger.warn(`[Adapter] Version mismatch for run ${runId}, node ${nodeId}: ${reason}`)\n\t\t\treturn\n\t\t}\n\n\t\t// persist the blueprintId and version for the reconcile method to find later\n\t\tconst hasBlueprintId = await context.has('blueprintId' as any)\n\t\tif (!hasBlueprintId) {\n\t\t\tawait context.set('blueprintId' as any, blueprintId)\n\t\t\tawait context.set('blueprintVersion' as any, blueprint.metadata?.version || null)\n\t\t\t// also store in coordination store as fallback\n\t\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\t\tawait this.store.setIfNotExist(blueprintKey, blueprintId, 3600)\n\t\t}\n\n\t\t// heartbeat to extend TTLs of coordination keys for long-running jobs\n\t\tconst joinLockKey = `flowcraft:joinlock:${runId}:${nodeId}`\n\t\tconst fanInKey = `flowcraft:fanin:${runId}:${nodeId}`\n\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\tconst heartbeatInterval = setInterval(async () => {\n\t\t\tawait this.store.extendTTL(joinLockKey, 3600)\n\t\t\tawait this.store.extendTTL(fanInKey, 3600)\n\t\t\tawait this.store.extendTTL(blueprintKey, 3600)\n\t\t\tthis.logger.debug(`[Adapter] Extended TTLs for run ${runId}, node ${nodeId}`)\n\t\t}, 1800000) // 30 minutes\n\n\t\ttry {\n\t\t\tconst contextData = await context.toJSON()\n\t\t\tconst state = new WorkflowState(contextData, context)\n\n\t\t\tconst result: NodeResult<any, any> = await this.runtime.executeNode(blueprint, nodeId, state)\n\t\t\tawait context.set(`_outputs.${nodeId}` as any, result.output)\n\n\t\t\tconst stateContext = state.getContext()\n\t\t\tif (stateContext instanceof TrackedAsyncContext) {\n\t\t\t\tconst deltas = stateContext.getDeltas()\n\t\t\t\tif (deltas.length > 0) {\n\t\t\t\t\tawait stateContext.patch(deltas)\n\t\t\t\t\tstateContext.clearDeltas()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\t\tconst isTerminalNode = analysis.terminalNodeIds.includes(nodeId)\n\n\t\t\tif (isTerminalNode) {\n\t\t\t\tconst allContextKeys = Object.keys(await context.toJSON())\n\t\t\t\tconst completedNodes = new Set<string>()\n\t\t\t\tfor (const key of allContextKeys) {\n\t\t\t\t\tif (key.startsWith('_outputs.')) {\n\t\t\t\t\t\tcompletedNodes.add(key.substring('_outputs.'.length))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst allTerminalNodesCompleted = analysis.terminalNodeIds.every((terminalId) => completedNodes.has(terminalId))\n\n\t\t\t\tif (allTerminalNodesCompleted) {\n\t\t\t\t\tthis.logger.info(`[Adapter] All terminal nodes completed for Run ID: ${runId}. Declaring workflow complete.`)\n\t\t\t\t\tconst finalContext = await context.toJSON()\n\t\t\t\t\tconst finalResult: WorkflowResult = {\n\t\t\t\t\t\tcontext: finalContext,\n\t\t\t\t\t\tserializedContext: this.serializer.serialize(finalContext),\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t}\n\t\t\t\t\tawait this.publishFinalResult(runId, {\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t\tpayload: finalResult,\n\t\t\t\t\t})\n\t\t\t\t\tclearInterval(heartbeatInterval)\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Terminal node '${nodeId}' completed for Run ID '${runId}', but other terminal nodes are still running.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst nextNodes = await this.runtime.determineNextNodes(blueprint, nodeId, result, context, runId)\n\n\t\t\t// stop if a branch terminates but it wasn't a terminal node\n\t\t\tif (nextNodes.length === 0 && !isTerminalNode) {\n\t\t\t\tthis.logger.info(\n\t\t\t\t\t`[Adapter] Non-terminal node '${nodeId}' reached end of branch for Run ID '${runId}'. This branch will now terminate.`,\n\t\t\t\t)\n\t\t\t\tclearInterval(heartbeatInterval)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor (const { node: nextNodeDef, edge } of nextNodes) {\n\t\t\t\tawait this.runtime.applyEdgeTransform(edge, result, nextNodeDef, context, undefined, runId)\n\t\t\t\tconst isReady = await this.isReadyForFanIn(runId, blueprint, nextNodeDef.id)\n\t\t\t\tif (isReady) {\n\t\t\t\t\tthis.logger.info(`[Adapter] Node '${nextNodeDef.id}' is ready. Enqueuing job.`)\n\t\t\t\t\tawait this.enqueueJob({ runId, blueprintId, nodeId: nextNodeDef.id })\n\t\t\t\t\tif (this.eventBus) {\n\t\t\t\t\t\tawait this.eventBus.emit({\n\t\t\t\t\t\t\ttype: 'job:enqueued',\n\t\t\t\t\t\t\tpayload: { runId, blueprintId, nodeId: nextNodeDef.id },\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(`[Adapter] Node '${nextNodeDef.id}' is waiting for other predecessors to complete.`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst duration = Date.now() - startTime\n\t\t\tif (this.eventBus) {\n\t\t\t\tawait this.eventBus.emit({\n\t\t\t\t\ttype: 'job:processed',\n\t\t\t\t\tpayload: { runId, blueprintId, nodeId, duration, success: true },\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (error: any) {\n\t\t\tconst reason = error.message || 'Unknown execution error'\n\t\t\tthis.logger.error(`[Adapter] FATAL: Job for node '${nodeId}' failed for Run ID '${runId}': ${reason}`)\n\t\t\tawait this.publishFinalResult(runId, { status: 'failed', reason })\n\t\t\tawait this.writePoisonPillForSuccessors(runId, blueprint, nodeId)\n\n\t\t\tif (this.eventBus) {\n\t\t\t\tawait this.eventBus.emit({\n\t\t\t\t\ttype: 'job:failed',\n\t\t\t\t\tpayload: { runId, blueprintId, nodeId, error },\n\t\t\t\t})\n\t\t\t}\n\t\t} finally {\n\t\t\tclearInterval(heartbeatInterval)\n\t\t}\n\t}\n\n\t/**\n\t * Encapsulates the fan-in join logic using the coordination store.\n\t */\n\tprotected async isReadyForFanIn(runId: string, blueprint: WorkflowBlueprint, targetNodeId: string): Promise<boolean> {\n\t\tconst targetNode = blueprint.nodes.find((n) => n.id === targetNodeId)\n\t\tif (!targetNode) {\n\t\t\tthrow new Error(`Node '${targetNodeId}' not found in blueprint`)\n\t\t}\n\t\tconst joinStrategy = targetNode.config?.joinStrategy || 'all'\n\t\tconst predecessors = blueprint.edges.filter((e) => e.target === targetNodeId)\n\n\t\tif (predecessors.length <= 1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${targetNodeId}`\n\t\tconst isPoisoned = await this.store.get(poisonKey)\n\t\tif (isPoisoned) {\n\t\t\tthis.logger.info(`[Adapter] Node '${targetNodeId}' is poisoned due to failed predecessor. Failing immediately.`)\n\t\t\tthrow new Error(`Node '${targetNodeId}' failed due to poisoned predecessor in run '${runId}'`)\n\t\t}\n\n\t\tif (joinStrategy === 'any') {\n\t\t\tconst lockKey = `flowcraft:joinlock:${runId}:${targetNodeId}`\n\t\t\tconst isLocked = await this.store.setIfNotExist(lockKey, 'locked', 3600)\n\t\t\tif (!isLocked) {\n\t\t\t\t// check if cancelled\n\t\t\t\tconst cancelKey = `flowcraft:fanin:cancel:${runId}:${targetNodeId}`\n\t\t\t\tconst isCancelled = !(await this.store.setIfNotExist(cancelKey, 'cancelled', 3600))\n\t\t\t\tif (isCancelled) {\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Node '${targetNodeId}' is cancelled due to failed predecessor. Failing immediately.`,\n\t\t\t\t\t)\n\t\t\t\t\tthrow new Error(`Node '${targetNodeId}' failed due to cancelled predecessor in run '${runId}'`)\n\t\t\t\t}\n\t\t\t\treturn false // already locked by another predecessor\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tconst fanInKey = `flowcraft:fanin:${runId}:${targetNodeId}`\n\t\t\tconst readyCount = await this.store.increment(fanInKey, 3600)\n\t\t\tif (readyCount >= predecessors.length) {\n\t\t\t\tawait this.store.delete(fanInKey)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles the state of a workflow run. It inspects the persisted\n\t * context to find completed nodes, determines the next set of executable\n\t * nodes (the frontier), and enqueues jobs for them if they aren't\n\t * already running. This is the core of the resume functionality.\n\t *\n\t * @param runId The unique ID of the workflow execution to reconcile.\n\t * @returns The set of node IDs that were enqueued for execution.\n\t */\n\tpublic async reconcile(runId: string): Promise<Set<string>> {\n\t\tconst context = this.createContext(runId)\n\t\tlet blueprintId = (await context.get('blueprintId' as any)) as string | undefined\n\n\t\tif (!blueprintId) {\n\t\t\t// fallback to coordination store\n\t\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\t\tblueprintId = await this.store.get(blueprintKey)\n\t\t\tif (blueprintId) {\n\t\t\t\t// set it back in context for future use\n\t\t\t\tawait context.set('blueprintId' as any, blueprintId)\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Cannot reconcile runId '${runId}': blueprintId not found in context or coordination store.`)\n\t\t\t}\n\t\t}\n\t\tconst blueprint = this.runtime.options.blueprints?.[blueprintId]\n\t\tif (blueprint && !(await context.has('blueprintVersion' as any))) {\n\t\t\tawait context.set('blueprintVersion' as any, blueprint.metadata?.version || null)\n\t\t}\n\t\tif (!blueprint) {\n\t\t\tthrow new Error(`Cannot reconcile runId '${runId}': Blueprint with ID '${blueprintId}' not found.`)\n\t\t}\n\n\t\tconst state = await context.toJSON()\n\t\tconst completedNodes = new Set<string>()\n\t\tfor (const key of Object.keys(state)) {\n\t\t\tif (key.startsWith('_outputs.')) {\n\t\t\t\tcompletedNodes.add(key.substring('_outputs.'.length))\n\t\t\t}\n\t\t}\n\n\t\tconst frontier = this.calculateResumedFrontier(blueprint, completedNodes)\n\n\t\tconst enqueuedNodes = new Set<string>()\n\t\tfor (const nodeId of frontier) {\n\t\t\tconst nodeDef = blueprint.nodes.find((n) => n.id === nodeId)\n\t\t\tconst joinStrategy = nodeDef?.config?.joinStrategy || 'all'\n\n\t\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${nodeId}`\n\t\t\tconst isPoisoned = await this.store.get(poisonKey)\n\t\t\tif (isPoisoned) {\n\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Node '${nodeId}' is poisoned, skipping.`, { runId })\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tlet shouldEnqueue = false\n\n\t\t\tif (joinStrategy === 'any') {\n\t\t\t\t// acquire the permanent join lock\n\t\t\t\tconst lockKey = `flowcraft:joinlock:${runId}:${nodeId}`\n\t\t\t\tif (await this.store.setIfNotExist(lockKey, 'locked-by-reconcile', 3600)) {\n\t\t\t\t\tshouldEnqueue = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Node '${nodeId}' is an 'any' join and is already locked.`, { runId })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// 'all' joins and single-predecessor nodes use a temporary lock\n\t\t\t\tconst lockKey = `flowcraft:nodelock:${runId}:${nodeId}`\n\t\t\t\tif (await this.store.setIfNotExist(lockKey, 'locked', 120)) {\n\t\t\t\t\tshouldEnqueue = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Node '${nodeId}' is already locked.`, { runId })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (shouldEnqueue) {\n\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Enqueuing ready job for node '${nodeId}'`, { runId })\n\t\t\t\tawait this.enqueueJob({ runId, blueprintId: blueprint.id, nodeId })\n\t\t\t\tenqueuedNodes.add(nodeId)\n\t\t\t}\n\t\t}\n\n\t\treturn enqueuedNodes\n\t}\n\n\tprivate calculateResumedFrontier(blueprint: WorkflowBlueprint, completedNodes: Set<string>): Set<string> {\n\t\tconst newFrontier = new Set<string>()\n\t\tconst allPredecessors = new Map<string, Set<string>>()\n\t\t// (logic extracted from the GraphTraverser)\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tallPredecessors.set(node.id, new Set())\n\t\t}\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tallPredecessors.get(edge.target)?.add(edge.source)\n\t\t}\n\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (completedNodes.has(node.id)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst predecessors = allPredecessors.get(node.id) ?? new Set()\n\t\t\tif (predecessors.size === 0 && !completedNodes.has(node.id)) {\n\t\t\t\tnewFrontier.add(node.id)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst joinStrategy = node.config?.joinStrategy || 'all'\n\t\t\tconst completedPredecessors = [...predecessors].filter((p) => completedNodes.has(p))\n\n\t\t\tconst isReady =\n\t\t\t\tjoinStrategy === 'any' ? completedPredecessors.length > 0 : completedPredecessors.length === predecessors.size\n\n\t\t\tif (isReady) {\n\t\t\t\tnewFrontier.add(node.id)\n\t\t\t}\n\t\t}\n\t\treturn newFrontier\n\t}\n\n\t/**\n\t * Writes a poison pill for 'all' join successors and a cancellation pill for 'any' join successors of a failed node to prevent stalling or ambiguous states.\n\t */\n\tprivate async writePoisonPillForSuccessors(\n\t\trunId: string,\n\t\tblueprint: WorkflowBlueprint,\n\t\tfailedNodeId: string,\n\t): Promise<void> {\n\t\tconst successors = blueprint.edges\n\t\t\t.filter((edge) => edge.source === failedNodeId)\n\t\t\t.map((edge) => edge.target)\n\t\t\t.map((targetId) => blueprint.nodes.find((node) => node.id === targetId))\n\t\t\t.filter((node) => node)\n\n\t\tfor (const successor of successors) {\n\t\t\tif (successor) {\n\t\t\t\tconst joinStrategy = successor.config?.joinStrategy || 'all'\n\t\t\t\tif (joinStrategy === 'all') {\n\t\t\t\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${successor.id}`\n\t\t\t\t\tawait this.store.setIfNotExist(poisonKey, 'poisoned', 3600)\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Wrote poison pill for 'all' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`,\n\t\t\t\t\t)\n\t\t\t\t} else if (joinStrategy === 'any') {\n\t\t\t\t\tconst cancelKey = `flowcraft:fanin:cancel:${runId}:${successor.id}`\n\t\t\t\t\tawait this.store.setIfNotExist(cancelKey, 'cancelled', 3600)\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Wrote cancellation pill for 'any' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/container-factory.ts","../src/error-mapper.ts","../src/flow.ts","../src/linter.ts","../src/runtime/adapter.ts"],"sourcesContent":["import { DIContainer, ServiceTokens } from './container'\nimport { PropertyEvaluator } from './evaluator'\nimport { NullLogger } from './logger'\nimport { DefaultOrchestrator } from './runtime/orchestrator'\nimport { JsonSerializer } from './serializer'\nimport type {\n\tIEvaluator,\n\tIEventBus,\n\tILogger,\n\tISerializer,\n\tMiddleware,\n\tNodeClass,\n\tNodeFunction,\n\tRuntimeDependencies,\n\tWorkflowBlueprint,\n} from './types'\n\nexport interface ContainerOptions<TDependencies extends RuntimeDependencies = RuntimeDependencies> {\n\tlogger?: ILogger\n\tserializer?: ISerializer\n\tevaluator?: IEvaluator\n\teventBus?: IEventBus\n\tmiddleware?: Middleware[]\n\tregistry?: Record<string, NodeFunction | NodeClass>\n\tblueprints?: Record<string, WorkflowBlueprint>\n\tdependencies?: TDependencies\n}\n\nexport function createDefaultContainer<TDependencies extends RuntimeDependencies = RuntimeDependencies>(\n\toptions: ContainerOptions<TDependencies> = {},\n): DIContainer {\n\tconst container = new DIContainer()\n\n\tcontainer.register(ServiceTokens.Logger, options.logger || new NullLogger())\n\tcontainer.register(ServiceTokens.Serializer, options.serializer || new JsonSerializer())\n\tcontainer.register(ServiceTokens.Evaluator, options.evaluator || new PropertyEvaluator())\n\tcontainer.register(ServiceTokens.EventBus, options.eventBus || { emit: async () => {} })\n\tcontainer.register(ServiceTokens.Middleware, options.middleware || [])\n\tcontainer.register(ServiceTokens.NodeRegistry, options.registry || {})\n\tcontainer.register(ServiceTokens.BlueprintRegistry, options.blueprints || {})\n\tcontainer.register(ServiceTokens.Dependencies, options.dependencies || ({} as TDependencies))\n\n\tcontainer.registerFactory(ServiceTokens.Orchestrator, () => new DefaultOrchestrator())\n\n\treturn container\n}\n","import { FlowcraftError } from './errors'\nimport type { SourceLocation, WorkflowBlueprint } from './types'\n\n/**\n * Creates an error mapper function that enhances runtime errors with source location information.\n * The mapper looks up node IDs in the provided manifest blueprints and returns enhanced errors\n * that point to the original TypeScript source code.\n *\n * @param manifestBlueprints - The compiled blueprint manifest containing source location data\n * @returns A function that maps errors to enhanced errors with source location information\n */\nexport function createErrorMapper(manifestBlueprints: Record<string, WorkflowBlueprint>) {\n\tconst locationMap = new Map<string, SourceLocation>()\n\n\t// build a quick lookup map\n\tfor (const blueprint of Object.values(manifestBlueprints)) {\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (node._sourceLocation) {\n\t\t\t\tlocationMap.set(node.id, node._sourceLocation)\n\t\t\t}\n\t\t}\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tif (edge._sourceLocation) {\n\t\t\t\tconst edgeKey = `${edge.source}-${edge.target}`\n\t\t\t\tlocationMap.set(edgeKey, edge._sourceLocation)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn function mapError(error: Error): Error {\n\t\tif (error instanceof FlowcraftError && error.nodeId) {\n\t\t\tconst location = locationMap.get(error.nodeId)\n\t\t\tif (location) {\n\t\t\t\treturn new Error(\n\t\t\t\t\t`Workflow error at ${location.file}:${location.line}:${location.column}. Original error: ${error.message}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\t// try to extract nodeId from error message\n\t\tconst nodeIdMatch = error.message.match(/nodeId[:\\s]+([^\\s,]+)/i)\n\t\tif (nodeIdMatch) {\n\t\t\tconst nodeId = nodeIdMatch[1]\n\t\t\tconst location = locationMap.get(nodeId)\n\t\t\tif (location) {\n\t\t\t\treturn new Error(\n\t\t\t\t\t`Workflow error at ${location.file}:${location.line}:${location.column}. Original error: ${error.message}`,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\treturn error\n\t}\n}\n","import { isNodeClass } from './node'\nimport type { FlowRuntime } from './runtime/runtime'\nimport type {\n\tEdgeDefinition,\n\tNodeClass,\n\tNodeDefinition,\n\tNodeFunction,\n\tUIGraph,\n\tWorkflowBlueprint,\n\tWorkflowResult,\n} from './types'\n\n/**\n * Generates a deterministic hash for a function based on its source code and a unique counter.\n */\nlet hashCounter = 0\nfunction _hashFunction(fn: NodeFunction<any, any, any, any, any> | NodeClass<any, any, any, any, any>): string {\n\tconst source = fn.toString()\n\tlet hash = 0\n\tfor (let i = 0; i < source.length; i++) {\n\t\tconst char = source.charCodeAt(i)\n\t\thash = (hash << 5) - hash + char\n\t\thash = hash & hash // Convert to 32-bit integer\n\t}\n\t// Add counter to ensure uniqueness even for identical functions\n\treturn (Math.abs(hash) + hashCounter++).toString(16)\n}\n\n/**\n * A fluent API for programmatically constructing a WorkflowBlueprint.\n */\nexport class FlowBuilder<\n\tTContext extends Record<string, any> = Record<string, any>,\n\tTDependencies extends Record<string, any> = Record<string, any>,\n> {\n\tprivate blueprint: Partial<WorkflowBlueprint>\n\tprivate functionRegistry: Map<string, NodeFunction | NodeClass>\n\tprivate loopDefinitions: Array<{\n\t\tid: string\n\t\tstartNodeId: string\n\t\tendNodeId: string\n\t\tcondition: string\n\t}>\n\tprivate batchDefinitions: Array<{\n\t\tid: string\n\t\tscatterId: string\n\t\tgatherId: string\n\t}>\n\tprivate cycleEntryPoints: Map<string, string>\n\n\tconstructor(id: string) {\n\t\tthis.blueprint = { id, nodes: [], edges: [] }\n\t\tthis.functionRegistry = new Map()\n\t\tthis.loopDefinitions = []\n\t\tthis.batchDefinitions = []\n\t\tthis.cycleEntryPoints = new Map()\n\t}\n\n\tnode<TInput = any, TOutput = any, TAction extends string = string>(\n\t\tid: string,\n\t\timplementation:\n\t\t\t| NodeFunction<TContext, TDependencies, TInput, TOutput, TAction>\n\t\t\t| NodeClass<TContext, TDependencies, TInput, TOutput, TAction>,\n\t\toptions?: Omit<NodeDefinition, 'id' | 'uses'>,\n\t): this {\n\t\tlet usesKey: string\n\n\t\tif (isNodeClass(implementation)) {\n\t\t\tusesKey =\n\t\t\t\timplementation.name && implementation.name !== 'BaseNode'\n\t\t\t\t\t? implementation.name\n\t\t\t\t\t: `class_${_hashFunction(implementation)}`\n\t\t\tthis.functionRegistry.set(usesKey, implementation)\n\t\t} else {\n\t\t\tusesKey = `fn_${_hashFunction(implementation)}`\n\t\t\tthis.functionRegistry.set(usesKey, implementation as unknown as NodeFunction)\n\t\t}\n\n\t\tconst nodeDef: NodeDefinition = { id, uses: usesKey, ...options }\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\tedge(source: string, target: string, options?: Omit<EdgeDefinition, 'source' | 'target'>): this {\n\t\tconst edgeDef: EdgeDefinition = { source, target, ...options }\n\t\tthis.blueprint.edges?.push(edgeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a batch processing pattern.\n\t * It takes an input array, runs a worker node on each item in parallel, and gathers the results.\n\t * This method augments the Flow's TContext with a new key for the output array.\n\t *\n\t * @param id The base ID for this batch operation.\n\t * @param worker The node implementation to run on each item.\n\t * @param options Configuration for the batch operation.\n\t * @returns The Flow instance with an updated context type for chaining.\n\t */\n\tbatch<TWorkerInput, TWorkerOutput, TWorkerAction extends string, TOutputKey extends string>(\n\t\tid: string,\n\t\tworker:\n\t\t\t| NodeFunction<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>\n\t\t\t| NodeClass<TContext, TDependencies, TWorkerInput, TWorkerOutput, TWorkerAction>,\n\t\toptions: {\n\t\t\t/** The key in the context that holds the input array for the batch. */\n\t\t\tinputKey: keyof TContext\n\t\t\t/** The key in the context where the array of results will be stored. */\n\t\t\toutputKey: TOutputKey\n\t\t\t/** The number of items to process in each chunk to limit memory usage. */\n\t\t\tchunkSize?: number\n\t\t},\n\t): FlowBuilder<TContext & { [K in TOutputKey]: TWorkerOutput[] }, TDependencies> {\n\t\tconst { inputKey, outputKey } = options\n\t\tconst scatterId = `${id}_scatter`\n\t\tconst gatherId = `${id}_gather`\n\n\t\tthis.batchDefinitions.push({ id, scatterId, gatherId })\n\n\t\tlet workerUsesKey: string\n\t\tif (isNodeClass(worker)) {\n\t\t\tworkerUsesKey =\n\t\t\t\tworker.name && worker.name !== 'BaseNode' ? worker.name : `class_batch_worker_${_hashFunction(worker)}`\n\t\t\tthis.functionRegistry.set(workerUsesKey, worker)\n\t\t} else {\n\t\t\tworkerUsesKey = `fn_batch_worker_${_hashFunction(worker)}`\n\t\t\tthis.functionRegistry.set(workerUsesKey, worker as unknown as NodeFunction)\n\t\t}\n\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: scatterId,\n\t\t\tuses: 'batch-scatter',\n\t\t\tinputs: inputKey as string,\n\t\t\tparams: { workerUsesKey, outputKey: outputKey as string, gatherNodeId: gatherId, chunkSize: options.chunkSize },\n\t\t})\n\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid: gatherId,\n\t\t\tuses: 'batch-gather',\n\t\t\tparams: { outputKey, gatherNodeId: gatherId },\n\t\t\tconfig: { joinStrategy: 'all' },\n\t\t})\n\n\t\tthis.edge(scatterId, gatherId)\n\n\t\treturn this as unknown as FlowBuilder<TContext & { [K in TOutputKey]: TWorkerOutput[] }, TDependencies>\n\t}\n\n\t/**\n\t * Creates a sleep node that pauses workflow execution for a specified duration.\n\t * @param id A unique identifier for the sleep node.\n\t * @param options Configuration for the sleep duration.\n\t */\n\tsleep(\n\t\tid: string,\n\t\toptions: {\n\t\t\t/** The duration to sleep in milliseconds or a string like '5s', '1m', '2h', '1d'. */\n\t\t\tduration: number | string\n\t\t},\n\t): this {\n\t\tconst nodeDef: NodeDefinition = {\n\t\t\tid,\n\t\t\tuses: 'sleep',\n\t\t\tparams: { duration: options.duration },\n\t\t}\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a wait node that pauses workflow execution for external input.\n\t * @param id A unique identifier for the wait node.\n\t * @param options Optional configuration for the wait node.\n\t */\n\twait(id: string, options?: Omit<NodeDefinition, 'id' | 'uses'>): this {\n\t\tconst nodeDef: NodeDefinition = { id, uses: 'wait', ...options }\n\t\tthis.blueprint.nodes?.push(nodeDef)\n\t\treturn this\n\t}\n\n\t/**\n\t * Creates a loop pattern in the workflow graph.\n\t * @param id A unique identifier for the loop construct.\n\t * @param options Defines the start, end, and continuation condition of the loop.\n\t * @param options.startNodeId The ID of the first node inside the loop body.\n\t * @param options.endNodeId The ID of the last node inside the loop body.\n\t * @param options.condition An expression that, if true, causes the loop to run again.\n\t */\n\tloop(\n\t\tid: string,\n\t\toptions: {\n\t\t\t/** The ID of the first node inside the loop body. */\n\t\t\tstartNodeId: string\n\t\t\t/** The ID of the last node inside the loop body. */\n\t\t\tendNodeId: string\n\t\t\t/** An expression that, if true, causes the loop to run again. */\n\t\t\tcondition: string\n\t\t},\n\t): this {\n\t\tconst { startNodeId, endNodeId, condition } = options\n\t\tthis.loopDefinitions.push({ id, startNodeId, endNodeId, condition })\n\n\t\tthis.blueprint.nodes?.push({\n\t\t\tid,\n\t\t\tuses: 'loop-controller',\n\t\t\tparams: { condition },\n\t\t\tconfig: { joinStrategy: 'any' },\n\t\t})\n\n\t\tthis.edge(endNodeId, id)\n\t\tthis.edge(id, startNodeId, {\n\t\t\taction: 'continue',\n\t\t\ttransform: `context.${endNodeId}`,\n\t\t})\n\n\t\treturn this\n\t}\n\n\t/**\n\t * Sets the preferred entry point for a cycle in non-DAG workflows.\n\t * This helps remove ambiguity when the runtime needs to choose a starting node for cycles.\n\t * @param nodeId The ID of the node to use as the entry point for cycles containing this node.\n\t */\n\tsetCycleEntryPoint(nodeId: string): this {\n\t\tthis.cycleEntryPoints.set(nodeId, nodeId)\n\t\treturn this\n\t}\n\n\ttoBlueprint(): WorkflowBlueprint {\n\t\tif (!this.blueprint.nodes || this.blueprint.nodes.length === 0) {\n\t\t\tthrow new Error('Cannot build a blueprint with no nodes.')\n\t\t}\n\t\tconst finalEdges: EdgeDefinition[] = []\n\t\tconst processedOriginalEdges = new Set<EdgeDefinition>()\n\t\tconst allOriginalEdges = this.blueprint.edges || []\n\n\t\t// loop edge re-wiring\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst edgesToRewire = allOriginalEdges.filter((e) => e.source === loopDef.id && e.target !== loopDef.startNodeId)\n\t\t\tfor (const edge of edgesToRewire) {\n\t\t\t\tfinalEdges.push({ ...edge, action: edge.action || 'break', transform: `context.${loopDef.endNodeId}` })\n\t\t\t\tprocessedOriginalEdges.add(edge)\n\t\t\t}\n\t\t}\n\n\t\t// batch edge re-wiring\n\t\tfor (const batchDef of this.batchDefinitions) {\n\t\t\tconst incomingEdges = allOriginalEdges.filter((e) => e.target === batchDef.id)\n\t\t\tfor (const edge of incomingEdges) {\n\t\t\t\tfinalEdges.push({ ...edge, target: batchDef.scatterId })\n\t\t\t\tprocessedOriginalEdges.add(edge)\n\t\t\t}\n\n\t\t\tconst outgoingEdges = allOriginalEdges.filter((e) => e.source === batchDef.id)\n\t\t\tfor (const edge of outgoingEdges) {\n\t\t\t\tfinalEdges.push({ ...edge, source: batchDef.gatherId })\n\t\t\t\tprocessedOriginalEdges.add(edge)\n\t\t\t}\n\t\t}\n\n\t\t// all remaining edges\n\t\tfor (const edge of allOriginalEdges) {\n\t\t\tif (!processedOriginalEdges.has(edge)) {\n\t\t\t\tfinalEdges.push(edge)\n\t\t\t}\n\t\t}\n\t\tthis.blueprint.edges = finalEdges\n\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst startNode = this.blueprint.nodes?.find((n) => n.id === loopDef.startNodeId)\n\t\t\tconst endNode = this.blueprint.nodes?.find((n) => n.id === loopDef.endNodeId)\n\n\t\t\tif (!startNode) {\n\t\t\t\tthrow new Error(`Loop '${loopDef.id}' references non-existent start node '${loopDef.startNodeId}'.`)\n\t\t\t}\n\t\t\tif (!endNode) {\n\t\t\t\tthrow new Error(`Loop '${loopDef.id}' references non-existent end node '${loopDef.endNodeId}'.`)\n\t\t\t}\n\t\t}\n\n\t\tif (this.cycleEntryPoints.size > 0) {\n\t\t\tthis.blueprint.metadata = {\n\t\t\t\t...this.blueprint.metadata,\n\t\t\t\tcycleEntryPoints: Array.from(this.cycleEntryPoints.keys()),\n\t\t\t}\n\t\t}\n\n\t\treturn this.blueprint as WorkflowBlueprint\n\t}\n\n\tgetFunctionRegistry() {\n\t\treturn this.functionRegistry\n\t}\n\n\t/**\n\t * Runs this flow on the given runtime, automatically passing the function registry.\n\t * Convenience wrapper around `runtime.run(blueprint, initialState, { functionRegistry })`.\n\t */\n\tasync run(\n\t\truntime: FlowRuntime<TContext, TDependencies>,\n\t\tinitialState: Partial<TContext> | string = {},\n\t\toptions?: {\n\t\t\tstrict?: boolean\n\t\t\tsignal?: AbortSignal\n\t\t\tconcurrency?: number\n\t\t},\n\t): Promise<WorkflowResult<TContext>> {\n\t\treturn runtime.run(this.toBlueprint(), initialState, {\n\t\t\t...options,\n\t\t\tfunctionRegistry: this.functionRegistry,\n\t\t})\n\t}\n\n\t/**\n\t * Resumes this flow on the given runtime, automatically passing the function registry.\n\t * Convenience wrapper around `runtime.resume(blueprint, ...)`.\n\t */\n\tasync resume(\n\t\truntime: FlowRuntime<TContext, TDependencies>,\n\t\tserializedContext: string,\n\t\tresumeData: { output?: any; action?: string },\n\t\tnodeId?: string,\n\t\toptions?: {\n\t\t\tstrict?: boolean\n\t\t\tsignal?: AbortSignal\n\t\t\tconcurrency?: number\n\t\t},\n\t): Promise<WorkflowResult<TContext>> {\n\t\treturn runtime.resume(this.toBlueprint(), serializedContext, resumeData, nodeId, {\n\t\t\t...options,\n\t\t\tfunctionRegistry: this.functionRegistry,\n\t\t})\n\t}\n\n\ttoGraphRepresentation(): UIGraph {\n\t\tconst blueprint = this.toBlueprint()\n\t\tconst uiNodes: UIGraph['nodes'] = []\n\t\tconst uiEdges: UIGraph['edges'] = []\n\n\t\tconst ignoredNodeIds = new Set<string>()\n\n\t\t// replace loop-controllers with direct, cyclical edges\n\t\tfor (const loopDef of this.loopDefinitions) {\n\t\t\tconst id = loopDef.id\n\t\t\tignoredNodeIds.add(id)\n\n\t\t\t// direct edge from the end of loop to start\n\t\t\tuiEdges.push({\n\t\t\t\tsource: loopDef.endNodeId,\n\t\t\t\ttarget: loopDef.startNodeId,\n\t\t\t\tdata: {\n\t\t\t\t\tisLoopback: true,\n\t\t\t\t\tcondition: loopDef.condition,\n\t\t\t\t\tlabel: `continue if: ${loopDef.condition}`,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// re-wire any 'break' edges\n\t\t\tconst breakEdges = blueprint.edges.filter((edge) => edge.source === id && edge.action === 'break')\n\t\t\tfor (const breakEdge of breakEdges) {\n\t\t\t\tuiEdges.push({\n\t\t\t\t\t...breakEdge,\n\t\t\t\t\tsource: loopDef.endNodeId,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\t// re-wire any 'incoming' edges\n\t\t\tconst incomingEdges = blueprint.edges.filter((edge) => edge.target === id && edge.source !== loopDef.endNodeId)\n\t\t\tfor (const incomingEdge of incomingEdges) {\n\t\t\t\tuiEdges.push({\n\t\t\t\t\t...incomingEdge,\n\t\t\t\t\tsource: loopDef.startNodeId,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\n\t\t// replace scatter/gather pairs with a single representative worker node\n\t\tconst scatterNodes = blueprint.nodes.filter((n) => n.uses === 'batch-scatter')\n\t\tfor (const scatterNode of scatterNodes) {\n\t\t\tconst gatherNodeId = scatterNode.params?.gatherNodeId\n\t\t\tif (!gatherNodeId) continue\n\n\t\t\tignoredNodeIds.add(scatterNode.id)\n\t\t\tignoredNodeIds.add(gatherNodeId)\n\n\t\t\t// single node to represent parallel work\n\t\t\tconst batchId = scatterNode.id.replace('_scatter', '')\n\t\t\tconst gatherNode = blueprint.nodes.find((n) => n.id === gatherNodeId)\n\n\t\t\tuiNodes.push({\n\t\t\t\tid: batchId,\n\t\t\t\tuses: scatterNode.params?.workerUsesKey,\n\t\t\t\ttype: 'batch-worker',\n\t\t\t\tdata: {\n\t\t\t\t\tlabel: `Batch: ${batchId}`,\n\t\t\t\t\tisBatchPlaceholder: true,\n\t\t\t\t\tworkerUsesKey: scatterNode.params?.workerUsesKey,\n\t\t\t\t\tinputKey: scatterNode.inputs,\n\t\t\t\t\toutputKey: gatherNode?.params?.outputKey,\n\t\t\t\t},\n\t\t\t})\n\n\t\t\t// re-wire incoming edges\n\t\t\tconst incomingEdges = blueprint.edges.filter((e) => e.target === scatterNode.id)\n\t\t\tfor (const edge of incomingEdges) {\n\t\t\t\tuiEdges.push({ ...edge, target: batchId })\n\t\t\t}\n\n\t\t\t// re-wire outgoing edges\n\t\t\tconst outgoingEdges = blueprint.edges.filter((e) => e.source === gatherNodeId)\n\t\t\tfor (const edge of outgoingEdges) {\n\t\t\t\tuiEdges.push({ ...edge, source: batchId })\n\t\t\t}\n\t\t}\n\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (!ignoredNodeIds.has(node.id)) {\n\t\t\t\tuiNodes.push(node)\n\t\t\t}\n\t\t}\n\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tif (!ignoredNodeIds.has(edge.source) && !ignoredNodeIds.has(edge.target)) {\n\t\t\t\tconst alreadyAdded = uiEdges.some(\n\t\t\t\t\t(e) => e.source === edge.source && e.target === edge.target && e.action === edge.action,\n\t\t\t\t)\n\t\t\t\tif (!alreadyAdded) {\n\t\t\t\t\tuiEdges.push(edge)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn { nodes: uiNodes, edges: uiEdges }\n\t}\n}\n\n/**\n * Helper function to create a new Flow builder instance.\n */\nexport function createFlow<\n\tTContext extends Record<string, any> = Record<string, any>,\n\tTDependencies extends Record<string, any> = Record<string, any>,\n>(id: string): FlowBuilder<TContext, TDependencies> {\n\treturn new FlowBuilder(id)\n}\n","import { analyzeBlueprint } from './analysis'\nimport type { NodeClass, NodeFunction, WorkflowBlueprint } from './types'\n\nexport type LinterIssueCode =\n\t| 'INVALID_EDGE_SOURCE'\n\t| 'INVALID_EDGE_TARGET'\n\t| 'MISSING_NODE_IMPLEMENTATION'\n\t| 'ORPHAN_NODE'\n\t| 'INVALID_BATCH_WORKER_KEY'\n\t| 'INVALID_SUBFLOW_BLUEPRINT_ID'\n\nexport interface LinterIssue {\n\tcode: LinterIssueCode\n\tmessage: string\n\tnodeId?: string\n\trelatedId?: string\n}\n\nexport interface LinterResult {\n\tisValid: boolean\n\tissues: LinterIssue[]\n}\n\n/**\n * Statically analyzes a workflow blueprint against a registry of implementations\n * to find common errors before runtime.\n *\n * @param blueprint The WorkflowBlueprint to analyze.\n * @param registry A map of node implementations (functions or classes) to check against.\n * @returns A LinterResult object containing any issues found.\n */\nexport function lintBlueprint(\n\tblueprint: WorkflowBlueprint,\n\tregistry: Map<string, NodeFunction | NodeClass> | Record<string, NodeFunction | NodeClass>,\n\tblueprints?: Record<string, WorkflowBlueprint>,\n): LinterResult {\n\tconst issues: LinterIssue[] = []\n\tconst nodeIds = new Set(blueprint.nodes.map((n) => n.id))\n\tconst registryKeys = registry instanceof Map ? new Set(registry.keys()) : new Set(Object.keys(registry))\n\n\t// check for missing node implementations\n\tfor (const node of blueprint.nodes) {\n\t\tif (!node.uses.startsWith('batch-') && !node.uses.startsWith('loop-') && !registryKeys.has(node.uses)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'MISSING_NODE_IMPLEMENTATION',\n\t\t\t\tmessage: `Node implementation key '${node.uses}' is not found in the provided registry.`,\n\t\t\t\tnodeId: node.id,\n\t\t\t})\n\t\t}\n\t}\n\n\t// check for dynamic node validation\n\tfor (const node of blueprint.nodes) {\n\t\tif (node.uses.startsWith('batch-') && node.params?.workerUsesKey) {\n\t\t\tif (!registryKeys.has(node.params.workerUsesKey)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'INVALID_BATCH_WORKER_KEY',\n\t\t\t\t\tmessage: `Batch node '${node.id}' references workerUsesKey '${node.params.workerUsesKey}' which is not found in the registry.`,\n\t\t\t\t\tnodeId: node.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t\tif (node.uses === 'subflow' && node.params?.blueprintId) {\n\t\t\tif (!blueprints?.[node.params.blueprintId]) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'INVALID_SUBFLOW_BLUEPRINT_ID',\n\t\t\t\t\tmessage: `Subflow node '${node.id}' references blueprintId '${node.params.blueprintId}' which is not found in the blueprints registry.`,\n\t\t\t\t\tnodeId: node.id,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t// check for graph integrity (edges must point to valid nodes)\n\tfor (const edge of blueprint.edges || []) {\n\t\tif (!nodeIds.has(edge.source)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'INVALID_EDGE_SOURCE',\n\t\t\t\tmessage: `Edge source '${edge.source}' does not correspond to a valid node ID.`,\n\t\t\t\trelatedId: edge.target,\n\t\t\t})\n\t\t}\n\t\tif (!nodeIds.has(edge.target)) {\n\t\t\tissues.push({\n\t\t\t\tcode: 'INVALID_EDGE_TARGET',\n\t\t\t\tmessage: `Edge target '${edge.target}' does not correspond to a valid node ID.`,\n\t\t\t\trelatedId: edge.source,\n\t\t\t})\n\t\t}\n\t}\n\n\t// check for orphan nodes (not connected to the main graph)\n\tif (blueprint.nodes.length > 1) {\n\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\tconst connectedNodes = new Set<string>()\n\t\tconst nodesToVisit = [...analysis.startNodeIds]\n\t\tconst visited = new Set<string>()\n\n\t\twhile (nodesToVisit.length > 0) {\n\t\t\tconst currentId = nodesToVisit.pop()\n\t\t\tif (!currentId || visited.has(currentId)) continue\n\n\t\t\tvisited.add(currentId)\n\t\t\tconnectedNodes.add(currentId)\n\n\t\t\tfor (const e of blueprint.edges.filter((e) => e.source === currentId)) {\n\t\t\t\tnodesToVisit.push(e.target)\n\t\t\t}\n\t\t}\n\n\t\tfor (const nodeId of nodeIds) {\n\t\t\tif (!connectedNodes.has(nodeId)) {\n\t\t\t\tissues.push({\n\t\t\t\t\tcode: 'ORPHAN_NODE',\n\t\t\t\t\tmessage: `Node '${nodeId}' is not reachable from any start node.`,\n\t\t\t\t\tnodeId,\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\treturn {\n\t\tisValid: issues.length === 0,\n\t\tissues,\n\t}\n}\n","import { analyzeBlueprint } from '../analysis'\nimport { TrackedAsyncContext } from '../context'\nimport { ConsoleLogger } from '../logger'\nimport { JsonSerializer } from '../serializer'\nimport type {\n\tIAsyncContext,\n\tIEventBus,\n\tILogger,\n\tISerializer,\n\tNodeResult,\n\tRuntimeOptions,\n\tWorkflowBlueprint,\n\tWorkflowResult,\n} from '../types'\nimport { FlowRuntime } from './runtime'\nimport { WorkflowState } from './state'\n\n/**\n * Defines the contract for an atomic, distributed key-value store required by\n * the adapter for coordination tasks like fan-in joins and locking.\n */\nexport interface ICoordinationStore {\n\t/** Atomically increments a key and returns the new value. Ideal for 'all' joins. */\n\tincrement: (key: string, ttlSeconds: number) => Promise<number>\n\t/** Sets a key only if it does not already exist. Ideal for 'any' joins (locking). */\n\tsetIfNotExist: (key: string, value: string, ttlSeconds: number) => Promise<boolean>\n\t/** Extends the TTL of an existing key. Used for heartbeat mechanism in long-running jobs. */\n\textendTTL: (key: string, ttlSeconds: number) => Promise<boolean>\n\t/** Deletes a key. Used for cleanup. */\n\tdelete: (key: string) => Promise<void>\n\t/** Gets the value of a key. */\n\tget: (key: string) => Promise<string | undefined>\n}\n\n/** Configuration options for constructing a BaseDistributedAdapter. */\nexport interface AdapterOptions {\n\truntimeOptions: RuntimeOptions<any>\n\tcoordinationStore: ICoordinationStore\n\teventBus?: IEventBus\n}\n\n/** The data payload expected for a job in the queue. */\nexport interface JobPayload {\n\trunId: string\n\tblueprintId: string\n\tnodeId: string\n}\n\n/**\n * The base class for all distributed adapters. It handles the technology-agnostic\n * orchestration logic and leaves queue-specific implementation to subclasses.\n */\nexport abstract class BaseDistributedAdapter {\n\tprotected readonly runtime: FlowRuntime<any, any>\n\tprotected readonly store: ICoordinationStore\n\tprotected readonly serializer: ISerializer\n\tprotected readonly logger: ILogger\n\tprotected readonly eventBus?: IEventBus\n\n\tconstructor(options: AdapterOptions) {\n\t\tconst runtimeOptions = {\n\t\t\t...options.runtimeOptions,\n\t\t\tdependencies: {\n\t\t\t\t...options.runtimeOptions.dependencies,\n\t\t\t\tadapter: this,\n\t\t\t} as any,\n\t\t}\n\t\tthis.runtime = new FlowRuntime(runtimeOptions)\n\t\tthis.store = options.coordinationStore\n\t\tthis.serializer = options.runtimeOptions.serializer || new JsonSerializer()\n\t\tthis.logger = options.runtimeOptions.logger || new ConsoleLogger()\n\t\tthis.eventBus = options.eventBus\n\t\tthis.logger.info('[Adapter] BaseDistributedAdapter initialized.')\n\t}\n\n\t/**\n\t * Starts the worker, which begins listening for and processing jobs from the queue.\n\t */\n\tpublic start(): void {\n\t\tthis.logger.info('[Adapter] Starting worker...')\n\t\tthis.processJobs(this.handleJob.bind(this))\n\t}\n\n\t/**\n\t * Creates a technology-specific distributed context for a given workflow run.\n\t * @param runId The unique ID for the workflow execution.\n\t */\n\tprotected abstract createContext(runId: string): IAsyncContext<Record<string, any>>\n\t/**\n\t * Sets up the listener for the message queue. The implementation should call the\n\t * provided `handler` function for each new job received.\n\t * @param handler The core logic to execute for each job.\n\t */\n\tprotected abstract processJobs(handler: (job: JobPayload) => Promise<void>): void\n\n\t/**\n\t * Enqueues a new job onto the message queue.\n\t * @param job The payload for the job to be enqueued.\n\t */\n\tprotected abstract enqueueJob(job: JobPayload): Promise<void>\n\n\t/**\n\t * Publishes the final result of a completed or failed workflow run.\n\t * @param runId The unique ID of the workflow run.\n\t * @param result The final status and payload of the workflow.\n\t */\n\tprotected abstract publishFinalResult(\n\t\trunId: string,\n\t\tresult: {\n\t\t\tstatus: 'completed' | 'failed'\n\t\t\tpayload?: WorkflowResult\n\t\t\treason?: string\n\t\t},\n\t): Promise<void>\n\n\t/**\n\t * Registers a webhook endpoint for a specific node in a workflow run.\n\t * @param runId The unique ID of the workflow run.\n\t * @param nodeId The ID of the node that will wait for the webhook.\n\t * @returns The URL and event name for the webhook.\n\t */\n\tpublic abstract registerWebhookEndpoint(runId: string, nodeId: string): Promise<{ url: string; event: string }>\n\n\t/**\n\t * Hook called at the start of job processing. Subclasses can override this\n\t * to perform additional setup (e.g., timestamp tracking for reconciliation).\n\t */\n\tprotected async onJobStart(_runId: string, _blueprintId: string, _nodeId: string): Promise<void> {\n\t\t// default implementation does nothing\n\t}\n\n\t/**\n\t * The main handler for processing a single job from the queue.\n\t */\n\tprotected async handleJob(job: JobPayload): Promise<void> {\n\t\tconst { runId, blueprintId, nodeId } = job\n\t\tconst startTime = Date.now()\n\n\t\tawait this.onJobStart(runId, blueprintId, nodeId)\n\n\t\tconst blueprint = this.runtime.options.blueprints?.[blueprintId]\n\t\tif (!blueprint) {\n\t\t\tconst reason = `Blueprint with ID '${blueprintId}' not found in the worker's runtime registry.`\n\t\t\tthis.logger.error(`[Adapter] FATAL: ${reason}`)\n\t\t\tawait this.publishFinalResult(runId, { status: 'failed', reason })\n\t\t\treturn\n\t\t}\n\n\t\tconst context = this.createContext(runId)\n\n\t\tconst storedVersion = await context.get('blueprintVersion' as any)\n\t\tconst currentVersion = blueprint.metadata?.version || null\n\t\tif (storedVersion !== currentVersion) {\n\t\t\tconst reason = `Blueprint version mismatch: stored version '${storedVersion}', current version '${currentVersion}'. Rejecting job to prevent state corruption.`\n\t\t\tthis.logger.warn(`[Adapter] Version mismatch for run ${runId}, node ${nodeId}: ${reason}`)\n\t\t\treturn\n\t\t}\n\n\t\t// persist the blueprintId and version for the reconcile method to find later\n\t\tconst hasBlueprintId = await context.has('blueprintId' as any)\n\t\tif (!hasBlueprintId) {\n\t\t\tawait context.set('blueprintId' as any, blueprintId)\n\t\t\tawait context.set('blueprintVersion' as any, blueprint.metadata?.version || null)\n\t\t\t// also store in coordination store as fallback\n\t\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\t\tawait this.store.setIfNotExist(blueprintKey, blueprintId, 3600)\n\t\t}\n\n\t\t// heartbeat to extend TTLs of coordination keys for long-running jobs\n\t\tconst joinLockKey = `flowcraft:joinlock:${runId}:${nodeId}`\n\t\tconst fanInKey = `flowcraft:fanin:${runId}:${nodeId}`\n\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\tconst heartbeatInterval = setInterval(async () => {\n\t\t\tawait this.store.extendTTL(joinLockKey, 3600)\n\t\t\tawait this.store.extendTTL(fanInKey, 3600)\n\t\t\tawait this.store.extendTTL(blueprintKey, 3600)\n\t\t\tthis.logger.debug(`[Adapter] Extended TTLs for run ${runId}, node ${nodeId}`)\n\t\t}, 1800000) // 30 minutes\n\n\t\ttry {\n\t\t\tconst contextData = await context.toJSON()\n\t\t\tconst state = new WorkflowState(contextData, context)\n\n\t\t\tconst result: NodeResult<any, any> = await this.runtime.executeNode(blueprint, nodeId, state)\n\t\t\tawait context.set(`_outputs.${nodeId}` as any, result.output)\n\n\t\t\tconst stateContext = state.getContext()\n\t\t\tif (stateContext instanceof TrackedAsyncContext) {\n\t\t\t\tconst deltas = stateContext.getDeltas()\n\t\t\t\tif (deltas.length > 0) {\n\t\t\t\t\tawait stateContext.patch(deltas)\n\t\t\t\t\tstateContext.clearDeltas()\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst analysis = analyzeBlueprint(blueprint)\n\t\t\tconst isTerminalNode = analysis.terminalNodeIds.includes(nodeId)\n\n\t\t\tif (isTerminalNode) {\n\t\t\t\tconst allContextKeys = Object.keys(await context.toJSON())\n\t\t\t\tconst completedNodes = new Set<string>()\n\t\t\t\tfor (const key of allContextKeys) {\n\t\t\t\t\tif (key.startsWith('_outputs.')) {\n\t\t\t\t\t\tcompletedNodes.add(key.substring('_outputs.'.length))\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst allTerminalNodesCompleted = analysis.terminalNodeIds.every((terminalId) => completedNodes.has(terminalId))\n\n\t\t\t\tif (allTerminalNodesCompleted) {\n\t\t\t\t\tthis.logger.info(`[Adapter] All terminal nodes completed for Run ID: ${runId}. Declaring workflow complete.`)\n\t\t\t\t\tconst finalContext = await context.toJSON()\n\t\t\t\t\tconst finalResult: WorkflowResult = {\n\t\t\t\t\t\tcontext: finalContext,\n\t\t\t\t\t\tserializedContext: this.serializer.serialize(finalContext),\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t}\n\t\t\t\t\tawait this.publishFinalResult(runId, {\n\t\t\t\t\t\tstatus: 'completed',\n\t\t\t\t\t\tpayload: finalResult,\n\t\t\t\t\t})\n\t\t\t\t\tclearInterval(heartbeatInterval)\n\t\t\t\t\treturn\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Terminal node '${nodeId}' completed for Run ID '${runId}', but other terminal nodes are still running.`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst nextNodes = await this.runtime.determineNextNodes(blueprint, nodeId, result, context, runId)\n\n\t\t\t// stop if a branch terminates but it wasn't a terminal node\n\t\t\tif (nextNodes.length === 0 && !isTerminalNode) {\n\t\t\t\tthis.logger.info(\n\t\t\t\t\t`[Adapter] Non-terminal node '${nodeId}' reached end of branch for Run ID '${runId}'. This branch will now terminate.`,\n\t\t\t\t)\n\t\t\t\tclearInterval(heartbeatInterval)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tfor (const { node: nextNodeDef, edge } of nextNodes) {\n\t\t\t\tawait this.runtime.applyEdgeTransform(edge, result, nextNodeDef, context, undefined, runId)\n\t\t\t\tconst isReady = await this.isReadyForFanIn(runId, blueprint, nextNodeDef.id)\n\t\t\t\tif (isReady) {\n\t\t\t\t\tthis.logger.info(`[Adapter] Node '${nextNodeDef.id}' is ready. Enqueuing job.`)\n\t\t\t\t\tawait this.enqueueJob({ runId, blueprintId, nodeId: nextNodeDef.id })\n\t\t\t\t\tif (this.eventBus) {\n\t\t\t\t\t\tawait this.eventBus.emit({\n\t\t\t\t\t\t\ttype: 'job:enqueued',\n\t\t\t\t\t\t\tpayload: { runId, blueprintId, nodeId: nextNodeDef.id },\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(`[Adapter] Node '${nextNodeDef.id}' is waiting for other predecessors to complete.`)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst duration = Date.now() - startTime\n\t\t\tif (this.eventBus) {\n\t\t\t\tawait this.eventBus.emit({\n\t\t\t\t\ttype: 'job:processed',\n\t\t\t\t\tpayload: { runId, blueprintId, nodeId, duration, success: true },\n\t\t\t\t})\n\t\t\t}\n\t\t} catch (error: any) {\n\t\t\tconst reason = error.message || 'Unknown execution error'\n\t\t\tthis.logger.error(`[Adapter] FATAL: Job for node '${nodeId}' failed for Run ID '${runId}': ${reason}`)\n\t\t\tawait this.publishFinalResult(runId, { status: 'failed', reason })\n\t\t\tawait this.writePoisonPillForSuccessors(runId, blueprint, nodeId)\n\n\t\t\tif (this.eventBus) {\n\t\t\t\tawait this.eventBus.emit({\n\t\t\t\t\ttype: 'job:failed',\n\t\t\t\t\tpayload: { runId, blueprintId, nodeId, error },\n\t\t\t\t})\n\t\t\t}\n\t\t} finally {\n\t\t\tclearInterval(heartbeatInterval)\n\t\t}\n\t}\n\n\t/**\n\t * Encapsulates the fan-in join logic using the coordination store.\n\t */\n\tprotected async isReadyForFanIn(runId: string, blueprint: WorkflowBlueprint, targetNodeId: string): Promise<boolean> {\n\t\tconst targetNode = blueprint.nodes.find((n) => n.id === targetNodeId)\n\t\tif (!targetNode) {\n\t\t\tthrow new Error(`Node '${targetNodeId}' not found in blueprint`)\n\t\t}\n\t\tconst joinStrategy = targetNode.config?.joinStrategy || 'all'\n\t\tconst predecessors = blueprint.edges.filter((e) => e.target === targetNodeId)\n\n\t\tif (predecessors.length <= 1) {\n\t\t\treturn true\n\t\t}\n\n\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${targetNodeId}`\n\t\tconst isPoisoned = await this.store.get(poisonKey)\n\t\tif (isPoisoned) {\n\t\t\tthis.logger.info(`[Adapter] Node '${targetNodeId}' is poisoned due to failed predecessor. Failing immediately.`)\n\t\t\tthrow new Error(`Node '${targetNodeId}' failed due to poisoned predecessor in run '${runId}'`)\n\t\t}\n\n\t\tif (joinStrategy === 'any') {\n\t\t\tconst lockKey = `flowcraft:joinlock:${runId}:${targetNodeId}`\n\t\t\tconst isLocked = await this.store.setIfNotExist(lockKey, 'locked', 3600)\n\t\t\tif (!isLocked) {\n\t\t\t\t// check if cancelled\n\t\t\t\tconst cancelKey = `flowcraft:fanin:cancel:${runId}:${targetNodeId}`\n\t\t\t\tconst isCancelled = !(await this.store.setIfNotExist(cancelKey, 'cancelled', 3600))\n\t\t\t\tif (isCancelled) {\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Node '${targetNodeId}' is cancelled due to failed predecessor. Failing immediately.`,\n\t\t\t\t\t)\n\t\t\t\t\tthrow new Error(`Node '${targetNodeId}' failed due to cancelled predecessor in run '${runId}'`)\n\t\t\t\t}\n\t\t\t\treturn false // already locked by another predecessor\n\t\t\t}\n\t\t\treturn true\n\t\t} else {\n\t\t\tconst fanInKey = `flowcraft:fanin:${runId}:${targetNodeId}`\n\t\t\tconst readyCount = await this.store.increment(fanInKey, 3600)\n\t\t\tif (readyCount >= predecessors.length) {\n\t\t\t\tawait this.store.delete(fanInKey)\n\t\t\t\treturn true\n\t\t\t}\n\t\t\treturn false\n\t\t}\n\t}\n\n\t/**\n\t * Reconciles the state of a workflow run. It inspects the persisted\n\t * context to find completed nodes, determines the next set of executable\n\t * nodes (the frontier), and enqueues jobs for them if they aren't\n\t * already running. This is the core of the resume functionality.\n\t *\n\t * @param runId The unique ID of the workflow execution to reconcile.\n\t * @returns The set of node IDs that were enqueued for execution.\n\t */\n\tpublic async reconcile(runId: string): Promise<Set<string>> {\n\t\tconst context = this.createContext(runId)\n\t\tlet blueprintId = (await context.get('blueprintId' as any)) as string | undefined\n\n\t\tif (!blueprintId) {\n\t\t\t// fallback to coordination store\n\t\t\tconst blueprintKey = `flowcraft:blueprint:${runId}`\n\t\t\tblueprintId = await this.store.get(blueprintKey)\n\t\t\tif (blueprintId) {\n\t\t\t\t// set it back in context for future use\n\t\t\t\tawait context.set('blueprintId' as any, blueprintId)\n\t\t\t} else {\n\t\t\t\tthrow new Error(`Cannot reconcile runId '${runId}': blueprintId not found in context or coordination store.`)\n\t\t\t}\n\t\t}\n\t\tconst blueprint = this.runtime.options.blueprints?.[blueprintId]\n\t\tif (blueprint && !(await context.has('blueprintVersion' as any))) {\n\t\t\tawait context.set('blueprintVersion' as any, blueprint.metadata?.version || null)\n\t\t}\n\t\tif (!blueprint) {\n\t\t\tthrow new Error(`Cannot reconcile runId '${runId}': Blueprint with ID '${blueprintId}' not found.`)\n\t\t}\n\n\t\tconst state = await context.toJSON()\n\t\tconst completedNodes = new Set<string>()\n\t\tfor (const key of Object.keys(state)) {\n\t\t\tif (key.startsWith('_outputs.')) {\n\t\t\t\tcompletedNodes.add(key.substring('_outputs.'.length))\n\t\t\t}\n\t\t}\n\n\t\tconst frontier = this.calculateResumedFrontier(blueprint, completedNodes)\n\n\t\tconst enqueuedNodes = new Set<string>()\n\t\tfor (const nodeId of frontier) {\n\t\t\tconst nodeDef = blueprint.nodes.find((n) => n.id === nodeId)\n\t\t\tconst joinStrategy = nodeDef?.config?.joinStrategy || 'all'\n\n\t\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${nodeId}`\n\t\t\tconst isPoisoned = await this.store.get(poisonKey)\n\t\t\tif (isPoisoned) {\n\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Node '${nodeId}' is poisoned, skipping.`, { runId })\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tlet shouldEnqueue = false\n\n\t\t\tif (joinStrategy === 'any') {\n\t\t\t\t// acquire the permanent join lock\n\t\t\t\tconst lockKey = `flowcraft:joinlock:${runId}:${nodeId}`\n\t\t\t\tif (await this.store.setIfNotExist(lockKey, 'locked-by-reconcile', 3600)) {\n\t\t\t\t\tshouldEnqueue = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Node '${nodeId}' is an 'any' join and is already locked.`, { runId })\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// 'all' joins and single-predecessor nodes use a temporary lock\n\t\t\t\tconst lockKey = `flowcraft:nodelock:${runId}:${nodeId}`\n\t\t\t\tif (await this.store.setIfNotExist(lockKey, 'locked', 120)) {\n\t\t\t\t\tshouldEnqueue = true\n\t\t\t\t} else {\n\t\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Node '${nodeId}' is already locked.`, { runId })\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (shouldEnqueue) {\n\t\t\t\tthis.logger.info(`[Adapter] Reconciling: Enqueuing ready job for node '${nodeId}'`, { runId })\n\t\t\t\tawait this.enqueueJob({ runId, blueprintId: blueprint.id, nodeId })\n\t\t\t\tenqueuedNodes.add(nodeId)\n\t\t\t}\n\t\t}\n\n\t\treturn enqueuedNodes\n\t}\n\n\tprivate calculateResumedFrontier(blueprint: WorkflowBlueprint, completedNodes: Set<string>): Set<string> {\n\t\tconst newFrontier = new Set<string>()\n\t\tconst allPredecessors = new Map<string, Set<string>>()\n\t\t// (logic extracted from the GraphTraverser)\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tallPredecessors.set(node.id, new Set())\n\t\t}\n\t\tfor (const edge of blueprint.edges) {\n\t\t\tallPredecessors.get(edge.target)?.add(edge.source)\n\t\t}\n\n\t\tfor (const node of blueprint.nodes) {\n\t\t\tif (completedNodes.has(node.id)) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst predecessors = allPredecessors.get(node.id) ?? new Set()\n\t\t\tif (predecessors.size === 0 && !completedNodes.has(node.id)) {\n\t\t\t\tnewFrontier.add(node.id)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst joinStrategy = node.config?.joinStrategy || 'all'\n\t\t\tconst completedPredecessors = [...predecessors].filter((p) => completedNodes.has(p))\n\n\t\t\tconst isReady =\n\t\t\t\tjoinStrategy === 'any' ? completedPredecessors.length > 0 : completedPredecessors.length === predecessors.size\n\n\t\t\tif (isReady) {\n\t\t\t\tnewFrontier.add(node.id)\n\t\t\t}\n\t\t}\n\t\treturn newFrontier\n\t}\n\n\t/**\n\t * Writes a poison pill for 'all' join successors and a cancellation pill for 'any' join successors of a failed node to prevent stalling or ambiguous states.\n\t */\n\tprivate async writePoisonPillForSuccessors(\n\t\trunId: string,\n\t\tblueprint: WorkflowBlueprint,\n\t\tfailedNodeId: string,\n\t): Promise<void> {\n\t\tconst successors = blueprint.edges\n\t\t\t.filter((edge) => edge.source === failedNodeId)\n\t\t\t.map((edge) => edge.target)\n\t\t\t.map((targetId) => blueprint.nodes.find((node) => node.id === targetId))\n\t\t\t.filter((node) => node)\n\n\t\tfor (const successor of successors) {\n\t\t\tif (successor) {\n\t\t\t\tconst joinStrategy = successor.config?.joinStrategy || 'all'\n\t\t\t\tif (joinStrategy === 'all') {\n\t\t\t\t\tconst poisonKey = `flowcraft:fanin:poison:${runId}:${successor.id}`\n\t\t\t\t\tawait this.store.setIfNotExist(poisonKey, 'poisoned', 3600)\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Wrote poison pill for 'all' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`,\n\t\t\t\t\t)\n\t\t\t\t} else if (joinStrategy === 'any') {\n\t\t\t\t\tconst cancelKey = `flowcraft:fanin:cancel:${runId}:${successor.id}`\n\t\t\t\t\tawait this.store.setIfNotExist(cancelKey, 'cancelled', 3600)\n\t\t\t\t\tthis.logger.info(\n\t\t\t\t\t\t`[Adapter] Wrote cancellation pill for 'any' join node '${successor.id}' due to failed predecessor '${failedNodeId}'`,\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],"mappings":"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"}
@@ -950,7 +950,7 @@ var BatchScatterNode = class extends BaseNode {
950
950
  //#endregion
951
951
  //#region src/nodes/sleep.ts
952
952
  var SleepNode = class extends BaseNode {
953
- async exec(_prepResult, context) {
953
+ async exec(prepResult, context) {
954
954
  const durationParam = this.params?.duration;
955
955
  let durationMs;
956
956
  if (typeof durationParam === "string") {
@@ -981,7 +981,7 @@ var SleepNode = class extends BaseNode {
981
981
  reason: "timer",
982
982
  wakeUpAt
983
983
  });
984
- return { output: void 0 };
984
+ return { output: prepResult };
985
985
  }
986
986
  };
987
987
 
@@ -1599,6 +1599,7 @@ var NodeExecutorFactory = class {
1599
1599
  var WorkflowScheduler = class {
1600
1600
  runtime;
1601
1601
  activeWorkflows = /* @__PURE__ */ new Map();
1602
+ resumeResults = /* @__PURE__ */ new Map();
1602
1603
  intervalId;
1603
1604
  checkIntervalMs;
1604
1605
  constructor(runtime, checkIntervalMs = 1e3) {
@@ -1617,13 +1618,14 @@ var WorkflowScheduler = class {
1617
1618
  this.intervalId = void 0;
1618
1619
  }
1619
1620
  }
1620
- registerAwaitingWorkflow(executionId, blueprintId, serializedContext, awaitingNodeId, wakeUpAt) {
1621
+ registerAwaitingWorkflow(executionId, blueprintId, serializedContext, awaitingNodeId, wakeUpAt, functionRegistry) {
1621
1622
  this.activeWorkflows.set(executionId, {
1622
1623
  executionId,
1623
1624
  blueprintId,
1624
1625
  serializedContext,
1625
1626
  awaitingNodeId,
1626
- wakeUpAt
1627
+ wakeUpAt,
1628
+ functionRegistry
1627
1629
  });
1628
1630
  }
1629
1631
  unregisterWorkflow(executionId) {
@@ -1639,7 +1641,8 @@ var WorkflowScheduler = class {
1639
1641
  console.warn(`Blueprint ${workflow.blueprintId} not found, skipping resumption`);
1640
1642
  continue;
1641
1643
  }
1642
- const result = await this.runtime.resume(blueprint, workflow.serializedContext, { output: void 0 }, workflow.awaitingNodeId);
1644
+ const result = await this.runtime.resume(blueprint, workflow.serializedContext, { output: void 0 }, workflow.awaitingNodeId, { functionRegistry: workflow.functionRegistry });
1645
+ this.resumeResults.set(workflow.executionId, result);
1643
1646
  if (result.status === "completed" || result.status === "failed") this.unregisterWorkflow(workflow.executionId);
1644
1647
  } catch (error) {
1645
1648
  console.error(`Failed to resume workflow ${workflow.executionId}:`, error);
@@ -1649,6 +1652,9 @@ var WorkflowScheduler = class {
1649
1652
  getActiveWorkflows() {
1650
1653
  return Array.from(this.activeWorkflows.values());
1651
1654
  }
1655
+ getResumeResult(executionId) {
1656
+ return this.resumeResults.get(executionId);
1657
+ }
1652
1658
  };
1653
1659
 
1654
1660
  //#endregion
@@ -1748,10 +1754,16 @@ var WorkflowLogicHandler = class {
1748
1754
  const hasExplicitInputs = targetNode.inputs !== void 0;
1749
1755
  const hasEdgeTransform = edge.transform !== void 0;
1750
1756
  if (!hasExplicitInputs && !hasSinglePredecessor && !hasEdgeTransform) return;
1757
+ let sourceOutput = sourceResult.output;
1758
+ if (hasEdgeTransform && hasExplicitInputs && typeof targetNode.inputs === "string") {
1759
+ const inputsKey = targetNode.inputs;
1760
+ const resolvedKey = inputsKey.startsWith("_") ? inputsKey : `_outputs.${inputsKey}`;
1761
+ if (await asyncContext.has(resolvedKey) && inputsKey !== edge.source) sourceOutput = await asyncContext.get(resolvedKey);
1762
+ }
1751
1763
  const finalInput = edge.transform ? this.evaluator.evaluate(edge.transform, {
1752
- input: sourceResult.output,
1764
+ input: sourceOutput,
1753
1765
  context: await asyncContext.toJSON()
1754
- }) : sourceResult.output;
1766
+ }) : sourceOutput;
1755
1767
  const inputKey = `_inputs.${targetNode.id}`;
1756
1768
  await asyncContext.set(inputKey, finalInput);
1757
1769
  await this.eventBus.emit({
@@ -1764,7 +1776,7 @@ var WorkflowLogicHandler = class {
1764
1776
  executionId: executionId || "unknown"
1765
1777
  }
1766
1778
  });
1767
- if (!hasExplicitInputs) targetNode.inputs = inputKey;
1779
+ if (!hasExplicitInputs || hasEdgeTransform) targetNode.inputs = inputKey;
1768
1780
  }
1769
1781
  async resolveNodeInput(nodeId, blueprint, context) {
1770
1782
  const nodeDef = blueprint.nodes.find((n) => n.id === nodeId);
@@ -1959,7 +1971,7 @@ var FlowRuntime = class {
1959
1971
  const awaitingNodeIds = executionContext.state.getAwaitingNodeIds();
1960
1972
  for (const nodeId of awaitingNodeIds) {
1961
1973
  const details = executionContext.state.getAwaitingDetails(nodeId);
1962
- if (details?.reason === "timer") this.scheduler.registerAwaitingWorkflow(executionContext.executionId, executionContext.blueprint.id, result.serializedContext, nodeId, details.wakeUpAt);
1974
+ if (details?.reason === "timer") this.scheduler.registerAwaitingWorkflow(executionContext.executionId, executionContext.blueprint.id, result.serializedContext, nodeId, details.wakeUpAt, options?.functionRegistry);
1963
1975
  }
1964
1976
  }
1965
1977
  return result;
@@ -2088,8 +2100,11 @@ var FlowRuntime = class {
2088
2100
  resumeData = { output: finalSubflowOutput };
2089
2101
  await contextImpl.delete(subflowStateKey);
2090
2102
  }
2091
- workflowState.addCompletedNode(awaitingNodeId, resumeData.output);
2092
- const nextSteps = await this.determineNextNodes(blueprint, awaitingNodeId, resumeData, contextImpl, executionId);
2103
+ const existingOutput = await workflowState.getContext().get(`_outputs.${awaitingNodeId}`);
2104
+ const nodeOutput = resumeData.output !== void 0 ? resumeData.output : existingOutput;
2105
+ workflowState.addCompletedNode(awaitingNodeId, nodeOutput);
2106
+ const nodeResult = { output: nodeOutput };
2107
+ const nextSteps = await this.determineNextNodes(blueprint, awaitingNodeId, nodeResult, contextImpl, executionId);
2093
2108
  if (nextSteps.length === 0) {
2094
2109
  workflowState.clearAwaiting(awaitingNodeId);
2095
2110
  const result = await workflowState.toResult(this.serializer, executionId);
@@ -2097,7 +2112,7 @@ var FlowRuntime = class {
2097
2112
  return result;
2098
2113
  }
2099
2114
  const allPredecessors = new GraphTraverser(blueprint).getAllPredecessors();
2100
- for (const { node, edge } of nextSteps) await this.applyEdgeTransform(edge, resumeData, node, contextImpl, allPredecessors, executionId);
2115
+ for (const { node, edge } of nextSteps) await this.applyEdgeTransform(edge, nodeResult, node, contextImpl, allPredecessors, executionId);
2101
2116
  const traverser = GraphTraverser.fromState(blueprint, workflowState);
2102
2117
  const nextNodeDefs = nextSteps.map((s) => s.node);
2103
2118
  for (const nodeDef of nextNodeDefs) traverser.addToFrontier(nodeDef.id);
@@ -2162,16 +2177,6 @@ var FlowRuntime = class {
2162
2177
  getExecutorForNode(nodeId, context) {
2163
2178
  return this.executorFactory.createExecutorForNode(nodeId, context);
2164
2179
  }
2165
- createForSubflow(subBlueprint, initialSubState, executionId, signal) {
2166
- return new ExecutionContext(subBlueprint, new WorkflowState(initialSubState), this.registry, executionId, this, {
2167
- logger: this.logger,
2168
- eventBus: this.eventBus,
2169
- serializer: this.serializer,
2170
- evaluator: this.evaluator,
2171
- middleware: this.middleware,
2172
- dependencies: this.dependencies
2173
- }, signal);
2174
- }
2175
2180
  async determineNextNodes(blueprint, nodeId, result, context, executionId) {
2176
2181
  return this.logicHandler.determineNextNodes(blueprint, nodeId, result, context, executionId);
2177
2182
  }
@@ -2212,5 +2217,5 @@ var FlowRuntime = class {
2212
2217
  };
2213
2218
 
2214
2219
  //#endregion
2215
- export { PersistentEventBusAdapter as A, DIContainer as C, generateMermaid as D, checkForCycles as E, generateMermaidForRun as O, UnsafeEvaluator as S, analyzeBlueprint as T, TrackedAsyncContext as _, sanitizeBlueprint as a, NullLogger as b, isNodeClass as c, executeBatch as d, processResults as f, Context as g, AsyncContextView as h, NodeExecutor as i, InMemoryEventStore as k, JsonSerializer as l, WorkflowState as m, ClassNodeExecutor as n, GraphTraverser as o, ExecutionContext as p, FunctionNodeExecutor as r, BaseNode as s, FlowRuntime as t, DefaultOrchestrator as u, FlowcraftError as v, ServiceTokens as w, PropertyEvaluator as x, ConsoleLogger as y };
2216
- //# sourceMappingURL=runtime-CmefIAu_.mjs.map
2220
+ export { ServiceTokens as A, TrackedAsyncContext as C, PropertyEvaluator as D, NullLogger as E, InMemoryEventStore as F, PersistentEventBusAdapter as I, checkForCycles as M, generateMermaid as N, UnsafeEvaluator as O, generateMermaidForRun as P, Context as S, ConsoleLogger as T, executeBatch as _, sanitizeBlueprint as a, WorkflowState as b, SubflowNode as c, BatchScatterNode as d, BatchGatherNode as f, DefaultOrchestrator as g, JsonSerializer as h, NodeExecutor as i, analyzeBlueprint as j, DIContainer as k, GraphTraverser as l, isNodeClass as m, ClassNodeExecutor as n, WebhookNode as o, BaseNode as p, FunctionNodeExecutor as r, WaitNode as s, FlowRuntime as t, SleepNode as u, processResults as v, FlowcraftError as w, AsyncContextView as x, ExecutionContext as y };
2221
+ //# sourceMappingURL=runtime-gjjfM1q1.mjs.map