@bian-womp/spark-graph 0.2.7 → 0.2.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 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,
@@ -963,6 +964,15 @@ class GraphRuntime {
963
964
  node.stats.progress = Math.max(0, Math.min(1, Number(p) || 0));
964
965
  },
965
966
  };
967
+ // Built-in support: invalidate event reruns or re-emits without per-node wiring
968
+ if (event &&
969
+ typeof event === "object" &&
970
+ event.type === "invalidate") {
971
+ if (this.allInboundHaveValue(nodeId))
972
+ this.scheduleInputsChanged(nodeId);
973
+ else
974
+ this.invalidateDownstream(nodeId);
975
+ }
966
976
  node.runtime.onExternalEvent?.(event, ctx);
967
977
  }
968
978
  dispose() {
@@ -1131,12 +1141,14 @@ class GraphRuntime {
1131
1141
  set.add(e.target.handle);
1132
1142
  prevInbound.set(e.target.nodeId, set);
1133
1143
  }
1134
- // Capture previous outgoing map before rebuilding edges
1135
- const prevOutgoing = new Map();
1144
+ // Capture previous per-handle target sets before rebuilding edges
1145
+ const prevOutTargets = new Map();
1136
1146
  for (const e of this.edges) {
1137
- const set = prevOutgoing.get(e.source.nodeId) ?? new Set();
1138
- set.add(e.source.handle);
1139
- prevOutgoing.set(e.source.nodeId, set);
1147
+ const tmap = prevOutTargets.get(e.source.nodeId) ?? new Map();
1148
+ const tset = tmap.get(e.source.handle) ?? new Set();
1149
+ tset.add(`${e.target.nodeId}.${e.target.handle}`);
1150
+ tmap.set(e.source.handle, tset);
1151
+ prevOutTargets.set(e.source.nodeId, tmap);
1140
1152
  }
1141
1153
  // Rebuild edges mapping with coercions
1142
1154
  this.edges = def.edges.map((e) => {
@@ -1227,14 +1239,16 @@ class GraphRuntime {
1227
1239
  this.scheduleInputsChanged(nodeId);
1228
1240
  }
1229
1241
  }
1230
- // Re-emit outputs only for sources whose outgoing edge set changed
1231
- const nextOutgoing = new Map();
1242
+ // Re-emit outputs when per-handle target sets change (precise and simple)
1243
+ const nextOutTargets = new Map();
1232
1244
  for (const e of this.edges) {
1233
- const set = nextOutgoing.get(e.source.nodeId) ?? new Set();
1234
- set.add(e.source.handle);
1235
- nextOutgoing.set(e.source.nodeId, set);
1245
+ const tmap = nextOutTargets.get(e.source.nodeId) ?? new Map();
1246
+ const tset = tmap.get(e.source.handle) ?? new Set();
1247
+ tset.add(`${e.target.nodeId}.${e.target.handle}`);
1248
+ tmap.set(e.source.handle, tset);
1249
+ nextOutTargets.set(e.source.nodeId, tmap);
1236
1250
  }
1237
- const setsEqual = (a, b) => {
1251
+ const setsEqualStr = (a, b) => {
1238
1252
  if (!a && !b)
1239
1253
  return true;
1240
1254
  if (!a || !b)
@@ -1246,15 +1260,28 @@ class GraphRuntime {
1246
1260
  return false;
1247
1261
  return true;
1248
1262
  };
1249
- const reemitCandidates = new Set([
1250
- ...Array.from(prevOutgoing.keys()),
1251
- ...Array.from(nextOutgoing.keys()),
1263
+ const nodesToCheck = new Set([
1264
+ ...Array.from(prevOutTargets.keys()),
1265
+ ...Array.from(nextOutTargets.keys()),
1252
1266
  ]);
1253
- for (const nodeId of reemitCandidates) {
1254
- const prev = prevOutgoing.get(nodeId);
1255
- const next = nextOutgoing.get(nodeId);
1256
- if (!setsEqual(prev, next))
1257
- this.reemitNodeOutputs(nodeId);
1267
+ for (const nodeId of nodesToCheck) {
1268
+ const pmap = prevOutTargets.get(nodeId) ?? new Map();
1269
+ const nmap = nextOutTargets.get(nodeId) ?? new Map();
1270
+ const handles = new Set([
1271
+ ...Array.from(pmap.keys()),
1272
+ ...Array.from(nmap.keys()),
1273
+ ]);
1274
+ for (const handle of handles) {
1275
+ const pset = pmap.get(handle) ?? new Set();
1276
+ const nset = nmap.get(handle) ?? new Set();
1277
+ if (!setsEqualStr(pset, nset)) {
1278
+ const val = this.getOutput(nodeId, handle);
1279
+ if (val !== undefined)
1280
+ this.propagate(nodeId, handle, val);
1281
+ else if (this.allInboundHaveValue(nodeId))
1282
+ this.scheduleInputsChanged(nodeId);
1283
+ }
1284
+ }
1258
1285
  }
1259
1286
  }
1260
1287
  }
@@ -2002,25 +2029,30 @@ function setupBasicGraphRegistry() {
2002
2029
  registry.registerType({
2003
2030
  id: "base.float",
2004
2031
  validate: (v) => typeof v === "number" && !Number.isNaN(v),
2032
+ bakeTarget: { nodeTypeId: "base.input.number", inputHandle: "Value" },
2005
2033
  }, { withArray: true, arrayPickFirstDefined: true });
2006
2034
  registry.registerType({
2007
2035
  id: "base.bool",
2008
2036
  validate: (v) => typeof v === "boolean",
2037
+ bakeTarget: { nodeTypeId: "base.input.bool", inputHandle: "Value" },
2009
2038
  }, { withArray: true, arrayPickFirstDefined: true });
2010
2039
  registry.registerType({
2011
2040
  id: "base.string",
2012
2041
  validate: (v) => typeof v === "string",
2042
+ bakeTarget: { nodeTypeId: "base.input.string", inputHandle: "Value" },
2013
2043
  }, { withArray: true, arrayPickFirstDefined: true });
2014
2044
  // Generic object value (JSON-compatible; object/array/primitive/null)
2015
2045
  registry.registerType({
2016
2046
  id: "base.object",
2017
2047
  validate: (v) => isJson(v),
2048
+ bakeTarget: { nodeTypeId: "base.input.object", inputHandle: "Value" },
2018
2049
  }, { withArray: false });
2019
2050
  registry.registerType({
2020
2051
  id: "base.vec3",
2021
2052
  validate: (v) => Array.isArray(v) &&
2022
2053
  v.length === 3 &&
2023
2054
  v.every((x) => typeof x === "number"),
2055
+ bakeTarget: { nodeTypeId: "base.input.vec3", inputHandle: "Value" },
2024
2056
  }, { withArray: true, arrayPickFirstDefined: true });
2025
2057
  // float -> vec3 : map x to [x,0,0]
2026
2058
  registry.registerCoercion("base.float", "base.vec3", (v) => {
@@ -2162,6 +2194,20 @@ function setupBasicGraphRegistry() {
2162
2194
  outputs: { Result: "base.bool" },
2163
2195
  impl: (ins) => ({ Result: Boolean(ins.Value) }),
2164
2196
  });
2197
+ registry.registerNode({
2198
+ id: "base.input.object",
2199
+ categoryId: "compute",
2200
+ inputs: { Value: "base.object" },
2201
+ outputs: { Result: "base.object" },
2202
+ impl: (ins) => ({ Result: ins.Value }),
2203
+ });
2204
+ registry.registerNode({
2205
+ id: "base.input.vec3",
2206
+ categoryId: "compute",
2207
+ inputs: { Value: "base.vec3" },
2208
+ outputs: { Result: "base.vec3" },
2209
+ impl: (ins) => ({ Result: ins.Value }),
2210
+ });
2165
2211
  // JSON parser node: base.stringToObject
2166
2212
  registry.registerNode({
2167
2213
  id: "base.string.toObject",