flowcraft 2.9.3 → 2.10.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 (127) hide show
  1. package/README.md +3 -3
  2. package/dist/adapter-DzeZVjSE.d.mts +133 -0
  3. package/dist/adapters/index.d.mts +2 -0
  4. package/dist/adapters/index.mjs +3 -0
  5. package/dist/adapters/persistent-event-bus.d.mts +2 -0
  6. package/dist/adapters/persistent-event-bus.mjs +59 -0
  7. package/dist/analysis-B5Twr7sD.d.mts +52 -0
  8. package/dist/analysis.d.mts +2 -0
  9. package/dist/analysis.mjs +164 -0
  10. package/dist/batch-gather-BhF-IzQR.d.mts +8 -0
  11. package/dist/batch-scatter-DD8TU0Wm.d.mts +8 -0
  12. package/dist/container-BKdd-9wf.d.mts +24 -0
  13. package/dist/container-factory-fDY2kkxt.d.mts +17 -0
  14. package/dist/container-factory.d.mts +2 -0
  15. package/dist/container-factory.mjs +23 -0
  16. package/dist/container.d.mts +2 -0
  17. package/dist/container.mjs +43 -0
  18. package/dist/context-ZVtzXuZu.d.mts +64 -0
  19. package/dist/context.d.mts +2 -0
  20. package/dist/context.mjs +145 -0
  21. package/dist/error-mapper-BAv_YQMQ.d.mts +14 -0
  22. package/dist/error-mapper.d.mts +2 -0
  23. package/dist/error-mapper.mjs +37 -0
  24. package/dist/errors-CyyIj3OO.d.mts +21 -0
  25. package/dist/errors.d.mts +2 -0
  26. package/dist/errors.mjs +24 -0
  27. package/dist/evaluator-Dnj5qJ92.d.mts +31 -0
  28. package/dist/evaluator.d.mts +2 -0
  29. package/dist/evaluator.mjs +80 -0
  30. package/dist/flow-CZGpYpl-.d.mts +94 -0
  31. package/dist/flow.d.mts +2 -0
  32. package/dist/flow.mjs +328 -0
  33. package/dist/index-9iG2qHLe.d.mts +1 -0
  34. package/dist/index-Bk0eNZmQ.d.mts +1 -0
  35. package/dist/index-CNgSR_kt.d.mts +1 -0
  36. package/dist/index-CW2WHUXP.d.mts +1 -0
  37. package/dist/index.d.mts +24 -1
  38. package/dist/index.mjs +31 -746
  39. package/dist/linter-B8KALEae.d.mts +25 -0
  40. package/dist/linter.d.mts +2 -0
  41. package/dist/linter.mjs +74 -0
  42. package/dist/logger-BvDgvNHQ.d.mts +19 -0
  43. package/dist/logger.d.mts +2 -0
  44. package/dist/logger.mjs +26 -0
  45. package/dist/node.d.mts +2 -0
  46. package/dist/node.mjs +55 -0
  47. package/dist/nodes/batch-gather.d.mts +2 -0
  48. package/dist/nodes/batch-gather.mjs +47 -0
  49. package/dist/nodes/batch-scatter.d.mts +2 -0
  50. package/dist/nodes/batch-scatter.mjs +52 -0
  51. package/dist/nodes/index.d.mts +7 -0
  52. package/dist/nodes/index.mjs +8 -0
  53. package/dist/nodes/sleep.d.mts +2 -0
  54. package/dist/nodes/sleep.mjs +41 -0
  55. package/dist/nodes/subflow.d.mts +2 -0
  56. package/dist/nodes/subflow.mjs +64 -0
  57. package/dist/nodes/wait.d.mts +2 -0
  58. package/dist/nodes/wait.mjs +12 -0
  59. package/dist/nodes/webhook.d.mts +2 -0
  60. package/dist/nodes/webhook.mjs +24 -0
  61. package/dist/orchestrator-DwMIJRFI.d.mts +8 -0
  62. package/dist/persistent-event-bus-COiQOpWh.d.mts +68 -0
  63. package/dist/replay-CVOy6d_L.d.mts +44 -0
  64. package/dist/runtime/adapter.d.mts +2 -0
  65. package/dist/runtime/adapter.mjs +349 -0
  66. package/dist/runtime/builtin-keys.d.mts +37 -0
  67. package/dist/runtime/builtin-keys.mjs +12 -0
  68. package/dist/runtime/execution-context.d.mts +2 -0
  69. package/dist/runtime/execution-context.mjs +26 -0
  70. package/dist/runtime/executors.d.mts +2 -0
  71. package/dist/runtime/executors.mjs +259 -0
  72. package/dist/runtime/index.d.mts +6 -0
  73. package/dist/runtime/index.mjs +10 -0
  74. package/dist/runtime/node-executor-factory.d.mts +11 -0
  75. package/dist/runtime/node-executor-factory.mjs +41 -0
  76. package/dist/runtime/orchestrator.d.mts +2 -0
  77. package/dist/runtime/orchestrator.mjs +41 -0
  78. package/dist/runtime/orchestrators/replay.d.mts +2 -0
  79. package/dist/{replay-BB11M6K1.mjs → runtime/orchestrators/replay.mjs} +1 -20
  80. package/dist/runtime/orchestrators/step-by-step.d.mts +15 -0
  81. package/dist/runtime/orchestrators/step-by-step.mjs +41 -0
  82. package/dist/runtime/orchestrators/utils.d.mts +2 -0
  83. package/dist/runtime/orchestrators/utils.mjs +79 -0
  84. package/dist/runtime/runtime.d.mts +2 -0
  85. package/dist/runtime/runtime.mjs +425 -0
  86. package/dist/runtime/scheduler.d.mts +2 -0
  87. package/dist/runtime/scheduler.mjs +64 -0
  88. package/dist/runtime/state.d.mts +2 -0
  89. package/dist/runtime/state.mjs +127 -0
  90. package/dist/runtime/traverser.d.mts +2 -0
  91. package/dist/runtime/traverser.mjs +213 -0
  92. package/dist/runtime/types.d.mts +2 -0
  93. package/dist/runtime/types.mjs +1 -0
  94. package/dist/runtime/workflow-logic-handler.d.mts +16 -0
  95. package/dist/runtime/workflow-logic-handler.mjs +159 -0
  96. package/dist/sanitizer-Bi00YjvO.d.mts +11 -0
  97. package/dist/sanitizer.d.mts +2 -0
  98. package/dist/sanitizer.mjs +37 -0
  99. package/dist/sdk.d.mts +1 -2
  100. package/dist/sdk.mjs +1 -2
  101. package/dist/serializer-BnmJr13R.d.mts +17 -0
  102. package/dist/serializer.d.mts +2 -0
  103. package/dist/serializer.mjs +34 -0
  104. package/dist/sleep-DpwYaY5b.d.mts +8 -0
  105. package/dist/subflow-n2IMsRe2.d.mts +8 -0
  106. package/dist/testing/event-logger.d.mts +62 -0
  107. package/dist/testing/event-logger.mjs +98 -0
  108. package/dist/testing/index.d.mts +5 -172
  109. package/dist/testing/index.mjs +6 -276
  110. package/dist/testing/run-with-trace.d.mts +37 -0
  111. package/dist/testing/run-with-trace.mjs +49 -0
  112. package/dist/testing/stepper.d.mts +78 -0
  113. package/dist/testing/stepper.mjs +100 -0
  114. package/dist/types-BcrXJEPI.d.mts +687 -0
  115. package/dist/types.d.mts +2 -0
  116. package/dist/types.mjs +1 -0
  117. package/dist/utils-BUEgr9V2.d.mts +34 -0
  118. package/dist/wait-2Q-LA7V7.d.mts +8 -0
  119. package/dist/webhook-BiCm-HLx.d.mts +12 -0
  120. package/package.json +4 -4
  121. package/dist/index-DUPpyNvU.d.mts +0 -1326
  122. package/dist/index.mjs.map +0 -1
  123. package/dist/replay-BB11M6K1.mjs.map +0 -1
  124. package/dist/runtime-lNm7WbD1.mjs +0 -2250
  125. package/dist/runtime-lNm7WbD1.mjs.map +0 -1
  126. package/dist/sdk.mjs.map +0 -1
  127. package/dist/testing/index.mjs.map +0 -1
