@maxdrellin/xenocline 0.0.6 → 0.0.8

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.
@@ -11,6 +11,31 @@ export type AggregationResult<U extends Output = Output> = {
11
11
  } | {
12
12
  status: 'NotYetReady';
13
13
  };
14
+ /**
15
+ * Aggregation method that processes inputs and returns an aggregation result.
16
+ *
17
+ * IMPORTANT: Aggregators should store their state in the context, not in
18
+ * module-level or closure variables, to ensure proper isolation between
19
+ * concurrent process executions.
20
+ *
21
+ * Pattern:
22
+ * ```typescript
23
+ * const aggregator = createAggregator('MyAgg', {
24
+ * aggregate: async (input, context) => {
25
+ * if (!context.myAggState) {
26
+ * context.myAggState = { count: 0, accumulated: 0 };
27
+ * }
28
+ * const state = context.myAggState;
29
+ * state.count++;
30
+ * state.accumulated += input.value;
31
+ * if (state.count === 2) {
32
+ * return { status: 'Ready', output: { result: state.accumulated } };
33
+ * }
34
+ * return { status: 'NotYetReady' };
35
+ * }
36
+ * });
37
+ * ```
38
+ */
14
39
  export type AggregateMethod<U extends Output = Output, C extends Context = Context> = (input: Input, context: C) => Promise<Readonly<AggregationResult<U>>>;
15
40
  export interface Aggregator<U extends Output = Output, C extends Context = Context> {
16
41
  name: string;
@@ -1 +1 @@
1
- {"version":3,"file":"aggregator.js","sources":["../src/aggregator.ts"],"sourcesContent":["import { clean } from \"./util/general\";\nimport { Context } from \"./context\";\nimport { Input } from \"./input\";\nimport { Output } from \"./output\";\n\n/**\n * Defines the possible outcomes of an aggregation operation.\n * It can either be 'Ready' with an output, or 'NotYetReady'.\n */\nexport type AggregationResult<U extends Output = Output> =\n | { status: 'Ready'; output: U }\n | { status: 'NotYetReady' };\n\nexport type AggregateMethod<U extends Output = Output, C extends Context = Context> = (\n input: Input,\n context: C\n) => Promise<Readonly<AggregationResult<U>>>;\n\n\nexport interface Aggregator<U extends Output = Output, C extends Context = Context> {\n name: string;\n aggregate: AggregateMethod<U, C>;\n}\n\nexport interface AggregatorOptions<U extends Output = Output, C extends Context = Context> {\n aggregate: AggregateMethod<U, C>;\n}\n\nexport const DEFAULT_AGGREGATOR_OPTIONS: AggregatorOptions<Output, Context> = {\n aggregate: async (input) => {\n return { status: 'Ready', output: input };\n }\n}\n\nexport const createAggregator = <U extends Output = Output, C extends Context = Context>(\n name: string,\n options?: Partial<AggregatorOptions<U, C>>\n): Readonly<Aggregator<U, C>> => {\n let aggregatorOptions: AggregatorOptions<U, C> = { ...DEFAULT_AGGREGATOR_OPTIONS } as unknown as AggregatorOptions<U, C>;\n if (options) {\n aggregatorOptions = { ...aggregatorOptions, ...clean(options) };\n }\n\n return { name, aggregate: aggregatorOptions.aggregate };\n}\n\nexport const isAggregator = <U extends Output = Output>(obj: any): obj is Aggregator<U> => {\n return obj !== undefined && obj !== null && typeof obj === 'object' && typeof obj.name === 'string' && typeof obj.aggregate === 'function';\n}\n\nexport const validateAggregator = (item: any, coordinates?: string[]): Array<{ coordinates: string[], error: string }> => {\n const errors: Array<{ coordinates: string[], error: string }> = [];\n const currentCoordinates = [...(coordinates || []), 'Aggregator'];\n\n if (item === undefined || item === null) {\n errors.push({ coordinates: [...currentCoordinates], error: 'Aggregator is undefined or null.' });\n return errors;\n }\n\n if (item.name === undefined || typeof item.name !== 'string') {\n errors.push({ coordinates: [...currentCoordinates], error: 'Aggregator name is undefined or not a string.' });\n }\n\n currentCoordinates.push(`Aggregator: ${item.name}`);\n\n if (item.aggregate === undefined || typeof item.aggregate !== 'function') {\n errors.push({ coordinates: [...currentCoordinates], error: 'Aggregator aggregate is undefined or not a function.' });\n }\n\n return errors;\n}"],"names":["DEFAULT_AGGREGATOR_OPTIONS","aggregate","input","status","output","createAggregator","name","options","aggregatorOptions","clean","isAggregator","obj","undefined"],"mappings":";;MA4BaA,0BAAAA,GAAiE;AAC1EC,IAAAA,SAAAA,EAAW,OAAOC,KAAAA,GAAAA;QACd,OAAO;YAAEC,MAAAA,EAAQ,OAAA;YAASC,MAAAA,EAAQF;AAAM,SAAA;AAC5C,IAAA;AACJ;AAEO,MAAMG,gBAAAA,GAAmB,CAC5BC,IAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,IAAIC,iBAAAA,GAA6C;AAAE,QAAA,GAAGR;AAA2B,KAAA;AACjF,IAAA,IAAIO,OAAAA,EAAS;QACTC,iBAAAA,GAAoB;AAAE,YAAA,GAAGA,iBAAiB;AAAE,YAAA,GAAGC,MAAMF,OAAAA;AAAS,SAAA;AAClE,IAAA;IAEA,OAAO;AAAED,QAAAA,IAAAA;AAAML,QAAAA,SAAAA,EAAWO,kBAAkBP;AAAU,KAAA;AAC1D;AAEO,MAAMS,eAAe,CAA4BC,GAAAA,GAAAA;AACpD,IAAA,OAAOA,GAAAA,KAAQC,SAAAA,IAAaD,GAAAA,KAAQ,IAAA,IAAQ,OAAOA,GAAAA,KAAQ,QAAA,IAAY,OAAOA,GAAAA,CAAIL,IAAI,KAAK,QAAA,IAAY,OAAOK,GAAAA,CAAIV,SAAS,KAAK,UAAA;AACpI;;;;"}
1
+ {"version":3,"file":"aggregator.js","sources":["../src/aggregator.ts"],"sourcesContent":["import { clean } from \"./util/general\";\nimport { Context } from \"./context\";\nimport { Input } from \"./input\";\nimport { Output } from \"./output\";\n\n/**\n * Defines the possible outcomes of an aggregation operation.\n * It can either be 'Ready' with an output, or 'NotYetReady'.\n */\nexport type AggregationResult<U extends Output = Output> =\n | { status: 'Ready'; output: U }\n | { status: 'NotYetReady' };\n\n/**\n * Aggregation method that processes inputs and returns an aggregation result.\n *\n * IMPORTANT: Aggregators should store their state in the context, not in\n * module-level or closure variables, to ensure proper isolation between\n * concurrent process executions.\n *\n * Pattern:\n * ```typescript\n * const aggregator = createAggregator('MyAgg', {\n * aggregate: async (input, context) => {\n * if (!context.myAggState) {\n * context.myAggState = { count: 0, accumulated: 0 };\n * }\n * const state = context.myAggState;\n * state.count++;\n * state.accumulated += input.value;\n * if (state.count === 2) {\n * return { status: 'Ready', output: { result: state.accumulated } };\n * }\n * return { status: 'NotYetReady' };\n * }\n * });\n * ```\n */\nexport type AggregateMethod<U extends Output = Output, C extends Context = Context> = (\n input: Input,\n context: C\n) => Promise<Readonly<AggregationResult<U>>>;\n\n\nexport interface Aggregator<U extends Output = Output, C extends Context = Context> {\n name: string;\n aggregate: AggregateMethod<U, C>;\n}\n\nexport interface AggregatorOptions<U extends Output = Output, C extends Context = Context> {\n aggregate: AggregateMethod<U, C>;\n}\n\nexport const DEFAULT_AGGREGATOR_OPTIONS: AggregatorOptions<Output, Context> = {\n aggregate: async (input) => {\n return { status: 'Ready', output: input };\n }\n}\n\nexport const createAggregator = <U extends Output = Output, C extends Context = Context>(\n name: string,\n options?: Partial<AggregatorOptions<U, C>>\n): Readonly<Aggregator<U, C>> => {\n let aggregatorOptions: AggregatorOptions<U, C> = { ...DEFAULT_AGGREGATOR_OPTIONS } as unknown as AggregatorOptions<U, C>;\n if (options) {\n aggregatorOptions = { ...aggregatorOptions, ...clean(options) };\n }\n\n return { name, aggregate: aggregatorOptions.aggregate };\n}\n\nexport const isAggregator = <U extends Output = Output>(obj: any): obj is Aggregator<U> => {\n return obj !== undefined && obj !== null && typeof obj === 'object' && typeof obj.name === 'string' && typeof obj.aggregate === 'function';\n}\n\nexport const validateAggregator = (item: any, coordinates?: string[]): Array<{ coordinates: string[], error: string }> => {\n const errors: Array<{ coordinates: string[], error: string }> = [];\n const currentCoordinates = [...(coordinates || []), 'Aggregator'];\n\n if (item === undefined || item === null) {\n errors.push({ coordinates: [...currentCoordinates], error: 'Aggregator is undefined or null.' });\n return errors;\n }\n\n if (item.name === undefined || typeof item.name !== 'string') {\n errors.push({ coordinates: [...currentCoordinates], error: 'Aggregator name is undefined or not a string.' });\n }\n\n currentCoordinates.push(`Aggregator: ${item.name}`);\n\n if (item.aggregate === undefined || typeof item.aggregate !== 'function') {\n errors.push({ coordinates: [...currentCoordinates], error: 'Aggregator aggregate is undefined or not a function.' });\n }\n\n return errors;\n}\n"],"names":["DEFAULT_AGGREGATOR_OPTIONS","aggregate","input","status","output","createAggregator","name","options","aggregatorOptions","clean","isAggregator","obj","undefined"],"mappings":";;MAqDaA,0BAAAA,GAAiE;AAC1EC,IAAAA,SAAAA,EAAW,OAAOC,KAAAA,GAAAA;QACd,OAAO;YAAEC,MAAAA,EAAQ,OAAA;YAASC,MAAAA,EAAQF;AAAM,SAAA;AAC5C,IAAA;AACJ;AAEO,MAAMG,gBAAAA,GAAmB,CAC5BC,IAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,IAAIC,iBAAAA,GAA6C;AAAE,QAAA,GAAGR;AAA2B,KAAA;AACjF,IAAA,IAAIO,OAAAA,EAAS;QACTC,iBAAAA,GAAoB;AAAE,YAAA,GAAGA,iBAAiB;AAAE,YAAA,GAAGC,MAAMF,OAAAA;AAAS,SAAA;AAClE,IAAA;IAEA,OAAO;AAAED,QAAAA,IAAAA;AAAML,QAAAA,SAAAA,EAAWO,kBAAkBP;AAAU,KAAA;AAC1D;AAEO,MAAMS,eAAe,CAA4BC,GAAAA,GAAAA;AACpD,IAAA,OAAOA,GAAAA,KAAQC,SAAAA,IAAaD,GAAAA,KAAQ,IAAA,IAAQ,OAAOA,GAAAA,KAAQ,QAAA,IAAY,OAAOA,GAAAA,CAAIL,IAAI,KAAK,QAAA,IAAY,OAAOK,GAAAA,CAAIV,SAAS,KAAK,UAAA;AACpI;;;;"}
@@ -1,4 +1,4 @@
1
- import { Context } from 'vm';
1
+ import { Context } from '../context';
2
2
  import { EventState } from '../event';
3
3
  import { Input } from '../input';
4
4
  import { AggregatorNode } from '../node/aggregatornode';
@@ -1 +1 @@
1
- {"version":3,"file":"aggregator.js","sources":["../../src/execution/aggregator.ts"],"sourcesContent":["import { Context } from 'vm';\nimport { createAggregatorEvent, EventState } from '../event';\nimport { Input } from '../input';\nimport { AggregatorNode } from '../node/aggregatornode';\nimport { Output } from '../output';\nimport { dispatchEvent } from './event';\n\nexport class Deferred<T> {\n promise: Promise<T>;\n resolve!: (value: T | PromiseLike<T>) => void;\n reject!: (reason?: any) => void;\n\n constructor() {\n this.promise = new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\n\nexport interface AggregatorState {\n aggregatorDeferreds: Map<string, Deferred<Output>>;\n registerPendingAggregator: (nodeId: string) => Deferred<Output>;\n resolvePendingAggregator: (nodeId: string, output: Output) => void;\n getPendingAggregator: (nodeId: string) => Deferred<Output> | undefined;\n pendingAggregatorIds: () => string[];\n}\n\nexport function createAggregatorState(): AggregatorState {\n return {\n aggregatorDeferreds: new Map<string, Deferred<Output>>(),\n registerPendingAggregator(nodeId: string) {\n let deferred = this.aggregatorDeferreds.get(nodeId);\n if (!deferred) {\n deferred = new Deferred<Output>();\n this.aggregatorDeferreds.set(nodeId, deferred);\n }\n return deferred;\n },\n resolvePendingAggregator(nodeId: string, output: Output) {\n const deferred = this.aggregatorDeferreds.get(nodeId);\n if (deferred) {\n deferred.resolve(output);\n this.aggregatorDeferreds.delete(nodeId);\n }\n },\n getPendingAggregator(nodeId: string) {\n return this.aggregatorDeferreds.get(nodeId);\n },\n pendingAggregatorIds() {\n return Array.from(this.aggregatorDeferreds.keys());\n },\n };\n}\n\nexport async function executeAggregatorNode(\n nodeId: string,\n node: AggregatorNode,\n input: Input,\n state: AggregatorState & { context: Context, eventState: EventState<Context> }\n): Promise<Output> {\n\n dispatchEvent(\n state.eventState,\n createAggregatorEvent(nodeId, 'start', node.aggregator, { input }),\n state.context\n );\n\n let deferred = state.getPendingAggregator(nodeId);\n const aggregationResult = await node.aggregator.aggregate(input, state.context);\n\n dispatchEvent(state.eventState, createAggregatorEvent(nodeId, 'aggregate', node.aggregator, { input, result: aggregationResult }), state.context);\n\n if (aggregationResult.status === 'Ready') {\n const output = aggregationResult.output;\n\n dispatchEvent(state.eventState, createAggregatorEvent(nodeId, 'ready', node.aggregator, { output }), state.context);\n\n state.resolvePendingAggregator(nodeId, output);\n return output;\n }\n\n dispatchEvent(state.eventState, createAggregatorEvent(nodeId, 'defer', node.aggregator, { input, result: aggregationResult }), state.context);\n deferred = deferred ?? state.registerPendingAggregator(nodeId);\n return deferred.promise;\n}\n"],"names":["Deferred","promise","resolve","reject","Promise","createAggregatorState","aggregatorDeferreds","Map","registerPendingAggregator","nodeId","deferred","get","set","resolvePendingAggregator","output","delete","getPendingAggregator","pendingAggregatorIds","Array","from","keys","executeAggregatorNode","node","input","state","dispatchEvent","eventState","createAggregatorEvent","aggregator","context","aggregationResult","aggregate","result","status"],"mappings":"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"}
1
+ {"version":3,"file":"aggregator.js","sources":["../../src/execution/aggregator.ts"],"sourcesContent":["import { Context } from '../context';\nimport { createAggregatorEvent, EventState } from '../event';\nimport { Input } from '../input';\nimport { AggregatorNode } from '../node/aggregatornode';\nimport { Output } from '../output';\nimport { dispatchEvent } from './event';\n\nexport class Deferred<T> {\n promise: Promise<T>;\n resolve!: (value: T | PromiseLike<T>) => void;\n reject!: (reason?: any) => void;\n\n constructor() {\n this.promise = new Promise<T>((resolve, reject) => {\n this.resolve = resolve;\n this.reject = reject;\n });\n }\n}\n\nexport interface AggregatorState {\n aggregatorDeferreds: Map<string, Deferred<Output>>;\n registerPendingAggregator: (nodeId: string) => Deferred<Output>;\n resolvePendingAggregator: (nodeId: string, output: Output) => void;\n getPendingAggregator: (nodeId: string) => Deferred<Output> | undefined;\n pendingAggregatorIds: () => string[];\n}\n\nexport function createAggregatorState(): AggregatorState {\n return {\n aggregatorDeferreds: new Map<string, Deferred<Output>>(),\n registerPendingAggregator(nodeId: string) {\n let deferred = this.aggregatorDeferreds.get(nodeId);\n if (!deferred) {\n deferred = new Deferred<Output>();\n this.aggregatorDeferreds.set(nodeId, deferred);\n }\n return deferred;\n },\n resolvePendingAggregator(nodeId: string, output: Output) {\n const deferred = this.aggregatorDeferreds.get(nodeId);\n if (deferred) {\n deferred.resolve(output);\n this.aggregatorDeferreds.delete(nodeId);\n }\n },\n getPendingAggregator(nodeId: string) {\n return this.aggregatorDeferreds.get(nodeId);\n },\n pendingAggregatorIds() {\n return Array.from(this.aggregatorDeferreds.keys());\n },\n };\n}\n\nexport async function executeAggregatorNode(\n nodeId: string,\n node: AggregatorNode,\n input: Input,\n state: AggregatorState & { context: Context, eventState: EventState<Context> }\n): Promise<Output> {\n\n dispatchEvent(\n state.eventState,\n createAggregatorEvent(nodeId, 'start', node.aggregator, { input }),\n state.context\n );\n\n let deferred = state.getPendingAggregator(nodeId);\n const aggregationResult = await node.aggregator.aggregate(input, state.context);\n\n dispatchEvent(state.eventState, createAggregatorEvent(nodeId, 'aggregate', node.aggregator, { input, result: aggregationResult }), state.context);\n\n if (aggregationResult.status === 'Ready') {\n const output = aggregationResult.output;\n\n dispatchEvent(state.eventState, createAggregatorEvent(nodeId, 'ready', node.aggregator, { output }), state.context);\n\n state.resolvePendingAggregator(nodeId, output);\n return output;\n }\n\n dispatchEvent(state.eventState, createAggregatorEvent(nodeId, 'defer', node.aggregator, { input, result: aggregationResult }), state.context);\n deferred = deferred ?? state.registerPendingAggregator(nodeId);\n return deferred.promise;\n}\n"],"names":["Deferred","promise","resolve","reject","Promise","createAggregatorState","aggregatorDeferreds","Map","registerPendingAggregator","nodeId","deferred","get","set","resolvePendingAggregator","output","delete","getPendingAggregator","pendingAggregatorIds","Array","from","keys","executeAggregatorNode","node","input","state","dispatchEvent","eventState","createAggregatorEvent","aggregator","context","aggregationResult","aggregate","result","status"],"mappings":"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"}
@@ -26,17 +26,26 @@ async function handleNextStep(nodeOutput, nodeId, next, state) {
26
26
  await handleNextStep(nodeOutput, decision.id, decisionOutcome, state); // outcome processed, decision.id is context for next step if it's an error source. The original nodeId is implicitly the source of this decision.
27
27
  await dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'end', decision), state.context);
