@particle-academy/fancy-flow 0.45.0 → 0.46.0
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.
- package/dist/{ConfigFieldRenderer-Dzy4f6rG.d.cts → ConfigFieldRenderer-Cyir8-B8.d.cts} +2 -2
- package/dist/{ConfigFieldRenderer-WqJr-td_.d.ts → ConfigFieldRenderer-iMkZj0VK.d.ts} +2 -2
- package/dist/{FlowViewer-BbbUJe2b.d.cts → FlowViewer-CY3_vHSg.d.cts} +1 -1
- package/dist/{FlowViewer-csuaTL5t.d.ts → FlowViewer-DGW6t-80.d.ts} +1 -1
- package/dist/{capabilities-Brn74URS.d.ts → capabilities-DMUhnyZD.d.ts} +1 -1
- package/dist/{capabilities-CYCeIx6I.d.cts → capabilities-Dmd26aAs.d.cts} +1 -1
- package/dist/chunk-BHM5HGXH.js +79 -0
- package/dist/chunk-BHM5HGXH.js.map +1 -0
- package/dist/{chunk-DHUFUDEX.js → chunk-HFUBVT7W.js} +3 -3
- package/dist/{chunk-DHUFUDEX.js.map → chunk-HFUBVT7W.js.map} +1 -1
- package/dist/{chunk-VLATLVGH.js → chunk-L43CWD63.js} +129 -5
- package/dist/chunk-L43CWD63.js.map +1 -0
- package/dist/{chunk-A6RFLGWV.js → chunk-MH4ZW4DJ.js} +3 -3
- package/dist/{chunk-A6RFLGWV.js.map → chunk-MH4ZW4DJ.js.map} +1 -1
- package/dist/{chunk-QZTGV3ZL.js → chunk-P5SYH6M6.js} +3 -3
- package/dist/{chunk-QZTGV3ZL.js.map → chunk-P5SYH6M6.js.map} +1 -1
- package/dist/{chunk-WBXGZVLB.js → chunk-RMU6YCIQ.js} +11 -5
- package/dist/chunk-RMU6YCIQ.js.map +1 -0
- package/dist/durable/index.d.cts +490 -0
- package/dist/durable/index.d.ts +490 -0
- package/dist/durable.cjs +927 -0
- package/dist/durable.cjs.map +1 -0
- package/dist/durable.js +552 -0
- package/dist/durable.js.map +1 -0
- package/dist/engine.cjs +128 -2
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +9 -7
- package/dist/engine.d.ts +9 -7
- package/dist/engine.js +4 -4
- package/dist/fields/react-fancy.d.cts +4 -3
- package/dist/fields/react-fancy.d.ts +4 -3
- package/dist/index.cjs +133 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +12 -10
- package/dist/index.d.ts +12 -10
- package/dist/index.js +13 -88
- package/dist/index.js.map +1 -1
- package/dist/layout/index.d.cts +1 -1
- package/dist/layout/index.d.ts +1 -1
- package/dist/llm/prism.d.cts +2 -2
- package/dist/llm/prism.d.ts +2 -2
- package/dist/llm/vercel-ai.d.cts +2 -2
- package/dist/llm/vercel-ai.d.ts +2 -2
- package/dist/pause-9iT4tCEV.d.cts +96 -0
- package/dist/pause-9iT4tCEV.d.ts +96 -0
- package/dist/registry/index.d.cts +7 -6
- package/dist/registry/index.d.ts +7 -6
- package/dist/{registry-Budds73w.d.cts → registry-D_kyUZEf.d.cts} +1 -1
- package/dist/{registry-D0I20HJ3.d.ts → registry-LHB3MlR9.d.ts} +1 -1
- package/dist/registry.cjs +133 -3
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +4 -4
- package/dist/{run-cohort-CV7EcwGG.d.ts → run-cohort-Cyeny2Uu.d.ts} +3 -34
- package/dist/{run-cohort-DitsaYgS.d.cts → run-cohort-DdPPowJ3.d.cts} +3 -34
- package/dist/run-flow-1KiXhy59.d.cts +55 -0
- package/dist/run-flow-CQvTDBYk.d.ts +55 -0
- package/dist/runtime/index.d.cts +5 -3
- package/dist/runtime/index.d.ts +5 -3
- package/dist/runtime.cjs +128 -2
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +3 -3
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/screens.d.cts +2 -2
- package/dist/screens.d.ts +2 -2
- package/dist/screens.js +6 -6
- package/dist/{types-D6ZSKBly.d.ts → types--S2Kzx7w.d.cts} +3 -97
- package/dist/{types-nQOA8jpk.d.cts → types-ChkCRonF.d.ts} +3 -97
- package/dist/types-Jx1TwehV.d.cts +247 -0
- package/dist/types-Jx1TwehV.d.ts +247 -0
- package/dist/ux.d.cts +3 -2
- package/dist/ux.d.ts +3 -2
- package/package.json +12 -2
- package/dist/chunk-VLATLVGH.js.map +0 -1
- package/dist/chunk-WBXGZVLB.js.map +0 -1
- package/dist/types-CMSrWVYM.d.cts +0 -109
- package/dist/types-CMSrWVYM.d.ts +0 -109
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import { Node, Edge } from '@xyflow/react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Who is running, which step this is, and how many times it has been tried.
|
|
5
|
+
*
|
|
6
|
+
* ## Why an engine needs this at all
|
|
7
|
+
*
|
|
8
|
+
* A node that WRITES to somebody else's system — charge a card, send a message,
|
|
9
|
+
* open a pull request — can only survive a retry if the retry carries the same
|
|
10
|
+
* idempotency key the first attempt did. Otherwise the provider treats the
|
|
11
|
+
* second call as a new request and the customer is charged twice.
|
|
12
|
+
*
|
|
13
|
+
* Until this existed the executor context was `{ node, inputs, emit, abort }`,
|
|
14
|
+
* which is not enough to derive one. Both obvious fallbacks are worse than
|
|
15
|
+
* sending no key at all:
|
|
16
|
+
*
|
|
17
|
+
* - **the node id alone** is stable across retries, and also across RUNS — two
|
|
18
|
+
* legitimate payments share a key and the provider silently collapses the
|
|
19
|
+
* second into the first. A payment that never happened, reported as success;
|
|
20
|
+
* - **a fresh random value** is unique per run, and also per ATTEMPT — a retry
|
|
21
|
+
* creates a second charge, which is the thing being avoided.
|
|
22
|
+
*
|
|
23
|
+
* ## What actually identifies a step
|
|
24
|
+
*
|
|
25
|
+
* Not `(run, node)`. A node legitimately executes more than once inside one
|
|
26
|
+
* run: once per subflow invocation, once per iteration of a loop an executor
|
|
27
|
+
* drives itself. `(run, node)` would give every one of those the same key, and
|
|
28
|
+
* a provider would honour exactly one of them.
|
|
29
|
+
*
|
|
30
|
+
* So a step is identified by the **path of invocations that led to it**, plus
|
|
31
|
+
* an optional **occurrence** for repetition at the same level:
|
|
32
|
+
*
|
|
33
|
+
* ```text
|
|
34
|
+
* runKey ":" segment ("/" segment)* segment := escape(id) ["#" occurrence]
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* And the part that is easy to get backwards: **`attempt` is NOT in the key.**
|
|
38
|
+
* It is carried here for logging and for {@link RunIdentity.isReplaySafe}, and
|
|
39
|
+
* putting it in the key would restore the exact bug the key exists to prevent.
|
|
40
|
+
*
|
|
41
|
+
* Pinned cross-runtime by `shared/flow-run-identity` in
|
|
42
|
+
* `@particle-academy/fancy-conformance`.
|
|
43
|
+
*/
|
|
44
|
+
/** The wire shape — what a queue job payload carries. */
|
|
45
|
+
type RunIdentityJson = {
|
|
46
|
+
runKey: string;
|
|
47
|
+
path?: string[];
|
|
48
|
+
attempt?: number;
|
|
49
|
+
firstAttemptAt?: string;
|
|
50
|
+
};
|
|
51
|
+
/**
|
|
52
|
+
* Escape one segment so the composition is injective.
|
|
53
|
+
*
|
|
54
|
+
* `%` FIRST, or the escaping is not reversible: escaping `/` before `%` turns a
|
|
55
|
+
* literal `a%2Fb` into the same text as the escaped form of `a/b`, which is the
|
|
56
|
+
* collision this exists to prevent, reintroduced by its own fix.
|
|
57
|
+
*/
|
|
58
|
+
declare function escapeSegment(value: string): string;
|
|
59
|
+
/**
|
|
60
|
+
* A run, a position inside it, and how many times this position has been tried.
|
|
61
|
+
*
|
|
62
|
+
* Immutable. {@link descend} returns a new identity rather than mutating, so an
|
|
63
|
+
* executor cannot change what its siblings see.
|
|
64
|
+
*/
|
|
65
|
+
declare class RunIdentity {
|
|
66
|
+
/** Stable for the whole run: same across retries, resumes, workers and hosts. */
|
|
67
|
+
readonly runKey: string;
|
|
68
|
+
/**
|
|
69
|
+
* Enclosing invocation segments, outermost first, ALREADY RENDERED.
|
|
70
|
+
*
|
|
71
|
+
* Empty at the top level. A subflow pushes the invoking node's id; an
|
|
72
|
+
* executor that loops pushes `id#i`.
|
|
73
|
+
*/
|
|
74
|
+
readonly path: readonly string[];
|
|
75
|
+
/**
|
|
76
|
+
* 1-based attempt of THIS logical step. Never part of the key.
|
|
77
|
+
*
|
|
78
|
+
* The durable driver sets it from the node's claim row, which is exact. A
|
|
79
|
+
* plain in-process `runFlow` gets whatever the host passed, which is
|
|
80
|
+
* run-scoped and therefore conservative — see `isReplaySafe`.
|
|
81
|
+
*/
|
|
82
|
+
readonly attempt: number;
|
|
83
|
+
/** ISO-8601 UTC instant of attempt 1 of this step. */
|
|
84
|
+
readonly firstAttemptAt: string;
|
|
85
|
+
constructor(runKey: string, path?: readonly string[], attempt?: number, firstAttemptAt?: string);
|
|
86
|
+
/**
|
|
87
|
+
* The identity of one execution of one node — stable across retries of that
|
|
88
|
+
* execution, distinct from every other execution of the same node.
|
|
89
|
+
*
|
|
90
|
+
* Pass `occurrence` when an executor runs the same node more than once at the
|
|
91
|
+
* same level (a loop body, one item of a fan-out it drives itself).
|
|
92
|
+
*/
|
|
93
|
+
stepKey(nodeId: string, occurrence?: number | null): string;
|
|
94
|
+
/**
|
|
95
|
+
* A child identity for work nested inside this step.
|
|
96
|
+
*
|
|
97
|
+
* `subflow` pushes the invoking node's id, so a node inside the child graph
|
|
98
|
+
* cannot collide with a same-named node in the parent. Attempt and
|
|
99
|
+
* `firstAttemptAt` are carried down unchanged: the nested work happens inside
|
|
100
|
+
* this step's attempt, and shares its clock.
|
|
101
|
+
*/
|
|
102
|
+
descend(segment: string, occurrence?: number | null): RunIdentity;
|
|
103
|
+
/** A copy on a different attempt, first-attempt clock preserved. */
|
|
104
|
+
withAttempt(attempt: number, firstAttemptAt?: string): RunIdentity;
|
|
105
|
+
/**
|
|
106
|
+
* May this attempt reuse the step key and still be deduplicated?
|
|
107
|
+
*
|
|
108
|
+
* Providers forget idempotency keys — Stripe after 24 hours. Past that
|
|
109
|
+
* window, resending the key creates a second charge and sending a fresh one
|
|
110
|
+
* creates a second charge, so **the caller must refuse rather than pick
|
|
111
|
+
* between them**: a loud stuck run beats a silent double write.
|
|
112
|
+
*
|
|
113
|
+
* `true` on attempt 1 whatever the elapsed time — nothing was sent on an
|
|
114
|
+
* earlier attempt, so there is nothing for the provider to have forgotten.
|
|
115
|
+
* That is what lets a run park on a human gate for a week and then write.
|
|
116
|
+
*
|
|
117
|
+
* `windowSeconds: null` means the provider does not expire keys. `0` means
|
|
118
|
+
* it does not dedupe at all, so no retry may reuse a key — it is a real
|
|
119
|
+
* window, not an absent one, and the two must not be conflated: reading `0`
|
|
120
|
+
* as `null` turns "this provider does not dedupe" into "this provider
|
|
121
|
+
* dedupes forever", which is the more dangerous of the two by a distance.
|
|
122
|
+
*/
|
|
123
|
+
isReplaySafe(windowSeconds: number | null | undefined, now?: Date | string): boolean;
|
|
124
|
+
toJSON(): Required<RunIdentityJson>;
|
|
125
|
+
/** Rebuild from a queue payload. */
|
|
126
|
+
static from(value: RunIdentity | RunIdentityJson | string): RunIdentity;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Public domain types for fancy-flow. Built-in nodes are layered on top of
|
|
131
|
+
* @xyflow/react's `Node` so consumers can mix custom xyflow nodes alongside
|
|
132
|
+
* the kit. Edges remain xyflow's standard `Edge`.
|
|
133
|
+
*/
|
|
134
|
+
|
|
135
|
+
type FlowNodeKind = "trigger" | "action" | "decision" | "output" | "note" | "subgraph";
|
|
136
|
+
/** Status surfaced on the node while a run is in progress. */
|
|
137
|
+
type NodeRunStatus = "idle" | "queued" | "running" | "done" | "error";
|
|
138
|
+
/** Port description on a node. Ports are visual handles xyflow can connect. */
|
|
139
|
+
type PortDescriptor = {
|
|
140
|
+
id: string;
|
|
141
|
+
label?: string;
|
|
142
|
+
/** Optional logical type for hosts that want to validate connections. */
|
|
143
|
+
type?: string;
|
|
144
|
+
};
|
|
145
|
+
/** Common shape every kit node carries in its `data` slot. */
|
|
146
|
+
type BaseNodeData = {
|
|
147
|
+
label: string;
|
|
148
|
+
description?: string;
|
|
149
|
+
/** Free-form configuration the host owns (form values, code, parameters). */
|
|
150
|
+
config?: Record<string, unknown>;
|
|
151
|
+
/** Set by the runner; hosts shouldn't edit this directly. */
|
|
152
|
+
status?: NodeRunStatus;
|
|
153
|
+
/** Optional human-readable status detail (e.g. error message, current step). */
|
|
154
|
+
statusText?: string;
|
|
155
|
+
/** Per-node accent override, e.g. for theming a custom subclass. */
|
|
156
|
+
color?: string;
|
|
157
|
+
/** Input ports rendered on the node. Defaults vary by kind. */
|
|
158
|
+
inputs?: PortDescriptor[];
|
|
159
|
+
/** Output ports rendered on the node. Defaults vary by kind. */
|
|
160
|
+
outputs?: PortDescriptor[];
|
|
161
|
+
};
|
|
162
|
+
type TriggerNodeData = BaseNodeData & {
|
|
163
|
+
kind: "trigger";
|
|
164
|
+
};
|
|
165
|
+
type ActionNodeData = BaseNodeData & {
|
|
166
|
+
kind: "action";
|
|
167
|
+
};
|
|
168
|
+
type DecisionNodeData = BaseNodeData & {
|
|
169
|
+
kind: "decision";
|
|
170
|
+
};
|
|
171
|
+
type OutputNodeData = BaseNodeData & {
|
|
172
|
+
kind: "output";
|
|
173
|
+
};
|
|
174
|
+
type NoteNodeData = BaseNodeData & {
|
|
175
|
+
kind: "note";
|
|
176
|
+
body?: string;
|
|
177
|
+
};
|
|
178
|
+
type SubgraphNodeData = BaseNodeData & {
|
|
179
|
+
kind: "subgraph";
|
|
180
|
+
/** Ids of the nodes contained in this subgraph. */
|
|
181
|
+
childIds?: string[];
|
|
182
|
+
/** Whether the subgraph is shown collapsed (default true — children hidden). */
|
|
183
|
+
collapsed?: boolean;
|
|
184
|
+
};
|
|
185
|
+
type FlowNodeData = TriggerNodeData | ActionNodeData | DecisionNodeData | OutputNodeData | NoteNodeData | SubgraphNodeData;
|
|
186
|
+
type FlowNode = Node<FlowNodeData>;
|
|
187
|
+
type FlowEdge = Edge;
|
|
188
|
+
/** A serializable graph — what hosts persist, what agents read/write. */
|
|
189
|
+
type FlowGraph = {
|
|
190
|
+
nodes: FlowNode[];
|
|
191
|
+
edges: FlowEdge[];
|
|
192
|
+
};
|
|
193
|
+
/** Per-node executor signature. Inputs are keyed by input-port id. */
|
|
194
|
+
type NodeExecutor<TIn = Record<string, unknown>, TOut = unknown> = (ctx: {
|
|
195
|
+
node: FlowNode;
|
|
196
|
+
inputs: TIn;
|
|
197
|
+
/** Stops the run if called. */
|
|
198
|
+
abort: (reason?: string) => never;
|
|
199
|
+
/** Lets the executor stream status updates and partial outputs. */
|
|
200
|
+
emit: (event: RunEvent) => void;
|
|
201
|
+
/**
|
|
202
|
+
* How deep this run is nested. 0 for a top-level run; `subflow` passes
|
|
203
|
+
* depth + 1 to its child, so runaway recursion can be reported by name
|
|
204
|
+
* rather than as a stack overflow.
|
|
205
|
+
*/
|
|
206
|
+
depth?: number;
|
|
207
|
+
/**
|
|
208
|
+
* Who is running, and which attempt of which step this is.
|
|
209
|
+
*
|
|
210
|
+
* `ctx.run.stepKey(ctx.node.id)` is the idempotency key for a node that
|
|
211
|
+
* writes to somebody else's system — stable across retries of this step,
|
|
212
|
+
* distinct for every other execution of the same node.
|
|
213
|
+
*
|
|
214
|
+
* `undefined` when the host supplied no identity, and that is a real
|
|
215
|
+
* answer: a write with no key must decline or accept one attempt, never
|
|
216
|
+
* invent a key. See `RunIdentity`.
|
|
217
|
+
*/
|
|
218
|
+
run?: RunIdentity;
|
|
219
|
+
}) => Promise<TOut> | TOut;
|
|
220
|
+
type ExecutorRegistry = Partial<Record<FlowNodeKind | string, NodeExecutor>>;
|
|
221
|
+
type RunEvent = {
|
|
222
|
+
type: "node-status";
|
|
223
|
+
nodeId: string;
|
|
224
|
+
status: NodeRunStatus;
|
|
225
|
+
text?: string;
|
|
226
|
+
} | {
|
|
227
|
+
type: "node-output";
|
|
228
|
+
nodeId: string;
|
|
229
|
+
portId: string;
|
|
230
|
+
value: unknown;
|
|
231
|
+
} | {
|
|
232
|
+
type: "log";
|
|
233
|
+
nodeId?: string;
|
|
234
|
+
level: "info" | "warn" | "error";
|
|
235
|
+
message: string;
|
|
236
|
+
detail?: unknown;
|
|
237
|
+
} | {
|
|
238
|
+
type: "run-start";
|
|
239
|
+
} | {
|
|
240
|
+
type: "run-end";
|
|
241
|
+
ok: boolean;
|
|
242
|
+
} | {
|
|
243
|
+
type: "run-error";
|
|
244
|
+
error: string;
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
export { type ActionNodeData as A, type BaseNodeData as B, type DecisionNodeData as D, type ExecutorRegistry as E, type FlowGraph as F, type NodeExecutor as N, type OutputNodeData as O, type PortDescriptor as P, type RunEvent as R, type SubgraphNodeData as S, type TriggerNodeData as T, type FlowNode as a, RunIdentity as b, type RunIdentityJson as c, type FlowEdge as d, type FlowNodeData as e, type FlowNodeKind as f, type NodeRunStatus as g, type NoteNodeData as h, escapeSegment as i };
|
package/dist/ux.d.cts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { EffectRegistry, DispatchActor } from '@particle-academy/fancy-auto-common';
|
|
2
2
|
export { AutoActivityEvent } from '@particle-academy/fancy-auto-common';
|
|
3
|
-
import { a as NodeCategory, C as ConfigField } from './types
|
|
4
|
-
import { E as ExecutorRegistry } from './types-
|
|
3
|
+
import { a as NodeCategory, C as ConfigField } from './types--S2Kzx7w.cjs';
|
|
4
|
+
import { E as ExecutorRegistry } from './types-Jx1TwehV.cjs';
|
|
5
5
|
import 'react';
|
|
6
6
|
import '@xyflow/react';
|
|
7
|
+
import './pause-9iT4tCEV.cjs';
|
|
7
8
|
|
|
8
9
|
/** Per-effect presentation for the palette node kind that drives it. */
|
|
9
10
|
type UxEffectMeta = {
|
package/dist/ux.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { EffectRegistry, DispatchActor } from '@particle-academy/fancy-auto-common';
|
|
2
2
|
export { AutoActivityEvent } from '@particle-academy/fancy-auto-common';
|
|
3
|
-
import { a as NodeCategory, C as ConfigField } from './types-
|
|
4
|
-
import { E as ExecutorRegistry } from './types-
|
|
3
|
+
import { a as NodeCategory, C as ConfigField } from './types-ChkCRonF.js';
|
|
4
|
+
import { E as ExecutorRegistry } from './types-Jx1TwehV.js';
|
|
5
5
|
import 'react';
|
|
6
6
|
import '@xyflow/react';
|
|
7
|
+
import './pause-9iT4tCEV.js';
|
|
7
8
|
|
|
8
9
|
/** Per-effect presentation for the palette node kind that drives it. */
|
|
9
10
|
type UxEffectMeta = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@particle-academy/fancy-flow",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.46.0",
|
|
4
4
|
"description": "Workflow editor + runner. Six built-in node kits (trigger / action / decision / output / note / subgraph), tokenized theme, topological execution with per-node status. React-flow bundled; consumers npm install fancy-flow and get nothing extra.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -73,6 +73,16 @@
|
|
|
73
73
|
"default": "./dist/engine.cjs"
|
|
74
74
|
}
|
|
75
75
|
},
|
|
76
|
+
"./durable": {
|
|
77
|
+
"import": {
|
|
78
|
+
"types": "./dist/durable/index.d.ts",
|
|
79
|
+
"default": "./dist/durable.js"
|
|
80
|
+
},
|
|
81
|
+
"require": {
|
|
82
|
+
"types": "./dist/durable/index.d.cts",
|
|
83
|
+
"default": "./dist/durable.cjs"
|
|
84
|
+
}
|
|
85
|
+
},
|
|
76
86
|
"./ux": {
|
|
77
87
|
"import": {
|
|
78
88
|
"types": "./dist/ux.d.ts",
|
|
@@ -170,7 +180,7 @@
|
|
|
170
180
|
"devDependencies": {
|
|
171
181
|
"@dagrejs/dagre": "^3.0.0",
|
|
172
182
|
"@particle-academy/fancy-cms-ui": "^0.2.0",
|
|
173
|
-
"@particle-academy/fancy-conformance": "^0.
|
|
183
|
+
"@particle-academy/fancy-conformance": "^0.4.0",
|
|
174
184
|
"@particle-academy/fancy-query": "^0.8.0",
|
|
175
185
|
"@particle-academy/fancy-screens": "^0.4.3",
|
|
176
186
|
"@particle-academy/react-fancy": "^5.17.1",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runtime/run-flow.ts"],"names":[],"mappings":"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","file":"chunk-VLATLVGH.js","sourcesContent":["import type {\n ExecutorRegistry,\n FlowEdge,\n FlowGraph,\n FlowNode,\n NodeExecutor,\n RunEvent,\n} from \"../types\";\n// Both modules are React-free by design — the `/engine` entry must not pull in\n// React. Import them directly rather than via the `registry` barrel, which\n// re-exports the RegistryNode component.\nimport { getNodeKind, kindIds } from \"../registry/registry\";\nimport { resolveNodePorts } from \"../registry/ports\";\n\nexport type RunOptions = {\n /** Stop the run after this many ms. Default: no timeout. */\n timeoutMs?: number;\n /** Abort signal — host can cancel the run. */\n signal?: AbortSignal;\n /** Initial inputs supplied to entry-point nodes (no incoming edges). */\n initialInputs?: Record<string, Record<string, unknown>>;\n /** Nesting depth — set by `subflow` when it runs a child graph. */\n depth?: number;\n};\n\nexport type RunResult = {\n ok: boolean;\n /** Outputs collected per node, keyed by node id. */\n outputs: Record<string, unknown>;\n /** Error captured if any node threw. */\n error?: string;\n};\n\n/**\n * runFlow — topological execution of a FlowGraph against an ExecutorRegistry.\n *\n * Each node runs once, when all upstream nodes have produced outputs on the\n * connected ports. Decision nodes (or any executor that returns `{ branch:\n * 'true' }`) can short-circuit specific output ports — only edges leaving\n * an \"active\" port propagate to downstream nodes.\n *\n * Cycles are detected and abort the run with an error.\n *\n * The `onEvent` callback receives a stream of `RunEvent`s — wire it to a\n * status feed, log panel, or store.\n */\nexport async function runFlow(\n graph: FlowGraph,\n executors: ExecutorRegistry,\n onEvent: (event: RunEvent) => void = () => {},\n options: RunOptions = {},\n): Promise<RunResult> {\n const { signal, initialInputs = {}, timeoutMs, depth = 0 } = options;\n const outputs: Record<string, unknown> = {};\n const portValues = new Map<string, unknown>(); // key: `${nodeId}:${portId}`\n const completed = new Set<string>();\n const errors: string[] = [];\n\n // Topological order via Kahn's algorithm. We allow nodes to run as soon\n // as their incoming edges' source ports have produced values, so the\n // order here is just a deterministic baseline used for cycle detection.\n const order = topoSort(graph);\n if (order === null) {\n const msg = \"Cycle detected in flow graph — aborting.\";\n onEvent({ type: \"run-error\", error: msg });\n return { ok: false, outputs, error: msg };\n }\n\n const incomingByNode = indexIncoming(graph.edges);\n const timer = timeoutMs ? setTimeout(() => errors.push(`Run timed out after ${timeoutMs}ms`), timeoutMs) : null;\n\n onEvent({ type: \"run-start\" });\n\n try {\n for (const node of order) {\n if (signal?.aborted) throw new Error(\"aborted\");\n if (errors.length) break;\n\n const incoming = incomingByNode.get(node.id) ?? [];\n\n // Run a node once any upstream branch reaches it. We iterate in\n // topological order, so by the time we reach this node every upstream\n // node has been processed — each incoming edge is therefore *settled*\n // (active or dead, never still-pending). Requiring ALL incoming edges to\n // be active wrongly skipped MERGE POINTS: when a Decision routes down one\n // branch, the other branch's edge stays dead forever, so an `every` check\n // skipped the shared continuation node and halted the run after the first\n // branch (#1). Run when AT LEAST ONE incoming edge is active —\n // collectInputs() only reads from the active ones. A genuine parallel\n // join still works: in topo order both of its inputs are already active.\n if (incoming.length > 0) {\n const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? \"out\"}`));\n if (!anyActive) {\n onEvent({ type: \"node-status\", nodeId: node.id, status: \"idle\", text: \"skipped\" });\n continue;\n }\n }\n\n // Notes/annotations + layout (lane/pool) nodes are visual only — never\n // executed and never fed to runners. Their config (a note's text, a lane's\n // title) stays in the document for editors + MCP tools, but the engine\n // walks straight past them. Edges cross lanes freely, so grouping never\n // affects topology.\n const visualKind = getNodeKind(node.type ?? \"\");\n const isLayout = visualKind?.category === \"layout\";\n const isAnnotation = node.type === \"note\" || visualKind?.category === \"annotation\";\n if (isLayout || isAnnotation) {\n onEvent({\n type: \"node-status\",\n nodeId: node.id,\n status: \"idle\",\n text: isLayout ? \"lane\" : \"annotation\",\n });\n continue;\n }\n\n onEvent({ type: \"node-status\", nodeId: node.id, status: \"running\" });\n\n const inputs = collectInputs(node, incoming, portValues, initialInputs);\n const exec = pickExecutor(executors, node);\n if (!exec) {\n const msg = `No executor registered for kind=${node.type}`;\n errors.push(msg);\n onEvent({ type: \"node-status\", nodeId: node.id, status: \"error\", text: msg });\n onEvent({ type: \"log\", nodeId: node.id, level: \"error\", message: msg });\n break;\n }\n\n try {\n const result = await Promise.resolve(\n exec({\n node,\n inputs,\n abort: (reason) => { throw new Error(reason ?? \"aborted\"); },\n emit: onEvent,\n depth,\n }),\n );\n outputs[node.id] = result;\n\n // Decide which output ports were activated. Three conventions:\n // 1) If result is `{ __port: \"out\", value: x }`, only that port emits.\n // 2) If result has `branch: <portId>`, only that port emits (decision sugar).\n // 3) Otherwise, the value is published on every declared output port.\n const activated = activatedPorts(node, result);\n for (const portId of activated.ports) {\n portValues.set(`${node.id}:${portId}`, activated.value);\n onEvent({ type: \"node-output\", nodeId: node.id, portId, value: activated.value });\n }\n completed.add(node.id);\n onEvent({ type: \"node-status\", nodeId: node.id, status: \"done\" });\n } catch (e) {\n const msg = e instanceof Error ? e.message : String(e);\n errors.push(msg);\n onEvent({ type: \"node-status\", nodeId: node.id, status: \"error\", text: msg });\n onEvent({ type: \"log\", nodeId: node.id, level: \"error\", message: msg });\n break;\n }\n }\n } finally {\n if (timer) clearTimeout(timer);\n }\n\n const ok = errors.length === 0;\n onEvent({ type: \"run-end\", ok });\n return ok ? { ok, outputs } : { ok, outputs, error: errors[0] };\n}\n\nfunction indexIncoming(edges: FlowEdge[]): Map<string, FlowEdge[]> {\n const map = new Map<string, FlowEdge[]>();\n for (const e of edges) {\n const list = map.get(e.target) ?? [];\n list.push(e);\n map.set(e.target, list);\n }\n return map;\n}\n\nfunction topoSort(graph: FlowGraph): FlowNode[] | null {\n const inDegree = new Map<string, number>();\n for (const n of graph.nodes) inDegree.set(n.id, 0);\n for (const e of graph.edges) inDegree.set(e.target, (inDegree.get(e.target) ?? 0) + 1);\n const queue: string[] = [];\n for (const [id, d] of inDegree) if (d === 0) queue.push(id);\n const ordered: string[] = [];\n while (queue.length) {\n const id = queue.shift()!;\n ordered.push(id);\n for (const e of graph.edges) {\n if (e.source !== id) continue;\n const next = (inDegree.get(e.target) ?? 0) - 1;\n inDegree.set(e.target, next);\n if (next === 0) queue.push(e.target);\n }\n }\n if (ordered.length !== graph.nodes.length) return null;\n const byId = new Map(graph.nodes.map((n) => [n.id, n]));\n return ordered.map((id) => byId.get(id)!).filter(Boolean);\n}\n\nfunction collectInputs(\n node: FlowNode,\n incoming: FlowEdge[],\n portValues: Map<string, unknown>,\n initial: Record<string, Record<string, unknown>>,\n): Record<string, unknown> {\n const inputs: Record<string, unknown> = { ...(initial[node.id] ?? {}) };\n for (const e of incoming) {\n const portId = e.targetHandle ?? \"in\";\n const key = `${e.source}:${e.sourceHandle ?? \"out\"}`;\n // A branch that never fired has no entry in portValues. Assigning its\n // `undefined` would CLOBBER the value the branch that DID fire already\n // contributed on this same handle — emptying every merge point downstream\n // of a decision, silently, with the run still reporting success.\n //\n // This is the other half of the merge-point bug above: that fix stopped the\n // shared continuation node being skipped, but it still arrived with nothing\n // whenever a later inactive edge happened to be last in `incoming`.\n if (!portValues.has(key)) continue;\n inputs[portId] = portValues.get(key);\n }\n return inputs;\n}\n\nfunction pickExecutor(\n executors: ExecutorRegistry,\n node: FlowNode,\n): NodeExecutor | undefined {\n if (executors[node.id]) return executors[node.id];\n if (node.type && executors[node.type]) return executors[node.type];\n\n // Try every id the kind answers to. Kinds are namespaced (`@fancy/switch_case`)\n // while a host may have bound its executor under the bare name it used before\n // — or vice versa. Without this, the rename would silently stop matching and\n // the node would fall through to `*` or simply not run: a breaking change\n // wearing the costume of a rename.\n const kind = node.type ? getNodeKind(node.type) : null;\n if (kind) {\n for (const id of kindIds(kind)) {\n if (executors[id]) return executors[id];\n }\n }\n\n return executors[\"*\"];\n}\n\nfunction activatedPorts(node: FlowNode, result: unknown): { ports: string[]; value: unknown } {\n if (result && typeof result === \"object\") {\n const r = result as Record<string, unknown>;\n if (typeof r.__port === \"string\") {\n return { ports: [r.__port], value: r.value };\n }\n if (typeof r.branch === \"string\") {\n return { ports: [r.branch], value: r.value ?? r };\n }\n }\n // Resolve through the shared helper so the ports the runtime activates are\n // the same ones the canvas drew — including config-driven ports, which the\n // node's `data` does not carry. Falls back to a lone `out` when a node\n // declares nothing.\n const kind = getNodeKind((node.data as any)?.kind ?? node.type ?? \"\") ?? undefined;\n const declared = resolveNodePorts(node, kind).outputs?.map((p) => p.id);\n return { ports: declared?.length ? declared : [\"out\"], value: result };\n}\n"]}
|