flowcraft 2.7.1 → 2.8.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.
Files changed (229) hide show
  1. package/README.md +1 -1
  2. package/dist/index-D3dyjW2G.d.mts +1269 -0
  3. package/dist/index.d.mts +2 -0
  4. package/dist/index.mjs +727 -0
  5. package/dist/index.mjs.map +1 -0
  6. package/dist/replay-BB11M6K1.mjs +107 -0
  7. package/dist/replay-BB11M6K1.mjs.map +1 -0
  8. package/dist/runtime-CmefIAu_.mjs +2216 -0
  9. package/dist/runtime-CmefIAu_.mjs.map +1 -0
  10. package/dist/{sdk.d.ts → sdk.d.mts} +14 -12
  11. package/dist/sdk.mjs +29 -0
  12. package/dist/sdk.mjs.map +1 -0
  13. package/dist/testing/index.d.mts +172 -0
  14. package/dist/testing/index.mjs +277 -0
  15. package/dist/testing/index.mjs.map +1 -0
  16. package/package.json +59 -59
  17. package/LICENSE +0 -21
  18. package/dist/adapters/index.d.ts +0 -4
  19. package/dist/adapters/index.js +0 -4
  20. package/dist/adapters/index.js.map +0 -1
  21. package/dist/adapters/persistent-event-bus.d.ts +0 -69
  22. package/dist/adapters/persistent-event-bus.js +0 -3
  23. package/dist/adapters/persistent-event-bus.js.map +0 -1
  24. package/dist/analysis.d.ts +0 -53
  25. package/dist/analysis.js +0 -3
  26. package/dist/analysis.js.map +0 -1
  27. package/dist/chunk-27STBUGG.js +0 -44
  28. package/dist/chunk-27STBUGG.js.map +0 -1
  29. package/dist/chunk-2TSADFQX.js +0 -46
  30. package/dist/chunk-2TSADFQX.js.map +0 -1
  31. package/dist/chunk-3Y5O5EGB.js +0 -3
  32. package/dist/chunk-3Y5O5EGB.js.map +0 -1
  33. package/dist/chunk-4PELJWF7.js +0 -29
  34. package/dist/chunk-4PELJWF7.js.map +0 -1
  35. package/dist/chunk-55J6XMHW.js +0 -3
  36. package/dist/chunk-55J6XMHW.js.map +0 -1
  37. package/dist/chunk-6RKHCJUU.js +0 -29
  38. package/dist/chunk-6RKHCJUU.js.map +0 -1
  39. package/dist/chunk-7EBKWATZ.js +0 -86
  40. package/dist/chunk-7EBKWATZ.js.map +0 -1
  41. package/dist/chunk-7EMUOH77.js +0 -90
  42. package/dist/chunk-7EMUOH77.js.map +0 -1
  43. package/dist/chunk-7M6FHFHP.js +0 -25
  44. package/dist/chunk-7M6FHFHP.js.map +0 -1
  45. package/dist/chunk-AKDL2ZX7.js +0 -287
  46. package/dist/chunk-AKDL2ZX7.js.map +0 -1
  47. package/dist/chunk-BC4G7OM6.js +0 -42
  48. package/dist/chunk-BC4G7OM6.js.map +0 -1
  49. package/dist/chunk-BCMR7Y4U.js +0 -76
  50. package/dist/chunk-BCMR7Y4U.js.map +0 -1
  51. package/dist/chunk-BCRWXTWX.js +0 -21
  52. package/dist/chunk-BCRWXTWX.js.map +0 -1
  53. package/dist/chunk-BEHVGFIM.js +0 -532
  54. package/dist/chunk-BEHVGFIM.js.map +0 -1
  55. package/dist/chunk-DL7KVYZF.js +0 -39
  56. package/dist/chunk-DL7KVYZF.js.map +0 -1
  57. package/dist/chunk-DV2CXHOY.js +0 -74
  58. package/dist/chunk-DV2CXHOY.js.map +0 -1
  59. package/dist/chunk-H4JTZYIT.js +0 -172
  60. package/dist/chunk-H4JTZYIT.js.map +0 -1
  61. package/dist/chunk-HFJXYY4E.js +0 -3
  62. package/dist/chunk-HFJXYY4E.js.map +0 -1
  63. package/dist/chunk-HNHM3FDK.js +0 -52
  64. package/dist/chunk-HNHM3FDK.js.map +0 -1
  65. package/dist/chunk-HXSK5P2X.js +0 -150
  66. package/dist/chunk-HXSK5P2X.js.map +0 -1
  67. package/dist/chunk-I53JB2KW.js +0 -26
  68. package/dist/chunk-I53JB2KW.js.map +0 -1
  69. package/dist/chunk-IDTYHLDQ.js +0 -16
  70. package/dist/chunk-IDTYHLDQ.js.map +0 -1
  71. package/dist/chunk-IKOTX22J.js +0 -85
  72. package/dist/chunk-IKOTX22J.js.map +0 -1
  73. package/dist/chunk-L3MX5MTA.js +0 -33
  74. package/dist/chunk-L3MX5MTA.js.map +0 -1
  75. package/dist/chunk-L46TQXCV.js +0 -144
  76. package/dist/chunk-L46TQXCV.js.map +0 -1
  77. package/dist/chunk-LM4ACVHL.js +0 -73
  78. package/dist/chunk-LM4ACVHL.js.map +0 -1
  79. package/dist/chunk-LNK7LZER.js +0 -51
  80. package/dist/chunk-LNK7LZER.js.map +0 -1
  81. package/dist/chunk-N63S5NEG.js +0 -107
  82. package/dist/chunk-N63S5NEG.js.map +0 -1
  83. package/dist/chunk-NVLZFLYM.js +0 -3
  84. package/dist/chunk-NVLZFLYM.js.map +0 -1
  85. package/dist/chunk-ONH7PIJZ.js +0 -300
  86. package/dist/chunk-ONH7PIJZ.js.map +0 -1
  87. package/dist/chunk-PH2IYZHV.js +0 -48
  88. package/dist/chunk-PH2IYZHV.js.map +0 -1
  89. package/dist/chunk-RAZWRNAJ.js +0 -54
  90. package/dist/chunk-RAZWRNAJ.js.map +0 -1
  91. package/dist/chunk-RM677CNU.js +0 -52
  92. package/dist/chunk-RM677CNU.js.map +0 -1
  93. package/dist/chunk-TKSSRS5U.js +0 -39
  94. package/dist/chunk-TKSSRS5U.js.map +0 -1
  95. package/dist/chunk-U7DKCIWT.js +0 -340
  96. package/dist/chunk-U7DKCIWT.js.map +0 -1
  97. package/dist/chunk-UNORA7EM.js +0 -103
  98. package/dist/chunk-UNORA7EM.js.map +0 -1
  99. package/dist/chunk-WWGFIYKW.js +0 -47
  100. package/dist/chunk-WWGFIYKW.js.map +0 -1
  101. package/dist/chunk-XZZWIJ4G.js +0 -25
  102. package/dist/chunk-XZZWIJ4G.js.map +0 -1
  103. package/dist/chunk-ZETQCNEF.js +0 -139
  104. package/dist/chunk-ZETQCNEF.js.map +0 -1
  105. package/dist/chunk-ZLW4QOTS.js +0 -192
  106. package/dist/chunk-ZLW4QOTS.js.map +0 -1
  107. package/dist/container-factory.d.ts +0 -17
  108. package/dist/container-factory.js +0 -13
  109. package/dist/container-factory.js.map +0 -1
  110. package/dist/container.d.ts +0 -23
  111. package/dist/container.js +0 -3
  112. package/dist/container.js.map +0 -1
  113. package/dist/context.d.ts +0 -65
  114. package/dist/context.js +0 -3
  115. package/dist/context.js.map +0 -1
  116. package/dist/error-mapper.d.ts +0 -15
  117. package/dist/error-mapper.js +0 -4
  118. package/dist/error-mapper.js.map +0 -1
  119. package/dist/errors.d.ts +0 -20
  120. package/dist/errors.js +0 -3
  121. package/dist/errors.js.map +0 -1
  122. package/dist/evaluator.d.ts +0 -32
  123. package/dist/evaluator.js +0 -3
  124. package/dist/evaluator.js.map +0 -1
  125. package/dist/flow.d.ts +0 -85
  126. package/dist/flow.js +0 -4
  127. package/dist/flow.js.map +0 -1
  128. package/dist/index.d.ts +0 -18
  129. package/dist/index.js +0 -38
  130. package/dist/index.js.map +0 -1
  131. package/dist/linter.d.ts +0 -26
  132. package/dist/linter.js +0 -4
  133. package/dist/linter.js.map +0 -1
  134. package/dist/logger.d.ts +0 -20
  135. package/dist/logger.js +0 -3
  136. package/dist/logger.js.map +0 -1
  137. package/dist/node.d.ts +0 -3
  138. package/dist/node.js +0 -3
  139. package/dist/node.js.map +0 -1
  140. package/dist/nodes/batch-gather.d.ts +0 -9
  141. package/dist/nodes/batch-gather.js +0 -4
  142. package/dist/nodes/batch-gather.js.map +0 -1
  143. package/dist/nodes/batch-scatter.d.ts +0 -9
  144. package/dist/nodes/batch-scatter.js +0 -4
  145. package/dist/nodes/batch-scatter.js.map +0 -1
  146. package/dist/nodes/sleep.d.ts +0 -9
  147. package/dist/nodes/sleep.js +0 -4
  148. package/dist/nodes/sleep.js.map +0 -1
  149. package/dist/nodes/subflow.d.ts +0 -9
  150. package/dist/nodes/subflow.js +0 -10
  151. package/dist/nodes/subflow.js.map +0 -1
  152. package/dist/nodes/wait.d.ts +0 -9
  153. package/dist/nodes/wait.js +0 -4
  154. package/dist/nodes/wait.js.map +0 -1
  155. package/dist/nodes/webhook.d.ts +0 -13
  156. package/dist/nodes/webhook.js +0 -4
  157. package/dist/nodes/webhook.js.map +0 -1
  158. package/dist/runtime/adapter.d.ts +0 -114
  159. package/dist/runtime/adapter.js +0 -28
  160. package/dist/runtime/adapter.js.map +0 -1
  161. package/dist/runtime/builtin-keys.d.ts +0 -38
  162. package/dist/runtime/builtin-keys.js +0 -10
  163. package/dist/runtime/builtin-keys.js.map +0 -1
  164. package/dist/runtime/execution-context.d.ts +0 -3
  165. package/dist/runtime/execution-context.js +0 -6
  166. package/dist/runtime/execution-context.js.map +0 -1
  167. package/dist/runtime/executors.d.ts +0 -3
  168. package/dist/runtime/executors.js +0 -4
  169. package/dist/runtime/executors.js.map +0 -1
  170. package/dist/runtime/index.d.ts +0 -7
  171. package/dist/runtime/index.js +0 -31
  172. package/dist/runtime/index.js.map +0 -1
  173. package/dist/runtime/node-executor-factory.d.ts +0 -12
  174. package/dist/runtime/node-executor-factory.js +0 -6
  175. package/dist/runtime/node-executor-factory.js.map +0 -1
  176. package/dist/runtime/orchestrator.d.ts +0 -9
  177. package/dist/runtime/orchestrator.js +0 -8
  178. package/dist/runtime/orchestrator.js.map +0 -1
  179. package/dist/runtime/orchestrators/replay.d.ts +0 -45
  180. package/dist/runtime/orchestrators/replay.js +0 -3
  181. package/dist/runtime/orchestrators/replay.js.map +0 -1
  182. package/dist/runtime/orchestrators/step-by-step.d.ts +0 -16
  183. package/dist/runtime/orchestrators/step-by-step.js +0 -5
  184. package/dist/runtime/orchestrators/step-by-step.js.map +0 -1
  185. package/dist/runtime/orchestrators/utils.d.ts +0 -35
  186. package/dist/runtime/orchestrators/utils.js +0 -4
  187. package/dist/runtime/orchestrators/utils.js.map +0 -1
  188. package/dist/runtime/runtime.d.ts +0 -3
  189. package/dist/runtime/runtime.js +0 -27
  190. package/dist/runtime/runtime.js.map +0 -1
  191. package/dist/runtime/scheduler.d.ts +0 -3
  192. package/dist/runtime/scheduler.js +0 -3
  193. package/dist/runtime/scheduler.js.map +0 -1
  194. package/dist/runtime/state.d.ts +0 -3
  195. package/dist/runtime/state.js +0 -5
  196. package/dist/runtime/state.js.map +0 -1
  197. package/dist/runtime/traverser.d.ts +0 -3
  198. package/dist/runtime/traverser.js +0 -4
  199. package/dist/runtime/traverser.js.map +0 -1
  200. package/dist/runtime/types.d.ts +0 -3
  201. package/dist/runtime/types.js +0 -3
  202. package/dist/runtime/types.js.map +0 -1
  203. package/dist/runtime/workflow-logic-handler.d.ts +0 -17
  204. package/dist/runtime/workflow-logic-handler.js +0 -5
  205. package/dist/runtime/workflow-logic-handler.js.map +0 -1
  206. package/dist/sanitizer.d.ts +0 -12
  207. package/dist/sanitizer.js +0 -3
  208. package/dist/sanitizer.js.map +0 -1
  209. package/dist/sdk.js +0 -20
  210. package/dist/sdk.js.map +0 -1
  211. package/dist/serializer.d.ts +0 -18
  212. package/dist/serializer.js +0 -3
  213. package/dist/serializer.js.map +0 -1
  214. package/dist/testing/event-logger.d.ts +0 -63
  215. package/dist/testing/event-logger.js +0 -3
  216. package/dist/testing/event-logger.js.map +0 -1
  217. package/dist/testing/index.d.ts +0 -7
  218. package/dist/testing/index.js +0 -37
  219. package/dist/testing/index.js.map +0 -1
  220. package/dist/testing/run-with-trace.d.ts +0 -38
  221. package/dist/testing/run-with-trace.js +0 -33
  222. package/dist/testing/run-with-trace.js.map +0 -1
  223. package/dist/testing/stepper.d.ts +0 -79
  224. package/dist/testing/stepper.js +0 -11
  225. package/dist/testing/stepper.js.map +0 -1
  226. package/dist/types-CKhffqyb.d.ts +0 -666
  227. package/dist/types.d.ts +0 -3
  228. package/dist/types.js +0 -3
  229. package/dist/types.js.map +0 -1
