@particle-academy/fancy-flow 0.71.0 → 0.72.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.
- package/dist/{chunk-3L47KJ6Q.js → chunk-3RXQGYGH.js} +3 -3
- package/dist/{chunk-3L47KJ6Q.js.map → chunk-3RXQGYGH.js.map} +1 -1
- package/dist/{chunk-PNTDTSMR.js → chunk-7DJ56PUA.js} +4 -4
- package/dist/{chunk-PNTDTSMR.js.map → chunk-7DJ56PUA.js.map} +1 -1
- package/dist/{chunk-EVGSL2EM.js → chunk-EX34GCKK.js} +162 -151
- package/dist/chunk-EX34GCKK.js.map +1 -0
- package/dist/{chunk-XI4AZD2S.js → chunk-GQ5LEA7Q.js} +3 -3
- package/dist/{chunk-XI4AZD2S.js.map → chunk-GQ5LEA7Q.js.map} +1 -1
- package/dist/{chunk-D2OBU3CE.js → chunk-K7KO3UJ3.js} +3 -3
- package/dist/{chunk-D2OBU3CE.js.map → chunk-K7KO3UJ3.js.map} +1 -1
- package/dist/{chunk-FPWJYSXX.js → chunk-LYYQO7EY.js} +4 -4
- package/dist/{chunk-FPWJYSXX.js.map → chunk-LYYQO7EY.js.map} +1 -1
- package/dist/{chunk-KQCX6KN7.js → chunk-VAF2SWTC.js} +3 -3
- package/dist/{chunk-KQCX6KN7.js.map → chunk-VAF2SWTC.js.map} +1 -1
- package/dist/durable/index.d.cts +90 -16
- package/dist/durable/index.d.ts +90 -16
- package/dist/durable.cjs +160 -83
- package/dist/durable.cjs.map +1 -1
- package/dist/durable.js +79 -14
- package/dist/durable.js.map +1 -1
- package/dist/engine.cjs +82 -71
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.cts +2 -2
- package/dist/engine.d.ts +2 -2
- package/dist/engine.js +4 -4
- package/dist/index.cjs +128 -117
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +12 -12
- package/dist/registry.cjs +82 -71
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +2 -2
- package/dist/{run-cohort-DL9JzzPU.d.cts → run-cohort-DNpPa0mq.d.cts} +1 -1
- package/dist/{run-cohort-CjUBWg6X.d.ts → run-cohort-rgT_FX3V.d.ts} +1 -1
- package/dist/{run-flow-B_8hgO5_.d.cts → run-flow-BLP9rXfO.d.ts} +22 -1
- package/dist/{run-flow-CxEGBOxd.d.ts → run-flow-pW0PllaZ.d.cts} +22 -1
- package/dist/runtime/index.d.cts +3 -3
- package/dist/runtime/index.d.ts +3 -3
- package/dist/runtime.cjs +82 -71
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +3 -3
- package/dist/schema.cjs +82 -71
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +3 -3
- package/dist/screens.cjs +82 -71
- package/dist/screens.cjs.map +1 -1
- package/dist/screens.js +4 -4
- package/dist/ux.cjs +82 -71
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-EVGSL2EM.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/analysis/graph-connectivity.ts"],"names":[],"mappings":";;;AAsCO,SAAS,uBAAuB,KAAA,EAAiC;AACtE,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,KAAA;AACzB,EAAA,MAAM,SAAwB,EAAC;AAE/B,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,WAAA,CAAY,GAAA,CAAI,KAAK,MAAM,CAAA;AAC3B,IAAA,WAAA,CAAY,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,EAC7B;AAMA,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,KAAW,CAAA;AAEhC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,MAAA,IAAU,QAAA,CAAS,IAAI,CAAA,EAAG;AAE9B,IAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG;AAC1D,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,KAAA,EAAO,OAAA;AAAA,QACP,QAAQ,IAAA,CAAK,EAAA;AAAA,QACb,OAAA,EACE,CAAA,MAAA,EAAS,IAAA,CAAK,EAAE,CAAA,oSAAA;AAAA,OAInB,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAEhD,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,YAAA,CAAa,MAAM,CAAA,EAAG;AAEtC,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,KAAA,EAAO,OAAA;AAAA,MACP,QAAQ,IAAA,CAAK,EAAA;AAAA,MACb,OAAA,EACE,SAAS,IAAA,CAAK,EAAE,iBAAiB,IAAA,CAAK,MAAM,CAAA,6JAAA,EAET,IAAA,CAAK,MAAM,CAAA,kCAAA;AAAA,KACjD,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,MAAA;AACT;AAyBO,SAAS,SAAS,IAAA,EAAyB;AAChD,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,EAAA,IAAI,CAAC,MAAM,OAAO,KAAA;AAClB,EAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,IAAA;AAE5B,EAAA,MAAM,IAAA,GAAO,YAAY,IAAI,CAAA;AAC7B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,EAAA,OAAO,OAAA,CAAQ,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,KAAK,IAAA,CAAK,QAAA,KAAa,YAAA,IAAgB,IAAA,CAAK,QAAA,KAAa,QAAA;AAC/F;AAWA,SAAS,aAAa,IAAA,EAAyB;AAM7C,EAAA,MAAM,GAAA,GAAO,KAAK,IAAA,EAA8C,OAAA;AAChE,EAAA,IAAI,MAAM,OAAA,CAAQ,GAAG,CAAA,EAAG,OAAO,IAAI,MAAA,KAAW,CAAA;AAE9C,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA,GAAO,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA,GAAI,IAAA;AAKlD,EAAA,IAAI,CAAC,MAAM,OAAO,KAAA;AAElB,EAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA,IAAK,IAAA,CAAK,QAAQ,MAAA,KAAW,CAAA;AAChE","file":"chunk-
|
|
1
|
+
{"version":3,"sources":["../src/analysis/graph-connectivity.ts"],"names":[],"mappings":";;;AAsCO,SAAS,uBAAuB,KAAA,EAAiC;AACtE,EAAA,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,GAAI,KAAA;AACzB,EAAA,MAAM,SAAwB,EAAC;AAE/B,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAY;AACpC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,WAAA,CAAY,GAAA,CAAI,KAAK,MAAM,CAAA;AAC3B,IAAA,WAAA,CAAY,GAAA,CAAI,KAAK,MAAM,CAAA;AAAA,EAC7B;AAMA,EAAA,MAAM,MAAA,GAAS,MAAM,MAAA,KAAW,CAAA;AAEhC,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,IAAI,MAAA,IAAU,QAAA,CAAS,IAAI,CAAA,EAAG;AAE9B,IAAA,IAAI,CAAC,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,IAAK,CAAC,WAAA,CAAY,GAAA,CAAI,IAAA,CAAK,EAAE,CAAA,EAAG;AAC1D,MAAA,MAAA,CAAO,IAAA,CAAK;AAAA,QACV,KAAA,EAAO,OAAA;AAAA,QACP,QAAQ,IAAA,CAAK,EAAA;AAAA,QACb,OAAA,EACE,CAAA,MAAA,EAAS,IAAA,CAAK,EAAE,CAAA,oSAAA;AAAA,OAInB,CAAA;AAAA,IACH;AAAA,EACF;AAEA,EAAA,MAAM,IAAA,GAAO,IAAI,GAAA,CAAI,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,KAAM,CAAC,CAAA,CAAE,EAAA,EAAI,CAAC,CAAC,CAAC,CAAA;AAEhD,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACxB,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,IAAA,CAAK,MAAM,CAAA;AACnC,IAAA,IAAI,CAAC,MAAA,IAAU,CAAC,YAAA,CAAa,MAAM,CAAA,EAAG;AAEtC,IAAA,MAAA,CAAO,IAAA,CAAK;AAAA,MACV,KAAA,EAAO,OAAA;AAAA,MACP,QAAQ,IAAA,CAAK,EAAA;AAAA,MACb,OAAA,EACE,SAAS,IAAA,CAAK,EAAE,iBAAiB,IAAA,CAAK,MAAM,CAAA,6JAAA,EAET,IAAA,CAAK,MAAM,CAAA,kCAAA;AAAA,KACjD,CAAA;AAAA,EACH;AAEA,EAAA,OAAO,MAAA;AACT;AAyBO,SAAS,SAAS,IAAA,EAAyB;AAChD,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA;AAClB,EAAA,IAAI,CAAC,MAAM,OAAO,KAAA;AAClB,EAAA,IAAI,IAAA,KAAS,QAAQ,OAAO,IAAA;AAE5B,EAAA,MAAM,IAAA,GAAO,YAAY,IAAI,CAAA;AAC7B,EAAA,IAAI,CAAC,MAAM,OAAO,IAAA;AAElB,EAAA,OAAO,OAAA,CAAQ,IAAI,CAAA,CAAE,QAAA,CAAS,MAAM,KAAK,IAAA,CAAK,QAAA,KAAa,YAAA,IAAgB,IAAA,CAAK,QAAA,KAAa,QAAA;AAC/F;AAWA,SAAS,aAAa,IAAA,EAAyB;AAM7C,EAAA,MAAM,GAAA,GAAO,KAAK,IAAA,EAA8C,OAAA;AAChE,EAAA,IAAI,MAAM,OAAA,CAAQ,GAAG,CAAA,EAAG,OAAO,IAAI,MAAA,KAAW,CAAA;AAE9C,EAAA,MAAM,OAAO,IAAA,CAAK,IAAA,GAAO,WAAA,CAAY,IAAA,CAAK,IAAI,CAAA,GAAI,IAAA;AAKlD,EAAA,IAAI,CAAC,MAAM,OAAO,KAAA;AAElB,EAAA,OAAO,MAAM,OAAA,CAAQ,IAAA,CAAK,OAAO,CAAA,IAAK,IAAA,CAAK,QAAQ,MAAA,KAAW,CAAA;AAChE","file":"chunk-VAF2SWTC.js","sourcesContent":["import { getNodeKind, kindIds } from \"../registry/registry\";\nimport type { FlowGraph, FlowNode } from \"../types\";\nimport type { ImportIssue } from \"../schema/workflow-schema\";\n\n/**\n * Refuse a graph whose nodes cannot take part in the workflow's dataflow.\n *\n * Two shapes, both of which import cleanly and then quietly do nothing. Neither\n * FAILS — which is what makes them worth refusing at authoring time, because a\n * run that reports success is the worst way for a workflow to be wrong. Both\n * were measured against the PHP twin's engine before this was written, and both\n * behave the same way here:\n *\n * ## 1. A FLOATING node — no inbound and no outbound edge\n *\n * It is NOT skipped. A node with no incoming edge is a root, so the topo sort\n * runs it: a three-node graph with one floating `log` executed `t,lonely,o`. It\n * runs disconnected — receiving nothing from the graph and reaching nobody in\n * it — which is precisely the state an author cannot see on a canvas.\n *\n * ## 2. An edge leaving a TERMINATOR\n *\n * A terminal kind — `output`, `log` — declares an EMPTY output port list. It\n * ends a chain. Measured: `t -> output -> log` imported clean and the `log` DID\n * run, with `{{ input }}` resolving to `\"\"`. `collectInputs` binds a payload\n * only when `\"<sourceId>:<handle>\"` exists, and a node publishing no ports never\n * creates that key — so the edge does not fail, it delivers nothing, and the\n * node downstream operates on a hole.\n *\n * That is the same silent-nothing the undelivered-edge diagnostic reports at run\n * time, but this one is decidable FROM THE DOCUMENT ALONE.\n *\n * ## What may float\n *\n * See {@link mayFloat}. Not only `note`: any `annotation` or `layout` kind (a\n * swimlane is never wired to anything — that is what a lane IS), and any kind\n * the registry does not know.\n */\nexport function checkGraphConnectivity(graph: FlowGraph): ImportIssue[] {\n const { nodes, edges } = graph;\n const issues: ImportIssue[] = [];\n\n const hasIncoming = new Set<string>();\n const hasOutgoing = new Set<string>();\n for (const edge of edges) {\n hasIncoming.add(edge.target);\n hasOutgoing.add(edge.source);\n }\n\n // A single-node graph is not \"floating\" — it is a graph with one step, which\n // is a legitimate (if small) workflow and what every graph looks like on the\n // way to a bigger one. Refusing it would make the editor unusable from the\n // first node placed.\n const single = nodes.length === 1;\n\n for (const node of nodes) {\n if (single || mayFloat(node)) continue;\n\n if (!hasIncoming.has(node.id) && !hasOutgoing.has(node.id)) {\n issues.push({\n level: \"error\",\n nodeId: node.id,\n message:\n `Node \"${node.id}\" is connected to nothing — no inbound edge and no outbound edge. ` +\n `It still RUNS (a node with no inbound edge is a root), but it receives nothing from ` +\n `the graph and reaches nobody in it, so it is either unwired or left behind by a ` +\n `deletion. Only a note, an annotation or a lane may float.`,\n });\n }\n }\n\n const byId = new Map(nodes.map((n) => [n.id, n]));\n\n for (const edge of edges) {\n const source = byId.get(edge.source);\n if (!source || !isTerminator(source)) continue;\n\n issues.push({\n level: \"error\",\n edgeId: edge.id,\n message:\n `Edge \"${edge.id}\" reads from \"${edge.source}\", which is a TERMINAL node and publishes ` +\n `no output ports at all. Nothing can ever travel this edge: it does not fail at run ` +\n `time, it delivers nothing, and \"${edge.target}\" runs anyway with an empty input.`,\n });\n }\n\n return issues;\n}\n\n/**\n * Which nodes are allowed to sit unconnected.\n *\n * Three answers, and the third is the one that took a second pass — it was\n * missed in the PHP twin's first release and shipped as 0.48.1:\n *\n * 1. **`note`**, across every id the kind answers to, so a graph saved with the\n * canonical `@particle-academy/note` stays an annotation rather than becoming\n * an unwireable node.\n * 2. **Any `annotation` or `layout` kind.** A host may register its own note,\n * and `@particle-academy/lane` is a swimlane the engine walks straight past.\n * Neither is a step and neither is ever wired.\n * 3. **A kind the registry has never heard of.** Not a loophole — the honest\n * answer. An unknown kind already produces its own issue, and we cannot know\n * whether it is a step, an annotation or a lane. Claiming it must be wired\n * would assert something unverifiable, and it lands hardest on the graphs\n * that deserve it least: a laned graph loaded by a runtime without `lane`\n * registered would report every swimlane twice, the second time wrongly.\n *\n * The note/annotation/layout half matches the test `run-flow` uses to skip a\n * node. The unknown-kind half deliberately does NOT — see the closing comment in\n * this file for why the two must not become one helper.\n */\nexport function mayFloat(node: FlowNode): boolean {\n const type = node.type;\n if (!type) return false;\n if (type === \"note\") return true;\n\n const kind = getNodeKind(type);\n if (!kind) return true;\n\n return kindIds(kind).includes(\"note\") || kind.category === \"annotation\" || kind.category === \"layout\";\n}\n\n/**\n * A kind that declares an EMPTY output list ends a chain.\n *\n * `[]` and `undefined` are different answers and only the first means this.\n * `undefined` is \"nobody declared what this publishes\", which resolves to `out`\n * and is most nodes in most graphs; `[]` is an explicit claim that there is\n * nothing to connect from. Reading them alike would refuse nearly every\n * workflow ever written.\n */\nfunction isTerminator(node: FlowNode): boolean {\n // A node carrying its own ports overrides its kind — the engine reads these\n // first, so an author who has said what this node publishes is believed.\n // (The PHP twin's importer DROPS node-level ports, so this branch is reachable\n // there only for a hand-built graph. Noted rather than smoothed over: the two\n // importers genuinely differ here.)\n const own = (node.data as { outputs?: unknown[] } | undefined)?.outputs;\n if (Array.isArray(own)) return own.length === 0;\n\n const kind = node.type ? getNodeKind(node.type) : null;\n\n // An unregistered kind falls back to `out` in the engine, so it is not a\n // terminator. Refusing here would break a host mid-registration, and would\n // use \"I do not know\" as evidence.\n if (!kind) return false;\n\n return Array.isArray(kind.outputs) && kind.outputs.length === 0;\n}\n\n/**\n * NOT shared with `frontier`'s / `run-flow`'s annotation test, deliberately.\n *\n * The two predicates look identical and have OPPOSITE safe defaults, which is\n * exactly how a shared helper becomes a bug:\n *\n * - **Here** an unknown kind may float. Being permissive costs nothing — the\n * unknown-kind issue already fires, and we would otherwise assert something\n * unverifiable.\n * - **In the engine** an unknown kind must NOT be treated as an annotation.\n * Being permissive there means SKIPPING a node the host meant to run, which\n * is silent and unrecoverable.\n *\n * So the note/annotation/layout half is the same question and the unknown half\n * is a different one. Collapsing them into one function would read as tidier and\n * would make a host's unregistered kind stop executing.\n */\n"]}
|
package/dist/durable/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { F as FlowGraph, R as RunEvent, c as RunIdentity, E as ExecutorRegistry, a as FlowNode, N as NodeExecutor, d as RunIdentityJson } from '../types-B-Syk9-M.cjs';
|
|
2
|
-
import { a as RunResult } from '../run-flow-
|
|
2
|
+
import { a as RunResult } from '../run-flow-pW0PllaZ.cjs';
|
|
3
3
|
import { b as PauseSignal } from '../pause-9iT4tCEV.cjs';
|
|
4
4
|
import '@xyflow/react';
|
|
5
5
|
|
|
@@ -73,7 +73,19 @@ interface NodeClaimStore {
|
|
|
73
73
|
claim(runKey: string, nodeId: string, owner: string): boolean | Promise<boolean>;
|
|
74
74
|
state(runKey: string): Record<string, NodeState> | Promise<Record<string, NodeState>>;
|
|
75
75
|
complete(runKey: string, nodeId: string, output: unknown, ports: readonly string[]): void | Promise<void>;
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Settle one node as skipped.
|
|
78
|
+
*
|
|
79
|
+
* Return whether THIS call moved the row to skipped: `false` when the row was
|
|
80
|
+
* already skipped — another caller's frontier made the same decision first.
|
|
81
|
+
* The coordinator emits a skipped node's diagnostics only for a call that
|
|
82
|
+
* settled it, so a warning arrives once however many callers race.
|
|
83
|
+
*
|
|
84
|
+
* Returning nothing is still allowed, for stores written before the boolean,
|
|
85
|
+
* and counts as "settled now". Such a store can report a skip twice under a
|
|
86
|
+
* race, and nothing worse.
|
|
87
|
+
*/
|
|
88
|
+
skip(runKey: string, nodeId: string): boolean | void | Promise<boolean | void>;
|
|
77
89
|
fail(runKey: string, nodeId: string, error: string): void | Promise<void>;
|
|
78
90
|
pause(runKey: string, nodeId: string, reason: string): void | Promise<void>;
|
|
79
91
|
}
|
|
@@ -90,7 +102,7 @@ declare class InMemoryClaimStore implements NodeClaimStore {
|
|
|
90
102
|
claim(runKey: string, nodeId: string, owner: string): boolean;
|
|
91
103
|
state(runKey: string): Record<string, NodeState>;
|
|
92
104
|
complete(runKey: string, nodeId: string, output: unknown, ports: readonly string[]): void;
|
|
93
|
-
skip(runKey: string, nodeId: string):
|
|
105
|
+
skip(runKey: string, nodeId: string): boolean;
|
|
94
106
|
fail(runKey: string, nodeId: string, error: string): void;
|
|
95
107
|
pause(runKey: string, nodeId: string, reason: string): void;
|
|
96
108
|
/**
|
|
@@ -157,8 +169,15 @@ declare const Frontier: {
|
|
|
157
169
|
* waited on.
|
|
158
170
|
*/
|
|
159
171
|
hasWorkInFlight(state: Record<string, NodeState>): boolean;
|
|
160
|
-
/**
|
|
161
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Persist the skip cascade so the next pass does not recompute it.
|
|
174
|
+
*
|
|
175
|
+
* Resolves to the ids THIS call settled, in cascade order. A node another
|
|
176
|
+
* caller had already settled is left out — its store reported `false` — so a
|
|
177
|
+
* caller acting on "I skipped this" acts once per node, not once per racer. A
|
|
178
|
+
* store whose `skip` returns nothing counts every id as settled here.
|
|
179
|
+
*/
|
|
180
|
+
settleSkips(store: NodeClaimStore, runKey: string, skipped: readonly string[]): Promise<string[]>;
|
|
162
181
|
};
|
|
163
182
|
|
|
164
183
|
/**
|
|
@@ -180,12 +199,38 @@ declare const Frontier: {
|
|
|
180
199
|
* - every node already completed is fed back as `resumeOutputs`, so the engine
|
|
181
200
|
* republishes it on the same ports and routes exactly as it did the first
|
|
182
201
|
* time;
|
|
183
|
-
* - every node EXCEPT the target is bound,
|
|
184
|
-
* that
|
|
185
|
-
* `*` fallback, so the fence holds whatever a host registered;
|
|
202
|
+
* - every node EXCEPT the target is bound, through `RunOptions.nodeExecutors`,
|
|
203
|
+
* to a FENCE that runs nothing and publishes only a port no edge reads;
|
|
186
204
|
* - so the engine walks its own topological order, skips its own dead branches,
|
|
187
|
-
* collects the target's inputs its own way, runs the target
|
|
188
|
-
*
|
|
205
|
+
* collects the target's inputs its own way, and runs the target.
|
|
206
|
+
*
|
|
207
|
+
* ## Why the fence does not stop the walk
|
|
208
|
+
*
|
|
209
|
+
* It used to abort the run. The target's own inputs never depend on a fenced
|
|
210
|
+
* node -- the frontier dispatches a node only once every source is settled, and
|
|
211
|
+
* settled sources are resumed, not fenced -- but an UNRELATED node can precede
|
|
212
|
+
* the target in topological order. Two siblings dispatched together are exactly
|
|
213
|
+
* that: when `b`'s job started while `a` was still running, the replay aborted
|
|
214
|
+
* at `a`, never reached `b`, and the coordinator read "the replay ended without
|
|
215
|
+
* running me" as "the engine decided I am unreachable". `b` was recorded
|
|
216
|
+
* skipped, never ran, and the run completed as a success. It did not even take
|
|
217
|
+
* two workers: the frontier lists ready nodes in NODE order and the engine walks
|
|
218
|
+
* EDGE order, so a graph where those disagree about siblings sent in-process
|
|
219
|
+
* `runToCompletion` down the same path.
|
|
220
|
+
*
|
|
221
|
+
* Walking past fences makes that inference honest again: when the replay
|
|
222
|
+
* finishes without an output for the target, it is because the engine found
|
|
223
|
+
* every inbound edge dead.
|
|
224
|
+
*
|
|
225
|
+
* ## Why the fences are not registry entries
|
|
226
|
+
*
|
|
227
|
+
* The registry is one flat object, and a key in it is tried as a node's id AND
|
|
228
|
+
* as its kind. Fencing the node called `host_kind` by writing
|
|
229
|
+
* `executors["host_kind"]` fenced every node of kind `host_kind` too, so a
|
|
230
|
+
* durable run of that graph ran nothing and reported success. And the registry
|
|
231
|
+
* is what `ctx.executors` hands a `subflow` child, so a child node sharing an id
|
|
232
|
+
* with any parent node ran the parent's fence. `nodeExecutors` matches node ids
|
|
233
|
+
* only and is never handed down; the registry reaches the engine untouched.
|
|
189
234
|
*
|
|
190
235
|
* The target's output is `result.outputs[nodeId]`, and the ports it activated
|
|
191
236
|
* arrive as the engine's own `node-output` events. Nothing about routing is
|
|
@@ -201,12 +246,19 @@ declare const Frontier: {
|
|
|
201
246
|
*/
|
|
202
247
|
|
|
203
248
|
/**
|
|
204
|
-
* The abort reason
|
|
249
|
+
* The abort reason a boundary used to report.
|
|
205
250
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
251
|
+
* Nothing aborts with it any more (see "Why the fence does not stop the walk");
|
|
252
|
+
* {@link isBoundary} still recognises it so a caller that checks for it keeps
|
|
253
|
+
* working.
|
|
208
254
|
*/
|
|
209
255
|
declare const BOUNDARY = "fancy-flow:node-boundary";
|
|
256
|
+
/**
|
|
257
|
+
* The port a fenced node publishes on. No edge reads it, so everything
|
|
258
|
+
* downstream of a fenced node is dark in the replay -- which never matters to
|
|
259
|
+
* the target, whose sources are all settled.
|
|
260
|
+
*/
|
|
261
|
+
declare const FENCE_PORT = "fancy-flow:fenced";
|
|
210
262
|
type ReplayResult = {
|
|
211
263
|
result: RunResult;
|
|
212
264
|
/** node id -> the ports its output activated, from the engine's own events. */
|
|
@@ -224,8 +276,8 @@ type ReplayOptions = {
|
|
|
224
276
|
/**
|
|
225
277
|
* Replay `graph` up to and through `nodeId`.
|
|
226
278
|
*
|
|
227
|
-
* Pass `nodeId = null` to PROBE: every node is
|
|
228
|
-
*
|
|
279
|
+
* Pass `nodeId = null` to PROBE: every node is fenced, so nothing executes and
|
|
280
|
+
* the engine reports only what it can determine structurally — a cycle, and
|
|
229
281
|
* the ports each resumed output republishes on.
|
|
230
282
|
*/
|
|
231
283
|
declare function replayUpTo(graph: FlowGraph, nodeId: string | null, executors: ExecutorRegistry, options?: ReplayOptions): Promise<ReplayResult>;
|
|
@@ -431,6 +483,13 @@ declare class Coordinator {
|
|
|
431
483
|
*
|
|
432
484
|
* Also settles the skip cascade, because a skip is a decision the frontier
|
|
433
485
|
* just made and a second caller must not make it again.
|
|
486
|
+
*
|
|
487
|
+
* And it is where a skipped node's undelivered-edge warnings are emitted. A
|
|
488
|
+
* skipped node never gets a job, so it never replays to warn for itself: its
|
|
489
|
+
* warning used to be raised inside OTHER jobs' replays, carrying its node id,
|
|
490
|
+
* and {@link forward} filtered it out. Asking at the skip decision — the one
|
|
491
|
+
* place that knows the node will never run — delivers it, and asking only for
|
|
492
|
+
* the nodes this call actually settled delivers it once.
|
|
434
493
|
*/
|
|
435
494
|
advance(): Promise<string[]>;
|
|
436
495
|
/**
|
|
@@ -476,6 +535,21 @@ declare class Coordinator {
|
|
|
476
535
|
* exact instead of conservative.
|
|
477
536
|
*/
|
|
478
537
|
private identityFor;
|
|
538
|
+
/**
|
|
539
|
+
* Send `runFlow`'s undelivered-edge warnings for nodes the frontier skipped.
|
|
540
|
+
*
|
|
541
|
+
* The check is `undeliveredEdgeWarnings` — the SAME function `runFlow` calls —
|
|
542
|
+
* fed the durable equivalent of the engine's own bookkeeping: a port key for
|
|
543
|
+
* every port a COMPLETED node's claim row stored, in stored order, and the set
|
|
544
|
+
* of COMPLETED ids. `state` is the snapshot the frontier decided from, and
|
|
545
|
+
* every predecessor of a skipped node had settled in it, so it holds all a
|
|
546
|
+
* target's sources.
|
|
547
|
+
*
|
|
548
|
+
* Only skipped nodes. A target that RUNS (it had another live inbound edge)
|
|
549
|
+
* already gets its warning from its own job's replay, whose events carry its
|
|
550
|
+
* node id and are forwarded — asking here too would send it twice.
|
|
551
|
+
*/
|
|
552
|
+
private warnForSkipped;
|
|
479
553
|
private completedOutputs;
|
|
480
554
|
/**
|
|
481
555
|
* Forward only the events the target node produced.
|
|
@@ -487,4 +561,4 @@ declare class Coordinator {
|
|
|
487
561
|
private forward;
|
|
488
562
|
}
|
|
489
563
|
|
|
490
|
-
export { BOUNDARY, Coordinator, type CoordinatorOptions, type DurableRunResult, Frontier, type FrontierResult, InMemoryClaimStore, type NodeClaimStore, type NodeOutcome, NodeRunStatus, type NodeRunStatusValue, type NodeState, NotAwaitingHuman, type ReplayOptions, type ReplayResult, RetryPolicy, type RetryPolicyOptions, SETTLED, Submissions, UNSAFE_TO_REPLAY, durableApproval, durableUserInput, isBoundary, isSettled, replayUpTo };
|
|
564
|
+
export { BOUNDARY, Coordinator, type CoordinatorOptions, type DurableRunResult, FENCE_PORT, Frontier, type FrontierResult, InMemoryClaimStore, type NodeClaimStore, type NodeOutcome, NodeRunStatus, type NodeRunStatusValue, type NodeState, NotAwaitingHuman, type ReplayOptions, type ReplayResult, RetryPolicy, type RetryPolicyOptions, SETTLED, Submissions, UNSAFE_TO_REPLAY, durableApproval, durableUserInput, isBoundary, isSettled, replayUpTo };
|
package/dist/durable/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { F as FlowGraph, R as RunEvent, c as RunIdentity, E as ExecutorRegistry, a as FlowNode, N as NodeExecutor, d as RunIdentityJson } from '../types-B-Syk9-M.js';
|
|
2
|
-
import { a as RunResult } from '../run-flow-
|
|
2
|
+
import { a as RunResult } from '../run-flow-BLP9rXfO.js';
|
|
3
3
|
import { b as PauseSignal } from '../pause-9iT4tCEV.js';
|
|
4
4
|
import '@xyflow/react';
|
|
5
5
|
|
|
@@ -73,7 +73,19 @@ interface NodeClaimStore {
|
|
|
73
73
|
claim(runKey: string, nodeId: string, owner: string): boolean | Promise<boolean>;
|
|
74
74
|
state(runKey: string): Record<string, NodeState> | Promise<Record<string, NodeState>>;
|
|
75
75
|
complete(runKey: string, nodeId: string, output: unknown, ports: readonly string[]): void | Promise<void>;
|
|
76
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Settle one node as skipped.
|
|
78
|
+
*
|
|
79
|
+
* Return whether THIS call moved the row to skipped: `false` when the row was
|
|
80
|
+
* already skipped — another caller's frontier made the same decision first.
|
|
81
|
+
* The coordinator emits a skipped node's diagnostics only for a call that
|
|
82
|
+
* settled it, so a warning arrives once however many callers race.
|
|
83
|
+
*
|
|
84
|
+
* Returning nothing is still allowed, for stores written before the boolean,
|
|
85
|
+
* and counts as "settled now". Such a store can report a skip twice under a
|
|
86
|
+
* race, and nothing worse.
|
|
87
|
+
*/
|
|
88
|
+
skip(runKey: string, nodeId: string): boolean | void | Promise<boolean | void>;
|
|
77
89
|
fail(runKey: string, nodeId: string, error: string): void | Promise<void>;
|
|
78
90
|
pause(runKey: string, nodeId: string, reason: string): void | Promise<void>;
|
|
79
91
|
}
|
|
@@ -90,7 +102,7 @@ declare class InMemoryClaimStore implements NodeClaimStore {
|
|
|
90
102
|
claim(runKey: string, nodeId: string, owner: string): boolean;
|
|
91
103
|
state(runKey: string): Record<string, NodeState>;
|
|
92
104
|
complete(runKey: string, nodeId: string, output: unknown, ports: readonly string[]): void;
|
|
93
|
-
skip(runKey: string, nodeId: string):
|
|
105
|
+
skip(runKey: string, nodeId: string): boolean;
|
|
94
106
|
fail(runKey: string, nodeId: string, error: string): void;
|
|
95
107
|
pause(runKey: string, nodeId: string, reason: string): void;
|
|
96
108
|
/**
|
|
@@ -157,8 +169,15 @@ declare const Frontier: {
|
|
|
157
169
|
* waited on.
|
|
158
170
|
*/
|
|
159
171
|
hasWorkInFlight(state: Record<string, NodeState>): boolean;
|
|
160
|
-
/**
|
|
161
|
-
|
|
172
|
+
/**
|
|
173
|
+
* Persist the skip cascade so the next pass does not recompute it.
|
|
174
|
+
*
|
|
175
|
+
* Resolves to the ids THIS call settled, in cascade order. A node another
|
|
176
|
+
* caller had already settled is left out — its store reported `false` — so a
|
|
177
|
+
* caller acting on "I skipped this" acts once per node, not once per racer. A
|
|
178
|
+
* store whose `skip` returns nothing counts every id as settled here.
|
|
179
|
+
*/
|
|
180
|
+
settleSkips(store: NodeClaimStore, runKey: string, skipped: readonly string[]): Promise<string[]>;
|
|
162
181
|
};
|
|
163
182
|
|
|
164
183
|
/**
|
|
@@ -180,12 +199,38 @@ declare const Frontier: {
|
|
|
180
199
|
* - every node already completed is fed back as `resumeOutputs`, so the engine
|
|
181
200
|
* republishes it on the same ports and routes exactly as it did the first
|
|
182
201
|
* time;
|
|
183
|
-
* - every node EXCEPT the target is bound,
|
|
184
|
-
* that
|
|
185
|
-
* `*` fallback, so the fence holds whatever a host registered;
|
|
202
|
+
* - every node EXCEPT the target is bound, through `RunOptions.nodeExecutors`,
|
|
203
|
+
* to a FENCE that runs nothing and publishes only a port no edge reads;
|
|
186
204
|
* - so the engine walks its own topological order, skips its own dead branches,
|
|
187
|
-
* collects the target's inputs its own way, runs the target
|
|
188
|
-
*
|
|
205
|
+
* collects the target's inputs its own way, and runs the target.
|
|
206
|
+
*
|
|
207
|
+
* ## Why the fence does not stop the walk
|
|
208
|
+
*
|
|
209
|
+
* It used to abort the run. The target's own inputs never depend on a fenced
|
|
210
|
+
* node -- the frontier dispatches a node only once every source is settled, and
|
|
211
|
+
* settled sources are resumed, not fenced -- but an UNRELATED node can precede
|
|
212
|
+
* the target in topological order. Two siblings dispatched together are exactly
|
|
213
|
+
* that: when `b`'s job started while `a` was still running, the replay aborted
|
|
214
|
+
* at `a`, never reached `b`, and the coordinator read "the replay ended without
|
|
215
|
+
* running me" as "the engine decided I am unreachable". `b` was recorded
|
|
216
|
+
* skipped, never ran, and the run completed as a success. It did not even take
|
|
217
|
+
* two workers: the frontier lists ready nodes in NODE order and the engine walks
|
|
218
|
+
* EDGE order, so a graph where those disagree about siblings sent in-process
|
|
219
|
+
* `runToCompletion` down the same path.
|
|
220
|
+
*
|
|
221
|
+
* Walking past fences makes that inference honest again: when the replay
|
|
222
|
+
* finishes without an output for the target, it is because the engine found
|
|
223
|
+
* every inbound edge dead.
|
|
224
|
+
*
|
|
225
|
+
* ## Why the fences are not registry entries
|
|
226
|
+
*
|
|
227
|
+
* The registry is one flat object, and a key in it is tried as a node's id AND
|
|
228
|
+
* as its kind. Fencing the node called `host_kind` by writing
|
|
229
|
+
* `executors["host_kind"]` fenced every node of kind `host_kind` too, so a
|
|
230
|
+
* durable run of that graph ran nothing and reported success. And the registry
|
|
231
|
+
* is what `ctx.executors` hands a `subflow` child, so a child node sharing an id
|
|
232
|
+
* with any parent node ran the parent's fence. `nodeExecutors` matches node ids
|
|
233
|
+
* only and is never handed down; the registry reaches the engine untouched.
|
|
189
234
|
*
|
|
190
235
|
* The target's output is `result.outputs[nodeId]`, and the ports it activated
|
|
191
236
|
* arrive as the engine's own `node-output` events. Nothing about routing is
|
|
@@ -201,12 +246,19 @@ declare const Frontier: {
|
|
|
201
246
|
*/
|
|
202
247
|
|
|
203
248
|
/**
|
|
204
|
-
* The abort reason
|
|
249
|
+
* The abort reason a boundary used to report.
|
|
205
250
|
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
251
|
+
* Nothing aborts with it any more (see "Why the fence does not stop the walk");
|
|
252
|
+
* {@link isBoundary} still recognises it so a caller that checks for it keeps
|
|
253
|
+
* working.
|
|
208
254
|
*/
|
|
209
255
|
declare const BOUNDARY = "fancy-flow:node-boundary";
|
|
256
|
+
/**
|
|
257
|
+
* The port a fenced node publishes on. No edge reads it, so everything
|
|
258
|
+
* downstream of a fenced node is dark in the replay -- which never matters to
|
|
259
|
+
* the target, whose sources are all settled.
|
|
260
|
+
*/
|
|
261
|
+
declare const FENCE_PORT = "fancy-flow:fenced";
|
|
210
262
|
type ReplayResult = {
|
|
211
263
|
result: RunResult;
|
|
212
264
|
/** node id -> the ports its output activated, from the engine's own events. */
|
|
@@ -224,8 +276,8 @@ type ReplayOptions = {
|
|
|
224
276
|
/**
|
|
225
277
|
* Replay `graph` up to and through `nodeId`.
|
|
226
278
|
*
|
|
227
|
-
* Pass `nodeId = null` to PROBE: every node is
|
|
228
|
-
*
|
|
279
|
+
* Pass `nodeId = null` to PROBE: every node is fenced, so nothing executes and
|
|
280
|
+
* the engine reports only what it can determine structurally — a cycle, and
|
|
229
281
|
* the ports each resumed output republishes on.
|
|
230
282
|
*/
|
|
231
283
|
declare function replayUpTo(graph: FlowGraph, nodeId: string | null, executors: ExecutorRegistry, options?: ReplayOptions): Promise<ReplayResult>;
|
|
@@ -431,6 +483,13 @@ declare class Coordinator {
|
|
|
431
483
|
*
|
|
432
484
|
* Also settles the skip cascade, because a skip is a decision the frontier
|
|
433
485
|
* just made and a second caller must not make it again.
|
|
486
|
+
*
|
|
487
|
+
* And it is where a skipped node's undelivered-edge warnings are emitted. A
|
|
488
|
+
* skipped node never gets a job, so it never replays to warn for itself: its
|
|
489
|
+
* warning used to be raised inside OTHER jobs' replays, carrying its node id,
|
|
490
|
+
* and {@link forward} filtered it out. Asking at the skip decision — the one
|
|
491
|
+
* place that knows the node will never run — delivers it, and asking only for
|
|
492
|
+
* the nodes this call actually settled delivers it once.
|
|
434
493
|
*/
|
|
435
494
|
advance(): Promise<string[]>;
|
|
436
495
|
/**
|
|
@@ -476,6 +535,21 @@ declare class Coordinator {
|
|
|
476
535
|
* exact instead of conservative.
|
|
477
536
|
*/
|
|
478
537
|
private identityFor;
|
|
538
|
+
/**
|
|
539
|
+
* Send `runFlow`'s undelivered-edge warnings for nodes the frontier skipped.
|
|
540
|
+
*
|
|
541
|
+
* The check is `undeliveredEdgeWarnings` — the SAME function `runFlow` calls —
|
|
542
|
+
* fed the durable equivalent of the engine's own bookkeeping: a port key for
|
|
543
|
+
* every port a COMPLETED node's claim row stored, in stored order, and the set
|
|
544
|
+
* of COMPLETED ids. `state` is the snapshot the frontier decided from, and
|
|
545
|
+
* every predecessor of a skipped node had settled in it, so it holds all a
|
|
546
|
+
* target's sources.
|
|
547
|
+
*
|
|
548
|
+
* Only skipped nodes. A target that RUNS (it had another live inbound edge)
|
|
549
|
+
* already gets its warning from its own job's replay, whose events carry its
|
|
550
|
+
* node id and are forwarded — asking here too would send it twice.
|
|
551
|
+
*/
|
|
552
|
+
private warnForSkipped;
|
|
479
553
|
private completedOutputs;
|
|
480
554
|
/**
|
|
481
555
|
* Forward only the events the target node produced.
|
|
@@ -487,4 +561,4 @@ declare class Coordinator {
|
|
|
487
561
|
private forward;
|
|
488
562
|
}
|
|
489
563
|
|
|
490
|
-
export { BOUNDARY, Coordinator, type CoordinatorOptions, type DurableRunResult, Frontier, type FrontierResult, InMemoryClaimStore, type NodeClaimStore, type NodeOutcome, NodeRunStatus, type NodeRunStatusValue, type NodeState, NotAwaitingHuman, type ReplayOptions, type ReplayResult, RetryPolicy, type RetryPolicyOptions, SETTLED, Submissions, UNSAFE_TO_REPLAY, durableApproval, durableUserInput, isBoundary, isSettled, replayUpTo };
|
|
564
|
+
export { BOUNDARY, Coordinator, type CoordinatorOptions, type DurableRunResult, FENCE_PORT, Frontier, type FrontierResult, InMemoryClaimStore, type NodeClaimStore, type NodeOutcome, NodeRunStatus, type NodeRunStatusValue, type NodeState, NotAwaitingHuman, type ReplayOptions, type ReplayResult, RetryPolicy, type RetryPolicyOptions, SETTLED, Submissions, UNSAFE_TO_REPLAY, durableApproval, durableUserInput, isBoundary, isSettled, replayUpTo };
|