28
28
  } catch (decisionError) {
29
+ const errorMessage = `Decision error on '${decision.id}' from node '${nodeId}': ${decisionError.message}`;
29
30
  // eslint-disable-next-line no-console
30
- console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR] Error in decision ${decision.id} from node ${nodeId}:`, {
31
+ console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR]`, {
32
+ error: errorMessage,
31
33
  decisionError,
32
- nodeId,
33
- decisionId: decision.id
34
+ decisionId: decision.id,
35
+ sourceNodeId: nodeId
34
36
  });
35
37
  state.errors.push({
36
38
  nodeId: decision.id,
37
- message: decisionError.message
39
+ message: errorMessage,
40
+ details: {
41
+ sourceNodeId: nodeId,
42
+ originalError: decisionError.message
43
+ }
38
44
  });
39
- // Note: 'end' event for this decision path is skipped due to error.
45
+ // Note: Decision errors are logged but do not halt the process.
46
+ // This allows other parallel decisions to continue executing.
47
+ // If you need decision errors to halt execution, consider throwing here
48
+ // or checking state.errors after process completion.
40
49
  }
41
50
  }
42
51
  } else if (Array.isArray(next) && next.length > 0 && next.every(isConnection)) {
@@ -62,15 +71,24 @@ async function handleNextStep(nodeOutput, nodeId, next, state) {
62
71
  state.context = nextContext;
63
72
  //console.log('[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_SUCCESS]', { nodeId, targetNodeId: connection.targetNodeId, nextInput, nextContext });
64
73
  } catch (transformError) {
74
+ const errorMessage = `Transform error on connection '${connection.id}' from node '${nodeId}' to '${connection.targetNodeId}': ${transformError.message}`;
65
75
  // eslint-disable-next-line no-console
66
- console.error(`[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_ERROR] Error in transform for connection from ${nodeId} to ${connection.targetNodeId}:`, {
76
+ console.error(`[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_ERROR]`, {
77
+ error: errorMessage,
67
78
  transformError,
68
- nodeId,
79
+ connectionId: connection.id,
80
+ sourceNodeId: nodeId,
69
81
  targetNodeId: connection.targetNodeId
70
82
  });
83
+ // Store error with connection ID for better traceability
71
84
  state.errors.push({
72
- nodeId: connection.targetNodeId,
73
- message: transformError.message
85
+ nodeId: connection.id,
86
+ message: errorMessage,
87
+ details: {
88
+ sourceNodeId: nodeId,
89
+ targetNodeId: connection.targetNodeId,
90
+ originalError: transformError.message
91
+ }
74
92
  });
75
93
  continue;
76
94
  }
@@ -90,18 +108,21 @@ async function handleNextStep(nodeOutput, nodeId, next, state) {
90
108
  const result = nodeOutput;
91
109
  if (termination.terminate) {
92
110
  //console.log('[_HANDLE_NEXT_STEP_TERMINATION_CALLING_TERMINATE_FN]', { nodeId, terminationId: termination.id });
93
- termination.terminate(nodeOutput, state.context);
111
+ await termination.terminate(nodeOutput, state.context);
94
112
  await dispatchEvent(state.eventState, createTerminationEvent(nodeId, 'terminate', termination, {
95
113
  output: nodeOutput
96
114
  }), state.context);
97
115
  }
98
116
  state.results[termination.id] = result;
99
117
  } else if (Array.isArray(next) && next.length === 0) {
100
- // Empty array, potentially from a Decision that leads to no connections or an empty Connection array.
101
- // This could mean an implicit termination for this path or simply no further action from this branch.
102
- // If it's considered an end state for the path, store the result.
118
+ // Empty array from a Decision means no path should be taken from this node.
119
+ // This is treated as an implicit termination, storing the result with a generated key.
120
+ // Note: This behavior means a Decision can dynamically terminate a process path.
103
121
  const result = nodeOutput;
104
- state.results[nodeId] = result; // Using nodeId as the key for this implicit termination
122
+ const implicitTerminationId = `${nodeId}_implicit_end`;
123
+ state.results[implicitTerminationId] = result;
124
+ // eslint-disable-next-line no-console
125
+ console.warn(`[_HANDLE_NEXT_STEP_IMPLICIT_TERMINATION] Node ${nodeId} received empty next array, treating as implicit termination with id: ${implicitTerminationId}`);
105
126
  } else {
106
127
  // If there is no next (e.g. next is undefined or null after a decision), or it's an unhandled type.
107
128
  // Consider this an end state and store the result with the nodeId
@@ -1 +1 @@
1
- {"version":3,"file":"next.js","sources":["../../src/execution/next.ts"],"sourcesContent":["import { executeNode } from './node';\nimport { ExecutionState } from './process';\nimport { Context } from '../context';\nimport { Input } from '../input';\nimport { Output } from '../output';\nimport { Connection, isConnection } from '../transition/connection';\nimport { Decision, isDecision } from '../transition/decision';\nimport { isTermination, Termination } from '../transition/termination';\nimport { dispatchEvent } from './event';\nimport { createConnectionEvent, createDecisionEvent, createTerminationEvent } from '../event';\n\n// Helper function to handle the next step in the process\nexport async function handleNextStep(\n nodeOutput: Output,\n nodeId: string,\n next: Termination | Connection[] | Decision[], // Can be Termination, Connection[], or Decision[] - will be narrowed down\n state: ExecutionState\n): Promise<void> {\n\n //console.log('[_HANDLE_NEXT_STEP_START]', { nodeId, nodeOutput, nextType: next && next.constructor ? next.constructor.name : typeof next, next });\n\n if (Array.isArray(next) && next.length > 0 && next.every(isDecision)) {\n const decisions = next as Decision<Output, Context>[];\n // console.log('[_HANDLE_NEXT_STEP_DECISIONS]', { nodeId, count: decisions.length, decisions });\n\n for (const decision of decisions) {\n // console.log('[_HANDLE_NEXT_STEP_DECISION_PROCESSING]', { nodeId, decisionId: decision.id, decision });\n await dispatchEvent(\n state.eventState,\n createDecisionEvent(nodeId, 'start', decision, { output: nodeOutput }),\n state.context\n );\n try {\n const decisionOutcome = await decision.decide(nodeOutput, state.context);\n await dispatchEvent(\n state.eventState,\n createDecisionEvent(nodeId, 'decide', decision, { output: nodeOutput, result: decisionOutcome }),\n state.context\n );\n // console.log('[_HANDLE_NEXT_STEP_DECISION_OUTCOME]', { nodeId, decisionId: decision.id, decisionOutcome });\n await handleNextStep(nodeOutput, decision.id, decisionOutcome, state); // outcome processed, decision.id is context for next step if it's an error source. The original nodeId is implicitly the source of this decision.\n await dispatchEvent(\n state.eventState,\n createDecisionEvent(nodeId, 'end', decision), // Fire 'end' event after successful processing\n state.context\n );\n } catch (decisionError: any) {\n // eslint-disable-next-line no-console\n console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR] Error in decision ${decision.id} from node ${nodeId}:`, { decisionError, nodeId, decisionId: decision.id });\n state.errors.push({ nodeId: decision.id, message: decisionError.message });\n // Note: 'end' event for this decision path is skipped due to error.\n }\n }\n } else if (Array.isArray(next) && next.length > 0 && next.every(isConnection)) {\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTIONS]', { nodeId, count: next.length, connections: next });\n const connections = next as Connection<Output, Context>[];\n const nextPhasePromises: Promise<Output>[] = [];\n for (const connection of connections) {\n\n let nextInput = nodeOutput as Input;\n let nextContext = state.context;\n\n await dispatchEvent(\n state.eventState,\n createConnectionEvent(nodeId, 'start', connection, { input: nextInput }),\n state.context\n );\n\n if (connection.transform) {\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_START]', { nodeId, targetNodeId: connection.targetNodeId });\n try {\n const context = state.context;\n [nextInput, nextContext] = await connection.transform(nodeOutput, context);\n await dispatchEvent(\n state.eventState,\n createConnectionEvent(nodeId, 'transform', connection, { input: nextInput, output: nodeOutput, context: nextContext }),\n state.context\n );\n state.context = nextContext;\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_SUCCESS]', { nodeId, targetNodeId: connection.targetNodeId, nextInput, nextContext });\n } catch (transformError: any) {\n // eslint-disable-next-line no-console\n console.error(`[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_ERROR] Error in transform for connection from ${nodeId} to ${connection.targetNodeId}:`, { transformError, nodeId, targetNodeId: connection.targetNodeId });\n state.errors.push({ nodeId: connection.targetNodeId, message: transformError.message });\n // Note: 'end' event for this connection path is skipped due to error in transform\n continue;\n }\n }\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTION_EXECUTE_TARGET]', { nodeId, targetNodeId: connection.targetNodeId, nextInput });\n nextPhasePromises.push(executeNode(connection.targetNodeId, nextInput, state));\n await dispatchEvent(\n state.eventState,\n createConnectionEvent(nodeId, 'end', connection), // Fire 'end' event after initiating next step\n state.context\n );\n }\n // Optional: await Promise.all(nextPhasePromises); // Current design relies on executeProcess waiting on activeExecutions\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTIONS_PROMISES_PUSHED]', { nodeId, count: nextPhasePromises.length });\n } else if (isTermination(next)) {\n\n //console.log('[_HANDLE_NEXT_STEP_TERMINATION]', { nodeId, termination: next });\n const termination = next as Termination<Output, Context>;\n await dispatchEvent(\n state.eventState,\n createTerminationEvent(nodeId, 'start', termination, { output: nodeOutput }),\n state.context\n );\n const result: Output = nodeOutput;\n if (termination.terminate) {\n\n //console.log('[_HANDLE_NEXT_STEP_TERMINATION_CALLING_TERMINATE_FN]', { nodeId, terminationId: termination.id });\n termination.terminate(nodeOutput, state.context);\n await dispatchEvent(\n state.eventState,\n createTerminationEvent(nodeId, 'terminate', termination, { output: nodeOutput }),\n state.context\n );\n }\n\n state.results[termination.id] = result;\n } else if (Array.isArray(next) && next.length === 0) {\n // Empty array, potentially from a Decision that leads to no connections or an empty Connection array.\n // This could mean an implicit termination for this path or simply no further action from this branch.\n // If it's considered an end state for the path, store the result.\n\n const result: Output = nodeOutput;\n state.results[nodeId] = result; // Using nodeId as the key for this implicit termination\n } else {\n // If there is no next (e.g. next is undefined or null after a decision), or it's an unhandled type.\n // Consider this an end state and store the result with the nodeId\n\n //console.log('[_HANDLE_NEXT_STEP_NO_NEXT_OR_UNHANDLED]', { nodeId, next, nodeOutput });\n const result: Output = nodeOutput;\n state.results[nodeId] = result;\n }\n\n //console.log('[_HANDLE_NEXT_STEP_END]', { nodeId });\n}\n"],"names":["handleNextStep","nodeOutput","nodeId","next","state","Array","isArray","length","every","isDecision","decisions","decision","dispatchEvent","eventState","createDecisionEvent","output","context","decisionOutcome","decide","result","id","decisionError","console","error","decisionId","errors","push","message","isConnection","connections","nextPhasePromises","connection","nextInput","nextContext","createConnectionEvent","input","transform","transformError","targetNodeId","executeNode","isTermination","termination","createTerminationEvent","terminate","results"],"mappings":"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"}
1
+ {"version":3,"file":"next.js","sources":["../../src/execution/next.ts"],"sourcesContent":["import { executeNode } from './node';\nimport { ExecutionState } from './process';\nimport { Context } from '../context';\nimport { Input } from '../input';\nimport { Output } from '../output';\nimport { Connection, isConnection } from '../transition/connection';\nimport { Decision, isDecision } from '../transition/decision';\nimport { isTermination, Termination } from '../transition/termination';\nimport { dispatchEvent } from './event';\nimport { createConnectionEvent, createDecisionEvent, createTerminationEvent } from '../event';\n\n// Helper function to handle the next step in the process\nexport async function handleNextStep(\n nodeOutput: Output,\n nodeId: string,\n next: Termination | Connection[] | Decision[], // Can be Termination, Connection[], or Decision[] - will be narrowed down\n state: ExecutionState\n): Promise<void> {\n\n //console.log('[_HANDLE_NEXT_STEP_START]', { nodeId, nodeOutput, nextType: next && next.constructor ? next.constructor.name : typeof next, next });\n\n if (Array.isArray(next) && next.length > 0 && next.every(isDecision)) {\n const decisions = next as Decision<Output, Context>[];\n // console.log('[_HANDLE_NEXT_STEP_DECISIONS]', { nodeId, count: decisions.length, decisions });\n\n for (const decision of decisions) {\n // console.log('[_HANDLE_NEXT_STEP_DECISION_PROCESSING]', { nodeId, decisionId: decision.id, decision });\n await dispatchEvent(\n state.eventState,\n createDecisionEvent(nodeId, 'start', decision, { output: nodeOutput }),\n state.context\n );\n try {\n const decisionOutcome = await decision.decide(nodeOutput, state.context);\n await dispatchEvent(\n state.eventState,\n createDecisionEvent(nodeId, 'decide', decision, { output: nodeOutput, result: decisionOutcome }),\n state.context\n );\n // console.log('[_HANDLE_NEXT_STEP_DECISION_OUTCOME]', { nodeId, decisionId: decision.id, decisionOutcome });\n await handleNextStep(nodeOutput, decision.id, decisionOutcome, state); // outcome processed, decision.id is context for next step if it's an error source. The original nodeId is implicitly the source of this decision.\n await dispatchEvent(\n state.eventState,\n createDecisionEvent(nodeId, 'end', decision), // Fire 'end' event after successful processing\n state.context\n );\n } catch (decisionError: any) {\n const errorMessage = `Decision error on '${decision.id}' from node '${nodeId}': ${decisionError.message}`;\n // eslint-disable-next-line no-console\n console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR]`, {\n error: errorMessage,\n decisionError,\n decisionId: decision.id,\n sourceNodeId: nodeId\n });\n state.errors.push({\n nodeId: decision.id,\n message: errorMessage,\n details: { sourceNodeId: nodeId, originalError: decisionError.message }\n });\n // Note: Decision errors are logged but do not halt the process.\n // This allows other parallel decisions to continue executing.\n // If you need decision errors to halt execution, consider throwing here\n // or checking state.errors after process completion.\n }\n }\n } else if (Array.isArray(next) && next.length > 0 && next.every(isConnection)) {\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTIONS]', { nodeId, count: next.length, connections: next });\n const connections = next as Connection<Output, Context>[];\n const nextPhasePromises: Promise<Output>[] = [];\n for (const connection of connections) {\n\n let nextInput = nodeOutput as Input;\n let nextContext = state.context;\n\n await dispatchEvent(\n state.eventState,\n createConnectionEvent(nodeId, 'start', connection, { input: nextInput }),\n state.context\n );\n\n if (connection.transform) {\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_START]', { nodeId, targetNodeId: connection.targetNodeId });\n try {\n const context = state.context;\n [nextInput, nextContext] = await connection.transform(nodeOutput, context);\n await dispatchEvent(\n state.eventState,\n createConnectionEvent(nodeId, 'transform', connection, { input: nextInput, output: nodeOutput, context: nextContext }),\n state.context\n );\n state.context = nextContext;\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_SUCCESS]', { nodeId, targetNodeId: connection.targetNodeId, nextInput, nextContext });\n } catch (transformError: any) {\n const errorMessage = `Transform error on connection '${connection.id}' from node '${nodeId}' to '${connection.targetNodeId}': ${transformError.message}`;\n // eslint-disable-next-line no-console\n console.error(`[_HANDLE_NEXT_STEP_CONNECTION_TRANSFORM_ERROR]`, {\n error: errorMessage,\n transformError,\n connectionId: connection.id,\n sourceNodeId: nodeId,\n targetNodeId: connection.targetNodeId\n });\n // Store error with connection ID for better traceability\n state.errors.push({\n nodeId: connection.id,\n message: errorMessage,\n details: { sourceNodeId: nodeId, targetNodeId: connection.targetNodeId, originalError: transformError.message }\n });\n // Skip this connection path - other parallel connections will still execute\n // This allows partial success in fan-out scenarios\n continue;\n }\n }\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTION_EXECUTE_TARGET]', { nodeId, targetNodeId: connection.targetNodeId, nextInput });\n nextPhasePromises.push(executeNode(connection.targetNodeId, nextInput, state));\n await dispatchEvent(\n state.eventState,\n createConnectionEvent(nodeId, 'end', connection), // Fire 'end' event after initiating next step\n state.context\n );\n }\n // Optional: await Promise.all(nextPhasePromises); // Current design relies on executeProcess waiting on activeExecutions\n\n //console.log('[_HANDLE_NEXT_STEP_CONNECTIONS_PROMISES_PUSHED]', { nodeId, count: nextPhasePromises.length });\n } else if (isTermination(next)) {\n\n //console.log('[_HANDLE_NEXT_STEP_TERMINATION]', { nodeId, termination: next });\n const termination = next as Termination<Output, Context>;\n await dispatchEvent(\n state.eventState,\n createTerminationEvent(nodeId, 'start', termination, { output: nodeOutput }),\n state.context\n );\n const result: Output = nodeOutput;\n if (termination.terminate) {\n\n //console.log('[_HANDLE_NEXT_STEP_TERMINATION_CALLING_TERMINATE_FN]', { nodeId, terminationId: termination.id });\n await termination.terminate(nodeOutput, state.context);\n await dispatchEvent(\n state.eventState,\n createTerminationEvent(nodeId, 'terminate', termination, { output: nodeOutput }),\n state.context\n );\n }\n\n state.results[termination.id] = result;\n } else if (Array.isArray(next) && next.length === 0) {\n // Empty array from a Decision means no path should be taken from this node.\n // This is treated as an implicit termination, storing the result with a generated key.\n // Note: This behavior means a Decision can dynamically terminate a process path.\n\n const result: Output = nodeOutput;\n const implicitTerminationId = `${nodeId}_implicit_end`;\n state.results[implicitTerminationId] = result;\n\n // eslint-disable-next-line no-console\n console.warn(`[_HANDLE_NEXT_STEP_IMPLICIT_TERMINATION] Node ${nodeId} received empty next array, treating as implicit termination with id: ${implicitTerminationId}`);\n } else {\n // If there is no next (e.g. next is undefined or null after a decision), or it's an unhandled type.\n // Consider this an end state and store the result with the nodeId\n\n //console.log('[_HANDLE_NEXT_STEP_NO_NEXT_OR_UNHANDLED]', { nodeId, next, nodeOutput });\n const result: Output = nodeOutput;\n state.results[nodeId] = result;\n }\n\n //console.log('[_HANDLE_NEXT_STEP_END]', { nodeId });\n}\n"],"names":["handleNextStep","nodeOutput","nodeId","next","state","Array","isArray","length","every","isDecision","decisions","decision","dispatchEvent","eventState","createDecisionEvent","output","context","decisionOutcome","decide","result","id","decisionError","errorMessage","message","console","error","decisionId","sourceNodeId","errors","push","details","originalError","isConnection","connections","nextPhasePromises","connection","nextInput","nextContext","createConnectionEvent","input","transform","transformError","targetNodeId","connectionId","executeNode","isTermination","termination","createTerminationEvent","terminate","results","implicitTerminationId","warn"],"mappings":"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"}
@@ -106,15 +106,21 @@ async function executeNode(nodeId, input, state) {
106
106
  await handleNextStep(output, decision.id, decisionOutcome, state);
107
107
  dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'end', decision), state.context);
108
108
  } catch (decisionError) {
109
+ const errorMessage = `Decision error on '${decision.id}' for node '${nodeId}': ${decisionError.message}`;
109
110
  // eslint-disable-next-line no-console
110
- console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR] Error in decision ${decision.id} for node ${nodeId}:`, {
111
+ console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR]`, {
112
+ error: errorMessage,
111
113
  decisionError,
112
- nodeId,
113
- decisionId: decision.id
114
+ decisionId: decision.id,
115
+ sourceNodeId: nodeId
114
116
  });
115
117
  state.errors.push({
116
118
  nodeId: decision.id,
117
- message: decisionError.message
119
+ message: errorMessage,
120
+ details: {
121
+ sourceNodeId: nodeId,
122
+ originalError: decisionError.message
123
+ }
118
124
  });
119
125
  }
