@particle-academy/fancy-flow 0.45.0 → 0.47.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/README.md +29 -0
- 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-DR3GWVDG.js +196 -0
- package/dist/chunk-DR3GWVDG.js.map +1 -0
- package/dist/{chunk-3BKU4SQZ.js → chunk-ISXRQELT.js} +3 -4
- package/dist/chunk-ISXRQELT.js.map +1 -0
- package/dist/{chunk-OWENS2H5.js → chunk-NDEX655E.js} +1603 -36
- package/dist/chunk-NDEX655E.js.map +1 -0
- package/dist/{chunk-QZTGV3ZL.js → chunk-QVFVL5LQ.js} +3 -3
- package/dist/{chunk-QZTGV3ZL.js.map → chunk-QVFVL5LQ.js.map} +1 -1
- package/dist/{chunk-DHUFUDEX.js → chunk-Z2GRNYR2.js} +7 -6
- package/dist/chunk-Z2GRNYR2.js.map +1 -0
- package/dist/{chunk-A6RFLGWV.js → chunk-ZKJJWBDJ.js} +3 -5
- package/dist/chunk-ZKJJWBDJ.js.map +1 -0
- package/dist/durable/index.d.cts +490 -0
- package/dist/durable/index.d.ts +490 -0
- package/dist/durable.cjs +10991 -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 +10223 -47
- 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 -6
- package/dist/engine.js.map +1 -1
- package/dist/fields/react-fancy.d.cts +4 -3
- package/dist/fields/react-fancy.d.ts +4 -3
- package/dist/index.cjs +336 -194
- 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 +15 -93
- 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-Bt55f63_.d.ts} +21 -2
- package/dist/{registry-D0I20HJ3.d.ts → registry-CkcSwmfR.d.cts} +21 -2
- package/dist/registry.cjs +3961 -3821
- package/dist/registry.cjs.map +1 -1
- package/dist/registry.js +3 -6
- 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 +1712 -430
- package/dist/runtime.cjs.map +1 -1
- package/dist/runtime.js +5 -6
- package/dist/schema/index.d.cts +1 -1
- package/dist/schema/index.d.ts +1 -1
- package/dist/schema.cjs +10369 -24
- package/dist/schema.cjs.map +1 -1
- package/dist/schema.js +4 -3
- package/dist/screens.cjs +1366 -38
- package/dist/screens.cjs.map +1 -1
- package/dist/screens.d.cts +2 -2
- package/dist/screens.d.ts +2 -2
- package/dist/screens.js +5 -8
- package/dist/screens.js.map +1 -1
- 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.cjs +10363 -2
- package/dist/ux.cjs.map +1 -1
- package/dist/ux.d.cts +3 -2
- package/dist/ux.d.ts +3 -2
- package/dist/ux.js +3 -1
- package/dist/ux.js.map +1 -1
- package/package.json +17 -3
- package/dist/chunk-3BKU4SQZ.js.map +0 -1
- package/dist/chunk-A6RFLGWV.js.map +0 -1
- package/dist/chunk-DHUFUDEX.js.map +0 -1
- package/dist/chunk-OWENS2H5.js.map +0 -1
- package/dist/chunk-PVROYW7C.js +0 -187
- package/dist/chunk-PVROYW7C.js.map +0 -1
- package/dist/chunk-TITD5W4Y.js +0 -26
- package/dist/chunk-TITD5W4Y.js.map +0 -1
- package/dist/chunk-VLATLVGH.js +0 -158
- package/dist/chunk-VLATLVGH.js.map +0 -1
- package/dist/chunk-WBXGZVLB.js +0 -1268
- package/dist/chunk-WBXGZVLB.js.map +0 -1
- package/dist/types-CMSrWVYM.d.cts +0 -109
- package/dist/types-CMSrWVYM.d.ts +0 -109
package/dist/index.cjs
CHANGED
|
@@ -1719,199 +1719,15 @@ var init_layout = __esm({
|
|
|
1719
1719
|
init_dagre_esm();
|
|
1720
1720
|
}
|
|
1721
1721
|
});
|
|
1722
|
-
|
|
1723
|
-
// src/registry/registry.ts
|
|
1724
|
-
var kinds = /* @__PURE__ */ new Map();
|
|
1725
|
-
var aliases = /* @__PURE__ */ new Map();
|
|
1726
|
-
var listeners = /* @__PURE__ */ new Set();
|
|
1727
|
-
var overrides = /* @__PURE__ */ new Map();
|
|
1728
|
-
function registerNodeKind(definition) {
|
|
1729
|
-
kinds.set(definition.name, definition);
|
|
1730
|
-
for (const alias of definition.aliases ?? []) aliases.set(alias, definition.name);
|
|
1731
|
-
notify();
|
|
1732
|
-
return () => {
|
|
1733
|
-
if (kinds.get(definition.name) === definition) {
|
|
1734
|
-
kinds.delete(definition.name);
|
|
1735
|
-
for (const alias of definition.aliases ?? []) {
|
|
1736
|
-
if (aliases.get(alias) === definition.name) aliases.delete(alias);
|
|
1737
|
-
}
|
|
1738
|
-
notify();
|
|
1739
|
-
}
|
|
1740
|
-
};
|
|
1741
|
-
}
|
|
1742
|
-
function resolveKindId(id2) {
|
|
1743
|
-
if (kinds.has(id2)) return id2;
|
|
1744
|
-
const canonical = aliases.get(id2);
|
|
1745
|
-
return canonical && kinds.has(canonical) ? canonical : null;
|
|
1746
|
-
}
|
|
1747
|
-
function overrideNodeKind(name, patch) {
|
|
1748
|
-
const canonical = resolveKindId(name) ?? name;
|
|
1749
|
-
const previous = overrides.get(canonical);
|
|
1750
|
-
overrides.set(canonical, { ...previous, ...patch });
|
|
1751
|
-
notify();
|
|
1752
|
-
return () => {
|
|
1753
|
-
if (previous) {
|
|
1754
|
-
overrides.set(canonical, previous);
|
|
1755
|
-
} else {
|
|
1756
|
-
overrides.delete(canonical);
|
|
1757
|
-
}
|
|
1758
|
-
notify();
|
|
1759
|
-
};
|
|
1760
|
-
}
|
|
1761
|
-
function clearNodeKindOverrides() {
|
|
1762
|
-
if (overrides.size === 0) return;
|
|
1763
|
-
overrides.clear();
|
|
1764
|
-
notify();
|
|
1765
|
-
}
|
|
1766
|
-
function withOverride(kind) {
|
|
1767
|
-
if (!kind) return null;
|
|
1768
|
-
const patch = overrides.get(kind.name);
|
|
1769
|
-
return patch ? { ...kind, ...patch } : kind;
|
|
1770
|
-
}
|
|
1771
|
-
function getNodeKind(name) {
|
|
1772
|
-
const canonical = resolveKindId(name);
|
|
1773
|
-
return canonical ? withOverride(kinds.get(canonical)) : null;
|
|
1774
|
-
}
|
|
1775
|
-
function kindIds(kind) {
|
|
1776
|
-
return [kind.name, ...kind.aliases ?? []];
|
|
1777
|
-
}
|
|
1778
|
-
function listNodeKinds(category) {
|
|
1779
|
-
const all = Array.from(kinds.values()).map(
|
|
1780
|
-
(k2) => withOverride(k2)
|
|
1781
|
-
);
|
|
1782
|
-
return category ? all.filter((k2) => k2.category === category) : all;
|
|
1783
|
-
}
|
|
1784
|
-
function onNodeKindsChanged(listener) {
|
|
1785
|
-
listeners.add(listener);
|
|
1786
|
-
return () => listeners.delete(listener);
|
|
1787
|
-
}
|
|
1788
|
-
function notify() {
|
|
1789
|
-
for (const l of listeners) l();
|
|
1790
|
-
}
|
|
1791
|
-
function defaultConfigFor(kind) {
|
|
1792
|
-
const fromKind = kind.defaultConfig ? { ...kind.defaultConfig } : {};
|
|
1793
|
-
for (const field of kind.configSchema ?? []) {
|
|
1794
|
-
if (fromKind[field.key] !== void 0) continue;
|
|
1795
|
-
if ("default" in field && field.default !== void 0) {
|
|
1796
|
-
fromKind[field.key] = field.default;
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1799
|
-
return fromKind;
|
|
1800
|
-
}
|
|
1801
|
-
function validateConfig(kind, config) {
|
|
1802
|
-
const issues = [];
|
|
1803
|
-
for (const field of kind.configSchema ?? []) {
|
|
1804
|
-
const value = config[field.key];
|
|
1805
|
-
if (field.required && (value === void 0 || value === null || value === "")) {
|
|
1806
|
-
issues.push({ key: field.key, message: `${field.label} is required` });
|
|
1807
|
-
continue;
|
|
1808
|
-
}
|
|
1809
|
-
if (value === void 0 || value === null) continue;
|
|
1810
|
-
const issue = validateField(field, value);
|
|
1811
|
-
if (issue) issues.push({ key: field.key, message: issue });
|
|
1812
|
-
}
|
|
1813
|
-
return issues;
|
|
1814
|
-
}
|
|
1815
|
-
function validateField(field, value) {
|
|
1816
|
-
switch (field.type) {
|
|
1817
|
-
case "text":
|
|
1818
|
-
case "textarea":
|
|
1819
|
-
case "expression":
|
|
1820
|
-
case "credential":
|
|
1821
|
-
return typeof value === "string" ? null : `${field.label} must be a string`;
|
|
1822
|
-
case "number": {
|
|
1823
|
-
if (typeof value !== "number" || !Number.isFinite(value)) return `${field.label} must be a number`;
|
|
1824
|
-
if (field.min !== void 0 && value < field.min) return `${field.label} must be >= ${field.min}`;
|
|
1825
|
-
if (field.max !== void 0 && value > field.max) return `${field.label} must be <= ${field.max}`;
|
|
1826
|
-
return null;
|
|
1827
|
-
}
|
|
1828
|
-
case "switch":
|
|
1829
|
-
return typeof value === "boolean" ? null : `${field.label} must be a boolean`;
|
|
1830
|
-
case "select": {
|
|
1831
|
-
const allowed = field.options.map((o) => o.value);
|
|
1832
|
-
return allowed.includes(String(value)) ? null : `${field.label} must be one of ${allowed.join(", ")}`;
|
|
1833
|
-
}
|
|
1834
|
-
case "json":
|
|
1835
|
-
return null;
|
|
1836
|
-
// permissive — just JSON-shaped
|
|
1837
|
-
case "repeater": {
|
|
1838
|
-
if (!Array.isArray(value)) return `${field.label} must be a list`;
|
|
1839
|
-
if (field.minItems !== void 0 && value.length < field.minItems) {
|
|
1840
|
-
return `${field.label} needs at least ${field.minItems}`;
|
|
1841
|
-
}
|
|
1842
|
-
if (field.maxItems !== void 0 && value.length > field.maxItems) {
|
|
1843
|
-
return `${field.label} allows at most ${field.maxItems}`;
|
|
1844
|
-
}
|
|
1845
|
-
for (let i = 0; i < value.length; i++) {
|
|
1846
|
-
const row = value[i];
|
|
1847
|
-
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
1848
|
-
return `${field.label} item ${i + 1} must be an object`;
|
|
1849
|
-
}
|
|
1850
|
-
for (const sub of field.fields) {
|
|
1851
|
-
const cell = row[sub.key];
|
|
1852
|
-
if (sub.required && (cell === void 0 || cell === null || cell === "")) {
|
|
1853
|
-
return `${field.label} item ${i + 1}: ${sub.label} is required`;
|
|
1854
|
-
}
|
|
1855
|
-
if (cell === void 0 || cell === null) continue;
|
|
1856
|
-
const issue = validateField(sub, cell);
|
|
1857
|
-
if (issue) return `${field.label} item ${i + 1}: ${issue}`;
|
|
1858
|
-
}
|
|
1859
|
-
}
|
|
1860
|
-
return null;
|
|
1861
|
-
}
|
|
1862
|
-
case "keyvalue": {
|
|
1863
|
-
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
1864
|
-
return `${field.label} must be a key/value map`;
|
|
1865
|
-
}
|
|
1866
|
-
const allowed = field.valueOptions?.map((o) => o.value);
|
|
1867
|
-
for (const [k2, v2] of Object.entries(value)) {
|
|
1868
|
-
if (typeof v2 !== "string") return `${field.label}: "${k2}" must be a string`;
|
|
1869
|
-
if (allowed && !allowed.includes(v2)) {
|
|
1870
|
-
return `${field.label}: "${k2}" must be one of ${allowed.join(", ")}`;
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
return null;
|
|
1874
|
-
}
|
|
1875
|
-
case "document":
|
|
1876
|
-
return null;
|
|
1877
|
-
// opaque to fancy-flow — the host's editor owns its shape
|
|
1878
|
-
default:
|
|
1879
|
-
return null;
|
|
1880
|
-
}
|
|
1881
|
-
}
|
|
1882
|
-
function categoryAccent(category) {
|
|
1883
|
-
switch (category) {
|
|
1884
|
-
case "trigger":
|
|
1885
|
-
return "#10b981";
|
|
1886
|
-
case "logic":
|
|
1887
|
-
return "#f59e0b";
|
|
1888
|
-
case "data":
|
|
1889
|
-
return "#0ea5e9";
|
|
1890
|
-
case "ai":
|
|
1891
|
-
return "#8b5cf6";
|
|
1892
|
-
case "io":
|
|
1893
|
-
return "#3b82f6";
|
|
1894
|
-
case "human":
|
|
1895
|
-
return "#ec4899";
|
|
1896
|
-
case "output":
|
|
1897
|
-
return "#a855f7";
|
|
1898
|
-
case "layout":
|
|
1899
|
-
return "#64748b";
|
|
1900
|
-
case "annotation":
|
|
1901
|
-
return "#eab308";
|
|
1902
|
-
default:
|
|
1903
|
-
return "#71717a";
|
|
1904
|
-
}
|
|
1905
|
-
}
|
|
1906
1722
|
var adapter = null;
|
|
1907
|
-
var
|
|
1723
|
+
var listeners = /* @__PURE__ */ new Set();
|
|
1908
1724
|
function registerRichInputAdapter(next) {
|
|
1909
1725
|
adapter = next;
|
|
1910
|
-
for (const l of
|
|
1726
|
+
for (const l of listeners) l();
|
|
1911
1727
|
return () => {
|
|
1912
1728
|
if (adapter === next) {
|
|
1913
1729
|
adapter = null;
|
|
1914
|
-
for (const l of
|
|
1730
|
+
for (const l of listeners) l();
|
|
1915
1731
|
}
|
|
1916
1732
|
};
|
|
1917
1733
|
}
|
|
@@ -1922,8 +1738,8 @@ function isRichInputEnabled() {
|
|
|
1922
1738
|
return adapter !== null && (adapter.renderDocument !== void 0 || adapter.renderEditor !== void 0);
|
|
1923
1739
|
}
|
|
1924
1740
|
function onRichInputAdapterChanged(fn2) {
|
|
1925
|
-
|
|
1926
|
-
return () =>
|
|
1741
|
+
listeners.add(fn2);
|
|
1742
|
+
return () => listeners.delete(fn2);
|
|
1927
1743
|
}
|
|
1928
1744
|
function RichInputPreview({ config }) {
|
|
1929
1745
|
const a = getRichInputAdapter();
|
|
@@ -11172,10 +10988,121 @@ function resolveNodePorts(node, kind) {
|
|
|
11172
10988
|
};
|
|
11173
10989
|
}
|
|
11174
10990
|
|
|
10991
|
+
// src/runtime/run-identity.ts
|
|
10992
|
+
function escapeSegment(value) {
|
|
10993
|
+
return value.replace(/%/g, "%25").replace(/\//g, "%2F").replace(/#/g, "%23");
|
|
10994
|
+
}
|
|
10995
|
+
function renderSegment(value, occurrence) {
|
|
10996
|
+
const escaped = escapeSegment(value);
|
|
10997
|
+
return occurrence === void 0 || occurrence === null ? escaped : `${escaped}#${occurrence}`;
|
|
10998
|
+
}
|
|
10999
|
+
function instant(value) {
|
|
11000
|
+
const ms = Date.parse(value);
|
|
11001
|
+
if (Number.isNaN(ms)) {
|
|
11002
|
+
throw new Error(
|
|
11003
|
+
`RunIdentity: firstAttemptAt is not a parseable timestamp: ${JSON.stringify(value)}`
|
|
11004
|
+
);
|
|
11005
|
+
}
|
|
11006
|
+
return ms;
|
|
11007
|
+
}
|
|
11008
|
+
var RunIdentity = class _RunIdentity {
|
|
11009
|
+
constructor(runKey, path = [], attempt = 1, firstAttemptAt = (/* @__PURE__ */ new Date()).toISOString()) {
|
|
11010
|
+
if (!runKey || runKey.trim() === "") {
|
|
11011
|
+
throw new Error("RunIdentity: runKey must be a non-empty string.");
|
|
11012
|
+
}
|
|
11013
|
+
this.runKey = runKey;
|
|
11014
|
+
this.path = Object.freeze([...path]);
|
|
11015
|
+
this.attempt = Math.max(1, Math.trunc(attempt));
|
|
11016
|
+
this.firstAttemptAt = firstAttemptAt;
|
|
11017
|
+
Object.freeze(this);
|
|
11018
|
+
}
|
|
11019
|
+
/**
|
|
11020
|
+
* The identity of one execution of one node — stable across retries of that
|
|
11021
|
+
* execution, distinct from every other execution of the same node.
|
|
11022
|
+
*
|
|
11023
|
+
* Pass `occurrence` when an executor runs the same node more than once at the
|
|
11024
|
+
* same level (a loop body, one item of a fan-out it drives itself).
|
|
11025
|
+
*/
|
|
11026
|
+
stepKey(nodeId, occurrence) {
|
|
11027
|
+
return `${this.runKey}:${[...this.path, renderSegment(nodeId, occurrence)].join("/")}`;
|
|
11028
|
+
}
|
|
11029
|
+
/**
|
|
11030
|
+
* A child identity for work nested inside this step.
|
|
11031
|
+
*
|
|
11032
|
+
* `subflow` pushes the invoking node's id, so a node inside the child graph
|
|
11033
|
+
* cannot collide with a same-named node in the parent. Attempt and
|
|
11034
|
+
* `firstAttemptAt` are carried down unchanged: the nested work happens inside
|
|
11035
|
+
* this step's attempt, and shares its clock.
|
|
11036
|
+
*/
|
|
11037
|
+
descend(segment, occurrence) {
|
|
11038
|
+
return new _RunIdentity(
|
|
11039
|
+
this.runKey,
|
|
11040
|
+
[...this.path, renderSegment(segment, occurrence)],
|
|
11041
|
+
this.attempt,
|
|
11042
|
+
this.firstAttemptAt
|
|
11043
|
+
);
|
|
11044
|
+
}
|
|
11045
|
+
/** A copy on a different attempt, first-attempt clock preserved. */
|
|
11046
|
+
withAttempt(attempt, firstAttemptAt) {
|
|
11047
|
+
return new _RunIdentity(
|
|
11048
|
+
this.runKey,
|
|
11049
|
+
this.path,
|
|
11050
|
+
attempt,
|
|
11051
|
+
firstAttemptAt ?? this.firstAttemptAt
|
|
11052
|
+
);
|
|
11053
|
+
}
|
|
11054
|
+
/**
|
|
11055
|
+
* May this attempt reuse the step key and still be deduplicated?
|
|
11056
|
+
*
|
|
11057
|
+
* Providers forget idempotency keys — Stripe after 24 hours. Past that
|
|
11058
|
+
* window, resending the key creates a second charge and sending a fresh one
|
|
11059
|
+
* creates a second charge, so **the caller must refuse rather than pick
|
|
11060
|
+
* between them**: a loud stuck run beats a silent double write.
|
|
11061
|
+
*
|
|
11062
|
+
* `true` on attempt 1 whatever the elapsed time — nothing was sent on an
|
|
11063
|
+
* earlier attempt, so there is nothing for the provider to have forgotten.
|
|
11064
|
+
* That is what lets a run park on a human gate for a week and then write.
|
|
11065
|
+
*
|
|
11066
|
+
* `windowSeconds: null` means the provider does not expire keys. `0` means
|
|
11067
|
+
* it does not dedupe at all, so no retry may reuse a key — it is a real
|
|
11068
|
+
* window, not an absent one, and the two must not be conflated: reading `0`
|
|
11069
|
+
* as `null` turns "this provider does not dedupe" into "this provider
|
|
11070
|
+
* dedupes forever", which is the more dangerous of the two by a distance.
|
|
11071
|
+
*/
|
|
11072
|
+
isReplaySafe(windowSeconds, now2 = /* @__PURE__ */ new Date()) {
|
|
11073
|
+
if (this.attempt <= 1) return true;
|
|
11074
|
+
if (windowSeconds === null || windowSeconds === void 0) return true;
|
|
11075
|
+
if (windowSeconds <= 0) return false;
|
|
11076
|
+
const nowMs = typeof now2 === "string" ? instant(now2) : now2.getTime();
|
|
11077
|
+
const elapsedSeconds = Math.max(0, (nowMs - instant(this.firstAttemptAt)) / 1e3);
|
|
11078
|
+
return elapsedSeconds <= windowSeconds;
|
|
11079
|
+
}
|
|
11080
|
+
toJSON() {
|
|
11081
|
+
return {
|
|
11082
|
+
runKey: this.runKey,
|
|
11083
|
+
path: [...this.path],
|
|
11084
|
+
attempt: this.attempt,
|
|
11085
|
+
firstAttemptAt: this.firstAttemptAt
|
|
11086
|
+
};
|
|
11087
|
+
}
|
|
11088
|
+
/** Rebuild from a queue payload. */
|
|
11089
|
+
static from(value) {
|
|
11090
|
+
if (value instanceof _RunIdentity) return value;
|
|
11091
|
+
if (typeof value === "string") return new _RunIdentity(value);
|
|
11092
|
+
return new _RunIdentity(
|
|
11093
|
+
value.runKey,
|
|
11094
|
+
value.path ?? [],
|
|
11095
|
+
value.attempt ?? 1,
|
|
11096
|
+
value.firstAttemptAt ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
11097
|
+
);
|
|
11098
|
+
}
|
|
11099
|
+
};
|
|
11100
|
+
|
|
11175
11101
|
// src/runtime/run-flow.ts
|
|
11176
11102
|
async function runFlow(graph, executors, onEvent = () => {
|
|
11177
11103
|
}, options = {}) {
|
|
11178
|
-
const { signal, initialInputs = {}, timeoutMs, depth = 0 } = options;
|
|
11104
|
+
const { signal, initialInputs = {}, timeoutMs, depth = 0, resumeOutputs = {} } = options;
|
|
11105
|
+
const run = options.run === void 0 ? void 0 : RunIdentity.from(options.run);
|
|
11179
11106
|
const outputs = {};
|
|
11180
11107
|
const portValues = /* @__PURE__ */ new Map();
|
|
11181
11108
|
const completed = /* @__PURE__ */ new Set();
|
|
@@ -11193,6 +11120,18 @@ async function runFlow(graph, executors, onEvent = () => {
|
|
|
11193
11120
|
for (const node of order) {
|
|
11194
11121
|
if (signal?.aborted) throw new Error("aborted");
|
|
11195
11122
|
if (errors.length) break;
|
|
11123
|
+
if (Object.prototype.hasOwnProperty.call(resumeOutputs, node.id)) {
|
|
11124
|
+
const stored = resumeOutputs[node.id];
|
|
11125
|
+
outputs[node.id] = stored;
|
|
11126
|
+
const activated = activatedPorts(node, stored);
|
|
11127
|
+
for (const portId of activated.ports) {
|
|
11128
|
+
portValues.set(`${node.id}:${portId}`, activated.value);
|
|
11129
|
+
onEvent({ type: "node-output", nodeId: node.id, portId, value: activated.value });
|
|
11130
|
+
}
|
|
11131
|
+
completed.add(node.id);
|
|
11132
|
+
onEvent({ type: "node-status", nodeId: node.id, status: "done", text: "resumed" });
|
|
11133
|
+
continue;
|
|
11134
|
+
}
|
|
11196
11135
|
const incoming = incomingByNode.get(node.id) ?? [];
|
|
11197
11136
|
if (incoming.length > 0) {
|
|
11198
11137
|
const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? "out"}`));
|
|
@@ -11232,7 +11171,8 @@ async function runFlow(graph, executors, onEvent = () => {
|
|
|
11232
11171
|
throw new Error(reason ?? "aborted");
|
|
11233
11172
|
},
|
|
11234
11173
|
emit: onEvent,
|
|
11235
|
-
depth
|
|
11174
|
+
depth,
|
|
11175
|
+
run
|
|
11236
11176
|
})
|
|
11237
11177
|
);
|
|
11238
11178
|
outputs[node.id] = result;
|
|
@@ -11385,7 +11325,13 @@ var subflowExecutor = async (ctx) => {
|
|
|
11385
11325
|
// require no configuration at all.
|
|
11386
11326
|
__parent: ctx.inputs
|
|
11387
11327
|
},
|
|
11388
|
-
depth: depth + 1
|
|
11328
|
+
depth: depth + 1,
|
|
11329
|
+
// Push THIS node onto the identity path, so a node inside the child graph
|
|
11330
|
+
// cannot share an idempotency key with a same-named node in the parent —
|
|
11331
|
+
// or with the same child graph invoked from a different parent node.
|
|
11332
|
+
// Attempt and the first-attempt clock ride down unchanged: the child's
|
|
11333
|
+
// work happens inside this node's attempt.
|
|
11334
|
+
run: ctx.run?.descend(ctx.node.id)
|
|
11389
11335
|
}
|
|
11390
11336
|
);
|
|
11391
11337
|
if (!result.ok) {
|
|
@@ -12241,6 +12187,199 @@ function registerBuiltinKinds() {
|
|
|
12241
12187
|
for (const k2 of KINDS) registerNodeKind(k2);
|
|
12242
12188
|
}
|
|
12243
12189
|
var BUILTIN_KINDS = KINDS;
|
|
12190
|
+
|
|
12191
|
+
// src/registry/registry.ts
|
|
12192
|
+
var kinds = /* @__PURE__ */ new Map();
|
|
12193
|
+
var aliases = /* @__PURE__ */ new Map();
|
|
12194
|
+
var listeners2 = /* @__PURE__ */ new Set();
|
|
12195
|
+
var overrides = /* @__PURE__ */ new Map();
|
|
12196
|
+
var builtinsEnsured = false;
|
|
12197
|
+
function ensureBuiltinKinds() {
|
|
12198
|
+
if (builtinsEnsured) return;
|
|
12199
|
+
builtinsEnsured = true;
|
|
12200
|
+
registerBuiltinKinds();
|
|
12201
|
+
}
|
|
12202
|
+
function registerNodeKind(definition) {
|
|
12203
|
+
ensureBuiltinKinds();
|
|
12204
|
+
kinds.set(definition.name, definition);
|
|
12205
|
+
for (const alias of definition.aliases ?? []) aliases.set(alias, definition.name);
|
|
12206
|
+
notify();
|
|
12207
|
+
return () => {
|
|
12208
|
+
if (kinds.get(definition.name) === definition) {
|
|
12209
|
+
kinds.delete(definition.name);
|
|
12210
|
+
for (const alias of definition.aliases ?? []) {
|
|
12211
|
+
if (aliases.get(alias) === definition.name) aliases.delete(alias);
|
|
12212
|
+
}
|
|
12213
|
+
notify();
|
|
12214
|
+
}
|
|
12215
|
+
};
|
|
12216
|
+
}
|
|
12217
|
+
function resolveKindId(id2) {
|
|
12218
|
+
ensureBuiltinKinds();
|
|
12219
|
+
if (kinds.has(id2)) return id2;
|
|
12220
|
+
const canonical = aliases.get(id2);
|
|
12221
|
+
return canonical && kinds.has(canonical) ? canonical : null;
|
|
12222
|
+
}
|
|
12223
|
+
function overrideNodeKind(name, patch) {
|
|
12224
|
+
const canonical = resolveKindId(name) ?? name;
|
|
12225
|
+
const previous = overrides.get(canonical);
|
|
12226
|
+
overrides.set(canonical, { ...previous, ...patch });
|
|
12227
|
+
notify();
|
|
12228
|
+
return () => {
|
|
12229
|
+
if (previous) {
|
|
12230
|
+
overrides.set(canonical, previous);
|
|
12231
|
+
} else {
|
|
12232
|
+
overrides.delete(canonical);
|
|
12233
|
+
}
|
|
12234
|
+
notify();
|
|
12235
|
+
};
|
|
12236
|
+
}
|
|
12237
|
+
function clearNodeKindOverrides() {
|
|
12238
|
+
if (overrides.size === 0) return;
|
|
12239
|
+
overrides.clear();
|
|
12240
|
+
notify();
|
|
12241
|
+
}
|
|
12242
|
+
function withOverride(kind) {
|
|
12243
|
+
if (!kind) return null;
|
|
12244
|
+
const patch = overrides.get(kind.name);
|
|
12245
|
+
return patch ? { ...kind, ...patch } : kind;
|
|
12246
|
+
}
|
|
12247
|
+
function getNodeKind(name) {
|
|
12248
|
+
const canonical = resolveKindId(name);
|
|
12249
|
+
return canonical ? withOverride(kinds.get(canonical)) : null;
|
|
12250
|
+
}
|
|
12251
|
+
function kindIds(kind) {
|
|
12252
|
+
return [kind.name, ...kind.aliases ?? []];
|
|
12253
|
+
}
|
|
12254
|
+
function listNodeKinds(category) {
|
|
12255
|
+
ensureBuiltinKinds();
|
|
12256
|
+
const all = Array.from(kinds.values()).map(
|
|
12257
|
+
(k2) => withOverride(k2)
|
|
12258
|
+
);
|
|
12259
|
+
return category ? all.filter((k2) => k2.category === category) : all;
|
|
12260
|
+
}
|
|
12261
|
+
function onNodeKindsChanged(listener) {
|
|
12262
|
+
listeners2.add(listener);
|
|
12263
|
+
return () => listeners2.delete(listener);
|
|
12264
|
+
}
|
|
12265
|
+
function notify() {
|
|
12266
|
+
for (const l of listeners2) l();
|
|
12267
|
+
}
|
|
12268
|
+
function defaultConfigFor(kind) {
|
|
12269
|
+
const fromKind = kind.defaultConfig ? { ...kind.defaultConfig } : {};
|
|
12270
|
+
for (const field of kind.configSchema ?? []) {
|
|
12271
|
+
if (fromKind[field.key] !== void 0) continue;
|
|
12272
|
+
if ("default" in field && field.default !== void 0) {
|
|
12273
|
+
fromKind[field.key] = field.default;
|
|
12274
|
+
}
|
|
12275
|
+
}
|
|
12276
|
+
return fromKind;
|
|
12277
|
+
}
|
|
12278
|
+
function validateConfig(kind, config) {
|
|
12279
|
+
const issues = [];
|
|
12280
|
+
for (const field of kind.configSchema ?? []) {
|
|
12281
|
+
const value = config[field.key];
|
|
12282
|
+
if (field.required && (value === void 0 || value === null || value === "")) {
|
|
12283
|
+
issues.push({ key: field.key, message: `${field.label} is required` });
|
|
12284
|
+
continue;
|
|
12285
|
+
}
|
|
12286
|
+
if (value === void 0 || value === null) continue;
|
|
12287
|
+
const issue = validateField(field, value);
|
|
12288
|
+
if (issue) issues.push({ key: field.key, message: issue });
|
|
12289
|
+
}
|
|
12290
|
+
return issues;
|
|
12291
|
+
}
|
|
12292
|
+
function validateField(field, value) {
|
|
12293
|
+
switch (field.type) {
|
|
12294
|
+
case "text":
|
|
12295
|
+
case "textarea":
|
|
12296
|
+
case "expression":
|
|
12297
|
+
case "credential":
|
|
12298
|
+
return typeof value === "string" ? null : `${field.label} must be a string`;
|
|
12299
|
+
case "number": {
|
|
12300
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return `${field.label} must be a number`;
|
|
12301
|
+
if (field.min !== void 0 && value < field.min) return `${field.label} must be >= ${field.min}`;
|
|
12302
|
+
if (field.max !== void 0 && value > field.max) return `${field.label} must be <= ${field.max}`;
|
|
12303
|
+
return null;
|
|
12304
|
+
}
|
|
12305
|
+
case "switch":
|
|
12306
|
+
return typeof value === "boolean" ? null : `${field.label} must be a boolean`;
|
|
12307
|
+
case "select": {
|
|
12308
|
+
const allowed = field.options.map((o) => o.value);
|
|
12309
|
+
return allowed.includes(String(value)) ? null : `${field.label} must be one of ${allowed.join(", ")}`;
|
|
12310
|
+
}
|
|
12311
|
+
case "json":
|
|
12312
|
+
return null;
|
|
12313
|
+
// permissive — just JSON-shaped
|
|
12314
|
+
case "repeater": {
|
|
12315
|
+
if (!Array.isArray(value)) return `${field.label} must be a list`;
|
|
12316
|
+
if (field.minItems !== void 0 && value.length < field.minItems) {
|
|
12317
|
+
return `${field.label} needs at least ${field.minItems}`;
|
|
12318
|
+
}
|
|
12319
|
+
if (field.maxItems !== void 0 && value.length > field.maxItems) {
|
|
12320
|
+
return `${field.label} allows at most ${field.maxItems}`;
|
|
12321
|
+
}
|
|
12322
|
+
for (let i = 0; i < value.length; i++) {
|
|
12323
|
+
const row = value[i];
|
|
12324
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) {
|
|
12325
|
+
return `${field.label} item ${i + 1} must be an object`;
|
|
12326
|
+
}
|
|
12327
|
+
for (const sub of field.fields) {
|
|
12328
|
+
const cell = row[sub.key];
|
|
12329
|
+
if (sub.required && (cell === void 0 || cell === null || cell === "")) {
|
|
12330
|
+
return `${field.label} item ${i + 1}: ${sub.label} is required`;
|
|
12331
|
+
}
|
|
12332
|
+
if (cell === void 0 || cell === null) continue;
|
|
12333
|
+
const issue = validateField(sub, cell);
|
|
12334
|
+
if (issue) return `${field.label} item ${i + 1}: ${issue}`;
|
|
12335
|
+
}
|
|
12336
|
+
}
|
|
12337
|
+
return null;
|
|
12338
|
+
}
|
|
12339
|
+
case "keyvalue": {
|
|
12340
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
12341
|
+
return `${field.label} must be a key/value map`;
|
|
12342
|
+
}
|
|
12343
|
+
const allowed = field.valueOptions?.map((o) => o.value);
|
|
12344
|
+
for (const [k2, v2] of Object.entries(value)) {
|
|
12345
|
+
if (typeof v2 !== "string") return `${field.label}: "${k2}" must be a string`;
|
|
12346
|
+
if (allowed && !allowed.includes(v2)) {
|
|
12347
|
+
return `${field.label}: "${k2}" must be one of ${allowed.join(", ")}`;
|
|
12348
|
+
}
|
|
12349
|
+
}
|
|
12350
|
+
return null;
|
|
12351
|
+
}
|
|
12352
|
+
case "document":
|
|
12353
|
+
return null;
|
|
12354
|
+
// opaque to fancy-flow — the host's editor owns its shape
|
|
12355
|
+
default:
|
|
12356
|
+
return null;
|
|
12357
|
+
}
|
|
12358
|
+
}
|
|
12359
|
+
function categoryAccent(category) {
|
|
12360
|
+
switch (category) {
|
|
12361
|
+
case "trigger":
|
|
12362
|
+
return "#10b981";
|
|
12363
|
+
case "logic":
|
|
12364
|
+
return "#f59e0b";
|
|
12365
|
+
case "data":
|
|
12366
|
+
return "#0ea5e9";
|
|
12367
|
+
case "ai":
|
|
12368
|
+
return "#8b5cf6";
|
|
12369
|
+
case "io":
|
|
12370
|
+
return "#3b82f6";
|
|
12371
|
+
case "human":
|
|
12372
|
+
return "#ec4899";
|
|
12373
|
+
case "output":
|
|
12374
|
+
return "#a855f7";
|
|
12375
|
+
case "layout":
|
|
12376
|
+
return "#64748b";
|
|
12377
|
+
case "annotation":
|
|
12378
|
+
return "#eab308";
|
|
12379
|
+
default:
|
|
12380
|
+
return "#71717a";
|
|
12381
|
+
}
|
|
12382
|
+
}
|
|
12244
12383
|
function NodeShellInner({
|
|
12245
12384
|
node,
|
|
12246
12385
|
accent,
|
|
@@ -15086,7 +15225,9 @@ function FlowViewer({
|
|
|
15086
15225
|
return {
|
|
15087
15226
|
node,
|
|
15088
15227
|
index: index2,
|
|
15089
|
-
|
|
15228
|
+
// The node's own label is the name the AUTHOR gave THIS node ("Summarize");
|
|
15229
|
+
// the kind label is the generic type name ("LLM Call"). Specific wins.
|
|
15230
|
+
title: node.data?.label ?? kind?.label ?? node.data?.kind ?? node.id,
|
|
15090
15231
|
description: kind?.description ?? null,
|
|
15091
15232
|
accent: kind?.accent ?? categoryAccent(kind?.category ?? "custom"),
|
|
15092
15233
|
status: statuses?.[node.id] ?? null
|
|
@@ -15364,7 +15505,7 @@ function evaluateConfig(config, context) {
|
|
|
15364
15505
|
}
|
|
15365
15506
|
|
|
15366
15507
|
// src/index.ts
|
|
15367
|
-
|
|
15508
|
+
ensureBuiltinKinds();
|
|
15368
15509
|
/*! Bundled license information:
|
|
15369
15510
|
|
|
15370
15511
|
@dagrejs/dagre/dist/dagre.esm.js:
|
|
@@ -15416,6 +15557,7 @@ exports.defineNode = defineNode;
|
|
|
15416
15557
|
exports.describeExpressionGrammar = describeExpressionGrammar;
|
|
15417
15558
|
exports.distributeNodes = distributeNodes;
|
|
15418
15559
|
exports.encodePause = encodePause;
|
|
15560
|
+
exports.ensureBuiltinKinds = ensureBuiltinKinds;
|
|
15419
15561
|
exports.evaluateConfig = evaluateConfig;
|
|
15420
15562
|
exports.evaluateExpression = evaluateExpression;
|
|
15421
15563
|
exports.exportWorkflow = exportWorkflow;
|