@@ -0,0 +1,172 @@
1
+ import { Ct as WorkflowState, F as PersistentEventBusAdapter, K as NodeClass, N as IEventStore, P as InMemoryEventStore, R as FlowcraftEvent, V as IEventBus, X as NodeFunction, at as WorkflowBlueprint, ct as WorkflowResult, ht as GraphTraverser, ut as FlowRuntime } from "../index-D3dyjW2G.mjs";
2
+
3
+ //#region src/testing/event-logger.d.ts
4
+ /**
5
+ * A test utility that implements IEventBus to capture all workflow events
6
+ * in memory, acting as a "flight recorder" for behavioral testing.
7
+ *
8
+ * @example
9
+ * // In your test file (e.g., resiliency.test.ts)
10
+ * it('should retry a node on failure', async () => {
11
+ * const eventLogger = new InMemoryEventLogger();
12
+ * const runtime = new FlowRuntime({ eventBus: eventLogger });
13
+ *
14
+ * const flow = createFlow('retry-flow')
15
+ * .node('api-call', vi.fn().mockRejectedValueOnce(new Error('fail')), {
16
+ * config: { maxRetries: 2 },
17
+ * });
18
+ *
19
+ * await runtime.run(flow.toBlueprint());
20
+ *
21
+ * // Assert against the captured event history to prove behavior.
22
+ * const retryEvents = eventLogger.filter('node:retry');
23
+ * expect(retryEvents).toHaveLength(1); // The first attempt is not a "retry"
24
+ * });
25
+ */
26
+ declare class InMemoryEventLogger implements IEventBus {
27
+ readonly events: FlowcraftEvent[];
28
+ /**
29
+ * Clears all captured events.
30
+ */
31
+ clear(): void;
32
+ /**
33
+ * The `emit` method required by the IEventBus interface.
34
+ * It simply pushes the received event into the internal events array.
35
+ * @param event The FlowcraftEvent to record.
36
+ */
37
+ emit(event: FlowcraftEvent): Promise<void>;
38
+ /**
39
+ * Finds the first event of a specific type.
40
+ * @param type The event type to find (e.g., 'node:error').
41
+ * @returns The first matching event, or undefined if not found.
42
+ */
43
+ find<T extends FlowcraftEvent['type']>(type: T): Extract<FlowcraftEvent, {
44
+ type: T;
45
+ }> | undefined;
46
+ /**
47
+ * Filters events to find all occurrences of a specific type.
48
+ * @param type The event type to filter by.
49
+ * @returns An array of matching events.
50
+ */
51
+ filter<T extends FlowcraftEvent['type']>(type: T): Extract<FlowcraftEvent, {
52
+ type: T;
53
+ }>[];
54
+ /**
55
+ * Prints a formatted log of all captured events to the console.
56
+ * Ideal for debugging failing tests.
57
+ * @param title A title for the log output.
58
+ */
59
+ printLog(title?: string): void;
60
+ }
61
+ //#endregion
62
+ //#region src/testing/run-with-trace.d.ts
63
+ /**
64
+ * A test helper that executes a workflow and automatically prints a detailed
65
+ * execution trace to the console if the workflow fails.
66
+ *
67
+ * @example
68
+ * // In your test file (e.g., my-workflow.test.ts)
69
+ * it('should process data correctly', async () => {
70
+ * const flow = createFlow('my-flow')
71
+ * .node('a', async () => ({ output: 1 }))
72
+ * .node('b', async ({ input }) => ({ output: input + 1 })) // Bug: returns { output: 3 }
73
+ * .edge('a', 'b')
74
+ *
75
+ * const runtime = new FlowRuntime({})
76
+ *
77
+ * // If this test fails, a full, human-readable trace of the execution
78
+ * // (inputs, outputs, context changes) is printed to the console.
79
+ * const result = await runWithTrace(runtime, flow.toBlueprint())
80
+ *
81
+ * expect(result.context.b).toBe(2)
82
+ * })
83
+ *
84
+ * @param runtime The original FlowRuntime instance (its options will be used).
85
+ * @param blueprint The WorkflowBlueprint to execute.
86
+ * @param initialState The initial state for the workflow run.
87
+ * @param options Additional options for the run.
88
+ * @returns The WorkflowResult if successful.
89
+ */
90
+ declare function runWithTrace<TContext extends Record<string, any>>(runtime: FlowRuntime<TContext, any>, blueprint: WorkflowBlueprint, initialState?: Partial<TContext> | string, options?: {
91
+ functionRegistry?: Map<string, any>;
92
+ strict?: boolean;
93
+ signal?: AbortSignal;
94
+ }): Promise<WorkflowResult<Record<string, any>>>;
95
+ //#endregion
96
+ //#region src/testing/stepper.d.ts
97
+ /**
98
+ * Represents the controlled, step-by-step execution of a workflow.
99
+ * Returned by the `createStepper` utility.
100
+ */
101
+ interface IWorkflowStepper<TContext extends Record<string, any>> {
102
+ /** The current state of the workflow. Can be inspected after each step. */
103
+ readonly state: WorkflowState<TContext>;
104
+ /** The graph traverser instance. Can be used to inspect the frontier or completed nodes. */
105
+ readonly traverser: GraphTraverser;
106
+ /**
107
+ * Executes the next "turn" or batch of ready nodes in the workflow.
108
+ * @param options Optional configuration for this specific step, like a cancellation signal.
109
+ * @returns A `WorkflowResult` representing the state after the step, or `null` if the workflow has already completed.
110
+ */
111
+ next(options?: {
112
+ signal?: AbortSignal;
113
+ concurrency?: number;
114
+ }): Promise<WorkflowResult<TContext> | null>;
115
+ /**
116
+ * Reverts the workflow to its previous state.
117
+ * @returns The `WorkflowResult` of the previous state, or `null` if there is no history to revert to.
118
+ */
119
+ prev(): Promise<WorkflowResult<TContext> | null>;
120
+ /**
121
+ * Resets the stepper to its initial state, clearing all progress and history.
122
+ */
123
+ reset(): void;
124
+ /**
125
+ * A convenience method to check if the workflow has any more steps to run.
126
+ * @returns `true` if the workflow is complete or stalled, `false` otherwise.
127
+ */
128
+ isDone(): boolean;
129
+ }
130
+ /**
131
+ * A test utility that creates a stepper to execute a workflow one "turn" at a time.
132
+ * This is invaluable for debugging and writing fine-grained tests where you need to
133
+ * assert the state of the workflow after each logical step.
134
+ *
135
+ * @example
136
+ * // In your test file
137
+ * it('should correctly execute step-by-step', async () => {
138
+ * const runtime = new FlowRuntime({ ... });
139
+ * const flow = createFlow('test')
140
+ * .node('a', async () => ({ output: 10 }))
141
+ * .node('b', async ({ input }) => ({ output: input * 2 }))
142
+ * .edge('a', 'b');
143
+ *
144
+ * const stepper = await createStepper(runtime, flow.toBlueprint(), flow.getFunctionRegistry());
145
+ *
146
+ * // First step (executes node 'a')
147
+ * const result1 = await stepper.next();
148
+ * expect(stepper.isDone()).toBe(false);
149
+ * expect(result1.status).toBe('stalled');
150
+ * expect(result1.context._outputs.a).toBe(10);
151
+ *
152
+ * // Second step (executes node 'b')
153
+ * const result2 = await stepper.next();
154
+ * expect(stepper.isDone()).toBe(true);
155
+ * expect(result2.status).toBe('completed');
156
+ * expect(result2.context._outputs.b).toBe(20);
157
+ *
158
+ * // Final step (no more work)
159
+ * const result3 = await stepper.next();
160
+ * expect(result3).toBeNull();
161
+ * });
162
+ *
163
+ * @param runtime The `FlowRuntime` instance, used for its configuration.
164
+ * @param blueprint The `WorkflowBlueprint` to execute.
165
+ * @param functionRegistry The function registry from createFlow, containing the node implementations.
166
+ * @param initialState The initial state for the workflow run.
167
+ * @returns A Promise that resolves to an `IWorkflowStepper` instance.
168
+ */
169
+ declare function createStepper<TContext extends Record<string, any>, TDependencies extends Record<string, any>>(runtime: FlowRuntime<TContext, TDependencies>, blueprint: WorkflowBlueprint, functionRegistry: Map<string, NodeFunction | NodeClass>, initialState?: Partial<TContext>): Promise<IWorkflowStepper<TContext>>;
170
+ //#endregion
171
+ export { IEventStore, IWorkflowStepper, InMemoryEventLogger, InMemoryEventStore, PersistentEventBusAdapter, createStepper, runWithTrace };
172
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1,277 @@
1
+ import { A as PersistentEventBusAdapter, d as executeBatch, f as processResults, k as InMemoryEventStore, m as WorkflowState, o as GraphTraverser, p as ExecutionContext, t as FlowRuntime, v as FlowcraftError } from "../runtime-CmefIAu_.mjs";
2
+
3
+ //#region src/testing/event-logger.ts
4
+ /**
5
+ * A test utility that implements IEventBus to capture all workflow events
6
+ * in memory, acting as a "flight recorder" for behavioral testing.
7
+ *
8
+ * @example
9
+ * // In your test file (e.g., resiliency.test.ts)
10
+ * it('should retry a node on failure', async () => {
11
+ * const eventLogger = new InMemoryEventLogger();
12
+ * const runtime = new FlowRuntime({ eventBus: eventLogger });
13
+ *
14
+ * const flow = createFlow('retry-flow')
15
+ * .node('api-call', vi.fn().mockRejectedValueOnce(new Error('fail')), {
16
+ * config: { maxRetries: 2 },
17
+ * });
18
+ *
19
+ * await runtime.run(flow.toBlueprint());
20
+ *
21
+ * // Assert against the captured event history to prove behavior.
22
+ * const retryEvents = eventLogger.filter('node:retry');
23
+ * expect(retryEvents).toHaveLength(1); // The first attempt is not a "retry"
24
+ * });
25
+ */
26
+ var InMemoryEventLogger = class {
27
+ events = [];
28
+ /**
29
+ * Clears all captured events.
30
+ */
31
+ clear() {
32
+ this.events.length = 0;
33
+ }
34
+ /**
35
+ * The `emit` method required by the IEventBus interface.
36
+ * It simply pushes the received event into the internal events array.
37
+ * @param event The FlowcraftEvent to record.
38
+ */
39
+ async emit(event) {
40
+ this.events.push(event);
41
+ }
42
+ /**
43
+ * Finds the first event of a specific type.
44
+ * @param type The event type to find (e.g., 'node:error').
45
+ * @returns The first matching event, or undefined if not found.
46
+ */
47
+ find(type) {
48
+ return this.events.find((e) => e.type === type);
49
+ }
50
+ /**
51
+ * Filters events to find all occurrences of a specific type.
52
+ * @param type The event type to filter by.
53
+ * @returns An array of matching events.
54
+ */
55
+ filter(type) {
56
+ return this.events.filter((e) => e.type === type);
57
+ }
58
+ /**
59
+ * Prints a formatted log of all captured events to the console.
60
+ * Ideal for debugging failing tests.
61
+ * @param title A title for the log output.
62
+ */
63
+ printLog(title = "Workflow Execution Trace") {
64
+ console.log(`\n--- ${title} ---`);
65
+ if (this.events.length === 0) {
66
+ console.log("No events were captured.");
67
+ console.log("----------------------------------\n");
68
+ return;
69
+ }
70
+ this.events.forEach((event, index) => {
71
+ const { type, payload } = event;
72
+ console.log(`\n[${index + 1}] ${type}`);
73
+ switch (type) {
74
+ case "node:start":
75
+ console.log(` - Node: "${payload.nodeId}" | Input: ${JSON.stringify(payload.input)}`);
76
+ break;
77
+ case "edge:evaluate":
78
+ console.log(` - Edge: "${payload.source}" -> "${payload.target}"`);
79
+ console.log(` - Condition: ${payload.condition || "N/A"} | Result: ${payload.result}`);
80
+ break;
81
+ case "context:change":
82
+ if (payload.op === "set") console.log(` - Node "${payload.sourceNode}" wrote to context -> Key: "${payload.key}" | Value: ${JSON.stringify(payload.value)}`);
83
+ else if (payload.op === "delete") console.log(` - Node "${payload.sourceNode}" deleted from context -> Key: "${payload.key}"`);
84
+ break;
85
+ case "node:finish":
86
+ console.log(` - Node: "${payload.nodeId}" | Result: ${JSON.stringify(payload.result)}`);
87
+ break;
88
+ case "node:error":
89
+ console.log(` - Node: "${payload.nodeId}"`);
90
+ console.error(" - Error:", payload.error);
91
+ break;
92
+ default: console.log(` - Payload: ${JSON.stringify(payload, null, 2)}`);
93
+ }
94
+ });
95
+ console.log(`\n--- End of Trace ---`);
96
+ }
97
+ };
98
+
99
+ //#endregion
100
+ //#region src/testing/run-with-trace.ts
101
+ /**
102
+ * A test helper that executes a workflow and automatically prints a detailed
103
+ * execution trace to the console if the workflow fails.
104
+ *
105
+ * @example
106
+ * // In your test file (e.g., my-workflow.test.ts)
107
+ * it('should process data correctly', async () => {
108
+ * const flow = createFlow('my-flow')
109
+ * .node('a', async () => ({ output: 1 }))
110
+ * .node('b', async ({ input }) => ({ output: input + 1 })) // Bug: returns { output: 3 }
111
+ * .edge('a', 'b')
112
+ *
113
+ * const runtime = new FlowRuntime({})
114
+ *
115
+ * // If this test fails, a full, human-readable trace of the execution
116
+ * // (inputs, outputs, context changes) is printed to the console.
117
+ * const result = await runWithTrace(runtime, flow.toBlueprint())
118
+ *
119
+ * expect(result.context.b).toBe(2)
120
+ * })
121
+ *
122
+ * @param runtime The original FlowRuntime instance (its options will be used).
123
+ * @param blueprint The WorkflowBlueprint to execute.
124
+ * @param initialState The initial state for the workflow run.
125
+ * @param options Additional options for the run.
126
+ * @returns The WorkflowResult if successful.
127
+ */
128
+ async function runWithTrace(runtime, blueprint, initialState = {}, options = {}) {
129
+ const eventLogger = new InMemoryEventLogger();
130
+ const testRuntime = new FlowRuntime({
131
+ ...runtime.options,
132
+ eventBus: eventLogger
133
+ });
134
+ try {
135
+ const result = await testRuntime.run(blueprint, initialState, options);
136
+ if (process.env.DEBUG) eventLogger.printLog(`Successful Trace: ${blueprint.id}`);
137
+ return result;
138
+ } catch (error) {
139
+ eventLogger.printLog(`Failing Test Trace: ${blueprint.id}`);
140
+ throw error;
141
+ }
142
+ }
143
+
144
+ //#endregion
145
+ //#region src/runtime/orchestrators/step-by-step.ts
146
+ /**
147
+ * An orchestrator that executes only one "tick" or "turn" of the workflow.
148
+ * It processes a single batch of ready nodes from the frontier and then returns,
149
+ * allowing the caller to inspect the intermediate state before proceeding.
150
+ *
151
+ * Useful for debugging, testing, or building interactive tools.
152
+ */
153
+ var StepByStepOrchestrator = class {
154
+ async run(context, traverser) {
155
+ try {
156
+ context.signal?.throwIfAborted();
157
+ } catch (error) {
158
+ if (error instanceof DOMException && error.name === "AbortError") throw new FlowcraftError("Workflow cancelled", { isFatal: false });
159
+ throw error;
160
+ }
161
+ if (!traverser.hasMoreWork()) {
162
+ const isTraversalComplete = !traverser.hasMoreWork();
163
+ const status = context.state.getStatus(isTraversalComplete);
164
+ const result = await context.state.toResult(context.services.serializer, context.executionId);
165
+ result.status = status;
166
+ return result;
167
+ }
168
+ const allReadyNodes = traverser.getReadyNodes();
169
+ const nodesToExecute = context.concurrency ? allReadyNodes.slice(0, context.concurrency) : allReadyNodes;
170
+ const nodesToSkip = context.concurrency ? allReadyNodes.slice(context.concurrency) : [];
171
+ await processResults(await executeBatch(nodesToExecute, traverser.getDynamicBlueprint(), context.state, (nodeId) => context.runtime.getExecutorForNode(nodeId, context), context.runtime, context.concurrency), traverser, context.state, context.runtime, context.blueprint, context.executionId);
172
+ for (const { nodeId } of nodesToSkip) traverser.addToFrontier(nodeId);
173
+ const isTraversalComplete = !traverser.hasMoreWork();
174
+ const status = context.state.getStatus(isTraversalComplete);
175
+ const result = await context.state.toResult(context.services.serializer, context.executionId);
176
+ result.status = status;
177
+ return result;
178
+ }
179
+ };
180
+
181
+ //#endregion
182
+ //#region src/testing/stepper.ts
183
+ /**
184
+ * A test utility that creates a stepper to execute a workflow one "turn" at a time.
185
+ * This is invaluable for debugging and writing fine-grained tests where you need to
186
+ * assert the state of the workflow after each logical step.
187
+ *
188
+ * @example
189
+ * // In your test file
190
+ * it('should correctly execute step-by-step', async () => {
191
+ * const runtime = new FlowRuntime({ ... });
192
+ * const flow = createFlow('test')
193
+ * .node('a', async () => ({ output: 10 }))
194
+ * .node('b', async ({ input }) => ({ output: input * 2 }))
195
+ * .edge('a', 'b');
196
+ *
197
+ * const stepper = await createStepper(runtime, flow.toBlueprint(), flow.getFunctionRegistry());
198
+ *
199
+ * // First step (executes node 'a')
200
+ * const result1 = await stepper.next();
201
+ * expect(stepper.isDone()).toBe(false);
202
+ * expect(result1.status).toBe('stalled');
203
+ * expect(result1.context._outputs.a).toBe(10);
204
+ *
205
+ * // Second step (executes node 'b')
206
+ * const result2 = await stepper.next();
207
+ * expect(stepper.isDone()).toBe(true);
208
+ * expect(result2.status).toBe('completed');
209
+ * expect(result2.context._outputs.b).toBe(20);
210
+ *
211
+ * // Final step (no more work)
212
+ * const result3 = await stepper.next();
213
+ * expect(result3).toBeNull();
214
+ * });
215
+ *
216
+ * @param runtime The `FlowRuntime` instance, used for its configuration.
217
+ * @param blueprint The `WorkflowBlueprint` to execute.
218
+ * @param functionRegistry The function registry from createFlow, containing the node implementations.
219
+ * @param initialState The initial state for the workflow run.
220
+ * @returns A Promise that resolves to an `IWorkflowStepper` instance.
221
+ */
222
+ async function createStepper(runtime, blueprint, functionRegistry, initialState = {}) {
223
+ const _initialBlueprint = structuredClone(blueprint);
224
+ const _initialState = structuredClone(initialState);
225
+ let state;
226
+ let traverser;
227
+ const history = [];
228
+ const orchestrator = new StepByStepOrchestrator();
229
+ const executionId = globalThis.crypto?.randomUUID();
230
+ const nodeRegistry = new Map([...runtime.registry, ...functionRegistry]);
231
+ const initialize = () => {
232
+ state = new WorkflowState(_initialState);
233
+ traverser = new GraphTraverser(_initialBlueprint);
234
+ history.length = 0;
235
+ };
236
+ initialize();
237
+ const stepper = {
238
+ get state() {
239
+ return state;
240
+ },
241
+ get traverser() {
242
+ return traverser;
243
+ },
244
+ isDone() {
245
+ return !traverser.hasMoreWork() && !state.isAwaiting();
246
+ },
247
+ reset() {
248
+ initialize();
249
+ },
250
+ async prev() {
251
+ const previousStateJson = history.pop();
252
+ if (!previousStateJson) return null;
253
+ state = new WorkflowState(runtime.serializer.deserialize(previousStateJson));
254
+ traverser = GraphTraverser.fromState(_initialBlueprint, state);
255
+ return state.toResult(runtime.serializer, void 0);
256
+ },
257
+ async next(options = {}) {
258
+ if (stepper.isDone()) return null;
259
+ const serializedContext = (await state.toResult(runtime.serializer, void 0)).serializedContext;
260
+ history.push(serializedContext);
261
+ const executionContext = new ExecutionContext(_initialBlueprint, state, nodeRegistry, executionId, runtime, {
262
+ logger: runtime.logger,
263
+ eventBus: runtime.eventBus,
264
+ serializer: runtime.serializer,
265
+ evaluator: runtime.evaluator,
266
+ middleware: runtime.middleware,
267
+ dependencies: runtime.dependencies
268
+ }, options.signal, options.concurrency);
269
+ return orchestrator.run(executionContext, traverser);
270
+ }
271
+ };
272
+ return stepper;
273
+ }
274
+
275
+ //#endregion
276
+ export { InMemoryEventLogger, InMemoryEventStore, PersistentEventBusAdapter, createStepper, runWithTrace };
277
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/testing/event-logger.ts","../../src/testing/run-with-trace.ts","../../src/runtime/orchestrators/step-by-step.ts","../../src/testing/stepper.ts"],"sourcesContent":["import type { FlowcraftEvent, IEventBus } from '../types'\n\n/**\n * A test utility that implements IEventBus to capture all workflow events\n * in memory, acting as a \"flight recorder\" for behavioral testing.\n *\n * @example\n * // In your test file (e.g., resiliency.test.ts)\n * it('should retry a node on failure', async () => {\n * const eventLogger = new InMemoryEventLogger();\n * const runtime = new FlowRuntime({ eventBus: eventLogger });\n *\n * const flow = createFlow('retry-flow')\n * .node('api-call', vi.fn().mockRejectedValueOnce(new Error('fail')), {\n * config: { maxRetries: 2 },\n * });\n *\n * await runtime.run(flow.toBlueprint());\n *\n * // Assert against the captured event history to prove behavior.\n * const retryEvents = eventLogger.filter('node:retry');\n * expect(retryEvents).toHaveLength(1); // The first attempt is not a \"retry\"\n * });\n */\nexport class InMemoryEventLogger implements IEventBus {\n\tpublic readonly events: FlowcraftEvent[] = []\n\n\t/**\n\t * Clears all captured events.\n\t */\n\tpublic clear(): void {\n\t\tthis.events.length = 0\n\t}\n\n\t/**\n\t * The `emit` method required by the IEventBus interface.\n\t * It simply pushes the received event into the internal events array.\n\t * @param event The FlowcraftEvent to record.\n\t */\n\tpublic async emit(event: FlowcraftEvent): Promise<void> {\n\t\tthis.events.push(event)\n\t}\n\n\t/**\n\t * Finds the first event of a specific type.\n\t * @param type The event type to find (e.g., 'node:error').\n\t * @returns The first matching event, or undefined if not found.\n\t */\n\tpublic find<T extends FlowcraftEvent['type']>(type: T): Extract<FlowcraftEvent, { type: T }> | undefined {\n\t\treturn this.events.find((e) => e.type === type) as Extract<FlowcraftEvent, { type: T }> | undefined\n\t}\n\n\t/**\n\t * Filters events to find all occurrences of a specific type.\n\t * @param type The event type to filter by.\n\t * @returns An array of matching events.\n\t */\n\tpublic filter<T extends FlowcraftEvent['type']>(type: T): Extract<FlowcraftEvent, { type: T }>[] {\n\t\treturn this.events.filter((e) => e.type === type) as Extract<FlowcraftEvent, { type: T }>[]\n\t}\n\n\t/**\n\t * Prints a formatted log of all captured events to the console.\n\t * Ideal for debugging failing tests.\n\t * @param title A title for the log output.\n\t */\n\tpublic printLog(title = 'Workflow Execution Trace'): void {\n\t\tconsole.log(`\\n--- ${title} ---`)\n\t\tif (this.events.length === 0) {\n\t\t\tconsole.log('No events were captured.')\n\t\t\tconsole.log('----------------------------------\\n')\n\t\t\treturn\n\t\t}\n\n\t\tthis.events.forEach((event, index) => {\n\t\t\tconst { type, payload } = event\n\t\t\tconsole.log(`\\n[${index + 1}] ${type}`)\n\n\t\t\t// Custom formatting for a more intuitive trace\n\t\t\tswitch (type) {\n\t\t\t\tcase 'node:start':\n\t\t\t\t\tconsole.log(` - Node: \"${payload.nodeId}\" | Input: ${JSON.stringify(payload.input)}`)\n\t\t\t\t\tbreak\n\t\t\t\tcase 'edge:evaluate':\n\t\t\t\t\tconsole.log(` - Edge: \"${payload.source}\" -> \"${payload.target}\"`)\n\t\t\t\t\tconsole.log(` - Condition: ${payload.condition || 'N/A'} | Result: ${payload.result}`)\n\t\t\t\t\tbreak\n\t\t\t\tcase 'context:change':\n\t\t\t\t\tif (payload.op === 'set') {\n\t\t\t\t\t\tconsole.log(\n\t\t\t\t\t\t\t` - Node \"${payload.sourceNode}\" wrote to context -> Key: \"${payload.key}\" | Value: ${JSON.stringify(payload.value)}`,\n\t\t\t\t\t\t)\n\t\t\t\t\t} else if (payload.op === 'delete') {\n\t\t\t\t\t\tconsole.log(` - Node \"${payload.sourceNode}\" deleted from context -> Key: \"${payload.key}\"`)\n\t\t\t\t\t}\n\t\t\t\t\tbreak\n\t\t\t\tcase 'node:finish':\n\t\t\t\t\tconsole.log(` - Node: \"${payload.nodeId}\" | Result: ${JSON.stringify(payload.result)}`)\n\t\t\t\t\tbreak\n\t\t\t\tcase 'node:error':\n\t\t\t\t\tconsole.log(` - Node: \"${payload.nodeId}\"`)\n\t\t\t\t\tconsole.error(' - Error:', payload.error)\n\t\t\t\t\tbreak\n\t\t\t\tdefault:\n\t\t\t\t\tconsole.log(` - Payload: ${JSON.stringify(payload, null, 2)}`)\n\t\t\t}\n\t\t})\n\t\tconsole.log(`\\n--- End of Trace ---`)\n\t}\n}\n","import { FlowRuntime } from '../runtime'\nimport type { WorkflowBlueprint } from '../types'\nimport { InMemoryEventLogger } from './event-logger'\n\n/**\n * A test helper that executes a workflow and automatically prints a detailed\n * execution trace to the console if the workflow fails.\n *\n * @example\n * // In your test file (e.g., my-workflow.test.ts)\n * it('should process data correctly', async () => {\n * const flow = createFlow('my-flow')\n * .node('a', async () => ({ output: 1 }))\n * .node('b', async ({ input }) => ({ output: input + 1 })) // Bug: returns { output: 3 }\n * .edge('a', 'b')\n *\n * const runtime = new FlowRuntime({})\n *\n * // If this test fails, a full, human-readable trace of the execution\n * // (inputs, outputs, context changes) is printed to the console.\n * const result = await runWithTrace(runtime, flow.toBlueprint())\n *\n * expect(result.context.b).toBe(2)\n * })\n *\n * @param runtime The original FlowRuntime instance (its options will be used).\n * @param blueprint The WorkflowBlueprint to execute.\n * @param initialState The initial state for the workflow run.\n * @param options Additional options for the run.\n * @returns The WorkflowResult if successful.\n */\nexport async function runWithTrace<TContext extends Record<string, any>>(\n\truntime: FlowRuntime<TContext, any>,\n\tblueprint: WorkflowBlueprint,\n\tinitialState: Partial<TContext> | string = {},\n\toptions: {\n\t\tfunctionRegistry?: Map<string, any>\n\t\tstrict?: boolean\n\t\tsignal?: AbortSignal\n\t} = {},\n) {\n\tconst eventLogger = new InMemoryEventLogger()\n\tconst testRuntime = new FlowRuntime({\n\t\t...runtime.options,\n\t\teventBus: eventLogger,\n\t})\n\n\ttry {\n\t\tconst result = await testRuntime.run(blueprint, initialState, options)\n\t\tif (process.env.DEBUG) {\n\t\t\teventLogger.printLog(`Successful Trace: ${blueprint.id}`)\n\t\t}\n\t\treturn result\n\t} catch (error) {\n\t\teventLogger.printLog(`Failing Test Trace: ${blueprint.id}`)\n\t\tthrow error\n\t}\n}\n","import { FlowcraftError } from '../../errors'\n\nimport type { WorkflowResult } from '../../types'\nimport type { ExecutionContext } from '../execution-context'\nimport type { GraphTraverser } from '../traverser'\nimport type { IOrchestrator } from '../types'\nimport { executeBatch, processResults } from './utils'\n\n/**\n * An orchestrator that executes only one \"tick\" or \"turn\" of the workflow.\n * It processes a single batch of ready nodes from the frontier and then returns,\n * allowing the caller to inspect the intermediate state before proceeding.\n *\n * Useful for debugging, testing, or building interactive tools.\n */\nexport class StepByStepOrchestrator implements IOrchestrator {\n\tpublic async run(context: ExecutionContext<any, any>, traverser: GraphTraverser): Promise<WorkflowResult<any>> {\n\t\ttry {\n\t\t\tcontext.signal?.throwIfAborted()\n\t\t} catch (error) {\n\t\t\tif (error instanceof DOMException && error.name === 'AbortError') {\n\t\t\t\tthrow new FlowcraftError('Workflow cancelled', { isFatal: false })\n\t\t\t}\n\t\t\tthrow error\n\t\t}\n\n\t\tif (!traverser.hasMoreWork()) {\n\t\t\tconst isTraversalComplete = !traverser.hasMoreWork()\n\t\t\tconst status = context.state.getStatus(isTraversalComplete)\n\t\t\tconst result = await context.state.toResult(context.services.serializer, context.executionId)\n\t\t\tresult.status = status\n\t\t\treturn result\n\t\t}\n\n\t\tconst allReadyNodes = traverser.getReadyNodes()\n\t\tconst nodesToExecute = context.concurrency ? allReadyNodes.slice(0, context.concurrency) : allReadyNodes\n\t\tconst nodesToSkip = context.concurrency ? allReadyNodes.slice(context.concurrency) : []\n\n\t\tconst settledResults = await executeBatch(\n\t\t\tnodesToExecute,\n\t\t\ttraverser.getDynamicBlueprint(),\n\t\t\tcontext.state,\n\t\t\t(nodeId: string) => context.runtime.getExecutorForNode(nodeId, context),\n\t\t\tcontext.runtime,\n\t\t\tcontext.concurrency,\n\t\t)\n\n\t\tawait processResults(\n\t\t\tsettledResults,\n\t\t\ttraverser,\n\t\t\tcontext.state,\n\t\t\tcontext.runtime,\n\t\t\tcontext.blueprint,\n\t\t\tcontext.executionId,\n\t\t)\n\n\t\tfor (const { nodeId } of nodesToSkip) {\n\t\t\ttraverser.addToFrontier(nodeId)\n\t\t}\n\n\t\tconst isTraversalComplete = !traverser.hasMoreWork()\n\t\tconst status = context.state.getStatus(isTraversalComplete)\n\t\tconst result = await context.state.toResult(context.services.serializer, context.executionId)\n\t\tresult.status = status\n\t\treturn result\n\t}\n}\n","import type { FlowRuntime } from '../runtime'\nimport { ExecutionContext } from '../runtime/execution-context'\nimport { StepByStepOrchestrator } from '../runtime/orchestrators/step-by-step'\nimport { WorkflowState } from '../runtime/state'\nimport { GraphTraverser } from '../runtime/traverser'\nimport type { NodeClass, NodeFunction, WorkflowBlueprint, WorkflowResult } from '../types'\n\n/**\n * Represents the controlled, step-by-step execution of a workflow.\n * Returned by the `createStepper` utility.\n */\nexport interface IWorkflowStepper<TContext extends Record<string, any>> {\n\t/** The current state of the workflow. Can be inspected after each step. */\n\treadonly state: WorkflowState<TContext>\n\n\t/** The graph traverser instance. Can be used to inspect the frontier or completed nodes. */\n\treadonly traverser: GraphTraverser\n\n\t/**\n\t * Executes the next \"turn\" or batch of ready nodes in the workflow.\n\t * @param options Optional configuration for this specific step, like a cancellation signal.\n\t * @returns A `WorkflowResult` representing the state after the step, or `null` if the workflow has already completed.\n\t */\n\tnext(options?: { signal?: AbortSignal; concurrency?: number }): Promise<WorkflowResult<TContext> | null>\n\n\t/**\n\t * Reverts the workflow to its previous state.\n\t * @returns The `WorkflowResult` of the previous state, or `null` if there is no history to revert to.\n\t */\n\tprev(): Promise<WorkflowResult<TContext> | null>\n\n\t/**\n\t * Resets the stepper to its initial state, clearing all progress and history.\n\t */\n\treset(): void\n\n\t/**\n\t * A convenience method to check if the workflow has any more steps to run.\n\t * @returns `true` if the workflow is complete or stalled, `false` otherwise.\n\t */\n\tisDone(): boolean\n}\n\n/**\n * A test utility that creates a stepper to execute a workflow one \"turn\" at a time.\n * This is invaluable for debugging and writing fine-grained tests where you need to\n * assert the state of the workflow after each logical step.\n *\n * @example\n * // In your test file\n * it('should correctly execute step-by-step', async () => {\n * const runtime = new FlowRuntime({ ... });\n * const flow = createFlow('test')\n * .node('a', async () => ({ output: 10 }))\n * .node('b', async ({ input }) => ({ output: input * 2 }))\n * .edge('a', 'b');\n *\n * const stepper = await createStepper(runtime, flow.toBlueprint(), flow.getFunctionRegistry());\n *\n * // First step (executes node 'a')\n * const result1 = await stepper.next();\n * expect(stepper.isDone()).toBe(false);\n * expect(result1.status).toBe('stalled');\n * expect(result1.context._outputs.a).toBe(10);\n *\n * // Second step (executes node 'b')\n * const result2 = await stepper.next();\n * expect(stepper.isDone()).toBe(true);\n * expect(result2.status).toBe('completed');\n * expect(result2.context._outputs.b).toBe(20);\n *\n * // Final step (no more work)\n * const result3 = await stepper.next();\n * expect(result3).toBeNull();\n * });\n *\n * @param runtime The `FlowRuntime` instance, used for its configuration.\n * @param blueprint The `WorkflowBlueprint` to execute.\n * @param functionRegistry The function registry from createFlow, containing the node implementations.\n * @param initialState The initial state for the workflow run.\n * @returns A Promise that resolves to an `IWorkflowStepper` instance.\n */\nexport async function createStepper<TContext extends Record<string, any>, TDependencies extends Record<string, any>>(\n\truntime: FlowRuntime<TContext, TDependencies>,\n\tblueprint: WorkflowBlueprint,\n\tfunctionRegistry: Map<string, NodeFunction | NodeClass>,\n\tinitialState: Partial<TContext> = {},\n): Promise<IWorkflowStepper<TContext>> {\n\tconst _initialBlueprint = structuredClone(blueprint)\n\tconst _initialState = structuredClone(initialState)\n\n\tlet state: WorkflowState<TContext>\n\tlet traverser: GraphTraverser\n\tconst history: string[] = []\n\n\tconst orchestrator = new StepByStepOrchestrator()\n\tconst executionId = globalThis.crypto?.randomUUID()\n\tconst nodeRegistry = new Map([...runtime.registry, ...functionRegistry])\n\n\tconst initialize = () => {\n\t\tstate = new WorkflowState<TContext>(_initialState)\n\t\ttraverser = new GraphTraverser(_initialBlueprint)\n\t\thistory.length = 0\n\t}\n\n\tinitialize()\n\n\tconst stepper: IWorkflowStepper<TContext> = {\n\t\tget state() {\n\t\t\treturn state\n\t\t},\n\t\tget traverser() {\n\t\t\treturn traverser\n\t\t},\n\t\tisDone() {\n\t\t\treturn !traverser.hasMoreWork() && !state.isAwaiting()\n\t\t},\n\t\treset() {\n\t\t\tinitialize()\n\t\t},\n\t\tasync prev() {\n\t\t\tconst previousStateJson = history.pop()\n\t\t\tif (!previousStateJson) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst previousStateData = runtime.serializer.deserialize(previousStateJson) as Partial<TContext>\n\n\t\t\tstate = new WorkflowState(previousStateData)\n\t\t\ttraverser = GraphTraverser.fromState(_initialBlueprint, state)\n\n\t\t\treturn state.toResult(runtime.serializer, undefined)\n\t\t},\n\t\tasync next(options: { signal?: AbortSignal; concurrency?: number } = {}) {\n\t\t\tif (stepper.isDone()) {\n\t\t\t\treturn null\n\t\t\t}\n\n\t\t\tconst serializedContext = (await state.toResult(runtime.serializer, undefined)).serializedContext\n\t\t\thistory.push(serializedContext)\n\n\t\t\tconst executionContext = new ExecutionContext(\n\t\t\t\t_initialBlueprint,\n\t\t\t\tstate,\n\t\t\t\tnodeRegistry,\n\t\t\t\texecutionId,\n\t\t\t\truntime,\n\t\t\t\t{\n\t\t\t\t\tlogger: runtime.logger,\n\t\t\t\t\teventBus: runtime.eventBus,\n\t\t\t\t\tserializer: runtime.serializer,\n\t\t\t\t\tevaluator: runtime.evaluator,\n\t\t\t\t\tmiddleware: runtime.middleware,\n\t\t\t\t\tdependencies: runtime.dependencies,\n\t\t\t\t},\n\t\t\t\toptions.signal,\n\t\t\t\toptions.concurrency,\n\t\t\t)\n\t\t\treturn orchestrator.run(executionContext, traverser)\n\t\t},\n\t}\n\n\treturn stepper\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;AAwBA,IAAa,sBAAb,MAAsD;CACrD,AAAgB,SAA2B,EAAE;;;;CAK7C,AAAO,QAAc;AACpB,OAAK,OAAO,SAAS;;;;;;;CAQtB,MAAa,KAAK,OAAsC;AACvD,OAAK,OAAO,KAAK,MAAM;;;;;;;CAQxB,AAAO,KAAuC,MAA2D;AACxG,SAAO,KAAK,OAAO,MAAM,MAAM,EAAE,SAAS,KAAK;;;;;;;CAQhD,AAAO,OAAyC,MAAiD;AAChG,SAAO,KAAK,OAAO,QAAQ,MAAM,EAAE,SAAS,KAAK;;;;;;;CAQlD,AAAO,SAAS,QAAQ,4BAAkC;AACzD,UAAQ,IAAI,SAAS,MAAM,MAAM;AACjC,MAAI,KAAK,OAAO,WAAW,GAAG;AAC7B,WAAQ,IAAI,2BAA2B;AACvC,WAAQ,IAAI,uCAAuC;AACnD;;AAGD,OAAK,OAAO,SAAS,OAAO,UAAU;GACrC,MAAM,EAAE,MAAM,YAAY;AAC1B,WAAQ,IAAI,MAAM,QAAQ,EAAE,IAAI,OAAO;AAGvC,WAAQ,MAAR;IACC,KAAK;AACJ,aAAQ,IAAI,cAAc,QAAQ,OAAO,aAAa,KAAK,UAAU,QAAQ,MAAM,GAAG;AACtF;IACD,KAAK;AACJ,aAAQ,IAAI,cAAc,QAAQ,OAAO,QAAQ,QAAQ,OAAO,GAAG;AACnE,aAAQ,IAAI,kBAAkB,QAAQ,aAAa,MAAM,aAAa,QAAQ,SAAS;AACvF;IACD,KAAK;AACJ,SAAI,QAAQ,OAAO,MAClB,SAAQ,IACP,aAAa,QAAQ,WAAW,8BAA8B,QAAQ,IAAI,aAAa,KAAK,UAAU,QAAQ,MAAM,GACpH;cACS,QAAQ,OAAO,SACzB,SAAQ,IAAI,aAAa,QAAQ,WAAW,kCAAkC,QAAQ,IAAI,GAAG;AAE9F;IACD,KAAK;AACJ,aAAQ,IAAI,cAAc,QAAQ,OAAO,cAAc,KAAK,UAAU,QAAQ,OAAO,GAAG;AACxF;IACD,KAAK;AACJ,aAAQ,IAAI,cAAc,QAAQ,OAAO,GAAG;AAC5C,aAAQ,MAAM,cAAc,QAAQ,MAAM;AAC1C;IACD,QACC,SAAQ,IAAI,gBAAgB,KAAK,UAAU,SAAS,MAAM,EAAE,GAAG;;IAEhE;AACF,UAAQ,IAAI,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AC5EvC,eAAsB,aACrB,SACA,WACA,eAA2C,EAAE,EAC7C,UAII,EAAE,EACL;CACD,MAAM,cAAc,IAAI,qBAAqB;CAC7C,MAAM,cAAc,IAAI,YAAY;EACnC,GAAG,QAAQ;EACX,UAAU;EACV,CAAC;AAEF,KAAI;EACH,MAAM,SAAS,MAAM,YAAY,IAAI,WAAW,cAAc,QAAQ;AACtE,MAAI,QAAQ,IAAI,MACf,aAAY,SAAS,qBAAqB,UAAU,KAAK;AAE1D,SAAO;UACC,OAAO;AACf,cAAY,SAAS,uBAAuB,UAAU,KAAK;AAC3D,QAAM;;;;;;;;;;;;;ACxCR,IAAa,yBAAb,MAA6D;CAC5D,MAAa,IAAI,SAAqC,WAAyD;AAC9G,MAAI;AACH,WAAQ,QAAQ,gBAAgB;WACxB,OAAO;AACf,OAAI,iBAAiB,gBAAgB,MAAM,SAAS,aACnD,OAAM,IAAI,eAAe,sBAAsB,EAAE,SAAS,OAAO,CAAC;AAEnE,SAAM;;AAGP,MAAI,CAAC,UAAU,aAAa,EAAE;GAC7B,MAAM,sBAAsB,CAAC,UAAU,aAAa;GACpD,MAAM,SAAS,QAAQ,MAAM,UAAU,oBAAoB;GAC3D,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,QAAQ,SAAS,YAAY,QAAQ,YAAY;AAC7F,UAAO,SAAS;AAChB,UAAO;;EAGR,MAAM,gBAAgB,UAAU,eAAe;EAC/C,MAAM,iBAAiB,QAAQ,cAAc,cAAc,MAAM,GAAG,QAAQ,YAAY,GAAG;EAC3F,MAAM,cAAc,QAAQ,cAAc,cAAc,MAAM,QAAQ,YAAY,GAAG,EAAE;AAWvF,QAAM,eATiB,MAAM,aAC5B,gBACA,UAAU,qBAAqB,EAC/B,QAAQ,QACP,WAAmB,QAAQ,QAAQ,mBAAmB,QAAQ,QAAQ,EACvE,QAAQ,SACR,QAAQ,YACR,EAIA,WACA,QAAQ,OACR,QAAQ,SACR,QAAQ,WACR,QAAQ,YACR;AAED,OAAK,MAAM,EAAE,YAAY,YACxB,WAAU,cAAc,OAAO;EAGhC,MAAM,sBAAsB,CAAC,UAAU,aAAa;EACpD,MAAM,SAAS,QAAQ,MAAM,UAAU,oBAAoB;EAC3D,MAAM,SAAS,MAAM,QAAQ,MAAM,SAAS,QAAQ,SAAS,YAAY,QAAQ,YAAY;AAC7F,SAAO,SAAS;AAChB,SAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACkBT,eAAsB,cACrB,SACA,WACA,kBACA,eAAkC,EAAE,EACE;CACtC,MAAM,oBAAoB,gBAAgB,UAAU;CACpD,MAAM,gBAAgB,gBAAgB,aAAa;CAEnD,IAAI;CACJ,IAAI;CACJ,MAAM,UAAoB,EAAE;CAE5B,MAAM,eAAe,IAAI,wBAAwB;CACjD,MAAM,cAAc,WAAW,QAAQ,YAAY;CACnD,MAAM,eAAe,IAAI,IAAI,CAAC,GAAG,QAAQ,UAAU,GAAG,iBAAiB,CAAC;CAExE,MAAM,mBAAmB;AACxB,UAAQ,IAAI,cAAwB,cAAc;AAClD,cAAY,IAAI,eAAe,kBAAkB;AACjD,UAAQ,SAAS;;AAGlB,aAAY;CAEZ,MAAM,UAAsC;EAC3C,IAAI,QAAQ;AACX,UAAO;;EAER,IAAI,YAAY;AACf,UAAO;;EAER,SAAS;AACR,UAAO,CAAC,UAAU,aAAa,IAAI,CAAC,MAAM,YAAY;;EAEvD,QAAQ;AACP,eAAY;;EAEb,MAAM,OAAO;GACZ,MAAM,oBAAoB,QAAQ,KAAK;AACvC,OAAI,CAAC,kBACJ,QAAO;AAKR,WAAQ,IAAI,cAFc,QAAQ,WAAW,YAAY,kBAAkB,CAE/B;AAC5C,eAAY,eAAe,UAAU,mBAAmB,MAAM;AAE9D,UAAO,MAAM,SAAS,QAAQ,YAAY,OAAU;;EAErD,MAAM,KAAK,UAA0D,EAAE,EAAE;AACxE,OAAI,QAAQ,QAAQ,CACnB,QAAO;GAGR,MAAM,qBAAqB,MAAM,MAAM,SAAS,QAAQ,YAAY,OAAU,EAAE;AAChF,WAAQ,KAAK,kBAAkB;GAE/B,MAAM,mBAAmB,IAAI,iBAC5B,mBACA,OACA,cACA,aACA,SACA;IACC,QAAQ,QAAQ;IAChB,UAAU,QAAQ;IAClB,YAAY,QAAQ;IACpB,WAAW,QAAQ;IACnB,YAAY,QAAQ;IACpB,cAAc,QAAQ;IACtB,EACD,QAAQ,QACR,QAAQ,YACR;AACD,UAAO,aAAa,IAAI,kBAAkB,UAAU;;EAErD;AAED,QAAO"}
package/package.json CHANGED
@@ -1,60 +1,60 @@
1
1
  {
2
- "name": "flowcraft",
3
- "type": "module",
4
- "version": "2.7.1",
5
- "description": "A lightweight workflow engine",
6
- "license": "MIT",
7
- "homepage": "https://flowcraft.js.org",
8
- "repository": {
9
- "type": "git",
10
- "url": "https://github.com/gorango/flowcraft.git"
11
- },
12
- "bugs": "https://github.com/gorango/flowcraft/issues",
13
- "keywords": [
14
- "agentic-workflows",
15
- "ai-agent",
16
- "background-jobs",
17
- "dag",
18
- "data-pipelines",
19
- "declarative-workflows",
20
- "distributed-systems",
21
- "etl",
22
- "llm-orchestration",
23
- "orchestration",
24
- "rag",
25
- "state-machine",
26
- "workflow-engine"
27
- ],
28
- "exports": {
29
- ".": {
30
- "types": "./dist/index.d.ts",
31
- "import": "./dist/index.js"
32
- },
33
- "./sdk": {
34
- "types": "./dist/sdk.d.ts",
35
- "import": "./dist/sdk.js"
36
- },
37
- "./testing": {
38
- "types": "./dist/testing/index.d.ts",
39
- "import": "./dist/testing/index.js"
40
- }
41
- },
42
- "files": [
43
- "dist"
44
- ],
45
- "devDependencies": {
46
- "@types/node": "^24.9.2",
47
- "@vitest/coverage-v8": "4.0.4",
48
- "tsup": "^8.5.0",
49
- "typescript": "^5.9.3",
50
- "vitest": "^4.0.4"
51
- },
52
- "scripts": {
53
- "dev": "tsup --watch",
54
- "build": "tsup",
55
- "typecheck": "tsc --noEmit",
56
- "test": "vitest run",
57
- "test:watch": "vitest",
58
- "test:coverage": "vitest run --coverage"
59
- }
60
- }
2
+ "name": "flowcraft",
3
+ "type": "module",
4
+ "version": "2.8.1",
5
+ "description": "A lightweight workflow engine",
6
+ "license": "MIT",
7
+ "homepage": "https://flowcraft.js.org",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/gorango/flowcraft.git"
11
+ },
12
+ "bugs": "https://github.com/gorango/flowcraft/issues",
13
+ "keywords": [
14
+ "agentic-workflows",
15
+ "ai-agent",
16
+ "background-jobs",
17
+ "dag",
18
+ "data-pipelines",
19
+ "declarative-workflows",
20
+ "distributed-systems",
21
+ "etl",
22
+ "llm-orchestration",
23
+ "orchestration",
24
+ "rag",
25
+ "state-machine",
26
+ "workflow-engine"
27
+ ],
28
+ "exports": {
29
+ ".": {
30
+ "types": "./dist/index.d.mts",
31
+ "import": "./dist/index.mjs"
32
+ },
33
+ "./sdk": {
34
+ "types": "./dist/sdk.d.mts",
35
+ "import": "./dist/sdk.mjs"
36
+ },
37
+ "./testing": {
38
+ "types": "./dist/testing/index.d.mts",
39
+ "import": "./dist/testing/index.mjs"
40
+ }
41
+ },
42
+ "files": [
43
+ "dist"
44
+ ],
45
+ "scripts": {
46
+ "dev": "tsdown --watch",
47
+ "build": "tsdown",
48
+ "typecheck": "tsc --noEmit",
49
+ "test": "vitest run",
50
+ "test:watch": "vitest",
51
+ "test:coverage": "vitest run --coverage"
52
+ },
53
+ "devDependencies": {
54
+ "@types/node": "^25.5.0",
55
+ "@vitest/coverage-v8": "4.0.4",
56
+ "tsdown": "^0.21.7",
57
+ "typescript": "^6.0.2",
58
+ "vitest": "^4.1.2"
59
+ }
60
+ }
package/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- The MIT License (MIT)
2
-
3
- Copyright (c) 2024 Feathers
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
@@ -1,4 +0,0 @@
1
- export { IEventStore, InMemoryEventStore, PersistentEventBusAdapter } from './persistent-event-bus.js';
2
- import '../types-CKhffqyb.js';
3
- import '../errors.js';
4
- import '../container.js';
@@ -1,4 +0,0 @@
1
- import '../chunk-3Y5O5EGB.js';
2
- export { InMemoryEventStore, PersistentEventBusAdapter } from '../chunk-2TSADFQX.js';
3
- //# sourceMappingURL=index.js.map
4
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":[],"names":[],"mappings":"","file":"index.js"}