120
126
  })();
@@ -155,6 +161,14 @@ async function executeNode(nodeId, input, state) {
155
161
  nodeId,
156
162
  message: error.message
157
163
  });
164
+ // Clean up any pending aggregator deferred on error
165
+ if (state.aggregatorDeferreds.has(nodeId)) {
166
+ const deferred = state.aggregatorDeferreds.get(nodeId);
167
+ if (deferred) {
168
+ deferred.reject(error);
169
+ }
170
+ state.aggregatorDeferreds.delete(nodeId);
171
+ }
158
172
  throw error;
159
173
  } finally{
160
174
  //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_FINALLY]', { nodeId, hasAggregatorDeferred: state.aggregatorDeferreds.has(nodeId) });
@@ -1 +1 @@
1
- {"version":3,"file":"node.js","sources":["../../src/execution/node.ts"],"sourcesContent":["import { Context } from '../context';\nimport { createAggregatorNodeEvent, createPhaseNodeEvent } from '../event/node';\nimport { createDecisionEvent } from '../event/transition';\nimport { Input } from '../input';\nimport { AggregatorNode, isAggregatorNode } from '../node/aggregatornode';\nimport { isPhaseNode, PhaseNode } from '../node/phasenode';\nimport { Output } from '../output';\nimport { Connection } from '../transition/connection';\nimport { Decision, isDecision } from '../transition/decision';\nimport { Termination } from '../transition/termination';\nimport {\n executeAggregatorNode\n} from './aggregator';\nimport { dispatchEvent } from './event';\nimport { handleNextStep } from './next';\nimport { executePhase } from './phase';\nimport { ExecutionState } from './process';\n\nexport async function executeNode(\n nodeId: string,\n input: Input,\n state: ExecutionState\n): Promise<Output> {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_START]', { nodeId, input, phaseResultsKeys: Object.keys(state.phaseResults), activeExecutionsKeys: Array.from(state.activeExecutions.keys()), aggregatorDeferredsKeys: Array.from(state.aggregatorDeferreds.keys()) });\n\n // 1. Check if result is already cached in phaseResults (final output, node fully completed)\n if (state.phaseResults[nodeId]) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_CACHE_HIT_PHASERESULTS]', { nodeId, result: state.phaseResults[nodeId] });\n return state.phaseResults[nodeId];\n }\n\n const node = state.process.phases[nodeId] as PhaseNode | AggregatorNode;\n if (!node) {\n const error = new Error(`Node with ID \"${nodeId}\" not found.`);\n\n //console.error('[EXECUTE_NODE_RECURSIVE_NODE_NOT_FOUND]', { nodeId, error });\n state.errors.push({ nodeId, message: error.message });\n throw error;\n }\n\n //console.log('[EXECUTE_NODE_RECURSIVE_NODE_FOUND]', { nodeId, nodeType: node.constructor.name, node });\n\n // 2. Handle active/pending executions\n // If it's an aggregator that has a deferred promise, it means it's pending.\n // We need to re-evaluate it with the current input. The IIFE below will handle this.\n if (state.activeExecutions.has(nodeId) && !isAggregatorNode(node)) {\n // For non-aggregators, if already active, return the promise.\n // Aggregators will fall through to the IIFE to allow input processing.\n // The IIFE itself handles returning a shared deferred promise if needed.\n\n //console.log('[EXECUTE_NODE_RECURSIVE_ACTIVE_EXECUTION_HIT_NON_AGGREGATOR]', { nodeId });\n return state.activeExecutions.get(nodeId)!;\n }\n // If it IS an aggregator and state.activeExecutions.has(nodeId),\n // it means its deferred.promise might be in activeExecutions from a previous input that made it pending.\n // The IIFE logic below will correctly retrieve this deferred (if it exists and is still relevant)\n // from state.aggregatorDeferreds.get(nodeId) and use its promise, or process the input.\n\n\n //console.log('[EXECUTE_NODE_RECURSIVE_CONTINUING_TO_EXECUTION_LOGIC]', { nodeId, isActive: state.activeExecutions.has(nodeId), isAggregator: isAggregatorNode(node), hasDeferred: state.aggregatorDeferreds.has(nodeId) });\n // If it's an aggregator and it's pending (has a deferred), we fall through to re-execute its logic within the IIFE.\n // If it's the first call to any node, we fall through.\n\n // 3. Mark as active and execute (or re-evaluate pending aggregator)\n const executionPromise = (async (): Promise<Output> => {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_START]', { nodeId, input });\n try {\n let output: Output;\n\n if (isAggregatorNode(node)) {\n\n dispatchEvent(\n state.eventState,\n createAggregatorNodeEvent(nodeId, 'start', node, { input }),\n state.context\n );\n\n output = await executeAggregatorNode(nodeId, node, input, state);\n } else if (isPhaseNode(node)) {\n\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'start', node, { input }), state.context);\n\n if (node.prepare) {\n const [preparedInput, preparedContext] = await node.prepare(input, state.context);\n input = preparedInput;\n state.context = preparedContext;\n }\n\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'prepared', node, { input }), state.context);\n\n\n output = await executePhase(nodeId, node, input, state);\n\n if (node.process) {\n const [processedOutput, processedContext] = await node.process(output, state.context);\n output = processedOutput;\n state.context = processedContext;\n }\n\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'processed', node, { input, output }), state.context);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_PHASE_NODE_EXECUTE_END]', { nodeId, output });\n } else {\n const error = new Error(`Unknown or invalid node type for ID \"${nodeId}\". Expected PhaseNode or AggregatorNode.`);\n\n //console.error('[EXECUTE_NODE_RECURSIVE_UNKNOWN_NODE_TYPE]', { nodeId, node, error });\n throw error;\n }\n\n state.phaseResults[nodeId] = output; // Set final output once ready/executed\n\n //console.log('[EXECUTE_NODE_RECURSIVE_PHASE_RESULT_CACHED]', { nodeId, output });\n\n // 4. Handle next step\n if (node.next) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_HANDLING_NEXT_STEP]', { nodeId, nextType: node.next.constructor.name, next: node.next });\n if (Array.isArray(node.next) && node.next.length > 0 && node.next.every(isDecision)) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISIONS_FOUND]', { nodeId, count: node.next.length, decisions: node.next });\n const decisions = node.next as Decision<Output, Context>[];\n const decisionExecutionPromises: Promise<void>[] = [];\n for (const decision of decisions) {\n\n dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'start', decision, { output }), state.context);\n\n const decisionPromise = (async () => {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISION_EXECUTE_START]', { nodeId, decisionId: decision.id, output });\n try {\n const decisionOutcome = await decision.decide(output, state.context);\n dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'decide', decision, { output, result: decisionOutcome }), state.context);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISION_OUTCOME]', { nodeId, decisionId: decision.id, decisionOutcome });\n await handleNextStep(output, decision.id, decisionOutcome, state);\n dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'end', decision), state.context);\n } catch (decisionError: any) {\n // eslint-disable-next-line no-console\n console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR] Error in decision ${decision.id} for node ${nodeId}:`, { decisionError, nodeId, decisionId: decision.id });\n state.errors.push({ nodeId: decision.id, message: decisionError.message });\n }\n })();\n decisionExecutionPromises.push(decisionPromise);\n }\n\n //console.log('[EXECUTE_NODE_RECURSIVE_WAITING_FOR_DECISIONS]', { nodeId, count: decisionExecutionPromises.length });\n await Promise.all(decisionExecutionPromises);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISIONS_COMPLETE]', { nodeId });\n } else {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_CALLING_HANDLE_NEXT_STEP_FOR_NON_DECISION]', { nodeId, next: node.next });\n await handleNextStep(output, nodeId, node.next as Termination<Output, Context> | Connection<Output, Context>[] | Decision<Output, Context>[], state);\n }\n } else {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_NO_NEXT_NODE_IMPLICIT_TERMINATION]', { nodeId, output });\n const result: Output = output;\n state.results[nodeId] = result;\n }\n\n if (isPhaseNode(node)) {\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'end', node, { input, output }), state.context);\n } else {\n dispatchEvent(state.eventState, createAggregatorNodeEvent(nodeId, 'end', node, { input, output }), state.context);\n }\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_RETURNING_OUTPUT]', { nodeId, output });\n return output;\n } catch (error: any) {\n // eslint-disable-next-line no-console\n console.error(`[EXECUTE_NODE_RECURSIVE_IIFE_ERROR] Error executing node ${nodeId}:`, { error, nodeId });\n state.errors.push({ nodeId, message: error.message });\n throw error;\n } finally {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_FINALLY]', { nodeId, hasAggregatorDeferred: state.aggregatorDeferreds.has(nodeId) });\n // If a node completed (not pending via deferred mechanism) or an error occurred.\n // An aggregator that is still pending (has a deferred) should keep its promise in activeExecutions.\n if (!state.aggregatorDeferreds.has(nodeId)) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_FINALLY_DELETING_ACTIVE_EXECUTION]', { nodeId });\n state.activeExecutions.delete(nodeId);\n }\n }\n })();\n\n // Store the promise from the IIFE.\n // If it's an aggregator that went pending, executionPromise IS deferred.promise.\n // If it's an aggregator that became ready, executionPromise is a promise resolving to its output.\n // If it's a phase node, executionPromise is a promise resolving to its output.\n\n //console.log('[EXECUTE_NODE_RECURSIVE_SETTING_ACTIVE_EXECUTION]', { nodeId });\n state.activeExecutions.set(nodeId, executionPromise);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_END_RETURNING_PROMISE]', { nodeId });\n return executionPromise;\n\n}\n\n"],"names":["executeNode","nodeId","input","state","phaseResults","node","process","phases","error","Error","errors","push","message","activeExecutions","has","isAggregatorNode","get","executionPromise","output","dispatchEvent","eventState","createAggregatorNodeEvent","context","executeAggregatorNode","isPhaseNode","createPhaseNodeEvent","prepare","preparedInput","preparedContext","executePhase","processedOutput","processedContext","next","Array","isArray","length","every","isDecision","decisions","decisionExecutionPromises","decision","createDecisionEvent","decisionPromise","decisionOutcome","decide","result","handleNextStep","id","decisionError","console","decisionId","Promise","all","results","aggregatorDeferreds","delete","set"],"mappings":"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"}
1
+ {"version":3,"file":"node.js","sources":["../../src/execution/node.ts"],"sourcesContent":["import { Context } from '../context';\nimport { createAggregatorNodeEvent, createPhaseNodeEvent } from '../event/node';\nimport { createDecisionEvent } from '../event/transition';\nimport { Input } from '../input';\nimport { AggregatorNode, isAggregatorNode } from '../node/aggregatornode';\nimport { isPhaseNode, PhaseNode } from '../node/phasenode';\nimport { Output } from '../output';\nimport { Connection } from '../transition/connection';\nimport { Decision, isDecision } from '../transition/decision';\nimport { Termination } from '../transition/termination';\nimport {\n executeAggregatorNode\n} from './aggregator';\nimport { dispatchEvent } from './event';\nimport { handleNextStep } from './next';\nimport { executePhase } from './phase';\nimport { ExecutionState } from './process';\n\nexport async function executeNode(\n nodeId: string,\n input: Input,\n state: ExecutionState\n): Promise<Output> {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_START]', { nodeId, input, phaseResultsKeys: Object.keys(state.phaseResults), activeExecutionsKeys: Array.from(state.activeExecutions.keys()), aggregatorDeferredsKeys: Array.from(state.aggregatorDeferreds.keys()) });\n\n // 1. Check if result is already cached in phaseResults (final output, node fully completed)\n if (state.phaseResults[nodeId]) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_CACHE_HIT_PHASERESULTS]', { nodeId, result: state.phaseResults[nodeId] });\n return state.phaseResults[nodeId];\n }\n\n const node = state.process.phases[nodeId] as PhaseNode | AggregatorNode;\n if (!node) {\n const error = new Error(`Node with ID \"${nodeId}\" not found.`);\n\n //console.error('[EXECUTE_NODE_RECURSIVE_NODE_NOT_FOUND]', { nodeId, error });\n state.errors.push({ nodeId, message: error.message });\n throw error;\n }\n\n //console.log('[EXECUTE_NODE_RECURSIVE_NODE_FOUND]', { nodeId, nodeType: node.constructor.name, node });\n\n // 2. Handle active/pending executions\n // If it's an aggregator that has a deferred promise, it means it's pending.\n // We need to re-evaluate it with the current input. The IIFE below will handle this.\n if (state.activeExecutions.has(nodeId) && !isAggregatorNode(node)) {\n // For non-aggregators, if already active, return the promise.\n // Aggregators will fall through to the IIFE to allow input processing.\n // The IIFE itself handles returning a shared deferred promise if needed.\n\n //console.log('[EXECUTE_NODE_RECURSIVE_ACTIVE_EXECUTION_HIT_NON_AGGREGATOR]', { nodeId });\n return state.activeExecutions.get(nodeId)!;\n }\n // If it IS an aggregator and state.activeExecutions.has(nodeId),\n // it means its deferred.promise might be in activeExecutions from a previous input that made it pending.\n // The IIFE logic below will correctly retrieve this deferred (if it exists and is still relevant)\n // from state.aggregatorDeferreds.get(nodeId) and use its promise, or process the input.\n\n\n //console.log('[EXECUTE_NODE_RECURSIVE_CONTINUING_TO_EXECUTION_LOGIC]', { nodeId, isActive: state.activeExecutions.has(nodeId), isAggregator: isAggregatorNode(node), hasDeferred: state.aggregatorDeferreds.has(nodeId) });\n // If it's an aggregator and it's pending (has a deferred), we fall through to re-execute its logic within the IIFE.\n // If it's the first call to any node, we fall through.\n\n // 3. Mark as active and execute (or re-evaluate pending aggregator)\n const executionPromise = (async (): Promise<Output> => {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_START]', { nodeId, input });\n try {\n let output: Output;\n\n if (isAggregatorNode(node)) {\n\n dispatchEvent(\n state.eventState,\n createAggregatorNodeEvent(nodeId, 'start', node, { input }),\n state.context\n );\n\n output = await executeAggregatorNode(nodeId, node, input, state);\n } else if (isPhaseNode(node)) {\n\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'start', node, { input }), state.context);\n\n if (node.prepare) {\n const [preparedInput, preparedContext] = await node.prepare(input, state.context);\n input = preparedInput;\n state.context = preparedContext;\n }\n\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'prepared', node, { input }), state.context);\n\n\n output = await executePhase(nodeId, node, input, state);\n\n if (node.process) {\n const [processedOutput, processedContext] = await node.process(output, state.context);\n output = processedOutput;\n state.context = processedContext;\n }\n\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'processed', node, { input, output }), state.context);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_PHASE_NODE_EXECUTE_END]', { nodeId, output });\n } else {\n const error = new Error(`Unknown or invalid node type for ID \"${nodeId}\". Expected PhaseNode or AggregatorNode.`);\n\n //console.error('[EXECUTE_NODE_RECURSIVE_UNKNOWN_NODE_TYPE]', { nodeId, node, error });\n throw error;\n }\n\n state.phaseResults[nodeId] = output; // Set final output once ready/executed\n\n //console.log('[EXECUTE_NODE_RECURSIVE_PHASE_RESULT_CACHED]', { nodeId, output });\n\n // 4. Handle next step\n if (node.next) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_HANDLING_NEXT_STEP]', { nodeId, nextType: node.next.constructor.name, next: node.next });\n if (Array.isArray(node.next) && node.next.length > 0 && node.next.every(isDecision)) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISIONS_FOUND]', { nodeId, count: node.next.length, decisions: node.next });\n const decisions = node.next as Decision<Output, Context>[];\n const decisionExecutionPromises: Promise<void>[] = [];\n for (const decision of decisions) {\n\n dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'start', decision, { output }), state.context);\n\n const decisionPromise = (async () => {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISION_EXECUTE_START]', { nodeId, decisionId: decision.id, output });\n try {\n const decisionOutcome = await decision.decide(output, state.context);\n dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'decide', decision, { output, result: decisionOutcome }), state.context);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISION_OUTCOME]', { nodeId, decisionId: decision.id, decisionOutcome });\n await handleNextStep(output, decision.id, decisionOutcome, state);\n dispatchEvent(state.eventState, createDecisionEvent(nodeId, 'end', decision), state.context);\n } catch (decisionError: any) {\n const errorMessage = `Decision error on '${decision.id}' for node '${nodeId}': ${decisionError.message}`;\n // eslint-disable-next-line no-console\n console.error(`[_HANDLE_NEXT_STEP_DECISION_ERROR]`, {\n error: errorMessage,\n decisionError,\n decisionId: decision.id,\n sourceNodeId: nodeId\n });\n state.errors.push({\n nodeId: decision.id,\n message: errorMessage,\n details: { sourceNodeId: nodeId, originalError: decisionError.message }\n });\n }\n })();\n decisionExecutionPromises.push(decisionPromise);\n }\n\n //console.log('[EXECUTE_NODE_RECURSIVE_WAITING_FOR_DECISIONS]', { nodeId, count: decisionExecutionPromises.length });\n await Promise.all(decisionExecutionPromises);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_DECISIONS_COMPLETE]', { nodeId });\n } else {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_CALLING_HANDLE_NEXT_STEP_FOR_NON_DECISION]', { nodeId, next: node.next });\n await handleNextStep(output, nodeId, node.next as Termination<Output, Context> | Connection<Output, Context>[] | Decision<Output, Context>[], state);\n }\n } else {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_NO_NEXT_NODE_IMPLICIT_TERMINATION]', { nodeId, output });\n const result: Output = output;\n state.results[nodeId] = result;\n }\n\n if (isPhaseNode(node)) {\n dispatchEvent(state.eventState, createPhaseNodeEvent(nodeId, 'end', node, { input, output }), state.context);\n } else {\n dispatchEvent(state.eventState, createAggregatorNodeEvent(nodeId, 'end', node, { input, output }), state.context);\n }\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_RETURNING_OUTPUT]', { nodeId, output });\n return output;\n } catch (error: any) {\n // eslint-disable-next-line no-console\n console.error(`[EXECUTE_NODE_RECURSIVE_IIFE_ERROR] Error executing node ${nodeId}:`, { error, nodeId });\n state.errors.push({ nodeId, message: error.message });\n // Clean up any pending aggregator deferred on error\n if (state.aggregatorDeferreds.has(nodeId)) {\n const deferred = state.aggregatorDeferreds.get(nodeId);\n if (deferred) {\n deferred.reject(error);\n }\n state.aggregatorDeferreds.delete(nodeId);\n }\n throw error;\n } finally {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_FINALLY]', { nodeId, hasAggregatorDeferred: state.aggregatorDeferreds.has(nodeId) });\n // If a node completed (not pending via deferred mechanism) or an error occurred.\n // An aggregator that is still pending (has a deferred) should keep its promise in activeExecutions.\n if (!state.aggregatorDeferreds.has(nodeId)) {\n\n //console.log('[EXECUTE_NODE_RECURSIVE_IIFE_FINALLY_DELETING_ACTIVE_EXECUTION]', { nodeId });\n state.activeExecutions.delete(nodeId);\n }\n }\n })();\n\n // Store the promise from the IIFE.\n // If it's an aggregator that went pending, executionPromise IS deferred.promise.\n // If it's an aggregator that became ready, executionPromise is a promise resolving to its output.\n // If it's a phase node, executionPromise is a promise resolving to its output.\n\n //console.log('[EXECUTE_NODE_RECURSIVE_SETTING_ACTIVE_EXECUTION]', { nodeId });\n state.activeExecutions.set(nodeId, executionPromise);\n\n //console.log('[EXECUTE_NODE_RECURSIVE_END_RETURNING_PROMISE]', { nodeId });\n return executionPromise;\n\n}\n\n"],"names":["executeNode","nodeId","input","state","phaseResults","node","process","phases","error","Error","errors","push","message","activeExecutions","has","isAggregatorNode","get","executionPromise","output","dispatchEvent","eventState","createAggregatorNodeEvent","context","executeAggregatorNode","isPhaseNode","createPhaseNodeEvent","prepare","preparedInput","preparedContext","executePhase","processedOutput","processedContext","next","Array","isArray","length","every","isDecision","decisions","decisionExecutionPromises","decision","createDecisionEvent","decisionPromise","decisionOutcome","decide","result","handleNextStep","id","decisionError","errorMessage","console","decisionId","sourceNodeId","details","originalError","Promise","all","results","aggregatorDeferreds","deferred","reject","delete","set"],"mappings":"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"}
@@ -74,13 +74,31 @@ async function executeProcess(processInstance, beginning, options) {
74
74
  collectedErrors: state.errors
75
75
  });