@@ -0,0 +1,213 @@
1
+ import { analyzeBlueprint } from "../analysis.mjs";
2
+ import { FlowcraftError } from "../errors.mjs";
3
+
4
+ //#region src/runtime/traverser.ts
5
+ var GraphTraverser = class GraphTraverser {
6
+ frontier = /* @__PURE__ */ new Set();
7
+ allPredecessors;
8
+ allSuccessors;
9
+ dynamicBlueprint;
10
+ completedNodes = /* @__PURE__ */ new Set();
11
+ nodesInLoops;
12
+ constructor(blueprint, isStrictMode = false) {
13
+ this.dynamicBlueprint = structuredClone(blueprint);
14
+ this.allPredecessors = /* @__PURE__ */ new Map();
15
+ this.allSuccessors = /* @__PURE__ */ new Map();
16
+ this.nodesInLoops = /* @__PURE__ */ new Map();
17
+ for (const node of this.dynamicBlueprint.nodes) {
18
+ this.allPredecessors.set(node.id, /* @__PURE__ */ new Set());
19
+ this.allSuccessors.set(node.id, /* @__PURE__ */ new Set());
20
+ }
21
+ for (const edge of this.dynamicBlueprint.edges) this.getPredecessors(edge.target).add(edge.source);
22
+ for (const edge of this.dynamicBlueprint.edges) this.getSuccessors(edge.source).add(edge.target);
23
+ const analysis = analyzeBlueprint(blueprint);
24
+ this.filterNodesInLoops(blueprint);
25
+ this.frontier = new Set(analysis.startNodeIds.filter((id) => !this.isFallbackNode(id)));
26
+ if (this.frontier.size === 0 && analysis.cycles.length > 0 && !isStrictMode) {
27
+ const uniqueStartNodes = /* @__PURE__ */ new Set();
28
+ const cycleEntryPoints = new Set(blueprint.metadata?.cycleEntryPoints || []);
29
+ for (const cycle of analysis.cycles) if (cycle.length > 0) {
30
+ const entryPoint = cycle.find((node) => cycleEntryPoints.has(node));
31
+ uniqueStartNodes.add(entryPoint || cycle[0]);
32
+ }
33
+ this.frontier = new Set(uniqueStartNodes);
34
+ }
35
+ }
36
+ /**
37
+ * Clears all nodes from the execution frontier.
38
+ */
39
+ clearFrontier() {
40
+ this.frontier.clear();
41
+ }
42
+ /**
43
+ * Creates and initializes a GraphTraverser from a saved workflow state.
44
+ * This is the correct way to prepare a traverser for a `resume` operation.
45
+ * @param blueprint The workflow blueprint.
46
+ * @param state The workflow state being resumed.
47
+ * @returns A configured GraphTraverser instance.
48
+ */
49
+ static fromState(blueprint, state) {
50
+ const traverser = new GraphTraverser(blueprint);
51
+ traverser.clearFrontier();
52
+ const completedNodes = state.getCompletedNodes();
53
+ traverser.completedNodes = new Set(completedNodes);
54
+ const hasOnlyConditionalIncomingEdges = (nodeId) => {
55
+ const incomingEdges = blueprint.edges.filter((e) => e.target === nodeId);
56
+ if (incomingEdges.length === 0) return false;
57
+ const fromCompletedPredecessors = incomingEdges.filter((e) => completedNodes.has(e.source));
58
+ if (fromCompletedPredecessors.length === 0) return false;
59
+ return fromCompletedPredecessors.every((e) => e.condition !== void 0);
60
+ };
61
+ for (const node of traverser.dynamicBlueprint.nodes) {
62
+ if (traverser.completedNodes.has(node.id)) continue;
63
+ if (hasOnlyConditionalIncomingEdges(node.id)) continue;
64
+ const requiredPredecessors = traverser.allPredecessors.get(node.id);
65
+ const joinStrategy = traverser.getJoinStrategy(node.id);
66
+ if (!requiredPredecessors || requiredPredecessors.size === 0) {
67
+ traverser.frontier.add(node.id);
68
+ continue;
69
+ }
70
+ const completedPredecessors = [...requiredPredecessors].filter((p) => traverser.completedNodes.has(p));
71
+ if (joinStrategy === "any" ? completedPredecessors.length > 0 : completedPredecessors.length === requiredPredecessors.size) traverser.frontier.add(node.id);
72
+ }
73
+ return traverser;
74
+ }
75
+ isFallbackNode(nodeId) {
76
+ return this.dynamicBlueprint.nodes.some((n) => n.config?.fallback === nodeId);
77
+ }
78
+ getJoinStrategy(nodeId) {
79
+ return this.dynamicBlueprint.nodes.find((n) => n.id === nodeId)?.config?.joinStrategy || "all";
80
+ }
81
+ filterNodesInLoops(blueprint) {
82
+ blueprint.nodes.forEach((node) => {
83
+ if (node.uses !== "loop-controller") return;
84
+ const nextInLoopId = blueprint.edges.find((e) => e.source === node.id && e.action === "continue")?.target;
85
+ if (!nextInLoopId) throw new FlowcraftError(`Loop '${node.id}' has no continue edge to start node. Ensure edges are wired inside the loop and incoming/breaking edges point to the loop controller.`, {
86
+ nodeId: node.id,
87
+ blueprintId: blueprint.id
88
+ });
89
+ const set = /* @__PURE__ */ new Set();
90
+ set.add(nextInLoopId);
91
+ this.nodesInLoops.set(node.id, this.getAllLoopSuccessors(nextInLoopId, blueprint, set));
92
+ });
93
+ }
94
+ getAllLoopSuccessors(nodeId, blueprint, set) {
95
+ this.getSuccessors(nodeId).forEach((successor) => {
96
+ if (set.has(successor)) return;
97
+ const node = this.getNode(successor, blueprint);
98
+ if (!node || node.uses === "loop-controller") return;
99
+ set.add(successor);
100
+ this.getAllLoopSuccessors(successor, blueprint, set);
101
+ });
102
+ return set;
103
+ }
104
+ getReadyNodes() {
105
+ const readyNodes = [];
106
+ for (const nodeId of this.frontier) {
107
+ const nodeDef = this.dynamicBlueprint.nodes.find((n) => n.id === nodeId);
108
+ if (nodeDef) readyNodes.push({
109
+ nodeId,
110
+ nodeDef
111
+ });
112
+ }
113
+ this.frontier.clear();
114
+ return readyNodes;
115
+ }
116
+ hasMoreWork() {
117
+ return this.frontier.size > 0;
118
+ }
119
+ markNodeCompleted(nodeId, result, nextNodes) {
120
+ this.completedNodes.add(nodeId);
121
+ if (result?.dynamicNodes && result.dynamicNodes.length > 0) {
122
+ const gatherNodeId = result.output?.gatherNodeId;
123
+ for (const dynamicNode of result.dynamicNodes) {
124
+ this.dynamicBlueprint.nodes.push(dynamicNode);
125
+ this.allPredecessors.set(dynamicNode.id, new Set([nodeId]));
126
+ if (gatherNodeId) this.getPredecessors(gatherNodeId).add(dynamicNode.id);
127
+ this.frontier.add(dynamicNode.id);
128
+ }
129
+ }
130
+ for (const node of nextNodes) {
131
+ const joinStrategy = this.getJoinStrategy(node.id);
132
+ if (joinStrategy !== "any" && this.completedNodes.has(node.id)) continue;
133
+ const requiredPredecessors = this.getPredecessors(node.id);
134
+ if (joinStrategy === "any" ? requiredPredecessors.has(nodeId) : [...requiredPredecessors].every((p) => this.completedNodes.has(p))) {
135
+ this.frontier.add(node.id);
136
+ if (node.uses === "loop-controller") this.getNodesInLoop(node.id).forEach((id) => {
137
+ this.resetNodeCompletion(id);
138
+ });
139
+ }
140
+ }
141
+ if (nextNodes.length === 0) {
142
+ for (const [potentialNextId, predecessors] of this.allPredecessors) if (predecessors.has(nodeId) && !this.completedNodes.has(potentialNextId)) {
143
+ if (this.dynamicBlueprint.edges.some((e) => e.target === potentialNextId && e.condition)) continue;
144
+ if (this.getJoinStrategy(potentialNextId) === "any" ? predecessors.has(nodeId) : [...predecessors].every((p) => this.completedNodes.has(p))) {
145
+ this.frontier.add(potentialNextId);
146
+ const node = this.getNode(potentialNextId, this.dynamicBlueprint);
147
+ if (!node) continue;
148
+ if (node.uses === "loop-controller") this.getNodesInLoop(node.id).forEach((id) => {
149
+ this.resetNodeCompletion(id);
150
+ });
151
+ }
152
+ }
153
+ }
154
+ }
155
+ getAllNodeIds() {
156
+ return new Set(this.dynamicBlueprint.nodes.map((n) => n.id));
157
+ }
158
+ getFallbackNodeIds() {
159
+ const fallbackNodeIds = /* @__PURE__ */ new Set();
160
+ for (const node of this.dynamicBlueprint.nodes) if (node.config?.fallback) fallbackNodeIds.add(node.config.fallback);
161
+ return fallbackNodeIds;
162
+ }
163
+ getCompletedNodes() {
164
+ return new Set(this.completedNodes);
165
+ }
166
+ getDynamicBlueprint() {
167
+ return this.dynamicBlueprint;
168
+ }
169
+ getAllPredecessors() {
170
+ return this.allPredecessors;
171
+ }
172
+ getAllSuccessors() {
173
+ return this.allSuccessors;
174
+ }
175
+ getPredecessors(nodeId) {
176
+ const predecessors = this.allPredecessors.get(nodeId);
177
+ if (!predecessors) return /* @__PURE__ */ new Set();
178
+ return predecessors;
179
+ }
180
+ getSuccessors(nodeId) {
181
+ const successors = this.allSuccessors.get(nodeId);
182
+ if (!successors) return /* @__PURE__ */ new Set();
183
+ return successors;
184
+ }
185
+ getNodesInLoop(id) {
186
+ const loopNodes = this.nodesInLoops.get(id);
187
+ if (!loopNodes) return /* @__PURE__ */ new Set();
188
+ return loopNodes;
189
+ }
190
+ resetNodeCompletion(nodeId) {
191
+ this.completedNodes.delete(nodeId);
192
+ }
193
+ getNode(nodeId, blueprint) {
194
+ return blueprint.nodes.find((n) => n.id === nodeId);
195
+ }
196
+ addDynamicNode(_nodeId, dynamicNode, predecessorId, gatherNodeId) {
197
+ this.dynamicBlueprint.nodes.push(dynamicNode);
198
+ this.allPredecessors.set(dynamicNode.id, new Set([predecessorId]));
199
+ if (gatherNodeId) this.allPredecessors.get(gatherNodeId)?.add(dynamicNode.id);
200
+ this.frontier.add(dynamicNode.id);
201
+ }
202
+ /**
203
+ * Manually adds a node ID back to the execution frontier.
204
+ * Used by orchestrators that need fine-grained control over steps.
205
+ * @param nodeId The ID of the node to add to the frontier.
206
+ */
207
+ addToFrontier(nodeId) {
208
+ this.frontier.add(nodeId);
209
+ }
210
+ };
211
+
212
+ //#endregion
213
+ export { GraphTraverser };
@@ -0,0 +1,2 @@
1
+ import { B as NodeExecutionResult, M as IOrchestrator, N as IRuntime, P as NodeExecutorFactory, V as NodeExecutor, j as ExecutionServices } from "../types-BcrXJEPI.mjs";
2
+ export { ExecutionServices, IOrchestrator, IRuntime, NodeExecutionResult, NodeExecutor, NodeExecutorFactory };
@@ -0,0 +1 @@
1
+ export { };
@@ -0,0 +1,16 @@
1
+ import { a as IEvaluator, m as NodeDefinition, n as EdgeDefinition, o as IEventBus, t as ContextImplementation, v as NodeResult, w as WorkflowBlueprint } from "../types-BcrXJEPI.mjs";
2
+
3
+ //#region src/runtime/workflow-logic-handler.d.ts
4
+ declare class WorkflowLogicHandler {
5
+ private readonly evaluator;
6
+ private readonly eventBus;
7
+ constructor(evaluator: IEvaluator, eventBus: IEventBus);
8
+ determineNextNodes(blueprint: WorkflowBlueprint, completedNodeId: string, result: NodeResult<any, any>, context: ContextImplementation<any>, executionId?: string): Promise<{
9
+ node: NodeDefinition;
10
+ edge: EdgeDefinition;
11
+ }[]>;
12
+ applyEdgeTransform(edge: EdgeDefinition, sourceResult: NodeResult<any, any>, targetNode: NodeDefinition, context: ContextImplementation<any>, allPredecessors?: Map<string, Set<string>>, executionId?: string): Promise<void>;
13
+ resolveNodeInput(nodeId: string, blueprint: WorkflowBlueprint, context: ContextImplementation<any>): Promise<any>;
14
+ }
15
+ //#endregion
16
+ export { WorkflowLogicHandler };
@@ -0,0 +1,159 @@
1
+ import { FlowcraftError } from "../errors.mjs";
2
+ import { AsyncContextView } from "../context.mjs";
3
+
4
+ //#region src/runtime/workflow-logic-handler.ts
5
+ var WorkflowLogicHandler = class {
6
+ constructor(evaluator, eventBus) {
7
+ this.evaluator = evaluator;
8
+ this.eventBus = eventBus;
9
+ }
10
+ async determineNextNodes(blueprint, completedNodeId, result, context, executionId) {
11
+ if (!result) return [];
12
+ const effectiveSourceNodeId = completedNodeId;
13
+ const directOutgoingEdges = blueprint.edges.filter((edge) => edge.source === effectiveSourceNodeId);
14
+ const inheritedOutgoingEdges = blueprint.nodes.filter((n) => n.config?.fallback === completedNodeId).flatMap((originalNode) => blueprint.edges.filter((edge) => edge.source === originalNode.id));
15
+ const allPossibleEdges = [...directOutgoingEdges, ...inheritedOutgoingEdges];
16
+ const outgoingEdges = [...new Map(allPossibleEdges.map((edge) => [`${edge.source}-${edge.target}-${edge.action || ""}-${edge.condition || ""}`, edge])).values()];
17
+ const matched = [];
18
+ const evaluateEdge = async (edge) => {
19
+ if (!edge.condition) return true;
20
+ const contextData = context.type === "sync" ? context.toJSON() : await context.toJSON();
21
+ const evaluationResult = !!this.evaluator.evaluate(edge.condition, {
22
+ ...contextData,
23
+ result
24
+ });
25
+ await this.eventBus.emit({
26
+ type: "edge:evaluate",
27
+ payload: {
28
+ source: edge.source,
29
+ target: edge.target,
30
+ condition: edge.condition,
31
+ result: evaluationResult
32
+ }
33
+ });
34
+ return evaluationResult;
35
+ };
36
+ if (blueprint.nodes.find((n) => n.id === completedNodeId)?.uses === "loop-controller") {
37
+ const conditionalEdges = outgoingEdges.filter((edge) => edge.condition);
38
+ for (const edge of conditionalEdges) if (await evaluateEdge(edge)) {
39
+ const targetNode = blueprint.nodes.find((n) => n.id === edge.target);
40
+ if (targetNode) matched.push({
41
+ node: targetNode,
42
+ edge
43
+ });
44
+ } else await this.eventBus.emit({
45
+ type: "node:skipped",
46
+ payload: {
47
+ nodeId: completedNodeId,
48
+ edge,
49
+ executionId: executionId || "",
50
+ blueprintId: blueprint.id
51
+ }
52
+ });
53
+ if (matched.length > 0) return matched;
54
+ }
55
+ if (result.action) {
56
+ const actionEdges = outgoingEdges.filter((edge) => edge.action === result.action);
57
+ for (const edge of actionEdges) if (await evaluateEdge(edge)) {
58
+ const targetNode = blueprint.nodes.find((n) => n.id === edge.target);
59
+ if (targetNode) matched.push({
60
+ node: targetNode,
61
+ edge
62
+ });
63
+ } else await this.eventBus.emit({
64
+ type: "node:skipped",
65
+ payload: {
66
+ nodeId: completedNodeId,
67
+ edge,
68
+ executionId: executionId || "",
69
+ blueprintId: blueprint.id
70
+ }
71
+ });
72
+ }
73
+ if (matched.length === 0) {
74
+ const defaultEdges = outgoingEdges.filter((edge) => !edge.action);
75
+ for (const edge of defaultEdges) if (await evaluateEdge(edge)) {
76
+ const targetNode = blueprint.nodes.find((n) => n.id === edge.target);
77
+ if (targetNode) matched.push({
78
+ node: targetNode,
79
+ edge
80
+ });
81
+ } else await this.eventBus.emit({
82
+ type: "node:skipped",
83
+ payload: {
84
+ nodeId: completedNodeId,
85
+ edge,
86
+ executionId: executionId || "",
87
+ blueprintId: blueprint.id
88
+ }
89
+ });
90
+ }
91
+ return matched;
92
+ }
93
+ async applyEdgeTransform(edge, sourceResult, targetNode, context, allPredecessors, executionId) {
94
+ const asyncContext = context.type === "sync" ? new AsyncContextView(context) : context;
95
+ const predecessors = allPredecessors?.get(targetNode.id);
96
+ predecessors && predecessors.size;
97
+ const hasExplicitInputs = targetNode.inputs !== void 0;
98
+ const hasEdgeTransform = edge.transform !== void 0;
99
+ let sourceOutput = sourceResult.output;
100
+ if (hasEdgeTransform && hasExplicitInputs && typeof targetNode.inputs === "string") {
101
+ const inputsKey = targetNode.inputs;
102
+ const resolvedKey = inputsKey.startsWith("_") ? inputsKey : `_outputs.${inputsKey}`;
103
+ if (await asyncContext.has(resolvedKey) && inputsKey !== edge.source) sourceOutput = await asyncContext.get(resolvedKey);
104
+ }
105
+ const finalInput = hasEdgeTransform ? this.evaluator.evaluate(edge.transform, {
106
+ input: sourceOutput,
107
+ context: await asyncContext.toJSON()
108
+ }) : sourceOutput;
109
+ const inputKey = `_inputs.${targetNode.id}`;
110
+ await asyncContext.set(inputKey, finalInput);
111
+ await this.eventBus.emit({
112
+ type: "context:change",
113
+ payload: {
114
+ sourceNode: edge.source,
115
+ key: inputKey,
116
+ op: "set",
117
+ value: finalInput,
118
+ executionId: executionId || "unknown"
119
+ }
120
+ });
121
+ if (!hasExplicitInputs || hasEdgeTransform) targetNode.inputs = inputKey;
122
+ }
123
+ async resolveNodeInput(nodeId, blueprint, context) {
124
+ const nodeDef = blueprint.nodes.find((n) => n.id === nodeId);
125
+ if (!nodeDef) throw new FlowcraftError(`Node '${nodeId}' not found in blueprint.`, {
126
+ nodeId,
127
+ blueprintId: blueprint.id,
128
+ isFatal: false
129
+ });
130
+ const asyncContext = context.type === "sync" ? new AsyncContextView(context) : context;
131
+ if (nodeDef.inputs) {
132
+ if (typeof nodeDef.inputs === "string") {
133
+ const key = nodeDef.inputs;
134
+ if (key.startsWith("_")) return await asyncContext.get(key);
135
+ const outputKey = `_outputs.${key}`;
136
+ if (await asyncContext.has(outputKey)) return await asyncContext.get(outputKey);
137
+ return await asyncContext.get(key);
138
+ }
139
+ if (typeof nodeDef.inputs === "object") {
140
+ const input = {};
141
+ for (const key in nodeDef.inputs) {
142
+ const contextKey = nodeDef.inputs[key];
143
+ if (contextKey.startsWith("_")) input[key] = await asyncContext.get(contextKey);
144
+ else {
145
+ const outputKey = `_outputs.${contextKey}`;
146
+ if (await asyncContext.has(outputKey)) input[key] = await asyncContext.get(outputKey);
147
+ else input[key] = await asyncContext.get(contextKey);
148
+ }
149
+ }
150
+ return input;
151
+ }
152
+ }
153
+ const inputKey = `_inputs.${nodeDef.id}`;
154
+ return await asyncContext.get(inputKey);
155
+ }
156
+ };
157
+
158
+ //#endregion
159
+ export { WorkflowLogicHandler };
@@ -0,0 +1,11 @@
1
+ import { w as WorkflowBlueprint } from "./types-BcrXJEPI.mjs";
2
+
3
+ //#region src/sanitizer.d.ts
4
+ /**
5
+ * Sanitizes a raw workflow blueprint by removing extra properties
6
+ * added by UI tools (e.g., position, style) and keeping only the
7
+ * properties defined in NodeDefinition and EdgeDefinition.
8
+ */
9
+ declare function sanitizeBlueprint(raw: any): WorkflowBlueprint;
10
+ //#endregion
11
+ export { sanitizeBlueprint as t };
@@ -0,0 +1,2 @@
1
+ import { t as sanitizeBlueprint } from "./sanitizer-Bi00YjvO.mjs";
2
+ export { sanitizeBlueprint };
@@ -0,0 +1,37 @@
1
+ //#region src/sanitizer.ts
2
+ /**
3
+ * Sanitizes a raw workflow blueprint by removing extra properties
4
+ * added by UI tools (e.g., position, style) and keeping only the
5
+ * properties defined in NodeDefinition and EdgeDefinition.
6
+ */
7
+ function sanitizeBlueprint(raw) {
8
+ let nodesArray = [];
9
+ if (Array.isArray(raw.nodes)) nodesArray = raw.nodes;
10
+ else if (typeof raw.nodes === "object" && raw.nodes !== null) nodesArray = Object.values(raw.nodes);
11
+ const nodes = nodesArray.map((node) => ({
12
+ id: node.id,
13
+ uses: node.uses,
14
+ params: node.params,
15
+ inputs: node.inputs,
16
+ config: node.config
17
+ }));
18
+ let edgesArray = [];
19
+ if (Array.isArray(raw.edges)) edgesArray = raw.edges;
20
+ else if (typeof raw.edges === "object" && raw.edges !== null) edgesArray = Object.values(raw.edges);
21
+ const edges = edgesArray.map((edge) => ({
22
+ source: edge.source,
23
+ target: edge.target,
24
+ action: edge.action,
25
+ condition: edge.condition,
26
+ transform: edge.transform
27
+ }));
28
+ return {
29
+ id: raw.id,
30
+ nodes,
31
+ edges,
32
+ metadata: raw.metadata
33
+ };
34
+ }
35
+
36
+ //#endregion
37
+ export { sanitizeBlueprint };
package/dist/sdk.d.mts CHANGED
@@ -27,5 +27,4 @@ declare function waitForEvent<T = any>(_eventName: string): Promise<T>;
27
27
  */
