@bian-womp/spark-graph 0.2.34 → 0.2.36
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 +71 -114
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +1 -0
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +71 -114
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +1 -0
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -485,41 +485,12 @@ class GraphRuntime {
|
|
|
485
485
|
queued: 0,
|
|
486
486
|
progress: 0,
|
|
487
487
|
},
|
|
488
|
+
initialInputs: n.initialInputs ?? {},
|
|
488
489
|
};
|
|
489
490
|
gr.nodes.set(n.nodeId, rn);
|
|
490
491
|
}
|
|
491
492
|
// Instantiate edges
|
|
492
493
|
gr.edges = GraphRuntime.buildEdges(def, registry, gr.resolvedByNode);
|
|
493
|
-
// After nodes and edges exist, seed registry-, dynamic- and graph-level defaults
|
|
494
|
-
for (const n of def.nodes) {
|
|
495
|
-
const node = gr.nodes.get(n.nodeId);
|
|
496
|
-
const desc = registry.nodes.get(n.typeId);
|
|
497
|
-
if (!node || !desc)
|
|
498
|
-
continue;
|
|
499
|
-
// Resolve registry-level defaults and dynamic (resolved) defaults
|
|
500
|
-
const regDefaults = desc.inputDefaults ?? {};
|
|
501
|
-
const dynDefaults = gr.resolvedByNode.get(n.nodeId)?.inputDefaults ?? {};
|
|
502
|
-
const graphDefaults = n.initialInputs ?? {};
|
|
503
|
-
// Apply precedence: graph-level overrides dynamic, which overrides registry-level
|
|
504
|
-
const merged = {
|
|
505
|
-
...regDefaults,
|
|
506
|
-
...dynDefaults,
|
|
507
|
-
...graphDefaults,
|
|
508
|
-
};
|
|
509
|
-
for (const [handle, value] of Object.entries(merged)) {
|
|
510
|
-
// Only seed if input has no inbound wiring
|
|
511
|
-
const hasInbound = gr.edges.some((e) => e.target.nodeId === n.nodeId && e.target.handle === handle);
|
|
512
|
-
if (hasInbound)
|
|
513
|
-
continue;
|
|
514
|
-
if (value === undefined)
|
|
515
|
-
continue;
|
|
516
|
-
// Clone to avoid accidental shared references
|
|
517
|
-
node.inputs[handle] =
|
|
518
|
-
typeof structuredClone === "function"
|
|
519
|
-
? structuredClone(value)
|
|
520
|
-
: JSON.parse(JSON.stringify(value));
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
494
|
// Schedule async recompute only for nodes that indicated Promise-based resolveHandles
|
|
524
495
|
for (const nodeId of initial.pending)
|
|
525
496
|
gr.scheduleRecomputeHandles(nodeId);
|
|
@@ -643,6 +614,8 @@ class GraphRuntime {
|
|
|
643
614
|
return;
|
|
644
615
|
const now = Date.now();
|
|
645
616
|
const policy = node.policy ?? {};
|
|
617
|
+
// Compute effective inputs (real inputs + defaults) for this execution
|
|
618
|
+
const effectiveInputs = this.getEffectiveInputs(nodeId);
|
|
646
619
|
if (policy.debounceMs &&
|
|
647
620
|
node.lastScheduledAt &&
|
|
648
621
|
now - node.lastScheduledAt < policy.debounceMs) {
|
|
@@ -650,7 +623,7 @@ class GraphRuntime {
|
|
|
650
623
|
node.queue.splice(0, node.queue.length);
|
|
651
624
|
node.runSeq += 1;
|
|
652
625
|
const rid = `${nodeId}:${node.runSeq}:${now}`;
|
|
653
|
-
node.queue.push({ runId: rid, inputs:
|
|
626
|
+
node.queue.push({ runId: rid, inputs: effectiveInputs });
|
|
654
627
|
return;
|
|
655
628
|
}
|
|
656
629
|
node.lastScheduledAt = now;
|
|
@@ -756,7 +729,7 @@ class GraphRuntime {
|
|
|
756
729
|
return;
|
|
757
730
|
if (mode === "queue") {
|
|
758
731
|
const maxQ = policy.maxQueue ?? 8;
|
|
759
|
-
node.queue.push({ runId: rid, inputs:
|
|
732
|
+
node.queue.push({ runId: rid, inputs: effectiveInputs });
|
|
760
733
|
if (node.queue.length > maxQ)
|
|
761
734
|
node.queue.shift();
|
|
762
735
|
const processNext = () => {
|
|
@@ -775,7 +748,7 @@ class GraphRuntime {
|
|
|
775
748
|
return;
|
|
776
749
|
}
|
|
777
750
|
// switch or merge
|
|
778
|
-
startRun(rid,
|
|
751
|
+
startRun(rid, effectiveInputs);
|
|
779
752
|
}
|
|
780
753
|
// Returns true if all inbound handles for the node currently have a value
|
|
781
754
|
allInboundHaveValue(nodeId) {
|
|
@@ -791,6 +764,62 @@ class GraphRuntime {
|
|
|
791
764
|
}
|
|
792
765
|
return true;
|
|
793
766
|
}
|
|
767
|
+
// Computes effective inputs for a node by merging real inputs with defaults
|
|
768
|
+
// Defaults are applied only for unbound handles that have no explicit value
|
|
769
|
+
// This method does NOT mutate node.inputs - defaults remain virtual
|
|
770
|
+
// Dynamic handles (from resolveHandles) do NOT get defaults applied - they are metadata-only
|
|
771
|
+
getEffectiveInputs(nodeId) {
|
|
772
|
+
const node = this.nodes.get(nodeId);
|
|
773
|
+
if (!node)
|
|
774
|
+
return {};
|
|
775
|
+
const registry = this.registry;
|
|
776
|
+
if (!registry)
|
|
777
|
+
return {};
|
|
778
|
+
const desc = registry.nodes.get(node.typeId);
|
|
779
|
+
if (!desc)
|
|
780
|
+
return {};
|
|
781
|
+
const resolved = this.resolvedByNode.get(nodeId);
|
|
782
|
+
const regDefaults = desc.inputDefaults ?? {};
|
|
783
|
+
const dynDefaults = resolved?.inputDefaults ?? {};
|
|
784
|
+
const graphDefaults = node.initialInputs ?? {};
|
|
785
|
+
// Identify which handles are dynamically resolved (not in registry statics)
|
|
786
|
+
// Dynamic handles are metadata-only and should not receive defaults in execution
|
|
787
|
+
const staticHandles = new Set(Object.keys(desc.inputs ?? {}));
|
|
788
|
+
const dynamicHandles = new Set(Object.keys(resolved?.inputs ?? {}).filter((h) => !staticHandles.has(h)));
|
|
789
|
+
// Precedence: graph > dynamic > registry
|
|
790
|
+
const mergedDefaults = {
|
|
791
|
+
...regDefaults,
|
|
792
|
+
...dynDefaults,
|
|
793
|
+
...graphDefaults,
|
|
794
|
+
};
|
|
795
|
+
// Start with real inputs only (no defaults)
|
|
796
|
+
const effective = { ...node.inputs };
|
|
797
|
+
// Build set of inbound handles (wired inputs)
|
|
798
|
+
const inbound = new Set(this.edges
|
|
799
|
+
.filter((e) => e.target.nodeId === nodeId)
|
|
800
|
+
.map((e) => e.target.handle));
|
|
801
|
+
// Apply defaults only for:
|
|
802
|
+
// 1. Unbound handles that have no explicit value
|
|
803
|
+
// 2. Static handles (not dynamically resolved)
|
|
804
|
+
// This prevents dynamic handles (like geo:*) from getting defaults in execution
|
|
805
|
+
// Dynamic handles are metadata-only for UI display, not execution values
|
|
806
|
+
for (const [handle, defaultValue] of Object.entries(mergedDefaults)) {
|
|
807
|
+
if (defaultValue === undefined)
|
|
808
|
+
continue;
|
|
809
|
+
if (inbound.has(handle))
|
|
810
|
+
continue; // Don't override wired inputs
|
|
811
|
+
if (effective[handle] !== undefined)
|
|
812
|
+
continue; // Already has value
|
|
813
|
+
if (dynamicHandles.has(handle))
|
|
814
|
+
continue; // Skip defaults for dynamic handles
|
|
815
|
+
// Clone to avoid shared references
|
|
816
|
+
effective[handle] =
|
|
817
|
+
typeof structuredClone === "function"
|
|
818
|
+
? structuredClone(defaultValue)
|
|
819
|
+
: JSON.parse(JSON.stringify(defaultValue));
|
|
820
|
+
}
|
|
821
|
+
return effective;
|
|
822
|
+
}
|
|
794
823
|
invalidateDownstream(nodeId) {
|
|
795
824
|
// Notifies dependents; for now we propagate current outputs
|
|
796
825
|
for (const e of this.edges.filter((e) => e.source.nodeId === nodeId)) {
|
|
@@ -1092,12 +1121,13 @@ class GraphRuntime {
|
|
|
1092
1121
|
// call onActivated for nodes that implement it
|
|
1093
1122
|
for (const node of this.nodes.values()) {
|
|
1094
1123
|
const ctrl = new AbortController();
|
|
1124
|
+
const effectiveInputs = this.getEffectiveInputs(node.nodeId);
|
|
1095
1125
|
const ctx = {
|
|
1096
1126
|
state: node.state,
|
|
1097
1127
|
setState: (next) => Object.assign(node.state, next),
|
|
1098
1128
|
emit: (handle, value) => this.propagate(node.nodeId, handle, value),
|
|
1099
1129
|
invalidateDownstream: () => this.invalidateDownstream(node.nodeId),
|
|
1100
|
-
getInput: (handle) =>
|
|
1130
|
+
getInput: (handle) => effectiveInputs[handle],
|
|
1101
1131
|
environment: this.environment,
|
|
1102
1132
|
runId: `${node.nodeId}:activation`,
|
|
1103
1133
|
abortSignal: ctrl.signal,
|
|
@@ -1125,12 +1155,13 @@ class GraphRuntime {
|
|
|
1125
1155
|
if (!node)
|
|
1126
1156
|
return;
|
|
1127
1157
|
const ctrl = new AbortController();
|
|
1158
|
+
const effectiveInputs = this.getEffectiveInputs(nodeId);
|
|
1128
1159
|
const ctx = {
|
|
1129
1160
|
state: node.state,
|
|
1130
1161
|
setState: (next) => Object.assign(node.state, next),
|
|
1131
1162
|
emit: (handle, value) => this.propagate(nodeId, handle, value),
|
|
1132
1163
|
invalidateDownstream: () => this.invalidateDownstream(nodeId),
|
|
1133
|
-
getInput: (handle) =>
|
|
1164
|
+
getInput: (handle) => effectiveInputs[handle],
|
|
1134
1165
|
environment: this.environment,
|
|
1135
1166
|
runId: `${nodeId}:external`,
|
|
1136
1167
|
abortSignal: ctrl.signal,
|
|
@@ -1387,16 +1418,18 @@ class GraphRuntime {
|
|
|
1387
1418
|
queued: 0,
|
|
1388
1419
|
progress: 0,
|
|
1389
1420
|
},
|
|
1421
|
+
initialInputs: n.initialInputs ?? {},
|
|
1390
1422
|
};
|
|
1391
1423
|
this.nodes.set(n.nodeId, rn);
|
|
1392
1424
|
// Activate new node
|
|
1393
1425
|
const ctrl = new AbortController();
|
|
1426
|
+
const effectiveInputs = this.getEffectiveInputs(rn.nodeId);
|
|
1394
1427
|
const ctx = {
|
|
1395
1428
|
state: rn.state,
|
|
1396
1429
|
setState: (next) => Object.assign(rn.state, next),
|
|
1397
1430
|
emit: (handle, value) => this.propagate(rn.nodeId, handle, value),
|
|
1398
1431
|
invalidateDownstream: () => this.invalidateDownstream(rn.nodeId),
|
|
1399
|
-
getInput: (handle) =>
|
|
1432
|
+
getInput: (handle) => effectiveInputs[handle],
|
|
1400
1433
|
environment: this.environment,
|
|
1401
1434
|
runId: `${rn.nodeId}:activation`,
|
|
1402
1435
|
abortSignal: ctrl.signal,
|
|
@@ -1409,8 +1442,9 @@ class GraphRuntime {
|
|
|
1409
1442
|
rn.runtime.onActivated?.(ctx);
|
|
1410
1443
|
}
|
|
1411
1444
|
else {
|
|
1412
|
-
// update params/policy
|
|
1445
|
+
// update params/policy and initialInputs
|
|
1413
1446
|
existing.params = n.params;
|
|
1447
|
+
existing.initialInputs = n.initialInputs ?? {};
|
|
1414
1448
|
if (!existing.stats) {
|
|
1415
1449
|
existing.stats = {
|
|
1416
1450
|
runs: 0,
|
|
@@ -1464,32 +1498,7 @@ class GraphRuntime {
|
|
|
1464
1498
|
}
|
|
1465
1499
|
}
|
|
1466
1500
|
}
|
|
1467
|
-
// If input lost inbound, try to re-seed from defaults
|
|
1468
1501
|
if (changed) {
|
|
1469
|
-
const defNode = def.nodes.find((n) => n.nodeId === nodeId);
|
|
1470
|
-
if (defNode) {
|
|
1471
|
-
const desc = registry.nodes.get(defNode.typeId);
|
|
1472
|
-
if (desc) {
|
|
1473
|
-
const regDefaults = desc.inputDefaults ?? {};
|
|
1474
|
-
const dynDefaults = this.resolvedByNode.get(defNode.nodeId)?.inputDefaults ?? {};
|
|
1475
|
-
const graphDefaults = defNode.initialInputs ?? {};
|
|
1476
|
-
const merged = {
|
|
1477
|
-
...regDefaults,
|
|
1478
|
-
...dynDefaults,
|
|
1479
|
-
...graphDefaults,
|
|
1480
|
-
};
|
|
1481
|
-
for (const h of Array.from(prevSet)) {
|
|
1482
|
-
if (!currSet.has(h) && node.inputs[h] === undefined) {
|
|
1483
|
-
const v = merged[h];
|
|
1484
|
-
if (v !== undefined)
|
|
1485
|
-
node.inputs[h] =
|
|
1486
|
-
typeof structuredClone === "function"
|
|
1487
|
-
? structuredClone(v)
|
|
1488
|
-
: JSON.parse(JSON.stringify(v));
|
|
1489
|
-
}
|
|
1490
|
-
}
|
|
1491
|
-
}
|
|
1492
|
-
}
|
|
1493
1502
|
// Clear buckets for handles that lost inbound
|
|
1494
1503
|
const bucketsForNode = this.arrayInputBuckets.get(nodeId);
|
|
1495
1504
|
if (bucketsForNode) {
|
|
@@ -1547,34 +1556,6 @@ class GraphRuntime {
|
|
|
1547
1556
|
}
|
|
1548
1557
|
}
|
|
1549
1558
|
}
|
|
1550
|
-
// Seed defaults for nodes (new or existing) where inputs are still undefined and not inbound
|
|
1551
|
-
for (const n of def.nodes) {
|
|
1552
|
-
const node = this.nodes.get(n.nodeId);
|
|
1553
|
-
const desc = registry.nodes.get(n.typeId);
|
|
1554
|
-
if (!node || !desc)
|
|
1555
|
-
continue;
|
|
1556
|
-
const regDefaults = desc.inputDefaults ?? {};
|
|
1557
|
-
const dynDefaults = this.resolvedByNode.get(n.nodeId)?.inputDefaults ?? {};
|
|
1558
|
-
const graphDefaults = n.initialInputs ?? {};
|
|
1559
|
-
const merged = {
|
|
1560
|
-
...regDefaults,
|
|
1561
|
-
...dynDefaults,
|
|
1562
|
-
...graphDefaults,
|
|
1563
|
-
};
|
|
1564
|
-
const inboundSet = nextInbound.get(n.nodeId) ?? new Set();
|
|
1565
|
-
for (const [handle, value] of Object.entries(merged)) {
|
|
1566
|
-
if (value === undefined)
|
|
1567
|
-
continue;
|
|
1568
|
-
if (inboundSet.has(handle))
|
|
1569
|
-
continue;
|
|
1570
|
-
if (node.inputs[handle] !== undefined)
|
|
1571
|
-
continue;
|
|
1572
|
-
node.inputs[handle] =
|
|
1573
|
-
typeof structuredClone === "function"
|
|
1574
|
-
? structuredClone(value)
|
|
1575
|
-
: JSON.parse(JSON.stringify(value));
|
|
1576
|
-
}
|
|
1577
|
-
}
|
|
1578
1559
|
// Prune array bucket contributions for edges that no longer exist
|
|
1579
1560
|
const validPerTarget = new Map();
|
|
1580
1561
|
for (const ed of this.edges) {
|
|
@@ -1653,30 +1634,6 @@ class GraphRuntime {
|
|
|
1653
1634
|
return;
|
|
1654
1635
|
this.resolvedByNode.set(nodeId, next);
|
|
1655
1636
|
this.updateNodeHandles(nodeId, next, registry);
|
|
1656
|
-
// Seed defaults for newly introduced inputs that are not inbound
|
|
1657
|
-
const inbound = this.edges
|
|
1658
|
-
.filter((e) => e.target.nodeId === nodeId)
|
|
1659
|
-
.map((e) => e.target.handle);
|
|
1660
|
-
for (const [handle, value] of Object.entries(inputDefaults)) {
|
|
1661
|
-
if (value === undefined)
|
|
1662
|
-
continue;
|
|
1663
|
-
if (inbound.includes(handle))
|
|
1664
|
-
continue;
|
|
1665
|
-
if (node.inputs[handle] === undefined) {
|
|
1666
|
-
node.inputs[handle] =
|
|
1667
|
-
typeof structuredClone === "function"
|
|
1668
|
-
? structuredClone(value)
|
|
1669
|
-
: JSON.parse(JSON.stringify(value));
|
|
1670
|
-
// Emit input value event for seeded defaults
|
|
1671
|
-
this.emit("value", {
|
|
1672
|
-
nodeId,
|
|
1673
|
-
handle,
|
|
1674
|
-
value: node.inputs[handle],
|
|
1675
|
-
io: "input",
|
|
1676
|
-
runtimeTypeId: getTypedOutputTypeId(node.inputs[handle]),
|
|
1677
|
-
});
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
1637
|
// Notify graph updated for UI parity
|
|
1681
1638
|
this.emit("invalidate", { reason: "graph-updated" });
|
|
1682
1639
|
}
|