76
76
  }
77
+ // Check for and reject any pending aggregators that never completed
77
78
  if (state.aggregatorDeferreds && state.aggregatorDeferreds.size > 0) {
78
79
  const pendingNodeIds = state.pendingAggregatorIds ? state.pendingAggregatorIds().join(', ') : 'unknown';
79
80
  // eslint-disable-next-line no-console
80
- console.warn(`[EXECUTE_PROCESS_PENDING_AGGREGATORS] Process execution may have pending aggregators: ${pendingNodeIds}.`, {
81
+ console.warn(`[EXECUTE_PROCESS_PENDING_AGGREGATORS] Process execution completed with pending aggregators: ${pendingNodeIds}. These will be rejected.`, {
81
82
  processName: processInstance.name,
82
83
  pendingNodeIds
83
84
  });
85
+ // Reject all pending aggregators to prevent hanging promises
86
+ for (const nodeId of state.aggregatorDeferreds.keys()){
87
+ const deferred = state.aggregatorDeferreds.get(nodeId);
88
+ if (deferred) {
89
+ const error = new Error(`Aggregator node '${nodeId}' did not receive all expected inputs before process completion. This may indicate a process design issue where not all paths leading to the aggregator were executed.`);
90
+ deferred.reject(error);
91
+ state.errors.push({
92
+ nodeId,
93
+ message: error.message,
94
+ details: {
95
+ reason: 'incomplete_aggregation'
96
+ }
97
+ });
98
+ }
99
+ }
100
+ // Clear the map after rejecting all
101
+ state.aggregatorDeferreds.clear();
84
102
  }
85
103
  dispatchEvent(state.eventState, createProcessEvent(processInstance.name, 'end', processInstance, {
86
104
  input: processExecutionOptions.input,
@@ -1 +1 @@
1
- {"version":3,"file":"process.js","sources":["../../src/execution/process.ts"],"sourcesContent":["import { executeNode } from './node';\nimport { Context } from '../context';\nimport {\n AggregatorState,\n createAggregatorState\n} from './aggregator';\nimport { EMPTY_INPUT, Input } from '../input';\nimport { Output } from '../output';\nimport { Process, validateProcess } from '../process';\nimport { Beginning } from '../transition/beginning';\nimport { clean } from '../util/general';\nimport { Event, EventHandler, EventState, createBeginningEvent, createEventState, createProcessEvent } from '../event';\nimport { dispatchEvent } from './event';\n\nexport interface PhaseResults {\n [key: string]: Output;\n}\n\nexport interface ProcessResults {\n [key: string]: Output;\n}\n\ninterface ProcessExecutionError {\n message: string;\n details?: any;\n nodeId?: string;\n}\n\nexport interface ProcessExecutionOptions<I extends Input = Input, C extends Context = Context> {\n input: I;\n context: C;\n eventHandlers?: ReadonlyArray<EventHandler<Event, C>>;\n}\n\nexport const DEFAULT_PROCESS_EXECUTION_OPTIONS: ProcessExecutionOptions<Input, Context> = {\n input: EMPTY_INPUT,\n context: {} as Context,\n eventHandlers: [],\n}\n\nexport interface ExecutionState<C extends Context = Context> extends AggregatorState {\n process: Readonly<Process>;\n context: C;\n phaseResults: Record<string, Output>;\n results: Record<string, Output>;\n activeExecutions: Map<string, Promise<Output>>;\n errors: ProcessExecutionError[];\n readonly eventState: Readonly<EventState<C>>;\n}\n\nexport async function executeProcess<I extends Input = Input, O extends Output = Output, C extends Context = Context>(\n processInstance: Readonly<Process>,\n beginning: Beginning<I, C>,\n options?: Partial<ProcessExecutionOptions<I, C>>\n): Promise<[Record<string, O>, Record<string, O>, C]> {\n\n const processExecutionOptions: ProcessExecutionOptions<I, C> = {\n ...(DEFAULT_PROCESS_EXECUTION_OPTIONS as unknown as ProcessExecutionOptions<I, C>),\n ...clean(options || {}),\n };\n if (options && options.input) {\n processExecutionOptions.input = options.input;\n }\n if (options && options.eventHandlers) {\n processExecutionOptions.eventHandlers = options.eventHandlers;\n }\n\n const validationErrors = validateProcess(processInstance);\n if (validationErrors.length > 0) {\n const errorMessages = validationErrors.map(err => err.error).join('\\n');\n throw new Error(`Invalid process definition:\\n${errorMessages}`);\n }\n\n const eventState = createEventState<C>(processExecutionOptions.eventHandlers);\n\n const state: ExecutionState<C> = {\n process: processInstance,\n context: processExecutionOptions.context as C,\n results: {},\n phaseResults: {},\n activeExecutions: new Map<string, Promise<Output>>(),\n errors: [],\n ...createAggregatorState(),\n eventState: eventState,\n };\n\n dispatchEvent(\n state.eventState,\n createProcessEvent(processInstance.name, 'start', processInstance, { input: processExecutionOptions.input, context: state.context }),\n state.context\n );\n\n dispatchEvent(\n state.eventState,\n createBeginningEvent(beginning.id, 'start', beginning as Beginning<I, C>, { input: processExecutionOptions.input }),\n state.context\n );\n\n const initialInput = await beginning.begin(processExecutionOptions.input, state.context);\n dispatchEvent(\n state.eventState,\n createBeginningEvent(beginning.id, 'begin', beginning as Beginning<I, C>, { input: initialInput }),\n state.context\n );\n\n const initialNodeId = beginning.targetNodeId;\n\n if (!state.process.phases[initialNodeId]) {\n throw new Error(`Start phase ID \"${initialNodeId}\" not found in process phases.`);\n }\n\n try {\n await executeNode(initialNodeId, initialInput, state as unknown as ExecutionState<Context>);\n\n const allPromises = Array.from(state.activeExecutions.values());\n if (allPromises.length > 0) {\n await Promise.all(allPromises);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n state.errors.push({ message: \"Critical error during process execution\", details: errorMessage, nodeId: initialNodeId });\n // eslint-disable-next-line no-console\n console.error(\"[EXECUTE_PROCESS_CRITICAL_ERROR]\", { processName: processInstance.name, error: errorMessage, collectedErrors: state.errors });\n }\n\n if (state.aggregatorDeferreds && state.aggregatorDeferreds.size > 0) {\n const pendingNodeIds = state.pendingAggregatorIds ? state.pendingAggregatorIds().join(', ') : 'unknown';\n // eslint-disable-next-line no-console\n console.warn(`[EXECUTE_PROCESS_PENDING_AGGREGATORS] Process execution may have pending aggregators: ${pendingNodeIds}.`, { processName: processInstance.name, pendingNodeIds });\n }\n\n dispatchEvent(\n state.eventState,\n createProcessEvent(processInstance.name, 'end', processInstance, { input: processExecutionOptions.input, context: state.context }),\n state.context\n );\n\n return [state.results as Record<string, O>, state.phaseResults as Record<string, O>, state.context];\n}\n\n\n"],"names":["DEFAULT_PROCESS_EXECUTION_OPTIONS","input","EMPTY_INPUT","context","eventHandlers","executeProcess","processInstance","beginning","options","processExecutionOptions","clean","validationErrors","validateProcess","length","errorMessages","map","err","error","join","Error","eventState","createEventState","state","process","results","phaseResults","activeExecutions","Map","errors","createAggregatorState","dispatchEvent","createProcessEvent","name","createBeginningEvent","id","initialInput","begin","initialNodeId","targetNodeId","phases","executeNode","allPromises","Array","from","values","Promise","all","errorMessage","message","String","push","details","nodeId","console","processName","collectedErrors","aggregatorDeferreds","size","pendingNodeIds","pendingAggregatorIds","warn"],"mappings":"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"}
1
+ {"version":3,"file":"process.js","sources":["../../src/execution/process.ts"],"sourcesContent":["import { executeNode } from './node';\nimport { Context } from '../context';\nimport {\n AggregatorState,\n createAggregatorState\n} from './aggregator';\nimport { EMPTY_INPUT, Input } from '../input';\nimport { Output } from '../output';\nimport { Process, validateProcess } from '../process';\nimport { Beginning } from '../transition/beginning';\nimport { clean } from '../util/general';\nimport { Event, EventHandler, EventState, createBeginningEvent, createEventState, createProcessEvent } from '../event';\nimport { dispatchEvent } from './event';\n\nexport interface PhaseResults {\n [key: string]: Output;\n}\n\nexport interface ProcessResults {\n [key: string]: Output;\n}\n\ninterface ProcessExecutionError {\n message: string;\n details?: any;\n nodeId?: string;\n}\n\nexport interface ProcessExecutionOptions<I extends Input = Input, C extends Context = Context> {\n input: I;\n context: C;\n eventHandlers?: ReadonlyArray<EventHandler<Event, C>>;\n}\n\nexport const DEFAULT_PROCESS_EXECUTION_OPTIONS: ProcessExecutionOptions<Input, Context> = {\n input: EMPTY_INPUT,\n context: {} as Context,\n eventHandlers: [],\n}\n\nexport interface ExecutionState<C extends Context = Context> extends AggregatorState {\n process: Readonly<Process>;\n context: C;\n phaseResults: Record<string, Output>;\n results: Record<string, Output>;\n activeExecutions: Map<string, Promise<Output>>;\n errors: ProcessExecutionError[];\n readonly eventState: Readonly<EventState<C>>;\n}\n\nexport async function executeProcess<I extends Input = Input, O extends Output = Output, C extends Context = Context>(\n processInstance: Readonly<Process>,\n beginning: Beginning<I, C>,\n options?: Partial<ProcessExecutionOptions<I, C>>\n): Promise<[Record<string, O>, Record<string, O>, C]> {\n\n const processExecutionOptions: ProcessExecutionOptions<I, C> = {\n ...(DEFAULT_PROCESS_EXECUTION_OPTIONS as unknown as ProcessExecutionOptions<I, C>),\n ...clean(options || {}),\n };\n if (options && options.input) {\n processExecutionOptions.input = options.input;\n }\n if (options && options.eventHandlers) {\n processExecutionOptions.eventHandlers = options.eventHandlers;\n }\n\n const validationErrors = validateProcess(processInstance);\n if (validationErrors.length > 0) {\n const errorMessages = validationErrors.map(err => err.error).join('\\n');\n throw new Error(`Invalid process definition:\\n${errorMessages}`);\n }\n\n const eventState = createEventState<C>(processExecutionOptions.eventHandlers);\n\n const state: ExecutionState<C> = {\n process: processInstance,\n context: processExecutionOptions.context as C,\n results: {},\n phaseResults: {},\n activeExecutions: new Map<string, Promise<Output>>(),\n errors: [],\n ...createAggregatorState(),\n eventState: eventState,\n };\n\n dispatchEvent(\n state.eventState,\n createProcessEvent(processInstance.name, 'start', processInstance, { input: processExecutionOptions.input, context: state.context }),\n state.context\n );\n\n dispatchEvent(\n state.eventState,\n createBeginningEvent(beginning.id, 'start', beginning as Beginning<I, C>, { input: processExecutionOptions.input }),\n state.context\n );\n\n const initialInput = await beginning.begin(processExecutionOptions.input, state.context);\n dispatchEvent(\n state.eventState,\n createBeginningEvent(beginning.id, 'begin', beginning as Beginning<I, C>, { input: initialInput }),\n state.context\n );\n\n const initialNodeId = beginning.targetNodeId;\n\n if (!state.process.phases[initialNodeId]) {\n throw new Error(`Start phase ID \"${initialNodeId}\" not found in process phases.`);\n }\n\n try {\n await executeNode(initialNodeId, initialInput, state as unknown as ExecutionState<Context>);\n\n const allPromises = Array.from(state.activeExecutions.values());\n if (allPromises.length > 0) {\n await Promise.all(allPromises);\n }\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n state.errors.push({ message: \"Critical error during process execution\", details: errorMessage, nodeId: initialNodeId });\n // eslint-disable-next-line no-console\n console.error(\"[EXECUTE_PROCESS_CRITICAL_ERROR]\", { processName: processInstance.name, error: errorMessage, collectedErrors: state.errors });\n }\n\n // Check for and reject any pending aggregators that never completed\n if (state.aggregatorDeferreds && state.aggregatorDeferreds.size > 0) {\n const pendingNodeIds = state.pendingAggregatorIds ? state.pendingAggregatorIds().join(', ') : 'unknown';\n // eslint-disable-next-line no-console\n console.warn(`[EXECUTE_PROCESS_PENDING_AGGREGATORS] Process execution completed with pending aggregators: ${pendingNodeIds}. These will be rejected.`, { processName: processInstance.name, pendingNodeIds });\n\n // Reject all pending aggregators to prevent hanging promises\n for (const nodeId of state.aggregatorDeferreds.keys()) {\n const deferred = state.aggregatorDeferreds.get(nodeId);\n if (deferred) {\n const error = new Error(`Aggregator node '${nodeId}' did not receive all expected inputs before process completion. This may indicate a process design issue where not all paths leading to the aggregator were executed.`);\n deferred.reject(error);\n state.errors.push({\n nodeId,\n message: error.message,\n details: { reason: 'incomplete_aggregation' }\n });\n }\n }\n // Clear the map after rejecting all\n state.aggregatorDeferreds.clear();\n }\n\n dispatchEvent(\n state.eventState,\n createProcessEvent(processInstance.name, 'end', processInstance, { input: processExecutionOptions.input, context: state.context }),\n state.context\n );\n\n return [state.results as Record<string, O>, state.phaseResults as Record<string, O>, state.context];\n}\n\n\n"],"names":["DEFAULT_PROCESS_EXECUTION_OPTIONS","input","EMPTY_INPUT","context","eventHandlers","executeProcess","processInstance","beginning","options","processExecutionOptions","clean","validationErrors","validateProcess","length","errorMessages","map","err","error","join","Error","eventState","createEventState","state","process","results","phaseResults","activeExecutions","Map","errors","createAggregatorState","dispatchEvent","createProcessEvent","name","createBeginningEvent","id","initialInput","begin","initialNodeId","targetNodeId","phases","executeNode","allPromises","Array","from","values","Promise","all","errorMessage","message","String","push","details","nodeId","console","processName","collectedErrors","aggregatorDeferreds","size","pendingNodeIds","pendingAggregatorIds","warn","keys","deferred","get","reject","reason","clear"],"mappings":"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"}
@@ -10,7 +10,29 @@ export interface PhaseNode<I extends Input = Input, // Input to this phase insta
10
10
  O extends Output = Output> extends Node {
11
11
  type: 'phase';
12
12
  phase: Phase<I, O>;
13
+ /**
14
+ * The prepare method is called before the phase is executed.
15
+ * It receives the input and current context, and returns both (potentially modified).
16
+ * The returned context replaces the process context.
17
+ *
18
+ * CONCURRENCY WARNING: In processes with parallel execution paths (fan-out),
19
+ * multiple nodes may execute simultaneously. Each node's prepare method receives
20
+ * the shared context and can modify it. These modifications happen sequentially
21
+ * within each node but parallel nodes may overwrite each other's context changes.
22
+ *
23
+ * Best practice: Use unique context keys per node/path to avoid conflicts,
24
+ * or ensure context modifications are idempotent and merge-safe.
25
+ */
13
26
  prepare?: PrepareMethod;
27
+ /**
28
+ * The process method is called after the phase is executed, but before the next node is executed.
29
+ * It receives the phase output and current context, and returns both (potentially modified).
30
+ * The returned context replaces the process context.
31
+ *
32
+ * CONCURRENCY WARNING: Same as prepare - parallel execution paths may cause
33
+ * context mutations to overwrite each other. Design your context updates carefully
34
+ * when using fan-out patterns.
35
+ */
14
36
  process?: ProcessMethod;
15
37
  }
16
38
  export interface PhaseNodeOptions<I extends Input = Input, O extends Output = Output, C extends Context = Context> {
@@ -1 +1 @@
1
- {"version":3,"file":"phasenode.js","sources":["../../src/node/phasenode.ts"],"sourcesContent":["import { Next } from '../transition/next';\nimport { Context } from '../context';\nimport { Input } from '../input';\nimport { Output } from '../output';\nimport { isPhase, Phase, validatePhase } from '../phase';\nimport { createNode, isNode, Node, validateNode } from './node';\nimport { clean } from '../util/general';\n\nexport type ProcessMethod<O extends Output = Output, C extends Context = Context> = (output: O, context: C) => Promise<[O, C]>;\nexport type PrepareMethod<I extends Input = Input, C extends Context = Context> = (input: I, context: C) => Promise<[I, C]>;\n\nexport interface PhaseNode<\n I extends Input = Input, // Input to this phase instance\n O extends Output = Output, // Output from this phase instance\n> extends Node {\n type: 'phase';\n phase: Phase<I, O>; // The actual phase instance\n\n // The prepare method is called before the phase is executed.\n // The input is the input for the phase, and the context is saved to the process context.\n prepare?: PrepareMethod;\n\n // The process method is called after the phase is executed, but before the next node is executed.\n // The output is the input for the next node, and the context is saved to the process context.\n process?: ProcessMethod;\n}\n\nexport interface PhaseNodeOptions<I extends Input = Input, O extends Output = Output, C extends Context = Context> {\n next?: Next<O, C>;\n prepare?: PrepareMethod<I, C>;\n process?: ProcessMethod<O, C>;\n}\n\nexport const DEFAULT_PHASE_NODE_OPTIONS: PhaseNodeOptions<Input, Output, Context> = {\n}\n\nexport const createPhaseNode = <I extends Input = Input, O extends Output = Output, C extends Context = Context>(\n id: string,\n phase: Phase<I, O>,\n options?: Partial<PhaseNodeOptions<I, O, C>>\n): Readonly<PhaseNode<I, O>> => {\n let phaseNodeOptions: PhaseNodeOptions<I, O, C> = { ...DEFAULT_PHASE_NODE_OPTIONS } as unknown as PhaseNodeOptions<I, O, C>;\n if (options) {\n phaseNodeOptions = { ...phaseNodeOptions, ...clean(options) };\n }\n\n return {\n ...createNode('phase', id, { next: phaseNodeOptions.next }),\n phase,\n prepare: phaseNodeOptions.prepare,\n process: phaseNodeOptions.process,\n } as PhaseNode<I, O>;\n};\n\nexport const isPhaseNode = (obj: any): obj is PhaseNode<any, any> => {\n if (!isNode(obj) || obj.type !== 'phase') {\n return false;\n }\n const potentialPhaseNode = obj as PhaseNode<any, any>;\n\n if (!(\n potentialPhaseNode.phase && typeof potentialPhaseNode.phase === 'object' && isPhase(potentialPhaseNode.phase)\n )) {\n return false;\n }\n\n return true;\n};\n\nexport const validatePhaseNode = (item: any, coordinates?: string[]): Array<{ coordinates: string[], error: string }> => {\n const errors: Array<{ coordinates: string[], error: string }> = [];\n const currentCoordinates = [...(coordinates || []), 'PhaseNode'];\n\n errors.push(...validateNode(item, currentCoordinates));\n\n currentCoordinates.push(`PhaseNode: ${item.id}`);\n\n if (item.phase === undefined) {\n errors.push({ coordinates: [...currentCoordinates], error: 'phase is undefined.' });\n } else {\n errors.push(...validatePhase(item.phase, currentCoordinates));\n }\n\n return errors;\n};"],"names":["DEFAULT_PHASE_NODE_OPTIONS","createPhaseNode","id","phase","options","phaseNodeOptions","clean","createNode","next","prepare","process","isPhaseNode","obj","isNode","type","potentialPhaseNode","isPhase"],"mappings":";;;;AAiCO,MAAMA,0BAAAA,GAAuE;AAG7E,MAAMC,eAAAA,GAAkB,CAC3BC,EAAAA,EACAC,KAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,IAAIC,gBAAAA,GAA8C;AAAE,QAAA,GAAGL;AAA2B,KAAA;AAClF,IAAA,IAAII,OAAAA,EAAS;QACTC,gBAAAA,GAAmB;AAAE,YAAA,GAAGA,gBAAgB;AAAE,YAAA,GAAGC,MAAMF,OAAAA;AAAS,SAAA;AAChE,IAAA;IAEA,OAAO;QACH,GAAGG,UAAAA,CAAW,SAASL,EAAAA,EAAI;AAAEM,YAAAA,IAAAA,EAAMH,iBAAiBG;SAAK,CAAE;AAC3DL,QAAAA,KAAAA;AACAM,QAAAA,OAAAA,EAASJ,iBAAiBI,OAAO;AACjCC,QAAAA,OAAAA,EAASL,iBAAiBK;AAC9B,KAAA;AACJ;AAEO,MAAMC,cAAc,CAACC,GAAAA,GAAAA;AACxB,IAAA,IAAI,CAACC,MAAAA,CAAOD,GAAAA,CAAAA,IAAQA,GAAAA,CAAIE,IAAI,KAAK,OAAA,EAAS;QACtC,OAAO,KAAA;AACX,IAAA;AACA,IAAA,MAAMC,kBAAAA,GAAqBH,GAAAA;AAE3B,IAAA,IAAI,EACAG,kBAAAA,CAAmBZ,KAAK,IAAI,OAAOY,kBAAAA,CAAmBZ,KAAK,KAAK,QAAA,IAAYa,OAAAA,CAAQD,kBAAAA,CAAmBZ,KAAK,CAAA,CAAA,EAC7G;QACC,OAAO,KAAA;AACX,IAAA;IAEA,OAAO,IAAA;AACX;;;;"}
1
+ {"version":3,"file":"phasenode.js","sources":["../../src/node/phasenode.ts"],"sourcesContent":["import { Next } from '../transition/next';\nimport { Context } from '../context';\nimport { Input } from '../input';\nimport { Output } from '../output';\nimport { isPhase, Phase, validatePhase } from '../phase';\nimport { createNode, isNode, Node, validateNode } from './node';\nimport { clean } from '../util/general';\n\nexport type ProcessMethod<O extends Output = Output, C extends Context = Context> = (output: O, context: C) => Promise<[O, C]>;\nexport type PrepareMethod<I extends Input = Input, C extends Context = Context> = (input: I, context: C) => Promise<[I, C]>;\n\nexport interface PhaseNode<\n I extends Input = Input, // Input to this phase instance\n O extends Output = Output, // Output from this phase instance\n> extends Node {\n type: 'phase';\n phase: Phase<I, O>; // The actual phase instance\n\n /**\n * The prepare method is called before the phase is executed.\n * It receives the input and current context, and returns both (potentially modified).\n * The returned context replaces the process context.\n *\n * CONCURRENCY WARNING: In processes with parallel execution paths (fan-out),\n * multiple nodes may execute simultaneously. Each node's prepare method receives\n * the shared context and can modify it. These modifications happen sequentially\n * within each node but parallel nodes may overwrite each other's context changes.\n *\n * Best practice: Use unique context keys per node/path to avoid conflicts,\n * or ensure context modifications are idempotent and merge-safe.\n */\n prepare?: PrepareMethod;\n\n /**\n * The process method is called after the phase is executed, but before the next node is executed.\n * It receives the phase output and current context, and returns both (potentially modified).\n * The returned context replaces the process context.\n *\n * CONCURRENCY WARNING: Same as prepare - parallel execution paths may cause\n * context mutations to overwrite each other. Design your context updates carefully\n * when using fan-out patterns.\n */\n process?: ProcessMethod;\n}\n\nexport interface PhaseNodeOptions<I extends Input = Input, O extends Output = Output, C extends Context = Context> {\n next?: Next<O, C>;\n prepare?: PrepareMethod<I, C>;\n process?: ProcessMethod<O, C>;\n}\n\nexport const DEFAULT_PHASE_NODE_OPTIONS: PhaseNodeOptions<Input, Output, Context> = {\n}\n\nexport const createPhaseNode = <I extends Input = Input, O extends Output = Output, C extends Context = Context>(\n id: string,\n phase: Phase<I, O>,\n options?: Partial<PhaseNodeOptions<I, O, C>>\n): Readonly<PhaseNode<I, O>> => {\n let phaseNodeOptions: PhaseNodeOptions<I, O, C> = { ...DEFAULT_PHASE_NODE_OPTIONS } as unknown as PhaseNodeOptions<I, O, C>;\n if (options) {\n phaseNodeOptions = { ...phaseNodeOptions, ...clean(options) };\n }\n\n return {\n ...createNode('phase', id, { next: phaseNodeOptions.next }),\n phase,\n prepare: phaseNodeOptions.prepare,\n process: phaseNodeOptions.process,\n } as PhaseNode<I, O>;\n};\n\nexport const isPhaseNode = (obj: any): obj is PhaseNode<any, any> => {\n if (!isNode(obj) || obj.type !== 'phase') {\n return false;\n }\n const potentialPhaseNode = obj as PhaseNode<any, any>;\n\n if (!(\n potentialPhaseNode.phase && typeof potentialPhaseNode.phase === 'object' && isPhase(potentialPhaseNode.phase)\n )) {\n return false;\n }\n\n return true;\n};\n\nexport const validatePhaseNode = (item: any, coordinates?: string[]): Array<{ coordinates: string[], error: string }> => {\n const errors: Array<{ coordinates: string[], error: string }> = [];\n const currentCoordinates = [...(coordinates || []), 'PhaseNode'];\n\n errors.push(...validateNode(item, currentCoordinates));\n\n currentCoordinates.push(`PhaseNode: ${item.id}`);\n\n if (item.phase === undefined) {\n errors.push({ coordinates: [...currentCoordinates], error: 'phase is undefined.' });\n } else {\n errors.push(...validatePhase(item.phase, currentCoordinates));\n }\n\n return errors;\n};\n"],"names":["DEFAULT_PHASE_NODE_OPTIONS","createPhaseNode","id","phase","options","phaseNodeOptions","clean","createNode","next","prepare","process","isPhaseNode","obj","isNode","type","potentialPhaseNode","isPhase"],"mappings":";;;;AAmDO,MAAMA,0BAAAA,GAAuE;AAG7E,MAAMC,eAAAA,GAAkB,CAC3BC,EAAAA,EACAC,KAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,IAAIC,gBAAAA,GAA8C;AAAE,QAAA,GAAGL;AAA2B,KAAA;AAClF,IAAA,IAAII,OAAAA,EAAS;QACTC,gBAAAA,GAAmB;AAAE,YAAA,GAAGA,gBAAgB;AAAE,YAAA,GAAGC,MAAMF,OAAAA;AAAS,SAAA;AAChE,IAAA;IAEA,OAAO;QACH,GAAGG,UAAAA,CAAW,SAASL,EAAAA,EAAI;AAAEM,YAAAA,IAAAA,EAAMH,iBAAiBG;SAAK,CAAE;AAC3DL,QAAAA,KAAAA;AACAM,QAAAA,OAAAA,EAASJ,iBAAiBI,OAAO;AACjCC,QAAAA,OAAAA,EAASL,iBAAiBK;AAC9B,KAAA;AACJ;AAEO,MAAMC,cAAc,CAACC,GAAAA,GAAAA;AACxB,IAAA,IAAI,CAACC,MAAAA,CAAOD,GAAAA,CAAAA,IAAQA,GAAAA,CAAIE,IAAI,KAAK,OAAA,EAAS;QACtC,OAAO,KAAA;AACX,IAAA;AACA,IAAA,MAAMC,kBAAAA,GAAqBH,GAAAA;AAE3B,IAAA,IAAI,EACAG,kBAAAA,CAAmBZ,KAAK,IAAI,OAAOY,kBAAAA,CAAmBZ,KAAK,KAAK,QAAA,IAAYa,OAAAA,CAAQD,kBAAAA,CAAmBZ,KAAK,CAAA,CAAA,EAC7G;QACC,OAAO,KAAA;AACX,IAAA;IAEA,OAAO,IAAA;AACX;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"beginning.js","sources":["../../src/transition/beginning.ts"],"sourcesContent":["// NEW: Termination type extending Transition\n/**\n * Represents a termination point in the process flow, consuming a phase's output.\n */\n\nimport { clean } from '../util/general';\nimport { Context } from '../context';\nimport { Input } from '../input';\nimport { createTransition, isTransition, Transition, validateTransition } from './transition';\n\nexport type BeginFunction<I extends Input = Input, C extends Context = Context> = (input: I, context: C) => Promise<Input>;\n\nexport interface Beginning<\n I extends Input = Input,\n C extends Context = Context,\n> extends Transition {\n type: 'beginning';\n targetNodeId: string;\n // Currently, Termination is a marker type.\n // Future properties could include:\n // reason?: string; // To describe why termination occurred\n begin: BeginFunction<I, C>; // A function to execute upon beginning\n}\n\nexport interface BeginningOptions<I extends Input = Input, C extends Context = Context> {\n begin: BeginFunction<I, C>;\n}\n\nexport const DEFAULT_BEGINNING_OPTIONS: BeginningOptions<Input, Context> = {\n begin: async (input) => {\n return input;\n }\n};\n\nexport const createBeginning = <I extends Input = Input, C extends Context = Context>(\n id: string,\n targetNodeId: string,\n options?: Partial<BeginningOptions<I, C>>\n): Readonly<Beginning<I, C>> => {\n let beginningOptions: BeginningOptions<I, C> = { ...DEFAULT_BEGINNING_OPTIONS };\n if (options) {\n beginningOptions = { ...beginningOptions, ...clean(options) };\n }\n\n return {\n ...createTransition('beginning', id),\n targetNodeId,\n begin: beginningOptions.begin,\n } as Beginning<I, C>;\n};\n\nexport const isBeginning = <I extends Input = Input, C extends Context = Context>(item: any): item is Beginning<I, C> => {\n return isTransition(item) &&\n item.type === 'beginning' &&\n ((item as Beginning<I, C>).begin === undefined || typeof (item as Beginning<I, C>).begin === 'function');\n};\n\nexport const validateBeginning = (item: any, coordinates?: string[]): Array<{ coordinates: string[], error: string }> => {\n const errors: Array<{ coordinates: string[], error: string }> = [];\n const currentCoordinates = [...(coordinates || []), 'Beginning'];\n\n errors.push(...validateTransition(item, currentCoordinates));\n\n if (errors.length === 0) {\n currentCoordinates.push(`Termination: ${item.id}`);\n\n if (item.begin !== undefined && typeof item.begin !== 'function') {\n errors.push({ coordinates: [...currentCoordinates], error: 'begin is not a function.' });\n }\n }\n\n return errors;\n};\n"],"names":["DEFAULT_BEGINNING_OPTIONS","begin","input","createBeginning","id","targetNodeId","options","beginningOptions","clean","createTransition","isBeginning","item","isTransition","type","undefined"],"mappings":";;;AAAA;MA4BaA,yBAAAA,GAA8D;AACvEC,IAAAA,KAAAA,EAAO,OAAOC,KAAAA,GAAAA;QACV,OAAOA,KAAAA;AACX,IAAA;AACJ;AAEO,MAAMC,eAAAA,GAAkB,CAC3BC,EAAAA,EACAC,YAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,IAAIC,gBAAAA,GAA2C;AAAE,QAAA,GAAGP;AAA0B,KAAA;AAC9E,IAAA,IAAIM,OAAAA,EAAS;QACTC,gBAAAA,GAAmB;AAAE,YAAA,GAAGA,gBAAgB;AAAE,YAAA,GAAGC,MAAMF,OAAAA;AAAS,SAAA;AAChE,IAAA;IAEA,OAAO;QACH,GAAGG,gBAAAA,CAAiB,aAAaL,EAAAA,CAAG;AACpCC,QAAAA,YAAAA;AACAJ,QAAAA,KAAAA,EAAOM,iBAAiBN;AAC5B,KAAA;AACJ;AAEO,MAAMS,cAAc,CAAuDC,IAAAA,GAAAA;AAC9E,IAAA,OAAOC,aAAaD,IAAAA,CAAAA,IAChBA,IAAAA,CAAKE,IAAI,KAAK,gBACZF,IAAAA,CAAyBV,KAAK,KAAKa,SAAAA,IAAa,OAAO,IAACH,CAAyBV,KAAK,KAAK,UAAS,CAAA;AAC9G;;;;"}
1
+ {"version":3,"file":"beginning.js","sources":["../../src/transition/beginning.ts"],"sourcesContent":["// NEW: Termination type extending Transition\n/**\n * Represents a termination point in the process flow, consuming a phase's output.\n */\n\nimport { clean } from '../util/general';\nimport { Context } from '../context';\nimport { Input } from '../input';\nimport { createTransition, isTransition, Transition, validateTransition } from './transition';\n\nexport type BeginFunction<I extends Input = Input, C extends Context = Context> = (input: I, context: C) => Promise<Input>;\n\nexport interface Beginning<\n I extends Input = Input,\n C extends Context = Context,\n> extends Transition {\n type: 'beginning';\n targetNodeId: string;\n // Currently, Termination is a marker type.\n // Future properties could include:\n // reason?: string; // To describe why termination occurred\n begin: BeginFunction<I, C>; // A function to execute upon beginning\n}\n\nexport interface BeginningOptions<I extends Input = Input, C extends Context = Context> {\n begin: BeginFunction<I, C>;\n}\n\nexport const DEFAULT_BEGINNING_OPTIONS: BeginningOptions<Input, Context> = {\n begin: async (input) => {\n return input;\n }\n};\n\nexport const createBeginning = <I extends Input = Input, C extends Context = Context>(\n id: string,\n targetNodeId: string,\n options?: Partial<BeginningOptions<I, C>>\n): Readonly<Beginning<I, C>> => {\n let beginningOptions: BeginningOptions<I, C> = { ...DEFAULT_BEGINNING_OPTIONS };\n if (options) {\n beginningOptions = { ...beginningOptions, ...clean(options) };\n }\n\n return {\n ...createTransition('beginning', id),\n targetNodeId,\n begin: beginningOptions.begin,\n } as Beginning<I, C>;\n};\n\nexport const isBeginning = <I extends Input = Input, C extends Context = Context>(item: any): item is Beginning<I, C> => {\n return isTransition(item) &&\n item.type === 'beginning' &&\n ((item as Beginning<I, C>).begin === undefined || typeof (item as Beginning<I, C>).begin === 'function');\n};\n\nexport const validateBeginning = (item: any, coordinates?: string[]): Array<{ coordinates: string[], error: string }> => {\n const errors: Array<{ coordinates: string[], error: string }> = [];\n const currentCoordinates = [...(coordinates || []), 'Beginning'];\n\n errors.push(...validateTransition(item, currentCoordinates));\n\n if (errors.length === 0) {\n currentCoordinates.push(`Beginning: ${item.id}`);\n\n if (item.begin !== undefined && typeof item.begin !== 'function') {\n errors.push({ coordinates: [...currentCoordinates], error: 'begin is not a function.' });\n }\n }\n\n return errors;\n};\n"],"names":["DEFAULT_BEGINNING_OPTIONS","begin","input","createBeginning","id","targetNodeId","options","beginningOptions","clean","createTransition","isBeginning","item","isTransition","type","undefined"],"mappings":";;;AAAA;MA4BaA,yBAAAA,GAA8D;AACvEC,IAAAA,KAAAA,EAAO,OAAOC,KAAAA,GAAAA;QACV,OAAOA,KAAAA;AACX,IAAA;AACJ;AAEO,MAAMC,eAAAA,GAAkB,CAC3BC,EAAAA,EACAC,YAAAA,EACAC,OAAAA,GAAAA;AAEA,IAAA,IAAIC,gBAAAA,GAA2C;AAAE,QAAA,GAAGP;AAA0B,KAAA;AAC9E,IAAA,IAAIM,OAAAA,EAAS;QACTC,gBAAAA,GAAmB;AAAE,YAAA,GAAGA,gBAAgB;AAAE,YAAA,GAAGC,MAAMF,OAAAA;AAAS,SAAA;AAChE,IAAA;IAEA,OAAO;QACH,GAAGG,gBAAAA,CAAiB,aAAaL,EAAAA,CAAG;AACpCC,QAAAA,YAAAA;AACAJ,QAAAA,KAAAA,EAAOM,iBAAiBN;AAC5B,KAAA;AACJ;AAEO,MAAMS,cAAc,CAAuDC,IAAAA,GAAAA;AAC9E,IAAA,OAAOC,aAAaD,IAAAA,CAAAA,IAChBA,IAAAA,CAAKE,IAAI,KAAK,gBACZF,IAAAA,CAAyBV,KAAK,KAAKa,SAAAA,IAAa,OAAO,IAACH,CAAyBV,KAAK,KAAK,UAAS,CAAA;AAC9G;;;;"}