28
28
  declare function createWebhook<T = any>(): Promise<Webhook<T>>;
29
29
  //#endregion
30
- export { Webhook, createWebhook, sleep, waitForEvent };
31
- //# sourceMappingURL=sdk.d.mts.map
30
+ export { Webhook, createWebhook, sleep, waitForEvent };
package/dist/sdk.mjs CHANGED
@@ -25,5 +25,4 @@ function createWebhook() {
25
25
  }
26
26
 
27
27
  //#endregion
28
- export { createWebhook, sleep, waitForEvent };
29
- //# sourceMappingURL=sdk.mjs.map
28
+ export { createWebhook, sleep, waitForEvent };
@@ -0,0 +1,17 @@
1
+ import { c as ISerializer } from "./types-BcrXJEPI.mjs";
2
+
3
+ //#region src/serializer.d.ts
4
+ /**
5
+ * A default serializer using standard JSON.
6
+ *
7
+ * @warning This implementation is lossy and does not handle complex data types
8
+ * like `Date`, `Map`, `Set`, `undefined`, etc. It is recommended to provide a robust
9
+ * serializer like `superjson` if working with complex data types.
10
+ */
11
+ declare class JsonSerializer implements ISerializer {
12
+ private hasWarned;
13
+ serialize(data: Record<string, any>): string;
14
+ deserialize(text: string): Record<string, any>;
15
+ }
16
+ //#endregion
17
+ export { JsonSerializer as t };
@@ -0,0 +1,2 @@
1
+ import { t as JsonSerializer } from "./serializer-BnmJr13R.mjs";
2
+ export { JsonSerializer };
@@ -0,0 +1,34 @@
1
+ //#region src/serializer.ts
2
+ /**
3
+ * A default serializer using standard JSON.
4
+ *
5
+ * @warning This implementation is lossy and does not handle complex data types
6
+ * like `Date`, `Map`, `Set`, `undefined`, etc. It is recommended to provide a robust
7
+ * serializer like `superjson` if working with complex data types.
8
+ */
9
+ var JsonSerializer = class {
10
+ hasWarned = false;
11
+ serialize(data) {
12
+ for (const value of Object.values(data)) if (value instanceof Map || value instanceof Set || value instanceof Date) {
13
+ if (!this.hasWarned) {
14
+ console.warn("[Flowcraft] Warning: Default JsonSerializer does not support Map, Set, or Date types. Data may be lost. Consider providing a custom ISerializer (e.g., using superjson).");
15
+ this.hasWarned = true;
16
+ }
17
+ }
18
+ try {
19
+ return JSON.stringify(data);
20
+ } catch {
21
+ console.warn("[Flowcraft] Warning: Circular reference detected in context. Using safe serialization.");
22
+ return JSON.stringify({
23
+ _circularReference: true,
24
+ message: "Context contains circular references"
25
+ });
26
+ }
27
+ }
28
+ deserialize(text) {
29
+ return JSON.parse(text);
30
+ }
31
+ };
32
+
33
+ //#endregion
34
+ export { JsonSerializer };
@@ -0,0 +1,8 @@
1
+ import { K as BaseNode, p as NodeContext, v as NodeResult } from "./types-BcrXJEPI.mjs";
2
+
3
+ //#region src/nodes/sleep.d.ts
4
+ declare class SleepNode extends BaseNode {
5
+ exec(prepResult: any, context: NodeContext<Record<string, any>, any, any>): Promise<Omit<NodeResult, 'error'>>;
6
+ }
7
+ //#endregion
8
+ export { SleepNode as t };
@@ -0,0 +1,8 @@
1
+ import { K as BaseNode, p as NodeContext, v as NodeResult } from "./types-BcrXJEPI.mjs";
2
+
3
+ //#region src/nodes/subflow.d.ts
4
+ declare class SubflowNode extends BaseNode {
5
+ exec(_prepResult: any, context: NodeContext<Record<string, any>, any, any>): Promise<Omit<NodeResult, 'error'>>;
6
+ }
7
+ //#endregion
8
+ export { SubflowNode as t };
@@ -0,0 +1,62 @@
1
+ import { o as IEventBus, r as FlowcraftEvent } from "../types-BcrXJEPI.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
+ export { InMemoryEventLogger };