@bian-womp/spark-workbench 0.2.32 → 0.2.34
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/lib/cjs/index.cjs +48 -11
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/contracts.d.ts +1 -0
- package/lib/cjs/src/core/contracts.d.ts.map +1 -1
- package/lib/cjs/src/misc/Inspector.d.ts.map +1 -1
- package/lib/cjs/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/lib/esm/index.js +48 -11
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/contracts.d.ts +1 -0
- package/lib/esm/src/core/contracts.d.ts.map +1 -1
- package/lib/esm/src/misc/Inspector.d.ts.map +1 -1
- package/lib/esm/src/misc/context/WorkbenchContext.provider.d.ts.map +1 -1
- package/package.json +4 -4
package/lib/cjs/index.cjs
CHANGED
|
@@ -567,7 +567,10 @@ class LocalGraphRunner extends AbstractGraphRunner {
|
|
|
567
567
|
this.build(payload.def);
|
|
568
568
|
this.setEnvironment?.(payload.environment || {}, { merge: false });
|
|
569
569
|
// Hydrate via runtime for exact restore and re-emit
|
|
570
|
-
this.runtime?.hydrate({
|
|
570
|
+
this.runtime?.hydrate({
|
|
571
|
+
inputs: payload.inputs || {},
|
|
572
|
+
outputs: payload.outputs || {},
|
|
573
|
+
});
|
|
571
574
|
}
|
|
572
575
|
dispose() {
|
|
573
576
|
super.dispose();
|
|
@@ -1629,7 +1632,7 @@ function toReactFlow(def, positions, registry, opts) {
|
|
|
1629
1632
|
});
|
|
1630
1633
|
const edges = def.edges.map((e) => {
|
|
1631
1634
|
const st = opts.edgeStatus?.[e.id];
|
|
1632
|
-
const isRunning =
|
|
1635
|
+
const isRunning = (st?.activeRuns || 0) > 0;
|
|
1633
1636
|
const hasError = !!st?.lastError;
|
|
1634
1637
|
const isInvalidEdge = !!opts.edgeValidation?.[e.id];
|
|
1635
1638
|
const sourceMissing = !validHandleMap[e.source.nodeId]?.outputs.has(e.source.handle);
|
|
@@ -1666,7 +1669,7 @@ function getNodeBorderClassNames(args) {
|
|
|
1666
1669
|
const hasError = !!status.lastError;
|
|
1667
1670
|
const hasValidationError = issues.some((i) => i.level === "error");
|
|
1668
1671
|
const hasValidationWarning = !hasValidationError && issues.length > 0;
|
|
1669
|
-
const isRunning =
|
|
1672
|
+
const isRunning = (status.activeRuns || 0) > 0;
|
|
1670
1673
|
const isInvalid = !!status.invalidated && !isRunning && !hasError;
|
|
1671
1674
|
// Keep border width constant to avoid layout reflow on selection toggles
|
|
1672
1675
|
const borderWidth = "border";
|
|
@@ -1796,8 +1799,16 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
1796
1799
|
const next = { ...prev };
|
|
1797
1800
|
for (const n of def.nodes) {
|
|
1798
1801
|
const cur = next[n.nodeId] ?? (next[n.nodeId] = {});
|
|
1802
|
+
const updates = {};
|
|
1799
1803
|
if (cur.invalidated === undefined) {
|
|
1800
|
-
|
|
1804
|
+
updates.invalidated = true;
|
|
1805
|
+
}
|
|
1806
|
+
// Ensure activeRunIds is always initialized as an array
|
|
1807
|
+
if (cur.activeRunIds === undefined) {
|
|
1808
|
+
updates.activeRunIds = [];
|
|
1809
|
+
}
|
|
1810
|
+
if (Object.keys(updates).length > 0) {
|
|
1811
|
+
next[n.nodeId] = { ...cur, ...updates };
|
|
1801
1812
|
}
|
|
1802
1813
|
}
|
|
1803
1814
|
return next;
|
|
@@ -2001,13 +2012,23 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2001
2012
|
return;
|
|
2002
2013
|
if (s.kind === "node-start") {
|
|
2003
2014
|
const id = s.nodeId;
|
|
2015
|
+
const runId = s.runId;
|
|
2016
|
+
// Validate runId is a non-empty string
|
|
2017
|
+
const isValidRunId = runId && typeof runId === "string" && runId.length > 0;
|
|
2018
|
+
if (!isValidRunId) {
|
|
2019
|
+
console.warn(`[WorkbenchContext] node-start event missing or invalid runId for node ${id}`, { runId, event: s });
|
|
2020
|
+
}
|
|
2004
2021
|
setNodeStatus((prev) => {
|
|
2005
2022
|
const current = prev[id]?.activeRuns ?? 0;
|
|
2023
|
+
const currentRunIds = prev[id]?.activeRunIds ?? [];
|
|
2006
2024
|
return {
|
|
2007
2025
|
...prev,
|
|
2008
2026
|
[id]: {
|
|
2009
2027
|
...prev[id],
|
|
2010
2028
|
activeRuns: current + 1,
|
|
2029
|
+
activeRunIds: isValidRunId
|
|
2030
|
+
? [...currentRunIds, runId]
|
|
2031
|
+
: currentRunIds,
|
|
2011
2032
|
progress: 0,
|
|
2012
2033
|
invalidated: false,
|
|
2013
2034
|
},
|
|
@@ -2029,13 +2050,23 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2029
2050
|
else if (s.kind === "node-done") {
|
|
2030
2051
|
const id = s.nodeId;
|
|
2031
2052
|
const runId = s.runId;
|
|
2053
|
+
// Validate runId is a non-empty string
|
|
2054
|
+
const isValidRunId = runId && typeof runId === "string" && runId.length > 0;
|
|
2032
2055
|
setNodeStatus((prev) => {
|
|
2033
2056
|
const current = prev[id]?.activeRuns ?? 0;
|
|
2034
|
-
const
|
|
2035
|
-
|
|
2057
|
+
const currentRunIds = prev[id]?.activeRunIds ?? [];
|
|
2058
|
+
if (isValidRunId && !currentRunIds.includes(runId)) {
|
|
2059
|
+
console.warn(`[WorkbenchContext] node-done event for unknown runId: node=${id} runId=${runId}`, { event: s, currentRunIds });
|
|
2060
|
+
return prev; // Ignore stale event
|
|
2061
|
+
}
|
|
2062
|
+
const nextActive = Math.max(0, current - 1); // Prevent negative values
|
|
2063
|
+
const nextRunIds = isValidRunId
|
|
2064
|
+
? currentRunIds.filter((rid) => rid !== runId)
|
|
2065
|
+
: currentRunIds;
|
|
2066
|
+
const hadError = !!(isValidRunId && errorRunsRef.current[id]?.[runId]);
|
|
2036
2067
|
const keepProgress = hadError || nextActive > 0;
|
|
2037
2068
|
// Clear error flag for this runId
|
|
2038
|
-
if (
|
|
2069
|
+
if (isValidRunId && errorRunsRef.current[id]?.[runId]) {
|
|
2039
2070
|
delete errorRunsRef.current[id]?.[runId];
|
|
2040
2071
|
}
|
|
2041
2072
|
return {
|
|
@@ -2043,6 +2074,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2043
2074
|
[id]: {
|
|
2044
2075
|
...prev[id],
|
|
2045
2076
|
activeRuns: nextActive,
|
|
2077
|
+
activeRunIds: nextRunIds,
|
|
2046
2078
|
progress: keepProgress ? prev[id]?.progress : 0,
|
|
2047
2079
|
lastError: hadError ? prev[id]?.lastError : undefined,
|
|
2048
2080
|
},
|
|
@@ -2072,7 +2104,7 @@ function WorkbenchProvider({ wb, runner, registry, setRegistry, overrides, uiVer
|
|
|
2072
2104
|
const current = prev[id]?.activeRuns ?? 0;
|
|
2073
2105
|
return {
|
|
2074
2106
|
...prev,
|
|
2075
|
-
[id]: { ...prev[id], activeRuns: current - 1 },
|
|
2107
|
+
[id]: { ...prev[id], activeRuns: Math.max(0, current - 1) }, // Prevent negative values
|
|
2076
2108
|
};
|
|
2077
2109
|
});
|
|
2078
2110
|
}
|
|
@@ -2354,7 +2386,7 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2354
2386
|
return String(value ?? "");
|
|
2355
2387
|
}
|
|
2356
2388
|
};
|
|
2357
|
-
const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, outputsMap, outputTypesMap, nodeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, clearSystemErrors, clearRegistryErrors, removeSystemError, removeRegistryError, } = useWorkbenchContext();
|
|
2389
|
+
const { registry, def, selectedNodeId, selectedEdgeId, inputsMap, outputsMap, outputTypesMap, nodeStatus, edgeStatus, validationByNode, validationByEdge, validationGlobal, valuesTick, updateEdgeType, systemErrors, registryErrors, clearSystemErrors, clearRegistryErrors, removeSystemError, removeRegistryError, } = useWorkbenchContext();
|
|
2358
2390
|
const nodeValidationIssues = validationByNode.issues;
|
|
2359
2391
|
const edgeValidationIssues = validationByEdge.issues;
|
|
2360
2392
|
const nodeValidationHandles = validationByNode;
|
|
@@ -2443,7 +2475,10 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2443
2475
|
return (jsxRuntime.jsxs("div", { className: `${widthClass} border-l border-gray-300 p-3 flex flex-col h-full min-h-0 overflow-auto`, children: [jsxRuntime.jsxs("div", { className: "flex-1 overflow-auto", children: [contextPanel && jsxRuntime.jsx("div", { className: "mb-2", children: contextPanel }), systemErrors.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-2 space-y-1", children: [systemErrors.map((err, i) => (jsxRuntime.jsxs("div", { className: "text-xs text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: err.code ? `Error ${err.code}` : "Error" }), jsxRuntime.jsx("div", { className: "break-words", children: err.message })] }), jsxRuntime.jsx("button", { className: "text-red-500 hover:text-red-700 text-[10px] px-1", onClick: () => removeSystemError(i), title: "Dismiss", children: jsxRuntime.jsx(react$1.XIcon, { size: 10 }) })] }, i))), systemErrors.length > 1 && (jsxRuntime.jsx("button", { className: "text-xs text-red-600 hover:text-red-800 underline", onClick: clearSystemErrors, children: "Clear all" }))] })), registryErrors.length > 0 && (jsxRuntime.jsxs("div", { className: "mb-2 space-y-1", children: [registryErrors.map((err, i) => (jsxRuntime.jsxs("div", { className: "text-xs text-amber-700 bg-amber-50 border border-amber-200 rounded px-2 py-1 flex items-start justify-between gap-2", children: [jsxRuntime.jsxs("div", { className: "flex-1", children: [jsxRuntime.jsx("div", { className: "font-semibold", children: "Registry Error" }), jsxRuntime.jsx("div", { className: "break-words", children: err.message }), err.attempt && err.maxAttempts && (jsxRuntime.jsxs("div", { className: "text-[10px] text-amber-600 mt-1", children: ["Attempt ", err.attempt, " of ", err.maxAttempts] }))] }), jsxRuntime.jsx("button", { className: "text-amber-500 hover:text-amber-700 text-[10px] px-1", onClick: () => removeRegistryError(i), title: "Dismiss", children: jsxRuntime.jsx(react$1.XIcon, { size: 10 }) })] }, i))), registryErrors.length > 1 && (jsxRuntime.jsx("button", { className: "text-xs text-amber-600 hover:text-amber-800 underline", onClick: clearRegistryErrors, children: "Clear all" }))] })), jsxRuntime.jsx("div", { className: "font-semibold mb-2", children: "Inspector" }), jsxRuntime.jsxs("div", { className: "text-xs text-gray-500 mb-2", children: ["valuesTick: ", valuesTick] }), jsxRuntime.jsx("div", { className: "flex-1", children: !selectedNode && !selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("div", { className: "text-gray-500", children: "Select a node or edge." }), globalValidationIssues && globalValidationIssues.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: globalValidationIssues.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), !!m.data?.edgeId && (jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2444
2476
|
e.stopPropagation();
|
|
2445
2477
|
deleteEdgeById(m.data?.edgeId);
|
|
2446
|
-
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }),
|
|
2478
|
+
}, title: "Delete referenced edge", children: "Delete edge" }))] }, i))) })] }))] })) : selectedEdge ? (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Edge: ", selectedEdge.id] }), jsxRuntime.jsxs("div", { children: [selectedEdge.source.nodeId, ".", selectedEdge.source.handle, " \u2192", " ", selectedEdge.target.nodeId, ".", selectedEdge.target.handle] }), (() => {
|
|
2479
|
+
const status = edgeStatus?.[selectedEdge.id];
|
|
2480
|
+
return status?.activeRuns > 0 ? (jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-blue-700 bg-blue-50 border border-blue-200 rounded px-2 py-1", children: [jsxRuntime.jsxs("div", { className: "font-semibold", children: ["Running (", status.activeRuns, ")"] }), jsxRuntime.jsx("div", { className: "text-[10px] text-blue-600 mt-1", children: "Note: Edge runIds are not available in stats events" })] })) : null;
|
|
2481
|
+
})(), jsxRuntime.jsx("div", { className: "mt-1", children: jsxRuntime.jsx("button", { className: "text-xs px-2 py-1 border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2447
2482
|
e.stopPropagation();
|
|
2448
2483
|
deleteEdgeById(selectedEdge.id);
|
|
2449
2484
|
}, title: "Delete this edge", children: "Delete edge" }) }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 mt-1", children: [jsxRuntime.jsxs("label", { className: "w-20 flex flex-col", children: [jsxRuntime.jsx("span", { children: "Type" }), jsxRuntime.jsx("span", { className: "text-gray-500 text-[11px]", children: "DataTypeId" })] }), jsxRuntime.jsxs("select", { className: "border border-gray-300 rounded px-2 py-1 focus:outline-none focus:ring-2 focus:ring-blue-500 w-full", value: selectedEdge.typeId ?? "", onChange: (e) => {
|
|
@@ -2453,7 +2488,9 @@ function Inspector({ debug, autoScroll, hideWorkbench, onAutoScrollChange, onHid
|
|
|
2453
2488
|
}, children: [jsxRuntime.jsx("option", { value: "", children: "(infer from source)" }), Array.from(registry.types.keys()).map((tid) => (jsxRuntime.jsx("option", { value: tid, children: tid }, tid)))] })] })] }), selectedEdgeValidation.length > 0 && (jsxRuntime.jsxs("div", { className: "mt-2 text-xs bg-red-50 border border-red-200 rounded px-2 py-1", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Validation" }), jsxRuntime.jsx("ul", { className: "list-disc ml-4", children: selectedEdgeValidation.map((m, i) => (jsxRuntime.jsxs("li", { className: "flex items-center gap-1", children: [jsxRuntime.jsx(IssueBadge, { level: m.level, size: 24, className: "w-6 h-6" }), jsxRuntime.jsx("span", { children: `${m.code}: ${m.message}` }), jsxRuntime.jsx("button", { className: "ml-2 text-[10px] px-1 py-[2px] border border-red-300 rounded text-red-700 hover:bg-red-50", onClick: (e) => {
|
|
2454
2489
|
e.stopPropagation();
|
|
2455
2490
|
deleteEdgeById(selectedEdge.id);
|
|
2456
|
-
}, title: "Delete this edge", children: "Delete edge" })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }),
|
|
2491
|
+
}, title: "Delete this edge", children: "Delete edge" })] }, i))) })] }))] })) : (jsxRuntime.jsxs("div", { children: [selectedNode && (jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsxs("div", { children: ["Node: ", selectedNode.nodeId] }), jsxRuntime.jsxs("div", { children: ["Type: ", selectedNode.typeId] }), selectedNodeStatus?.activeRuns &&
|
|
2492
|
+
selectedNodeStatus.activeRuns > 0 && (jsxRuntime.jsxs("div", { className: "mt-1 text-xs text-blue-700 bg-blue-50 border border-blue-200 rounded px-2 py-1", children: [jsxRuntime.jsxs("div", { className: "font-semibold", children: ["Running (", selectedNodeStatus.activeRuns, ")"] }), selectedNodeStatus.activeRunIds &&
|
|
2493
|
+
selectedNodeStatus.activeRunIds.length > 0 ? (jsxRuntime.jsxs("div", { className: "mt-1", children: [jsxRuntime.jsx("div", { className: "text-[10px] text-blue-600", children: "RunIds:" }), jsxRuntime.jsx("div", { className: "flex flex-wrap gap-1 mt-1", children: selectedNodeStatus.activeRunIds.map((runId, idx) => (jsxRuntime.jsx("span", { className: "text-[10px] px-1.5 py-0.5 bg-blue-100 border border-blue-300 rounded font-mono", children: runId }, idx))) })] })) : (jsxRuntime.jsx("div", { className: "text-[10px] text-blue-600 mt-1", children: "RunIds not available (some runs may have started without runId)" }))] })), !!selectedNodeStatus?.lastError && (jsxRuntime.jsx("div", { className: "mt-2 text-sm text-red-700 bg-red-50 border border-red-200 rounded px-2 py-1 break-words", children: String(selectedNodeStatus.lastError?.message ??
|
|
2457
2494
|
selectedNodeStatus.lastError) }))] })), jsxRuntime.jsxs("div", { className: "mb-2", children: [jsxRuntime.jsx("div", { className: "font-semibold mb-1", children: "Inputs" }), inputHandles.length === 0 ? (jsxRuntime.jsx("div", { className: "text-gray-500", children: "No inputs" })) : (inputHandles.map((h) => {
|
|
2458
2495
|
const typeId = sparkGraph.getInputTypeId(effectiveHandles.inputs, h);
|
|
2459
2496
|
const isLinked = def.edges.some((e) => e.target.nodeId === selectedNodeId &&
|