@bian-womp/spark-graph 0.2.21 → 0.2.22
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 +166 -14
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/core/categories.d.ts +2 -13
- package/lib/cjs/src/core/categories.d.ts.map +1 -1
- package/lib/cjs/src/core/types.d.ts +15 -1
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/examples/arrays.d.ts +5 -0
- package/lib/cjs/src/examples/arrays.d.ts.map +1 -0
- package/lib/cjs/src/examples/run.d.ts.map +1 -1
- package/lib/cjs/src/examples/shared.d.ts.map +1 -1
- package/lib/cjs/src/index.d.ts +2 -1
- package/lib/cjs/src/index.d.ts.map +1 -1
- package/lib/cjs/src/plugins/composite.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts +5 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +166 -15
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/core/categories.d.ts +2 -13
- package/lib/esm/src/core/categories.d.ts.map +1 -1
- package/lib/esm/src/core/types.d.ts +15 -1
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/examples/arrays.d.ts +5 -0
- package/lib/esm/src/examples/arrays.d.ts.map +1 -0
- package/lib/esm/src/examples/run.d.ts.map +1 -1
- package/lib/esm/src/examples/shared.d.ts.map +1 -1
- package/lib/esm/src/index.d.ts +2 -1
- package/lib/esm/src/index.d.ts.map +1 -1
- package/lib/esm/src/plugins/composite.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts +5 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -434,9 +434,10 @@ class GraphRuntime {
|
|
|
434
434
|
}
|
|
435
435
|
static create(def, registry, opts) {
|
|
436
436
|
const gr = new GraphRuntime();
|
|
437
|
+
gr.registry = registry;
|
|
437
438
|
gr.environment = opts?.environment ?? {};
|
|
438
439
|
// Precompute per-node resolved handles (use def-provided overrides; do not compute dynamically here)
|
|
439
|
-
gr.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
|
|
440
|
+
gr.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry, gr.environment);
|
|
440
441
|
// Instantiate nodes
|
|
441
442
|
for (const n of def.nodes) {
|
|
442
443
|
const desc = registry.nodes.get(n.typeId);
|
|
@@ -462,8 +463,10 @@ class GraphRuntime {
|
|
|
462
463
|
params: n.params,
|
|
463
464
|
policy: {
|
|
464
465
|
...cat.policy,
|
|
465
|
-
...
|
|
466
|
+
...desc.policy,
|
|
467
|
+
...n.params?.policy,
|
|
466
468
|
},
|
|
469
|
+
runSeq: 0,
|
|
467
470
|
activeControllers: new Set(),
|
|
468
471
|
queue: [],
|
|
469
472
|
stats: {
|
|
@@ -547,6 +550,9 @@ class GraphRuntime {
|
|
|
547
550
|
// Only schedule if all inbound inputs are present (or there are none)
|
|
548
551
|
if (anyChanged && this.allInboundHaveValue(nodeId))
|
|
549
552
|
this.scheduleInputsChanged(nodeId);
|
|
553
|
+
// Recompute dynamic handles for this node when its direct inputs change
|
|
554
|
+
if (anyChanged)
|
|
555
|
+
this.scheduleRecomputeHandles(nodeId);
|
|
550
556
|
}
|
|
551
557
|
}
|
|
552
558
|
getOutput(nodeId, output) {
|
|
@@ -619,12 +625,14 @@ class GraphRuntime {
|
|
|
619
625
|
now - node.lastScheduledAt < policy.debounceMs) {
|
|
620
626
|
// debounce: replace latest queued
|
|
621
627
|
node.queue.splice(0, node.queue.length);
|
|
622
|
-
|
|
628
|
+
node.runSeq += 1;
|
|
629
|
+
const rid = `${nodeId}:${node.runSeq}:${now}`;
|
|
623
630
|
node.queue.push({ runId: rid, inputs: { ...node.inputs } });
|
|
624
631
|
return;
|
|
625
632
|
}
|
|
626
633
|
node.lastScheduledAt = now;
|
|
627
|
-
|
|
634
|
+
node.runSeq += 1;
|
|
635
|
+
const rid = `${nodeId}:${node.runSeq}:${now}`;
|
|
628
636
|
node.latestRunId = rid;
|
|
629
637
|
const startRun = (runId, capturedInputs, onDone) => {
|
|
630
638
|
const controller = new AbortController();
|
|
@@ -847,6 +855,8 @@ class GraphRuntime {
|
|
|
847
855
|
io: "input",
|
|
848
856
|
runtimeTypeId: getTypedOutputTypeId(next),
|
|
849
857
|
});
|
|
858
|
+
// Recompute dynamic handles for the destination node on input change
|
|
859
|
+
this.scheduleRecomputeHandles(e.target.nodeId);
|
|
850
860
|
if (!this.paused && this.allInboundHaveValue(e.target.nodeId))
|
|
851
861
|
this.scheduleInputsChanged(e.target.nodeId);
|
|
852
862
|
}
|
|
@@ -904,7 +914,7 @@ class GraphRuntime {
|
|
|
904
914
|
}
|
|
905
915
|
}
|
|
906
916
|
// Helper: build map of resolved handles per node from def (prefer def.resolvedHandles, otherwise registry statics)
|
|
907
|
-
static computeResolvedHandleMap(def, registry) {
|
|
917
|
+
static computeResolvedHandleMap(def, registry, environment) {
|
|
908
918
|
const out = new Map();
|
|
909
919
|
for (const n of def.nodes) {
|
|
910
920
|
const desc = registry.nodes.get(n.typeId);
|
|
@@ -913,10 +923,36 @@ class GraphRuntime {
|
|
|
913
923
|
const overrideInputs = n.resolvedHandles?.inputs;
|
|
914
924
|
const overrideOutputs = n.resolvedHandles?.outputs;
|
|
915
925
|
const overrideDefaults = n.resolvedHandles?.inputDefaults;
|
|
916
|
-
//
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
926
|
+
// Resolve dynamic handles if available (initial pass: inputs may be undefined)
|
|
927
|
+
let dyn = {};
|
|
928
|
+
try {
|
|
929
|
+
if (typeof desc.resolveHandles === "function") {
|
|
930
|
+
dyn = desc.resolveHandles({
|
|
931
|
+
environment: environment || {},
|
|
932
|
+
params: n.params,
|
|
933
|
+
inputs: undefined,
|
|
934
|
+
});
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
catch {
|
|
938
|
+
// ignore dynamic resolution errors at this stage
|
|
939
|
+
}
|
|
940
|
+
// Merge base with dynamic and overrides (allow partial resolvedHandles)
|
|
941
|
+
const inputs = {
|
|
942
|
+
...desc.inputs,
|
|
943
|
+
...dyn.inputs,
|
|
944
|
+
...overrideInputs,
|
|
945
|
+
};
|
|
946
|
+
const outputs = {
|
|
947
|
+
...desc.outputs,
|
|
948
|
+
...dyn.outputs,
|
|
949
|
+
...overrideOutputs,
|
|
950
|
+
};
|
|
951
|
+
const inputDefaults = {
|
|
952
|
+
...desc.inputDefaults,
|
|
953
|
+
...dyn.inputDefaults,
|
|
954
|
+
...overrideDefaults,
|
|
955
|
+
};
|
|
920
956
|
out.set(n.nodeId, { inputs, outputs, inputDefaults });
|
|
921
957
|
}
|
|
922
958
|
return out;
|
|
@@ -1098,6 +1134,10 @@ class GraphRuntime {
|
|
|
1098
1134
|
}
|
|
1099
1135
|
setEnvironment(env) {
|
|
1100
1136
|
this.environment = { ...env };
|
|
1137
|
+
// Recompute dynamic handles for all nodes when environment changes
|
|
1138
|
+
for (const nodeId of this.nodes.keys()) {
|
|
1139
|
+
this.scheduleRecomputeHandles(nodeId);
|
|
1140
|
+
}
|
|
1101
1141
|
}
|
|
1102
1142
|
// Export a GraphDefinition reflecting the current runtime view
|
|
1103
1143
|
getGraphDef() {
|
|
@@ -1254,8 +1294,10 @@ class GraphRuntime {
|
|
|
1254
1294
|
params: n.params,
|
|
1255
1295
|
policy: {
|
|
1256
1296
|
...cat.policy,
|
|
1257
|
-
...
|
|
1297
|
+
...desc.policy,
|
|
1298
|
+
...n.params?.policy,
|
|
1258
1299
|
},
|
|
1300
|
+
runSeq: 0,
|
|
1259
1301
|
activeControllers: new Set(),
|
|
1260
1302
|
queue: [],
|
|
1261
1303
|
stats: {
|
|
@@ -1314,8 +1356,8 @@ class GraphRuntime {
|
|
|
1314
1356
|
tmap.set(e.source.handle, tset);
|
|
1315
1357
|
prevOutTargets.set(e.source.nodeId, tmap);
|
|
1316
1358
|
}
|
|
1317
|
-
// Precompute per-node resolved handles for updated graph
|
|
1318
|
-
this.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry);
|
|
1359
|
+
// Precompute per-node resolved handles for updated graph (include dynamic)
|
|
1360
|
+
this.resolvedByNode = GraphRuntime.computeResolvedHandleMap(def, registry, this.environment);
|
|
1319
1361
|
// Rebuild edges mapping with coercions
|
|
1320
1362
|
this.edges = GraphRuntime.buildEdges(def, registry, this.resolvedByNode);
|
|
1321
1363
|
// Build new inbound map
|
|
@@ -1475,6 +1517,83 @@ class GraphRuntime {
|
|
|
1475
1517
|
this.arrayInputBuckets.delete(nodeId);
|
|
1476
1518
|
}
|
|
1477
1519
|
}
|
|
1520
|
+
// Schedule a recomputation of dynamic handles for a node (async to avoid mutating during propagation)
|
|
1521
|
+
scheduleRecomputeHandles(nodeId) {
|
|
1522
|
+
// If no registry or node not found, skip
|
|
1523
|
+
if (!this.registry)
|
|
1524
|
+
return;
|
|
1525
|
+
const node = this.nodes.get(nodeId);
|
|
1526
|
+
if (!node)
|
|
1527
|
+
return;
|
|
1528
|
+
setTimeout(() => {
|
|
1529
|
+
try {
|
|
1530
|
+
this.recomputeHandlesForNode(nodeId);
|
|
1531
|
+
}
|
|
1532
|
+
catch {
|
|
1533
|
+
// ignore recompute errors
|
|
1534
|
+
}
|
|
1535
|
+
}, 0);
|
|
1536
|
+
}
|
|
1537
|
+
// Recompute dynamic handles for a single node using current inputs/environment
|
|
1538
|
+
recomputeHandlesForNode(nodeId) {
|
|
1539
|
+
const registry = this.registry;
|
|
1540
|
+
const node = this.nodes.get(nodeId);
|
|
1541
|
+
if (!node)
|
|
1542
|
+
return;
|
|
1543
|
+
const desc = registry.nodes.get(node.typeId);
|
|
1544
|
+
if (!desc)
|
|
1545
|
+
return;
|
|
1546
|
+
const resolveHandles = desc.resolveHandles;
|
|
1547
|
+
if (typeof resolveHandles !== "function")
|
|
1548
|
+
return;
|
|
1549
|
+
let r;
|
|
1550
|
+
try {
|
|
1551
|
+
r = resolveHandles({
|
|
1552
|
+
environment: this.environment || {},
|
|
1553
|
+
params: node.params,
|
|
1554
|
+
inputs: node.inputs || {},
|
|
1555
|
+
});
|
|
1556
|
+
}
|
|
1557
|
+
catch {
|
|
1558
|
+
return;
|
|
1559
|
+
}
|
|
1560
|
+
const inputs = { ...desc.inputs, ...r?.inputs };
|
|
1561
|
+
const outputs = { ...desc.outputs, ...r?.outputs };
|
|
1562
|
+
const inputDefaults = { ...desc.inputDefaults, ...r?.inputDefaults };
|
|
1563
|
+
const next = { inputs, outputs, inputDefaults };
|
|
1564
|
+
const before = this.resolvedByNode.get(nodeId);
|
|
1565
|
+
// Compare shallow-structurally via JSON
|
|
1566
|
+
if (JSON.stringify(before) === JSON.stringify(next))
|
|
1567
|
+
return;
|
|
1568
|
+
this.resolvedByNode.set(nodeId, next);
|
|
1569
|
+
this.updateNodeHandles(nodeId, next, registry);
|
|
1570
|
+
// Seed defaults for newly introduced inputs that are not inbound
|
|
1571
|
+
const inbound = this.edges
|
|
1572
|
+
.filter((e) => e.target.nodeId === nodeId)
|
|
1573
|
+
.map((e) => e.target.handle);
|
|
1574
|
+
for (const [handle, value] of Object.entries(inputDefaults)) {
|
|
1575
|
+
if (value === undefined)
|
|
1576
|
+
continue;
|
|
1577
|
+
if (inbound.includes(handle))
|
|
1578
|
+
continue;
|
|
1579
|
+
if (node.inputs[handle] === undefined) {
|
|
1580
|
+
node.inputs[handle] =
|
|
1581
|
+
typeof structuredClone === "function"
|
|
1582
|
+
? structuredClone(value)
|
|
1583
|
+
: JSON.parse(JSON.stringify(value));
|
|
1584
|
+
// Emit input value event for seeded defaults
|
|
1585
|
+
this.emit("value", {
|
|
1586
|
+
nodeId,
|
|
1587
|
+
handle,
|
|
1588
|
+
value: node.inputs[handle],
|
|
1589
|
+
io: "input",
|
|
1590
|
+
runtimeTypeId: getTypedOutputTypeId(node.inputs[handle]),
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
// Notify graph updated for UI parity
|
|
1595
|
+
this.emit("invalidate", { reason: "graph-updated" });
|
|
1596
|
+
}
|
|
1478
1597
|
}
|
|
1479
1598
|
|
|
1480
1599
|
class GraphBuilder {
|
|
@@ -1950,7 +2069,7 @@ const ComputeCategory = {
|
|
|
1950
2069
|
}
|
|
1951
2070
|
},
|
|
1952
2071
|
}),
|
|
1953
|
-
policy: {
|
|
2072
|
+
policy: { asyncConcurrency: "switch" },
|
|
1954
2073
|
};
|
|
1955
2074
|
|
|
1956
2075
|
const CompositeCategory = (registry) => ({
|
|
@@ -2006,7 +2125,6 @@ const CompositeCategory = (registry) => ({
|
|
|
2006
2125
|
},
|
|
2007
2126
|
};
|
|
2008
2127
|
},
|
|
2009
|
-
policy: { mode: "hybrid" },
|
|
2010
2128
|
});
|
|
2011
2129
|
|
|
2012
2130
|
// Helpers
|
|
@@ -3100,6 +3218,39 @@ function registerProgressNodes(registry) {
|
|
|
3100
3218
|
});
|
|
3101
3219
|
}
|
|
3102
3220
|
|
|
3221
|
+
function installLogging(engine) {
|
|
3222
|
+
engine.on("value", (e) => {
|
|
3223
|
+
const t = e.runtimeTypeId ? ` <${e.runtimeTypeId}>` : "";
|
|
3224
|
+
console.log(`[value:${e.io}]`, `${e.nodeId}.${e.handle}`, e.value, t);
|
|
3225
|
+
});
|
|
3226
|
+
engine.on("stats", (s) => {
|
|
3227
|
+
if (s.kind === "node-progress") {
|
|
3228
|
+
const pct = Math.round((s.progress ?? 0) * 100);
|
|
3229
|
+
console.log(`[progress] ${s.runId || s.nodeId}: ${pct}%`);
|
|
3230
|
+
}
|
|
3231
|
+
else if (s.kind === "node-done") {
|
|
3232
|
+
console.log(`[done] ${s.runId || s.nodeId} in ${s.durationMs ?? 0}ms`);
|
|
3233
|
+
}
|
|
3234
|
+
else if (s.kind === "node-start") {
|
|
3235
|
+
console.log(`[start] ${s.runId || s.nodeId}`);
|
|
3236
|
+
}
|
|
3237
|
+
else if (s.kind === "edge-start") {
|
|
3238
|
+
console.log(`[edge] ${s.source.nodeId}.${s.source.handle} -> ${s.target.nodeId}.${s.target.handle}`);
|
|
3239
|
+
}
|
|
3240
|
+
else if (s.kind === "edge-done") {
|
|
3241
|
+
console.log(`[edge] ${s.source.nodeId}.${s.source.handle} -> ${s.target.nodeId}.${s.target.handle} in ${s.durationMs ?? 0}ms`);
|
|
3242
|
+
}
|
|
3243
|
+
});
|
|
3244
|
+
engine.on("error", (e) => {
|
|
3245
|
+
if (e.kind === "node-run") {
|
|
3246
|
+
console.warn(`[error] ${e.runId || e.nodeId}`, e.err?.message ?? e.err);
|
|
3247
|
+
}
|
|
3248
|
+
else if (e.kind === "edge-convert") {
|
|
3249
|
+
console.warn(`[error] ${e.edgeId} ${e.source.nodeId}.${e.source.handle} -> ${e.target.nodeId}.${e.target.handle}`, e.err?.message ?? e.err);
|
|
3250
|
+
}
|
|
3251
|
+
});
|
|
3252
|
+
}
|
|
3253
|
+
|
|
3103
3254
|
function makeBasicGraphDefinition() {
|
|
3104
3255
|
return {
|
|
3105
3256
|
nodes: [
|
|
@@ -3303,6 +3454,7 @@ exports.createValidationGraphRegistry = createValidationGraphRegistry;
|
|
|
3303
3454
|
exports.getInputTypeId = getInputTypeId;
|
|
3304
3455
|
exports.getTypedOutputTypeId = getTypedOutputTypeId;
|
|
3305
3456
|
exports.getTypedOutputValue = getTypedOutputValue;
|
|
3457
|
+
exports.installLogging = installLogging;
|
|
3306
3458
|
exports.isInputPrivate = isInputPrivate;
|
|
3307
3459
|
exports.isTypedOutput = isTypedOutput;
|
|
3308
3460
|
exports.registerDelayNode = registerDelayNode;
|