@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
package/dist/engine.d.cts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-
|
|
1
|
+
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-pW0PllaZ.cjs';
|
|
2
2
|
import { F as FlowGraph, R as RunEvent, N as NodeExecutor, W as WorkflowResolver, a as FlowNode } from './types-B-Syk9-M.cjs';
|
|
3
3
|
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, e as FlowEdge, f as FlowNodeData, g as FlowNodeKind, h as NodeRunStatus, i as NoteNodeData, O as OutputNodeData, P as PortDescriptor, c as RunIdentity, d as RunIdentityJson, S as SubgraphNodeData, j as TriggerNodeData, k as escapeSegment } from './types-B-Syk9-M.cjs';
|
|
4
|
-
export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-
|
|
4
|
+
export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-DNpPa0mq.cjs';
|
|
5
5
|
export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, b as applyKindSchemaOverlay, c as clearKindSchemaOverlays, d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, e as resolveKindId, u as unregisterNodeKind, v as validateConfig } from './registry-B6SiTaLZ.cjs';
|
|
6
6
|
export { b as CapabilityRequirement, C as ConfigField, E as EmitsRelation, M as ManifestProblem, c as ManifestValidation, d as NODE_MANIFEST_SCHEMA_VERSION, a as NodeCategory, N as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, O as OutputField, h as OutputShape, P as PortSpec, S as SideEffects, i as checkCapabilities, j as checkRuntimeSupport, s as satisfiesRange, v as validateNodeManifest } from './types-BdkXxjFh.cjs';
|
|
7
7
|
export { E as EvaluateOptions, a as ExprContext, b as ExprValue, R as Resolution, U as UnresolvedPathError, c as UnresolvedPolicy, e as evaluateConfig, d as evaluateExpression, r as resolvePath, t as text, f as truthy, g as tryResolvePath } from './expr-BfXDrs4H.cjs';
|
package/dist/engine.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-
|
|
1
|
+
export { R as RunOptions, a as RunResult, r as runFlow } from './run-flow-BLP9rXfO.js';
|
|
2
2
|
import { F as FlowGraph, R as RunEvent, N as NodeExecutor, W as WorkflowResolver, a as FlowNode } from './types-B-Syk9-M.js';
|
|
3
3
|
export { A as ActionNodeData, B as BaseNodeData, D as DecisionNodeData, E as ExecutorRegistry, e as FlowEdge, f as FlowNodeData, g as FlowNodeKind, h as NodeRunStatus, i as NoteNodeData, O as OutputNodeData, P as PortDescriptor, c as RunIdentity, d as RunIdentityJson, S as SubgraphNodeData, j as TriggerNodeData, k as escapeSegment } from './types-B-Syk9-M.js';
|
|
4
|
-
export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-
|
|
4
|
+
export { C as CohortGuard, a as CohortOptions, b as CohortPolicy, c as CohortResult, r as runCohort } from './run-cohort-rgT_FX3V.js';
|
|
5
5
|
export { K as KindSchemaOverlay, a as KindSchemaOverlayResult, b as applyKindSchemaOverlay, c as clearKindSchemaOverlays, d as defaultConfigFor, g as getNodeKind, k as kindIds, l as listNodeKinds, r as registerNodeKind, e as resolveKindId, u as unregisterNodeKind, v as validateConfig } from './registry-JUcN_xWK.js';
|
|
6
6
|
export { b as CapabilityRequirement, C as ConfigField, E as EmitsRelation, M as ManifestProblem, c as ManifestValidation, d as NODE_MANIFEST_SCHEMA_VERSION, a as NodeCategory, N as NodeKindDefinition, e as NodePackageManifest, f as NodeRuntimeId, g as NodeRuntimeSpec, O as OutputField, h as OutputShape, P as PortSpec, S as SideEffects, i as checkCapabilities, j as checkRuntimeSupport, s as satisfiesRange, v as validateNodeManifest } from './types-CbekKMMO.js';
|
|
7
7
|
export { E as EvaluateOptions, a as ExprContext, b as ExprValue, R as Resolution, U as UnresolvedPathError, c as UnresolvedPolicy, e as evaluateConfig, d as evaluateExpression, r as resolvePath, t as text, f as truthy, g as tryResolvePath } from './expr-BfXDrs4H.js';
|
package/dist/engine.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export { runCohort } from './chunk-
|
|
1
|
+
export { runCohort } from './chunk-K7KO3UJ3.js';
|
|
2
2
|
import { decodePause } from './chunk-UEOE6B52.js';
|
|
3
3
|
export { LEGACY_PAUSE_PREFIXES, PAUSE_PREFIX, decodePause, encodePause, isPause, pauseForHuman } from './chunk-UEOE6B52.js';
|
|
4
|
-
export { checkGraphConnectivity, mayFloat } from './chunk-
|
|
5
|
-
import { runFlow, getNodeKind, resolveNodePorts } from './chunk-
|
|
6
|
-
export { RunIdentity, UnresolvedPathError, applyKindSchemaOverlay, clearKindSchemaOverlays, defaultConfigFor, escapeSegment, evaluateConfig, evaluateExpression, getNodeKind, kindIds, listNodeKinds, registerNodeKind, resolveKindId, resolvePath, runFlow, text, truthy, tryResolvePath, unregisterNodeKind, validateConfig } from './chunk-
|
|
4
|
+
export { checkGraphConnectivity, mayFloat } from './chunk-VAF2SWTC.js';
|
|
5
|
+
import { runFlow, getNodeKind, resolveNodePorts } from './chunk-EX34GCKK.js';
|
|
6
|
+
export { RunIdentity, UnresolvedPathError, applyKindSchemaOverlay, clearKindSchemaOverlays, defaultConfigFor, escapeSegment, evaluateConfig, evaluateExpression, getNodeKind, kindIds, listNodeKinds, registerNodeKind, resolveKindId, resolvePath, runFlow, text, truthy, tryResolvePath, unregisterNodeKind, validateConfig } from './chunk-EX34GCKK.js';
|
|
7
7
|
import { isResolutionFailure, registerLlmClient, registerWorkflowResolver } from './chunk-2QXKDLGT.js';
|
|
8
8
|
|
|
9
9
|
// src/marketplace/manifest.ts
|
package/dist/index.cjs
CHANGED
|
@@ -1899,70 +1899,6 @@ function resolveNodePorts(node, kind) {
|
|
|
1899
1899
|
};
|
|
1900
1900
|
}
|
|
1901
1901
|
|
|
1902
|
-
// src/expressions/variables.ts
|
|
1903
|
-
var ROOT = "$json";
|
|
1904
|
-
function expr(path) {
|
|
1905
|
-
return `{{ ${path} }}`;
|
|
1906
|
-
}
|
|
1907
|
-
function outputFieldsFor(node) {
|
|
1908
|
-
const kindName = node.data?.kind ?? node.type;
|
|
1909
|
-
if (!kindName) return [];
|
|
1910
|
-
const kind = getNodeKind(kindName);
|
|
1911
|
-
const shape = kind?.outputShape;
|
|
1912
|
-
if (!shape) return [];
|
|
1913
|
-
if (typeof shape === "function") {
|
|
1914
|
-
try {
|
|
1915
|
-
const config = node.data?.config ?? {};
|
|
1916
|
-
return shape(config) ?? [];
|
|
1917
|
-
} catch {
|
|
1918
|
-
return [];
|
|
1919
|
-
}
|
|
1920
|
-
}
|
|
1921
|
-
return shape;
|
|
1922
|
-
}
|
|
1923
|
-
function baseVariables() {
|
|
1924
|
-
return [
|
|
1925
|
-
{ expression: expr(ROOT), path: ROOT, type: "unknown", description: "The whole incoming value." }
|
|
1926
|
-
];
|
|
1927
|
-
}
|
|
1928
|
-
function availableVariables(graph, nodeId) {
|
|
1929
|
-
const out = baseVariables();
|
|
1930
|
-
const seen = /* @__PURE__ */ new Set([ROOT]);
|
|
1931
|
-
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
1932
|
-
const upstreamIds = graph.edges.filter((e) => e.target === nodeId).map((e) => e.source);
|
|
1933
|
-
for (const id2 of upstreamIds) {
|
|
1934
|
-
if (id2 === nodeId) continue;
|
|
1935
|
-
const upstream = byId.get(id2);
|
|
1936
|
-
if (!upstream) continue;
|
|
1937
|
-
const kindName = upstream.data?.kind ?? upstream.type;
|
|
1938
|
-
const label = upstream.data?.label ?? (kindName ? getNodeKind(kindName)?.label : void 0);
|
|
1939
|
-
for (const field of outputFieldsFor(upstream)) {
|
|
1940
|
-
const path = `${ROOT}.${field.path}`;
|
|
1941
|
-
if (seen.has(path)) continue;
|
|
1942
|
-
seen.add(path);
|
|
1943
|
-
out.push({
|
|
1944
|
-
expression: expr(path),
|
|
1945
|
-
path,
|
|
1946
|
-
type: field.type,
|
|
1947
|
-
description: field.description,
|
|
1948
|
-
source: label
|
|
1949
|
-
});
|
|
1950
|
-
}
|
|
1951
|
-
}
|
|
1952
|
-
return out;
|
|
1953
|
-
}
|
|
1954
|
-
function describeExpressionGrammar() {
|
|
1955
|
-
return {
|
|
1956
|
-
forms: [
|
|
1957
|
-
{ syntax: "{{ $json }}", meaning: "The whole value arriving on this node's input." },
|
|
1958
|
-
{ syntax: "{{ $json.field }}", meaning: "One key of that value. Dot-paths nest: $json.user.email." },
|
|
1959
|
-
{ syntax: "{{ $input.field }}", meaning: "An alias for $json \u2014 the same value, either spelling." },
|
|
1960
|
-
{ syntax: "{{ in }}", meaning: "The input by its context key, rather than through the alias." }
|
|
1961
|
-
],
|
|
1962
|
-
note: "Dot-paths only \u2014 no arithmetic, comparisons or function calls; anything else resolves to nothing. A field that is exactly one expression keeps the resolved value's type; mixed text interpolates as a string. fancy-flow's own runFlow does not interpolate: it hands config to your executor verbatim, and the fancy-flow-php runtime is what resolves these."
|
|
1963
|
-
};
|
|
1964
|
-
}
|
|
1965
|
-
|
|
1966
1902
|
// src/runtime/run-identity.ts
|
|
1967
1903
|
function escapeSegment(value) {
|
|
1968
1904
|
return value.replace(/%/g, "%25").replace(/\//g, "%2F").replace(/#/g, "%23");
|
|
@@ -2306,6 +2242,127 @@ function specForLane(lane) {
|
|
|
2306
2242
|
};
|
|
2307
2243
|
}
|
|
2308
2244
|
|
|
2245
|
+
// src/expressions/variables.ts
|
|
2246
|
+
var ROOT = "$json";
|
|
2247
|
+
function expr(path) {
|
|
2248
|
+
return `{{ ${path} }}`;
|
|
2249
|
+
}
|
|
2250
|
+
function outputFieldsFor(node) {
|
|
2251
|
+
const kindName = node.data?.kind ?? node.type;
|
|
2252
|
+
if (!kindName) return [];
|
|
2253
|
+
const kind = getNodeKind(kindName);
|
|
2254
|
+
const shape = kind?.outputShape;
|
|
2255
|
+
if (!shape) return [];
|
|
2256
|
+
if (typeof shape === "function") {
|
|
2257
|
+
try {
|
|
2258
|
+
const config = node.data?.config ?? {};
|
|
2259
|
+
return shape(config) ?? [];
|
|
2260
|
+
} catch {
|
|
2261
|
+
return [];
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
return shape;
|
|
2265
|
+
}
|
|
2266
|
+
function baseVariables() {
|
|
2267
|
+
return [
|
|
2268
|
+
{ expression: expr(ROOT), path: ROOT, type: "unknown", description: "The whole incoming value." }
|
|
2269
|
+
];
|
|
2270
|
+
}
|
|
2271
|
+
function availableVariables(graph, nodeId) {
|
|
2272
|
+
const out = baseVariables();
|
|
2273
|
+
const seen = /* @__PURE__ */ new Set([ROOT]);
|
|
2274
|
+
const byId = new Map(graph.nodes.map((n) => [n.id, n]));
|
|
2275
|
+
const upstreamIds = graph.edges.filter((e) => e.target === nodeId).map((e) => e.source);
|
|
2276
|
+
for (const id2 of upstreamIds) {
|
|
2277
|
+
if (id2 === nodeId) continue;
|
|
2278
|
+
const upstream = byId.get(id2);
|
|
2279
|
+
if (!upstream) continue;
|
|
2280
|
+
const kindName = upstream.data?.kind ?? upstream.type;
|
|
2281
|
+
const label = upstream.data?.label ?? (kindName ? getNodeKind(kindName)?.label : void 0);
|
|
2282
|
+
for (const field of outputFieldsFor(upstream)) {
|
|
2283
|
+
const path = `${ROOT}.${field.path}`;
|
|
2284
|
+
if (seen.has(path)) continue;
|
|
2285
|
+
seen.add(path);
|
|
2286
|
+
out.push({
|
|
2287
|
+
expression: expr(path),
|
|
2288
|
+
path,
|
|
2289
|
+
type: field.type,
|
|
2290
|
+
description: field.description,
|
|
2291
|
+
source: label
|
|
2292
|
+
});
|
|
2293
|
+
}
|
|
2294
|
+
}
|
|
2295
|
+
return out;
|
|
2296
|
+
}
|
|
2297
|
+
function describeExpressionGrammar() {
|
|
2298
|
+
return {
|
|
2299
|
+
forms: [
|
|
2300
|
+
{ syntax: "{{ $json }}", meaning: "The whole value arriving on this node's input." },
|
|
2301
|
+
{ syntax: "{{ $json.field }}", meaning: "One key of that value. Dot-paths nest: $json.user.email." },
|
|
2302
|
+
{ syntax: "{{ $input.field }}", meaning: "An alias for $json \u2014 the same value, either spelling." },
|
|
2303
|
+
{ syntax: "{{ in }}", meaning: "The input by its context key, rather than through the alias." }
|
|
2304
|
+
],
|
|
2305
|
+
note: "Dot-paths only \u2014 no arithmetic, comparisons or function calls; anything else resolves to nothing. A field that is exactly one expression keeps the resolved value's type; mixed text interpolates as a string. fancy-flow's own runFlow does not interpolate: it hands config to your executor verbatim, and the fancy-flow-php runtime is what resolves these."
|
|
2306
|
+
};
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
// src/runtime/undelivered-edges.ts
|
|
2310
|
+
function undeliveredEdgeWarnings(target, incoming, portValues, completed, nodesById) {
|
|
2311
|
+
const events = [];
|
|
2312
|
+
for (const e of incoming) {
|
|
2313
|
+
const handle = e.sourceHandle ?? "out";
|
|
2314
|
+
if (portValues.has(`${e.source}:${handle}`) || !completed.has(e.source)) continue;
|
|
2315
|
+
if (possiblePortIds(nodesById.get(e.source)).includes(handle)) continue;
|
|
2316
|
+
events.push({
|
|
2317
|
+
type: "log",
|
|
2318
|
+
nodeId: target.id,
|
|
2319
|
+
level: "warn",
|
|
2320
|
+
message: undeliveredEdgeMessage(e, target, portValues, nodesById),
|
|
2321
|
+
detail: { edge: e.id, source: e.source, sourceHandle: handle }
|
|
2322
|
+
});
|
|
2323
|
+
}
|
|
2324
|
+
return events;
|
|
2325
|
+
}
|
|
2326
|
+
function possiblePortIds(node) {
|
|
2327
|
+
if (!node) return ["out"];
|
|
2328
|
+
const own = node.data?.outputs;
|
|
2329
|
+
if (Array.isArray(own)) return own.map((p2) => p2.id);
|
|
2330
|
+
const kind = getNodeKind(node.data?.kind ?? node.type ?? "");
|
|
2331
|
+
if (!kind) return ["out"];
|
|
2332
|
+
const config = nodeConfig(node);
|
|
2333
|
+
const representative = typeof kind.outputs === "function" && !configDeclaresPorts(kind.name, config);
|
|
2334
|
+
const ids = (resolvePortSpec(kind.outputs, representative ? defaultConfigFor(kind) : config) ?? []).map((p2) => p2.id);
|
|
2335
|
+
return ids.length > 0 ? ids : ["out"];
|
|
2336
|
+
}
|
|
2337
|
+
function configDeclaresPorts(kindName, config) {
|
|
2338
|
+
const bare = kindName.startsWith("@") ? kindName.slice(kindName.lastIndexOf("/") + 1) : kindName;
|
|
2339
|
+
if (bare === "switch_case") {
|
|
2340
|
+
const cases = config.cases;
|
|
2341
|
+
return !!cases && typeof cases === "object" && Object.values(cases).some((port) => typeof port === "string" && port !== "");
|
|
2342
|
+
}
|
|
2343
|
+
if (bare === "llm_router") {
|
|
2344
|
+
const routes = config.routes;
|
|
2345
|
+
return Array.isArray(routes) && routes.some((route) => route?.port != null && route.port !== "");
|
|
2346
|
+
}
|
|
2347
|
+
return true;
|
|
2348
|
+
}
|
|
2349
|
+
function undeliveredEdgeMessage(edge, target, portValues, nodesById) {
|
|
2350
|
+
const handle = edge.sourceHandle ?? "out";
|
|
2351
|
+
const prefix = `${edge.source}:`;
|
|
2352
|
+
const available = [];
|
|
2353
|
+
for (const key of portValues.keys()) {
|
|
2354
|
+
if (key.startsWith(prefix)) available.push(key.slice(prefix.length));
|
|
2355
|
+
}
|
|
2356
|
+
let message = `Edge ${edge.id} reads port "${handle}" from node ${edge.source}, which never publishes it \u2014 nothing would reach ${target.id} at run time.`;
|
|
2357
|
+
if (available.length > 0) message += ` Available: ${available.join(", ")}.`;
|
|
2358
|
+
const source = nodesById.get(edge.source);
|
|
2359
|
+
if (source && outputFieldsFor(source).some((field) => field?.path === handle)) {
|
|
2360
|
+
message += ` Note: "${handle}" is a FIELD this node emits, not a port \u2014 read it downstream as {{ in.${handle} }} rather than naming it as a source handle.`;
|
|
2361
|
+
}
|
|
2362
|
+
if (edge.sourceHandle != null) message += " Leave sourceHandle off to read the node's output.";
|
|
2363
|
+
return message;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2309
2366
|
// src/runtime/workflow-props.ts
|
|
2310
2367
|
function typeOf(value) {
|
|
2311
2368
|
if (value === null) return "null";
|
|
@@ -2364,7 +2421,7 @@ function resolveWorkflowProps(declared, passed) {
|
|
|
2364
2421
|
// src/runtime/run-flow.ts
|
|
2365
2422
|
async function runFlow(graph, executors, onEvent = () => {
|
|
2366
2423
|
}, options = {}) {
|
|
2367
|
-
const { signal, initialInputs = {}, timeoutMs, depth = 0, resumeOutputs = {}, entryNodes } = options;
|
|
2424
|
+
const { signal, initialInputs = {}, timeoutMs, depth = 0, resumeOutputs = {}, entryNodes, nodeExecutors } = options;
|
|
2368
2425
|
const run = options.run === void 0 ? void 0 : RunIdentity.from(options.run);
|
|
2369
2426
|
const outputs = {};
|
|
2370
2427
|
const portValues = /* @__PURE__ */ new Map();
|
|
@@ -2427,17 +2484,8 @@ async function runFlow(graph, executors, onEvent = () => {
|
|
|
2427
2484
|
onEvent({ type: "node-status", nodeId: node.id, status: "idle", text: "skipped" });
|
|
2428
2485
|
continue;
|
|
2429
2486
|
}
|
|
2430
|
-
for (const
|
|
2431
|
-
|
|
2432
|
-
if (portValues.has(`${e.source}:${handle}`) || !completed.has(e.source)) continue;
|
|
2433
|
-
if (possiblePortIds(nodesById.get(e.source)).includes(handle)) continue;
|
|
2434
|
-
onEvent({
|
|
2435
|
-
type: "log",
|
|
2436
|
-
nodeId: node.id,
|
|
2437
|
-
level: "warn",
|
|
2438
|
-
message: undeliveredEdgeMessage(e, node, portValues, nodesById),
|
|
2439
|
-
detail: { edge: e.id, source: e.source, sourceHandle: handle }
|
|
2440
|
-
});
|
|
2487
|
+
for (const warning of undeliveredEdgeWarnings(node, incoming, portValues, completed, nodesById)) {
|
|
2488
|
+
onEvent(warning);
|
|
2441
2489
|
}
|
|
2442
2490
|
if (incoming.length > 0) {
|
|
2443
2491
|
const anyActive = incoming.some((e) => portValues.has(`${e.source}:${e.sourceHandle ?? "out"}`));
|
|
@@ -2461,7 +2509,7 @@ async function runFlow(graph, executors, onEvent = () => {
|
|
|
2461
2509
|
onEvent({ type: "node-status", nodeId: node.id, status: "running" });
|
|
2462
2510
|
announce(onEvent, node, "start");
|
|
2463
2511
|
const inputs = collectInputs(node, incoming, portValues, initialInputs, props, declaresProps);
|
|
2464
|
-
const exec = pickExecutor(executors, node);
|
|
2512
|
+
const exec = pickExecutor(executors, node, nodeExecutors);
|
|
2465
2513
|
if (!exec) {
|
|
2466
2514
|
const tried = executorLookupIds(node);
|
|
2467
2515
|
const msg = `No executor registered for kind=${node.type} \u2014 tried ${tried.map((id2) => `"${id2}"`).join(", ")}. Key your registry by one of those.`;
|
|
@@ -2581,7 +2629,9 @@ function executorLookupIds(node) {
|
|
|
2581
2629
|
ids.push("*");
|
|
2582
2630
|
return [...new Set(ids)];
|
|
2583
2631
|
}
|
|
2584
|
-
function pickExecutor(executors, node) {
|
|
2632
|
+
function pickExecutor(executors, node, nodeExecutors) {
|
|
2633
|
+
const pinned = nodeExecutors && Object.prototype.hasOwnProperty.call(nodeExecutors, node.id) ? nodeExecutors[node.id] : void 0;
|
|
2634
|
+
if (pinned) return pinned;
|
|
2585
2635
|
for (const id2 of executorLookupIds(node)) {
|
|
2586
2636
|
if (executors[id2]) return executors[id2];
|
|
2587
2637
|
}
|
|
@@ -2601,45 +2651,6 @@ function activatedPorts(node, result) {
|
|
|
2601
2651
|
const declared = resolveNodePorts(node, kind).outputs?.map((p2) => p2.id);
|
|
2602
2652
|
return { ports: declared?.length ? declared : ["out"], value: result };
|
|
2603
2653
|
}
|
|
2604
|
-
function possiblePortIds(node) {
|
|
2605
|
-
if (!node) return ["out"];
|
|
2606
|
-
const own = node.data?.outputs;
|
|
2607
|
-
if (Array.isArray(own)) return own.map((p2) => p2.id);
|
|
2608
|
-
const kind = getNodeKind(node.data?.kind ?? node.type ?? "");
|
|
2609
|
-
if (!kind) return ["out"];
|
|
2610
|
-
const config = nodeConfig(node);
|
|
2611
|
-
const representative = typeof kind.outputs === "function" && !configDeclaresPorts(kind.name, config);
|
|
2612
|
-
const ids = (resolvePortSpec(kind.outputs, representative ? defaultConfigFor(kind) : config) ?? []).map((p2) => p2.id);
|
|
2613
|
-
return ids.length > 0 ? ids : ["out"];
|
|
2614
|
-
}
|
|
2615
|
-
function configDeclaresPorts(kindName, config) {
|
|
2616
|
-
const bare = kindName.startsWith("@") ? kindName.slice(kindName.lastIndexOf("/") + 1) : kindName;
|
|
2617
|
-
if (bare === "switch_case") {
|
|
2618
|
-
const cases = config.cases;
|
|
2619
|
-
return !!cases && typeof cases === "object" && Object.values(cases).some((port) => typeof port === "string" && port !== "");
|
|
2620
|
-
}
|
|
2621
|
-
if (bare === "llm_router") {
|
|
2622
|
-
const routes = config.routes;
|
|
2623
|
-
return Array.isArray(routes) && routes.some((route) => route?.port != null && route.port !== "");
|
|
2624
|
-
}
|
|
2625
|
-
return true;
|
|
2626
|
-
}
|
|
2627
|
-
function undeliveredEdgeMessage(edge, target, portValues, nodesById) {
|
|
2628
|
-
const handle = edge.sourceHandle ?? "out";
|
|
2629
|
-
const prefix = `${edge.source}:`;
|
|
2630
|
-
const available = [];
|
|
2631
|
-
for (const key of portValues.keys()) {
|
|
2632
|
-
if (key.startsWith(prefix)) available.push(key.slice(prefix.length));
|
|
2633
|
-
}
|
|
2634
|
-
let message = `Edge ${edge.id} reads port "${handle}" from node ${edge.source}, which never publishes it \u2014 nothing would reach ${target.id} at run time.`;
|
|
2635
|
-
if (available.length > 0) message += ` Available: ${available.join(", ")}.`;
|
|
2636
|
-
const source = nodesById.get(edge.source);
|
|
2637
|
-
if (source && outputFieldsFor(source).some((field) => field?.path === handle)) {
|
|
2638
|
-
message += ` Note: "${handle}" is a FIELD this node emits, not a port \u2014 read it downstream as {{ in.${handle} }} rather than naming it as a source handle.`;
|
|
2639
|
-
}
|
|
2640
|
-
if (edge.sourceHandle != null) message += " Leave sourceHandle off to read the node's output.";
|
|
2641
|
-
return message;
|
|
2642
|
-
}
|
|
2643
2654
|
function announce(onEvent, node, phase) {
|
|
2644
2655
|
const data = node.data;
|
|
2645
2656
|
const raw = phase === "start" ? data?.startingMsg : data?.stoppingMsg;
|