@bian-womp/spark-graph 0.1.20 → 0.1.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 CHANGED
@@ -1,5 +1,34 @@
1
1
  'use strict';
2
2
 
3
+ function typed(typeId, value) {
4
+ return { __spark_type: typeId, __spark_value: value };
5
+ }
6
+ function isTypedOutput(v) {
7
+ return (!!v &&
8
+ typeof v === "object" &&
9
+ Object.prototype.hasOwnProperty.call(v, "__spark_type"));
10
+ }
11
+ function getTypedOutputTypeId(v) {
12
+ if (isTypedOutput(v))
13
+ return String(v.__spark_type);
14
+ return undefined;
15
+ }
16
+ function getTypedOutputValue(v) {
17
+ if (isTypedOutput(v))
18
+ return v.__spark_value;
19
+ return v;
20
+ }
21
+ function getInputTypeId(inputs, handle) {
22
+ const v = inputs ? inputs[handle] : undefined;
23
+ if (!v)
24
+ return undefined;
25
+ return typeof v === "string" ? v : v.typeId;
26
+ }
27
+ function isInputPrivate(inputs, handle) {
28
+ const v = inputs ? inputs[handle] : undefined;
29
+ return !!(v && typeof v === "object" && v.private);
30
+ }
31
+
3
32
  class CategoryRegistry {
4
33
  constructor() {
5
34
  this.categories = new Map();
@@ -372,25 +401,6 @@ class Registry {
372
401
  }
373
402
  }
374
403
 
375
- function typed(typeId, value) {
376
- return { __spark_type: typeId, __spark_value: value };
377
- }
378
- function isTypedOutput(v) {
379
- return (!!v &&
380
- typeof v === "object" &&
381
- Object.prototype.hasOwnProperty.call(v, "__spark_type"));
382
- }
383
- function getInputTypeId(inputs, handle) {
384
- const v = inputs ? inputs[handle] : undefined;
385
- if (!v)
386
- return undefined;
387
- return typeof v === "string" ? v : v.typeId;
388
- }
389
- function isInputPrivate(inputs, handle) {
390
- const v = inputs ? inputs[handle] : undefined;
391
- return !!(v && typeof v === "object" && v.private);
392
- }
393
-
394
404
  class GraphRuntime {
395
405
  constructor() {
396
406
  this.nodes = new Map();
@@ -565,12 +575,12 @@ class GraphRuntime {
565
575
  });
566
576
  if (anyAsync) {
567
577
  convertAsync = async (v, signal) => {
568
- if (!isTypedOutput(v))
578
+ const typeId = getTypedOutputTypeId(v);
579
+ if (!typeId)
569
580
  throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
570
- const typeId = String(v.__spark_type);
571
581
  if (!srcTypes.includes(typeId))
572
582
  throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
573
- const payload = v.__spark_value;
583
+ const payload = typeId;
574
584
  const res = registry.resolveCoercion(typeId, dstDeclared);
575
585
  if (!res)
576
586
  return payload;
@@ -581,12 +591,12 @@ class GraphRuntime {
581
591
  }
582
592
  else {
583
593
  convert = (v) => {
584
- if (!isTypedOutput(v))
594
+ const typeId = getTypedOutputTypeId(v);
595
+ if (!typeId)
585
596
  throw new Error(`Typed output required for union source; allowed: ${srcTypes.join("|")}`);
586
- const typeId = String(v.__spark_type);
587
597
  if (!srcTypes.includes(typeId))
588
598
  throw new Error(`Invalid typed output ${typeId}; allowed: ${srcTypes.join("|")}`);
589
- const payload = v.__spark_value;
599
+ const payload = typeId;
590
600
  const res = registry.resolveCoercion(typeId, dstDeclared);
591
601
  if (!res)
592
602
  return payload;
@@ -777,9 +787,7 @@ class GraphRuntime {
777
787
  handle: srcHandle,
778
788
  value,
779
789
  io: "output",
780
- runtimeTypeId: isTypedOutput(value)
781
- ? String(value.__spark_type)
782
- : undefined,
790
+ runtimeTypeId: getTypedOutputTypeId(value),
783
791
  });
784
792
  // fan-out along all edges from this output
785
793
  const outEdges = this.edges.filter((e) => e.source.nodeId === srcNodeId && e.source.handle === srcHandle);
@@ -811,7 +819,7 @@ class GraphRuntime {
811
819
  handle: e.target.handle,
812
820
  value: v,
813
821
  io: "input",
814
- runtimeTypeId: isTypedOutput(v) ? String(v.__spark_type) : undefined,
822
+ runtimeTypeId: getTypedOutputTypeId(v),
815
823
  });
816
824
  if (!this.paused && this.allInboundHaveValue(e.target.nodeId))
817
825
  this.scheduleInputsChanged(e.target.nodeId);
@@ -1778,6 +1786,96 @@ const lcg = (seed) => {
1778
1786
  let s = seed >>> 0 || 1;
1779
1787
  return () => (s = (s * 1664525 + 1013904223) >>> 0) / 0xffffffff;
1780
1788
  };
1789
+ // JSON Pointer helpers (RFC 6901 subset)
1790
+ function jsonPointerGet(obj, pointer) {
1791
+ if (!pointer || pointer === "/")
1792
+ return obj;
1793
+ if (!pointer.startsWith("/"))
1794
+ return undefined;
1795
+ const parts = pointer
1796
+ .split("/")
1797
+ .slice(1)
1798
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
1799
+ let cur = obj;
1800
+ for (const key of parts) {
1801
+ if (cur === undefined || cur === null)
1802
+ return undefined;
1803
+ cur = cur[key];
1804
+ }
1805
+ return cur;
1806
+ }
1807
+ function jsonPointerSet(obj, pointer, value) {
1808
+ if (!pointer || pointer === "/")
1809
+ return value;
1810
+ if (!pointer.startsWith("/"))
1811
+ return obj;
1812
+ const parts = pointer
1813
+ .split("/")
1814
+ .slice(1)
1815
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
1816
+ const root = structuredClone(obj);
1817
+ let cur = root;
1818
+ for (let i = 0; i < parts.length; i++) {
1819
+ const key = parts[i];
1820
+ if (i === parts.length - 1) {
1821
+ if (Array.isArray(cur) && key === "-")
1822
+ cur.push(value);
1823
+ else
1824
+ cur[key] = value;
1825
+ }
1826
+ else {
1827
+ const next = cur[key];
1828
+ if (next === undefined || next === null) {
1829
+ // create container heuristically
1830
+ const nextKey = parts[i + 1];
1831
+ cur[key] =
1832
+ typeof nextKey === "string" && /^[0-9]+$/.test(nextKey) ? [] : {};
1833
+ }
1834
+ cur = cur[key];
1835
+ }
1836
+ }
1837
+ return root;
1838
+ }
1839
+ function jsonPointerRemove(obj, pointer) {
1840
+ if (!pointer || pointer === "/")
1841
+ return undefined;
1842
+ if (!pointer.startsWith("/"))
1843
+ return obj;
1844
+ const parts = pointer
1845
+ .split("/")
1846
+ .slice(1)
1847
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
1848
+ const root = structuredClone(obj);
1849
+ let cur = root;
1850
+ for (let i = 0; i < parts.length - 1; i++) {
1851
+ const key = parts[i];
1852
+ if (cur === undefined || cur === null)
1853
+ return root;
1854
+ cur = cur[key];
1855
+ }
1856
+ const last = parts[parts.length - 1];
1857
+ if (Array.isArray(cur)) {
1858
+ const idx = last === "-" ? cur.length - 1 : Number(last);
1859
+ if (Number.isFinite(idx))
1860
+ cur.splice(idx, 1);
1861
+ }
1862
+ else if (cur && typeof cur === "object") {
1863
+ delete cur[last];
1864
+ }
1865
+ return root;
1866
+ }
1867
+ function deepMerge(a, b) {
1868
+ if (Array.isArray(a) && Array.isArray(b))
1869
+ return [...a, ...b];
1870
+ if (isPlainObject(a) && isPlainObject(b)) {
1871
+ const out = { ...a };
1872
+ for (const [k, v] of Object.entries(b)) {
1873
+ out[k] = k in out ? deepMerge(out[k], v) : v;
1874
+ }
1875
+ return out;
1876
+ }
1877
+ return b;
1878
+ }
1781
1879
  // JSON helpers
1782
1880
  const isPlainObject = (v) => {
1783
1881
  if (v === null || typeof v !== "object")
@@ -1816,7 +1914,7 @@ function setupBasicGraphRegistry() {
1816
1914
  registry.registerType({
1817
1915
  id: "base.object",
1818
1916
  validate: (v) => isJson(v),
1819
- }, { withArray: true, arrayPickFirstDefined: true });
1917
+ }, { withArray: false });
1820
1918
  registry.registerType({
1821
1919
  id: "base.vec3",
1822
1920
  validate: (v) => Array.isArray(v) &&
@@ -2209,6 +2307,195 @@ function setupBasicGraphRegistry() {
2209
2307
  },
2210
2308
  },
2211
2309
  });
2310
+ // ------------------------- JSON/object utilities -------------------------
2311
+ registry.registerNode({
2312
+ id: "base.objectGet",
2313
+ categoryId: "compute",
2314
+ inputs: { Object: "base.object", Pointers: "base.string[]" },
2315
+ outputs: { Values: "base.object" },
2316
+ impl: (ins) => {
2317
+ const obj = ins.Object;
2318
+ const pointers = (ins.Pointers || []).map(String);
2319
+ const out = {};
2320
+ for (const p of pointers)
2321
+ out[p] = jsonPointerGet(obj, p);
2322
+ return { Values: out };
2323
+ },
2324
+ });
2325
+ registry.registerNode({
2326
+ id: "base.objectSet",
2327
+ categoryId: "compute",
2328
+ inputs: {
2329
+ Object: "base.object",
2330
+ Pointers: "base.string[]",
2331
+ NewValues: "base.string[]",
2332
+ },
2333
+ outputs: { Result: "base.object" },
2334
+ impl: (ins) => {
2335
+ const pointers = (ins.Pointers || []).map(String);
2336
+ const values = (ins.NewValues || []).map(String);
2337
+ let cur = structuredClone(ins.Object);
2338
+ for (let i = 0; i < pointers.length; i++) {
2339
+ const p = pointers[i];
2340
+ const raw = values[i];
2341
+ let val = raw;
2342
+ if (typeof raw === "string") {
2343
+ try {
2344
+ val = JSON.parse(raw);
2345
+ }
2346
+ catch {
2347
+ /* keep as string */
2348
+ }
2349
+ }
2350
+ cur = jsonPointerSet(cur, p, val);
2351
+ }
2352
+ return { Result: cur };
2353
+ },
2354
+ });
2355
+ registry.registerNode({
2356
+ id: "base.objectRemove",
2357
+ categoryId: "compute",
2358
+ inputs: { Object: "base.object", Pointers: "base.string[]" },
2359
+ outputs: { Result: "base.object" },
2360
+ impl: (ins) => {
2361
+ const pointers = (ins.Pointers || []).map(String);
2362
+ let cur = structuredClone(ins.Object);
2363
+ for (const p of pointers)
2364
+ cur = jsonPointerRemove(cur, p);
2365
+ return { Result: cur };
2366
+ },
2367
+ });
2368
+ registry.registerNode({
2369
+ id: "base.objectMerge",
2370
+ categoryId: "compute",
2371
+ inputs: { A: "base.object", B: "base.object" },
2372
+ outputs: { Result: "base.object" },
2373
+ impl: (ins) => ({
2374
+ Result: deepMerge(ins.A, ins.B),
2375
+ }),
2376
+ });
2377
+ registry.registerNode({
2378
+ id: "base.objectKeys",
2379
+ categoryId: "compute",
2380
+ inputs: { Object: "base.object" },
2381
+ outputs: { Keys: "base.string[]" },
2382
+ impl: (ins) => {
2383
+ const obj = ins.Object;
2384
+ const keys = isPlainObject(obj)
2385
+ ? Object.keys(obj)
2386
+ : Array.isArray(obj)
2387
+ ? Object.keys(obj)
2388
+ : [];
2389
+ return { Keys: keys };
2390
+ },
2391
+ });
2392
+ registry.registerNode({
2393
+ id: "base.objectValues",
2394
+ categoryId: "compute",
2395
+ inputs: { Object: "base.object" },
2396
+ outputs: { Values: "base.object" },
2397
+ impl: (ins) => {
2398
+ const obj = ins.Object;
2399
+ const vals = isPlainObject(obj)
2400
+ ? Object.values(obj)
2401
+ : Array.isArray(obj)
2402
+ ? Object.values(obj)
2403
+ : [];
2404
+ return { Values: vals };
2405
+ },
2406
+ });
2407
+ registry.registerNode({
2408
+ id: "base.objectPatch",
2409
+ categoryId: "compute",
2410
+ inputs: { Object: "base.object", Ops: "base.object" },
2411
+ outputs: { Result: "base.object" },
2412
+ impl: (ins) => {
2413
+ const root = structuredClone(ins.Object);
2414
+ const opsRaw = ins.Ops;
2415
+ const ops = Array.isArray(opsRaw)
2416
+ ? opsRaw
2417
+ : opsRaw
2418
+ ? [opsRaw]
2419
+ : [];
2420
+ let cur = root;
2421
+ for (const op of ops) {
2422
+ if (!op || typeof op !== "object")
2423
+ continue;
2424
+ const kind = String(op.op || "");
2425
+ const path = String(op.path || "");
2426
+ if (kind === "add" || kind === "replace") {
2427
+ cur = jsonPointerSet(cur, path, op.value);
2428
+ }
2429
+ else if (kind === "remove") {
2430
+ cur = jsonPointerRemove(cur, path);
2431
+ }
2432
+ else if (kind === "test") {
2433
+ const got = jsonPointerGet(cur, path);
2434
+ const expected = op.value;
2435
+ const ok = JSON.stringify(got) === JSON.stringify(expected);
2436
+ if (!ok)
2437
+ throw new Error(`objectPatch test failed at ${path}`);
2438
+ }
2439
+ }
2440
+ return { Result: cur };
2441
+ },
2442
+ });
2443
+ registry.registerNode({
2444
+ id: "base.arrayConcat",
2445
+ categoryId: "compute",
2446
+ inputs: { A: "base.object", B: "base.object" },
2447
+ outputs: { Result: "base.object" },
2448
+ impl: (ins) => {
2449
+ const a = Array.isArray(ins.A) ? ins.A : [];
2450
+ const b = Array.isArray(ins.B) ? ins.B : [];
2451
+ return { Result: [...a, ...b] };
2452
+ },
2453
+ });
2454
+ registry.registerNode({
2455
+ id: "base.arrayFlatten",
2456
+ categoryId: "compute",
2457
+ inputs: { Objects: "base.object" },
2458
+ outputs: { Result: "base.object" },
2459
+ impl: (ins) => {
2460
+ const arr = Array.isArray(ins.Objects) ? ins.Objects : [];
2461
+ const out = [];
2462
+ for (const v of arr) {
2463
+ if (Array.isArray(v))
2464
+ out.push(...v);
2465
+ else
2466
+ out.push(v);
2467
+ }
2468
+ return { Result: out };
2469
+ },
2470
+ });
2471
+ registry.registerNode({
2472
+ id: "base.arraySortBy",
2473
+ categoryId: "compute",
2474
+ inputs: { Objects: "base.object", Pointers: "base.string[]" },
2475
+ outputs: { Result: "base.object" },
2476
+ impl: (ins) => {
2477
+ const arr = Array.isArray(ins.Objects) ? ins.Objects : [];
2478
+ const pointers = (ins.Pointers || []).map(String);
2479
+ const out = [...arr].sort((a, b) => {
2480
+ for (const p of pointers) {
2481
+ const av = jsonPointerGet(a, p);
2482
+ const bv = jsonPointerGet(b, p);
2483
+ if (av === bv)
2484
+ continue;
2485
+ if (av === undefined)
2486
+ return 1;
2487
+ if (bv === undefined)
2488
+ return -1;
2489
+ if (av < bv)
2490
+ return -1;
2491
+ if (av > bv)
2492
+ return 1;
2493
+ }
2494
+ return 0;
2495
+ });
2496
+ return { Result: out };
2497
+ },
2498
+ });
2212
2499
  return registry;
2213
2500
  }
2214
2501
  function registerDelayNode(registry) {
@@ -2487,6 +2774,8 @@ exports.createSimpleGraphRegistry = createSimpleGraphRegistry;
2487
2774
  exports.createValidationGraphDef = createValidationGraphDef;
2488
2775
  exports.createValidationGraphRegistry = createValidationGraphRegistry;
2489
2776
  exports.getInputTypeId = getInputTypeId;
2777
+ exports.getTypedOutputTypeId = getTypedOutputTypeId;
2778
+ exports.getTypedOutputValue = getTypedOutputValue;
2490
2779
  exports.isInputPrivate = isInputPrivate;
2491
2780
  exports.isTypedOutput = isTypedOutput;
2492
2781
  exports.registerDelayNode = registerDelayNode;