@bian-womp/spark-graph 0.3.7 → 0.3.9
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 +138 -12
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/type-utils.d.ts +18 -0
- package/lib/cjs/src/core/type-utils.d.ts.map +1 -0
- package/lib/cjs/src/core/types.d.ts +0 -6
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +1 -1
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/EdgePropagator.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/HandleResolver.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/graph-utils.d.ts.map +1 -1
- package/lib/cjs/src/runtime/components/types.d.ts +3 -1
- package/lib/cjs/src/runtime/components/types.d.ts.map +1 -1
- package/lib/esm/index.js +137 -13
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/type-utils.d.ts +18 -0
- package/lib/esm/src/core/type-utils.d.ts.map +1 -0
- package/lib/esm/src/core/types.d.ts +0 -6
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +1 -1
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/EdgePropagator.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/HandleResolver.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/graph-utils.d.ts.map +1 -1
- package/lib/esm/src/runtime/components/types.d.ts +3 -1
- package/lib/esm/src/runtime/components/types.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -28,6 +28,46 @@ function isInputPrivate(inputs, handle) {
|
|
|
28
28
|
const v = inputs ? inputs[handle] : undefined;
|
|
29
29
|
return !!(v && typeof v === "object" && v.private);
|
|
30
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Merge two InputHandleDescriptor values, with dynamic taking precedence.
|
|
33
|
+
* If both have metadata, merge the metadata objects (dynamic overrides static).
|
|
34
|
+
*/
|
|
35
|
+
function mergeInputHandleDescriptors(staticDesc, dynamicDesc) {
|
|
36
|
+
// If only one exists, return it
|
|
37
|
+
if (!staticDesc)
|
|
38
|
+
return dynamicDesc;
|
|
39
|
+
if (!dynamicDesc)
|
|
40
|
+
return staticDesc;
|
|
41
|
+
// If both are strings, dynamic wins
|
|
42
|
+
if (typeof staticDesc === "string" && typeof dynamicDesc === "string") {
|
|
43
|
+
return dynamicDesc;
|
|
44
|
+
}
|
|
45
|
+
const staticObj = typeof staticDesc === "string" ? { typeId: staticDesc } : staticDesc;
|
|
46
|
+
const dynamicObj = typeof dynamicDesc === "string" ? { typeId: dynamicDesc } : dynamicDesc;
|
|
47
|
+
// Merge: dynamic takes precedence, but merge metadata objects
|
|
48
|
+
const merged = {
|
|
49
|
+
typeId: dynamicObj.typeId ?? staticObj.typeId,
|
|
50
|
+
private: dynamicObj.private ?? staticObj.private,
|
|
51
|
+
};
|
|
52
|
+
// Merge metadata if either has it
|
|
53
|
+
if (staticObj.metadata || dynamicObj.metadata) {
|
|
54
|
+
merged.metadata = {
|
|
55
|
+
...staticObj.metadata,
|
|
56
|
+
...dynamicObj.metadata, // Dynamic metadata overrides static
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
// Return as InputHandleDescriptor (which accepts object form)
|
|
60
|
+
return merged;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Extract metadata from an InputHandleDescriptor
|
|
64
|
+
*/
|
|
65
|
+
function getInputHandleMetadata(inputs, handle) {
|
|
66
|
+
const v = inputs ? inputs[handle] : undefined;
|
|
67
|
+
if (!v || typeof v === "string")
|
|
68
|
+
return undefined;
|
|
69
|
+
return v.metadata;
|
|
70
|
+
}
|
|
31
71
|
|
|
32
72
|
class CategoryRegistry {
|
|
33
73
|
constructor() {
|
|
@@ -752,11 +792,34 @@ function tryHandleResolving(def, registry, environment) {
|
|
|
752
792
|
// ignore dynamic resolution errors at this stage
|
|
753
793
|
}
|
|
754
794
|
// Merge base with dynamic and overrides (allow partial resolvedHandles)
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
795
|
+
// Merge inputs properly, handling metadata
|
|
796
|
+
const inputs = {};
|
|
797
|
+
// First, add all static handles
|
|
798
|
+
if (desc.inputs) {
|
|
799
|
+
for (const [handle, staticDesc] of Object.entries(desc.inputs)) {
|
|
800
|
+
inputs[handle] = staticDesc;
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
// Then, merge dynamic handles
|
|
804
|
+
if (dyn.inputs) {
|
|
805
|
+
for (const [handle, dynamicDesc] of Object.entries(dyn.inputs)) {
|
|
806
|
+
const staticDesc = desc.inputs?.[handle];
|
|
807
|
+
const merged = mergeInputHandleDescriptors(staticDesc, dynamicDesc);
|
|
808
|
+
if (merged) {
|
|
809
|
+
inputs[handle] = merged;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
// Finally, merge overrides
|
|
814
|
+
if (overrideInputs) {
|
|
815
|
+
for (const [handle, overrideDesc] of Object.entries(overrideInputs)) {
|
|
816
|
+
const existingDesc = inputs[handle];
|
|
817
|
+
const merged = mergeInputHandleDescriptors(existingDesc, overrideDesc);
|
|
818
|
+
if (merged) {
|
|
819
|
+
inputs[handle] = merged;
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
}
|
|
760
823
|
const outputs = {
|
|
761
824
|
...desc.outputs,
|
|
762
825
|
...dyn.outputs,
|
|
@@ -1022,7 +1085,24 @@ class HandleResolver {
|
|
|
1022
1085
|
const nodeDesc = this.registry.nodes.get(node.typeId);
|
|
1023
1086
|
if (!nodeDesc)
|
|
1024
1087
|
return;
|
|
1025
|
-
|
|
1088
|
+
// Merge inputs properly, handling metadata
|
|
1089
|
+
const inputs = {};
|
|
1090
|
+
// First, add all static handles
|
|
1091
|
+
if (nodeDesc.inputs) {
|
|
1092
|
+
for (const [handle, staticDesc] of Object.entries(nodeDesc.inputs)) {
|
|
1093
|
+
inputs[handle] = staticDesc;
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
// Then, merge dynamic handles
|
|
1097
|
+
if (resolved?.inputs) {
|
|
1098
|
+
for (const [handle, dynamicDesc] of Object.entries(resolved.inputs)) {
|
|
1099
|
+
const staticDesc = nodeDesc.inputs?.[handle];
|
|
1100
|
+
const merged = mergeInputHandleDescriptors(staticDesc, dynamicDesc);
|
|
1101
|
+
if (merged) {
|
|
1102
|
+
inputs[handle] = merged;
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
}
|
|
1026
1106
|
const outputs = { ...nodeDesc.outputs, ...resolved?.outputs };
|
|
1027
1107
|
const inputDefaults = {
|
|
1028
1108
|
...nodeDesc.inputDefaults,
|
|
@@ -1238,14 +1318,35 @@ class EdgePropagator {
|
|
|
1238
1318
|
const processedValue = this.processArrayInput(edge, value);
|
|
1239
1319
|
// Check if value changed
|
|
1240
1320
|
const prev = dstNode.inputs[edge.target.handle];
|
|
1241
|
-
|
|
1242
|
-
|
|
1321
|
+
const valueChanged = !valuesEqual(prev, processedValue);
|
|
1322
|
+
// Check if we should execute even if value is same:
|
|
1323
|
+
// 1. If node has rerunOnSameInput policy (from node params or registry)
|
|
1324
|
+
// 2. If input was set after last successful run (stale input detection)
|
|
1325
|
+
const registry = this.graph.getRegistry();
|
|
1326
|
+
const desc = registry?.nodes.get(dstNode.typeId);
|
|
1327
|
+
const nodeRerunPolicy = dstNode.policy?.rerunOnSameInput === true;
|
|
1328
|
+
const descRerunPolicy = desc?.policy?.rerunOnSameInput === true;
|
|
1329
|
+
const shouldRerunOnSameInput = nodeRerunPolicy || descRerunPolicy;
|
|
1330
|
+
const inputWasSetAfterLastRun = dstNode.lastInputAt?.[edge.target.handle] &&
|
|
1331
|
+
dstNode.lastSuccessAt &&
|
|
1332
|
+
dstNode.lastInputAt[edge.target.handle] > dstNode.lastSuccessAt;
|
|
1333
|
+
const shouldExecute = valueChanged || shouldRerunOnSameInput || inputWasSetAfterLastRun;
|
|
1334
|
+
if (!shouldExecute) {
|
|
1335
|
+
return; // No change and no reason to rerun
|
|
1243
1336
|
}
|
|
1244
1337
|
// Set input value (respecting skipPropagateValues)
|
|
1245
1338
|
const shouldSetValue = this.shouldSetInputValue(effectiveRunContexts);
|
|
1246
|
-
if (shouldSetValue) {
|
|
1339
|
+
if (shouldSetValue && valueChanged) {
|
|
1247
1340
|
this.setTargetInput(edge, dstNode, processedValue);
|
|
1248
1341
|
}
|
|
1342
|
+
else if (shouldSetValue && !valueChanged) {
|
|
1343
|
+
// Even if value didn't change, update timestamp if we're forcing execution
|
|
1344
|
+
const now = Date.now();
|
|
1345
|
+
if (!dstNode.lastInputAt) {
|
|
1346
|
+
dstNode.lastInputAt = {};
|
|
1347
|
+
}
|
|
1348
|
+
dstNode.lastInputAt[edge.target.handle] = now;
|
|
1349
|
+
}
|
|
1249
1350
|
// Schedule downstream execution
|
|
1250
1351
|
this.executeDownstream(edge.target.nodeId, effectiveRunContexts);
|
|
1251
1352
|
}
|
|
@@ -1302,7 +1403,13 @@ class EdgePropagator {
|
|
|
1302
1403
|
* Set target input value and emit event
|
|
1303
1404
|
*/
|
|
1304
1405
|
setTargetInput(edge, dstNode, value) {
|
|
1406
|
+
const now = Date.now();
|
|
1305
1407
|
dstNode.inputs[edge.target.handle] = value;
|
|
1408
|
+
// Track when this input was set
|
|
1409
|
+
if (!dstNode.lastInputAt) {
|
|
1410
|
+
dstNode.lastInputAt = {};
|
|
1411
|
+
}
|
|
1412
|
+
dstNode.lastInputAt[edge.target.handle] = now;
|
|
1306
1413
|
this.eventEmitter.emit("value", {
|
|
1307
1414
|
nodeId: edge.target.nodeId,
|
|
1308
1415
|
handle: edge.target.handle,
|
|
@@ -1879,11 +1986,15 @@ class NodeExecutor {
|
|
|
1879
1986
|
: undefined;
|
|
1880
1987
|
if (!hadError)
|
|
1881
1988
|
node.stats.lastError = undefined;
|
|
1882
|
-
//
|
|
1989
|
+
// Track successful completion time (for detecting stale inputs)
|
|
1883
1990
|
const isCancelled = controller.signal.aborted &&
|
|
1884
1991
|
(controller.signal.reason === "snapshot" ||
|
|
1885
1992
|
controller.signal.reason === "node-deleted" ||
|
|
1886
1993
|
controller.signal.reason === "user-cancelled");
|
|
1994
|
+
if (!hadError && !isCancelled) {
|
|
1995
|
+
node.lastSuccessAt = Date.now();
|
|
1996
|
+
}
|
|
1997
|
+
// Only emit node-done if not cancelled (cancellation events emitted separately)
|
|
1887
1998
|
if (!isCancelled) {
|
|
1888
1999
|
this.eventEmitter.emit("stats", {
|
|
1889
2000
|
kind: "node-done",
|
|
@@ -2051,6 +2162,8 @@ class GraphRuntime {
|
|
|
2051
2162
|
progress: 0,
|
|
2052
2163
|
},
|
|
2053
2164
|
activeRunContextIds: new Set(),
|
|
2165
|
+
lastInputAt: {},
|
|
2166
|
+
lastSuccessAt: undefined,
|
|
2054
2167
|
};
|
|
2055
2168
|
gr.graph.setNode(n.nodeId, rn);
|
|
2056
2169
|
}
|
|
@@ -2404,6 +2517,8 @@ class GraphRuntime {
|
|
|
2404
2517
|
progress: 0,
|
|
2405
2518
|
},
|
|
2406
2519
|
activeRunContextIds: new Set(),
|
|
2520
|
+
lastInputAt: {},
|
|
2521
|
+
lastSuccessAt: undefined,
|
|
2407
2522
|
};
|
|
2408
2523
|
this.graph.setNode(n.nodeId, newNode);
|
|
2409
2524
|
const effectiveInputs = this.nodeExecutor.getEffectiveInputs(newNode.nodeId);
|
|
@@ -2550,8 +2665,17 @@ class GraphRuntime {
|
|
|
2550
2665
|
const afterTargetSet = afterMap.get(handle) ?? new Set();
|
|
2551
2666
|
if (!setsEqual(beforeTargetSet, afterTargetSet)) {
|
|
2552
2667
|
const val = this.getOutput(nodeId, handle);
|
|
2553
|
-
if (val !== undefined)
|
|
2554
|
-
|
|
2668
|
+
if (val !== undefined) {
|
|
2669
|
+
let runContextIdsToUse = undefined;
|
|
2670
|
+
if (this.runMode === "manual") {
|
|
2671
|
+
runContextIdsToUse = new Set([
|
|
2672
|
+
this.runContextManager.createRunContext(nodeId, undefined, {
|
|
2673
|
+
propagate: false,
|
|
2674
|
+
}),
|
|
2675
|
+
]);
|
|
2676
|
+
}
|
|
2677
|
+
this.propagate(nodeId, handle, val, runContextIdsToUse);
|
|
2678
|
+
}
|
|
2555
2679
|
}
|
|
2556
2680
|
}
|
|
2557
2681
|
}
|
|
@@ -5043,6 +5167,7 @@ exports.createValidationGraphDef = createValidationGraphDef;
|
|
|
5043
5167
|
exports.createValidationGraphRegistry = createValidationGraphRegistry;
|
|
5044
5168
|
exports.findMatchingPaths = findMatchingPaths;
|
|
5045
5169
|
exports.generateId = generateId;
|
|
5170
|
+
exports.getInputHandleMetadata = getInputHandleMetadata;
|
|
5046
5171
|
exports.getInputTypeId = getInputTypeId;
|
|
5047
5172
|
exports.getTypedOutputTypeId = getTypedOutputTypeId;
|
|
5048
5173
|
exports.getTypedOutputValue = getTypedOutputValue;
|
|
@@ -5051,6 +5176,7 @@ exports.installLogging = installLogging;
|
|
|
5051
5176
|
exports.isInputPrivate = isInputPrivate;
|
|
5052
5177
|
exports.isTypedOutput = isTypedOutput;
|
|
5053
5178
|
exports.mergeGraphDefinitions = mergeGraphDefinitions;
|
|
5179
|
+
exports.mergeInputHandleDescriptors = mergeInputHandleDescriptors;
|
|
5054
5180
|
exports.mergeInputsOutputs = mergeInputsOutputs;
|
|
5055
5181
|
exports.mergeRuntimeState = mergeRuntimeState;
|
|
5056
5182
|
exports.mergeSnapshotData = mergeSnapshotData;
|