@bian-womp/spark-graph 0.2.8 → 0.2.10
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 +95 -26
- package/lib/cjs/index.cjs.map +1 -1
- package/lib/cjs/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/cjs/src/builder/Registry.d.ts +4 -0
- package/lib/cjs/src/builder/Registry.d.ts.map +1 -1
- package/lib/cjs/src/core/types.d.ts +4 -0
- package/lib/cjs/src/core/types.d.ts.map +1 -1
- package/lib/cjs/src/misc/base.d.ts.map +1 -1
- package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/lib/esm/index.js +95 -26
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/src/builder/GraphBuilder.d.ts.map +1 -1
- package/lib/esm/src/builder/Registry.d.ts +4 -0
- package/lib/esm/src/builder/Registry.d.ts.map +1 -1
- package/lib/esm/src/core/types.d.ts +4 -0
- package/lib/esm/src/core/types.d.ts.map +1 -1
- package/lib/esm/src/misc/base.d.ts.map +1 -1
- package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
- package/package.json +2 -2
package/lib/cjs/index.cjs
CHANGED
|
@@ -342,7 +342,7 @@ class Registry {
|
|
|
342
342
|
}
|
|
343
343
|
// Enum support
|
|
344
344
|
registerEnum(desc) {
|
|
345
|
-
const { id, displayName, options, opts } = desc;
|
|
345
|
+
const { id, displayName, options, opts, bakeTarget } = desc;
|
|
346
346
|
const labelType = opts?.labelType ?? "base.string";
|
|
347
347
|
const valueType = opts?.valueType ?? "base.float";
|
|
348
348
|
const valueToLabel = new Map();
|
|
@@ -361,6 +361,7 @@ class Registry {
|
|
|
361
361
|
id,
|
|
362
362
|
displayName,
|
|
363
363
|
validate: (v) => typeof v === "number" && valueToLabel.has(Number(v)),
|
|
364
|
+
bakeTarget,
|
|
364
365
|
}, opts);
|
|
365
366
|
this.registerSerializer(id, {
|
|
366
367
|
serialize: (v) => v,
|
|
@@ -503,6 +504,7 @@ class GraphRuntime {
|
|
|
503
504
|
srcUnionTypes: Array.isArray(srcDeclared)
|
|
504
505
|
? [...srcDeclared]
|
|
505
506
|
: undefined,
|
|
507
|
+
dstDeclared,
|
|
506
508
|
stats: { runs: 0, inFlight: false, progress: 0 },
|
|
507
509
|
};
|
|
508
510
|
});
|
|
@@ -838,17 +840,26 @@ class GraphRuntime {
|
|
|
838
840
|
const dstNode = this.nodes.get(e.target.nodeId);
|
|
839
841
|
if (!dstNode)
|
|
840
842
|
return;
|
|
843
|
+
const dstIsArray = typeof e.dstDeclared === "string" && e.dstDeclared.endsWith("[]");
|
|
844
|
+
let next = v;
|
|
845
|
+
// If target input is an array type, append incoming values instead of last-write wins
|
|
846
|
+
if (dstIsArray) {
|
|
847
|
+
const toArray = (x) => Array.isArray(x) ? x : x === undefined ? [] : [x];
|
|
848
|
+
const prev = dstNode.inputs[e.target.handle];
|
|
849
|
+
const merged = [...toArray(prev), ...toArray(v)];
|
|
850
|
+
next = merged;
|
|
851
|
+
}
|
|
841
852
|
const prev = dstNode.inputs[e.target.handle];
|
|
842
|
-
const same = this.valuesEqual(prev,
|
|
853
|
+
const same = this.valuesEqual(prev, next);
|
|
843
854
|
if (!same) {
|
|
844
|
-
dstNode.inputs[e.target.handle] =
|
|
855
|
+
dstNode.inputs[e.target.handle] = next;
|
|
845
856
|
// Emit value event for input updates
|
|
846
857
|
this.emit("value", {
|
|
847
858
|
nodeId: e.target.nodeId,
|
|
848
859
|
handle: e.target.handle,
|
|
849
|
-
value:
|
|
860
|
+
value: next,
|
|
850
861
|
io: "input",
|
|
851
|
-
runtimeTypeId: getTypedOutputTypeId(
|
|
862
|
+
runtimeTypeId: getTypedOutputTypeId(next),
|
|
852
863
|
});
|
|
853
864
|
if (!this.paused && this.allInboundHaveValue(e.target.nodeId))
|
|
854
865
|
this.scheduleInputsChanged(e.target.nodeId);
|
|
@@ -963,6 +974,15 @@ class GraphRuntime {
|
|
|
963
974
|
node.stats.progress = Math.max(0, Math.min(1, Number(p) || 0));
|
|
964
975
|
},
|
|
965
976
|
};
|
|
977
|
+
// Built-in support: invalidate event reruns or re-emits without per-node wiring
|
|
978
|
+
if (event &&
|
|
979
|
+
typeof event === "object" &&
|
|
980
|
+
event.type === "invalidate") {
|
|
981
|
+
if (this.allInboundHaveValue(nodeId))
|
|
982
|
+
this.scheduleInputsChanged(nodeId);
|
|
983
|
+
else
|
|
984
|
+
this.invalidateDownstream(nodeId);
|
|
985
|
+
}
|
|
966
986
|
node.runtime.onExternalEvent?.(event, ctx);
|
|
967
987
|
}
|
|
968
988
|
dispose() {
|
|
@@ -1131,12 +1151,14 @@ class GraphRuntime {
|
|
|
1131
1151
|
set.add(e.target.handle);
|
|
1132
1152
|
prevInbound.set(e.target.nodeId, set);
|
|
1133
1153
|
}
|
|
1134
|
-
// Capture previous
|
|
1135
|
-
const
|
|
1154
|
+
// Capture previous per-handle target sets before rebuilding edges
|
|
1155
|
+
const prevOutTargets = new Map();
|
|
1136
1156
|
for (const e of this.edges) {
|
|
1137
|
-
const
|
|
1138
|
-
|
|
1139
|
-
|
|
1157
|
+
const tmap = prevOutTargets.get(e.source.nodeId) ?? new Map();
|
|
1158
|
+
const tset = tmap.get(e.source.handle) ?? new Set();
|
|
1159
|
+
tset.add(`${e.target.nodeId}.${e.target.handle}`);
|
|
1160
|
+
tmap.set(e.source.handle, tset);
|
|
1161
|
+
prevOutTargets.set(e.source.nodeId, tmap);
|
|
1140
1162
|
}
|
|
1141
1163
|
// Rebuild edges mapping with coercions
|
|
1142
1164
|
this.edges = def.edges.map((e) => {
|
|
@@ -1170,6 +1192,7 @@ class GraphRuntime {
|
|
|
1170
1192
|
typeId: effectiveTypeId ?? "untyped",
|
|
1171
1193
|
convert,
|
|
1172
1194
|
convertAsync,
|
|
1195
|
+
dstDeclared,
|
|
1173
1196
|
stats: { runs: 0, inFlight: false, progress: 0 },
|
|
1174
1197
|
};
|
|
1175
1198
|
});
|
|
@@ -1227,14 +1250,16 @@ class GraphRuntime {
|
|
|
1227
1250
|
this.scheduleInputsChanged(nodeId);
|
|
1228
1251
|
}
|
|
1229
1252
|
}
|
|
1230
|
-
// Re-emit outputs
|
|
1231
|
-
const
|
|
1253
|
+
// Re-emit outputs when per-handle target sets change (precise and simple)
|
|
1254
|
+
const nextOutTargets = new Map();
|
|
1232
1255
|
for (const e of this.edges) {
|
|
1233
|
-
const
|
|
1234
|
-
|
|
1235
|
-
|
|
1256
|
+
const tmap = nextOutTargets.get(e.source.nodeId) ?? new Map();
|
|
1257
|
+
const tset = tmap.get(e.source.handle) ?? new Set();
|
|
1258
|
+
tset.add(`${e.target.nodeId}.${e.target.handle}`);
|
|
1259
|
+
tmap.set(e.source.handle, tset);
|
|
1260
|
+
nextOutTargets.set(e.source.nodeId, tmap);
|
|
1236
1261
|
}
|
|
1237
|
-
const
|
|
1262
|
+
const setsEqualStr = (a, b) => {
|
|
1238
1263
|
if (!a && !b)
|
|
1239
1264
|
return true;
|
|
1240
1265
|
if (!a || !b)
|
|
@@ -1246,15 +1271,28 @@ class GraphRuntime {
|
|
|
1246
1271
|
return false;
|
|
1247
1272
|
return true;
|
|
1248
1273
|
};
|
|
1249
|
-
const
|
|
1250
|
-
...Array.from(
|
|
1251
|
-
...Array.from(
|
|
1274
|
+
const nodesToCheck = new Set([
|
|
1275
|
+
...Array.from(prevOutTargets.keys()),
|
|
1276
|
+
...Array.from(nextOutTargets.keys()),
|
|
1252
1277
|
]);
|
|
1253
|
-
for (const nodeId of
|
|
1254
|
-
const
|
|
1255
|
-
const
|
|
1256
|
-
|
|
1257
|
-
|
|
1278
|
+
for (const nodeId of nodesToCheck) {
|
|
1279
|
+
const pmap = prevOutTargets.get(nodeId) ?? new Map();
|
|
1280
|
+
const nmap = nextOutTargets.get(nodeId) ?? new Map();
|
|
1281
|
+
const handles = new Set([
|
|
1282
|
+
...Array.from(pmap.keys()),
|
|
1283
|
+
...Array.from(nmap.keys()),
|
|
1284
|
+
]);
|
|
1285
|
+
for (const handle of handles) {
|
|
1286
|
+
const pset = pmap.get(handle) ?? new Set();
|
|
1287
|
+
const nset = nmap.get(handle) ?? new Set();
|
|
1288
|
+
if (!setsEqualStr(pset, nset)) {
|
|
1289
|
+
const val = this.getOutput(nodeId, handle);
|
|
1290
|
+
if (val !== undefined)
|
|
1291
|
+
this.propagate(nodeId, handle, val);
|
|
1292
|
+
else if (this.allInboundHaveValue(nodeId))
|
|
1293
|
+
this.scheduleInputsChanged(nodeId);
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1258
1296
|
}
|
|
1259
1297
|
}
|
|
1260
1298
|
}
|
|
@@ -1300,6 +1338,8 @@ class GraphBuilder {
|
|
|
1300
1338
|
}
|
|
1301
1339
|
// edges validation: nodes exist, handles exist, type exists
|
|
1302
1340
|
const inboundCounts = new Map();
|
|
1341
|
+
// Track which inbound (nodeId::handle) are declared as array inputs, to allow multi-inbound without warning
|
|
1342
|
+
const inboundArrayOk = new Set();
|
|
1303
1343
|
for (const e of def.edges) {
|
|
1304
1344
|
if (edgeIds.has(e.id)) {
|
|
1305
1345
|
issues.push({
|
|
@@ -1340,7 +1380,7 @@ class GraphBuilder {
|
|
|
1340
1380
|
if (dstNode) {
|
|
1341
1381
|
const dstType = this.registry.nodes.get(dstNode.typeId);
|
|
1342
1382
|
if (dstType)
|
|
1343
|
-
_dstDeclared = dstType.inputs
|
|
1383
|
+
_dstDeclared = getInputTypeId(dstType.inputs, e.target.handle);
|
|
1344
1384
|
}
|
|
1345
1385
|
if (!effectiveTypeId) {
|
|
1346
1386
|
if (Array.isArray(_srcDeclared) && _dstDeclared) {
|
|
@@ -1487,9 +1527,19 @@ class GraphBuilder {
|
|
|
1487
1527
|
// Track multiple inbound edges targeting the same input handle
|
|
1488
1528
|
const inboundKey = `${e.target.nodeId}::${e.target.handle}`;
|
|
1489
1529
|
inboundCounts.set(inboundKey, (inboundCounts.get(inboundKey) ?? 0) + 1);
|
|
1530
|
+
// If the target input is declared as an array type, allow multi-inbound (runtime will append)
|
|
1531
|
+
if (dstNode) {
|
|
1532
|
+
const dstType = this.registry.nodes.get(dstNode.typeId);
|
|
1533
|
+
const declaredIn = dstType
|
|
1534
|
+
? getInputTypeId(dstType.inputs, e.target.handle)
|
|
1535
|
+
: undefined;
|
|
1536
|
+
if (typeof declaredIn === "string" && declaredIn.endsWith("[]")) {
|
|
1537
|
+
inboundArrayOk.add(inboundKey);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1490
1540
|
}
|
|
1491
1541
|
for (const [key, count] of inboundCounts) {
|
|
1492
|
-
if (count > 1) {
|
|
1542
|
+
if (count > 1 && !inboundArrayOk.has(key)) {
|
|
1493
1543
|
issues.push({
|
|
1494
1544
|
level: "warning",
|
|
1495
1545
|
code: "MULTI_INBOUND",
|
|
@@ -2002,25 +2052,30 @@ function setupBasicGraphRegistry() {
|
|
|
2002
2052
|
registry.registerType({
|
|
2003
2053
|
id: "base.float",
|
|
2004
2054
|
validate: (v) => typeof v === "number" && !Number.isNaN(v),
|
|
2055
|
+
bakeTarget: { nodeTypeId: "base.input.number", inputHandle: "Value" },
|
|
2005
2056
|
}, { withArray: true, arrayPickFirstDefined: true });
|
|
2006
2057
|
registry.registerType({
|
|
2007
2058
|
id: "base.bool",
|
|
2008
2059
|
validate: (v) => typeof v === "boolean",
|
|
2060
|
+
bakeTarget: { nodeTypeId: "base.input.bool", inputHandle: "Value" },
|
|
2009
2061
|
}, { withArray: true, arrayPickFirstDefined: true });
|
|
2010
2062
|
registry.registerType({
|
|
2011
2063
|
id: "base.string",
|
|
2012
2064
|
validate: (v) => typeof v === "string",
|
|
2065
|
+
bakeTarget: { nodeTypeId: "base.input.string", inputHandle: "Value" },
|
|
2013
2066
|
}, { withArray: true, arrayPickFirstDefined: true });
|
|
2014
2067
|
// Generic object value (JSON-compatible; object/array/primitive/null)
|
|
2015
2068
|
registry.registerType({
|
|
2016
2069
|
id: "base.object",
|
|
2017
2070
|
validate: (v) => isJson(v),
|
|
2071
|
+
bakeTarget: { nodeTypeId: "base.input.object", inputHandle: "Value" },
|
|
2018
2072
|
}, { withArray: false });
|
|
2019
2073
|
registry.registerType({
|
|
2020
2074
|
id: "base.vec3",
|
|
2021
2075
|
validate: (v) => Array.isArray(v) &&
|
|
2022
2076
|
v.length === 3 &&
|
|
2023
2077
|
v.every((x) => typeof x === "number"),
|
|
2078
|
+
bakeTarget: { nodeTypeId: "base.input.vec3", inputHandle: "Value" },
|
|
2024
2079
|
}, { withArray: true, arrayPickFirstDefined: true });
|
|
2025
2080
|
// float -> vec3 : map x to [x,0,0]
|
|
2026
2081
|
registry.registerCoercion("base.float", "base.vec3", (v) => {
|
|
@@ -2162,6 +2217,20 @@ function setupBasicGraphRegistry() {
|
|
|
2162
2217
|
outputs: { Result: "base.bool" },
|
|
2163
2218
|
impl: (ins) => ({ Result: Boolean(ins.Value) }),
|
|
2164
2219
|
});
|
|
2220
|
+
registry.registerNode({
|
|
2221
|
+
id: "base.input.object",
|
|
2222
|
+
categoryId: "compute",
|
|
2223
|
+
inputs: { Value: "base.object" },
|
|
2224
|
+
outputs: { Result: "base.object" },
|
|
2225
|
+
impl: (ins) => ({ Result: ins.Value }),
|
|
2226
|
+
});
|
|
2227
|
+
registry.registerNode({
|
|
2228
|
+
id: "base.input.vec3",
|
|
2229
|
+
categoryId: "compute",
|
|
2230
|
+
inputs: { Value: "base.vec3" },
|
|
2231
|
+
outputs: { Result: "base.vec3" },
|
|
2232
|
+
impl: (ins) => ({ Result: ins.Value }),
|
|
2233
|
+
});
|
|
2165
2234
|
// JSON parser node: base.stringToObject
|
|
2166
2235
|
registry.registerNode({
|
|
2167
2236
|
id: "base.string.toObject",
|