@bian-womp/spark-graph 0.3.45 → 0.3.47

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.
Files changed (163) hide show
  1. package/lib/cjs/index.cjs +14 -15
  2. package/lib/cjs/index.cjs.map +1 -1
  3. package/lib/cjs/src/core/types.d.ts +2 -1
  4. package/lib/cjs/src/core/types.d.ts.map +1 -1
  5. package/lib/cjs/src/runtime/GraphRuntime.d.ts +1 -1
  6. package/lib/cjs/src/runtime/GraphRuntime.d.ts.map +1 -1
  7. package/lib/cjs/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  8. package/lib/cjs/src/runtime/components/RunContextManager.d.ts +2 -1
  9. package/lib/cjs/src/runtime/components/RunContextManager.d.ts.map +1 -1
  10. package/lib/esm/index.js +14 -15
  11. package/lib/esm/index.js.map +1 -1
  12. package/lib/esm/src/core/types.d.ts +2 -1
  13. package/lib/esm/src/core/types.d.ts.map +1 -1
  14. package/lib/esm/src/runtime/GraphRuntime.d.ts +1 -1
  15. package/lib/esm/src/runtime/GraphRuntime.d.ts.map +1 -1
  16. package/lib/esm/src/runtime/components/NodeExecutor.d.ts.map +1 -1
  17. package/lib/esm/src/runtime/components/RunContextManager.d.ts +2 -1
  18. package/lib/esm/src/runtime/components/RunContextManager.d.ts.map +1 -1
  19. package/lib/src/builder/GraphBuilder.d.ts +43 -0
  20. package/lib/src/builder/GraphBuilder.d.ts.map +1 -0
  21. package/lib/src/builder/GraphBuilder.js +284 -0
  22. package/lib/src/builder/GraphBuilder.js.map +1 -0
  23. package/lib/src/builder/Registry.d.ts +93 -0
  24. package/lib/src/builder/Registry.d.ts.map +1 -0
  25. package/lib/src/builder/Registry.js +393 -0
  26. package/lib/src/builder/Registry.js.map +1 -0
  27. package/lib/src/core/categories.d.ts +22 -0
  28. package/lib/src/core/categories.d.ts.map +1 -0
  29. package/lib/src/core/categories.js +2 -0
  30. package/lib/src/core/categories.js.map +1 -0
  31. package/lib/src/core/order.d.ts +7 -0
  32. package/lib/src/core/order.d.ts.map +1 -0
  33. package/lib/src/core/order.js +66 -0
  34. package/lib/src/core/order.js.map +1 -0
  35. package/lib/src/core/type-utils.d.ts +29 -0
  36. package/lib/src/core/type-utils.d.ts.map +1 -0
  37. package/lib/src/core/type-utils.js +97 -0
  38. package/lib/src/core/type-utils.js.map +1 -0
  39. package/lib/src/core/types.d.ts +92 -0
  40. package/lib/src/core/types.d.ts.map +1 -0
  41. package/lib/src/core/types.js +2 -0
  42. package/lib/src/core/types.js.map +1 -0
  43. package/lib/src/examples/arrays.d.ts +5 -0
  44. package/lib/src/examples/arrays.d.ts.map +1 -0
  45. package/lib/src/examples/arrays.js +49 -0
  46. package/lib/src/examples/arrays.js.map +1 -0
  47. package/lib/src/examples/async.d.ts +5 -0
  48. package/lib/src/examples/async.d.ts.map +1 -0
  49. package/lib/src/examples/async.js +91 -0
  50. package/lib/src/examples/async.js.map +1 -0
  51. package/lib/src/examples/progress.d.ts +5 -0
  52. package/lib/src/examples/progress.d.ts.map +1 -0
  53. package/lib/src/examples/progress.js +51 -0
  54. package/lib/src/examples/progress.js.map +1 -0
  55. package/lib/src/examples/run.d.ts +2 -0
  56. package/lib/src/examples/run.d.ts.map +1 -0
  57. package/lib/src/examples/run.js +32 -0
  58. package/lib/src/examples/run.js.map +1 -0
  59. package/lib/src/examples/runMode.d.ts +2 -0
  60. package/lib/src/examples/runMode.d.ts.map +1 -0
  61. package/lib/src/examples/runMode.js +223 -0
  62. package/lib/src/examples/runMode.js.map +1 -0
  63. package/lib/src/examples/shared.d.ts +5 -0
  64. package/lib/src/examples/shared.d.ts.map +1 -0
  65. package/lib/src/examples/shared.js +49 -0
  66. package/lib/src/examples/shared.js.map +1 -0
  67. package/lib/src/examples/simple.d.ts +5 -0
  68. package/lib/src/examples/simple.d.ts.map +1 -0
  69. package/lib/src/examples/simple.js +79 -0
  70. package/lib/src/examples/simple.js.map +1 -0
  71. package/lib/src/examples/snapshot.d.ts +4 -0
  72. package/lib/src/examples/snapshot.d.ts.map +1 -0
  73. package/lib/src/examples/snapshot.js +58 -0
  74. package/lib/src/examples/snapshot.js.map +1 -0
  75. package/lib/src/examples/validation.d.ts +5 -0
  76. package/lib/src/examples/validation.d.ts.map +1 -0
  77. package/lib/src/examples/validation.js +105 -0
  78. package/lib/src/examples/validation.js.map +1 -0
  79. package/lib/src/index.d.ts +27 -0
  80. package/lib/src/index.d.ts.map +1 -0
  81. package/lib/src/index.js +19 -0
  82. package/lib/src/index.js.map +1 -0
  83. package/lib/src/misc/base.d.ts +51 -0
  84. package/lib/src/misc/base.d.ts.map +1 -0
  85. package/lib/src/misc/base.js +1122 -0
  86. package/lib/src/misc/base.js.map +1 -0
  87. package/lib/src/misc/utils/json.d.ts +22 -0
  88. package/lib/src/misc/utils/json.d.ts.map +1 -0
  89. package/lib/src/misc/utils/json.js +239 -0
  90. package/lib/src/misc/utils/json.js.map +1 -0
  91. package/lib/src/misc/utils/merge.d.ts +51 -0
  92. package/lib/src/misc/utils/merge.d.ts.map +1 -0
  93. package/lib/src/misc/utils/merge.js +600 -0
  94. package/lib/src/misc/utils/merge.js.map +1 -0
  95. package/lib/src/plugins/composite.d.ts +22 -0
  96. package/lib/src/plugins/composite.d.ts.map +1 -0
  97. package/lib/src/plugins/composite.js +59 -0
  98. package/lib/src/plugins/composite.js.map +1 -0
  99. package/lib/src/plugins/compute.d.ts +5 -0
  100. package/lib/src/plugins/compute.d.ts.map +1 -0
  101. package/lib/src/plugins/compute.js +39 -0
  102. package/lib/src/plugins/compute.js.map +1 -0
  103. package/lib/src/runtime/Engine.d.ts +28 -0
  104. package/lib/src/runtime/Engine.d.ts.map +1 -0
  105. package/lib/src/runtime/Engine.js +2 -0
  106. package/lib/src/runtime/Engine.js.map +1 -0
  107. package/lib/src/runtime/GraphLifecycleApi.d.ts +46 -0
  108. package/lib/src/runtime/GraphLifecycleApi.d.ts.map +1 -0
  109. package/lib/src/runtime/GraphLifecycleApi.js +2 -0
  110. package/lib/src/runtime/GraphLifecycleApi.js.map +1 -0
  111. package/lib/src/runtime/GraphRuntime.d.ts +94 -0
  112. package/lib/src/runtime/GraphRuntime.d.ts.map +1 -0
  113. package/lib/src/runtime/GraphRuntime.js +729 -0
  114. package/lib/src/runtime/GraphRuntime.js.map +1 -0
  115. package/lib/src/runtime/LocalEngine.d.ts +45 -0
  116. package/lib/src/runtime/LocalEngine.d.ts.map +1 -0
  117. package/lib/src/runtime/LocalEngine.js +89 -0
  118. package/lib/src/runtime/LocalEngine.js.map +1 -0
  119. package/lib/src/runtime/components/EdgePropagator.d.ts +101 -0
  120. package/lib/src/runtime/components/EdgePropagator.d.ts.map +1 -0
  121. package/lib/src/runtime/components/EdgePropagator.js +372 -0
  122. package/lib/src/runtime/components/EdgePropagator.js.map +1 -0
  123. package/lib/src/runtime/components/EventEmitter.d.ts +12 -0
  124. package/lib/src/runtime/components/EventEmitter.d.ts.map +1 -0
  125. package/lib/src/runtime/components/EventEmitter.js +33 -0
  126. package/lib/src/runtime/components/EventEmitter.js.map +1 -0
  127. package/lib/src/runtime/components/Graph.d.ts +211 -0
  128. package/lib/src/runtime/components/Graph.d.ts.map +1 -0
  129. package/lib/src/runtime/components/Graph.js +468 -0
  130. package/lib/src/runtime/components/Graph.js.map +1 -0
  131. package/lib/src/runtime/components/HandleResolver.d.ts +36 -0
  132. package/lib/src/runtime/components/HandleResolver.d.ts.map +1 -0
  133. package/lib/src/runtime/components/HandleResolver.js +231 -0
  134. package/lib/src/runtime/components/HandleResolver.js.map +1 -0
  135. package/lib/src/runtime/components/NodeExecutor.d.ts +110 -0
  136. package/lib/src/runtime/components/NodeExecutor.d.ts.map +1 -0
  137. package/lib/src/runtime/components/NodeExecutor.js +659 -0
  138. package/lib/src/runtime/components/NodeExecutor.js.map +1 -0
  139. package/lib/src/runtime/components/RunContextManager.d.ts +86 -0
  140. package/lib/src/runtime/components/RunContextManager.d.ts.map +1 -0
  141. package/lib/src/runtime/components/RunContextManager.js +302 -0
  142. package/lib/src/runtime/components/RunContextManager.js.map +1 -0
  143. package/lib/src/runtime/components/RuntimeValidatorManager.d.ts +31 -0
  144. package/lib/src/runtime/components/RuntimeValidatorManager.d.ts.map +1 -0
  145. package/lib/src/runtime/components/RuntimeValidatorManager.js +55 -0
  146. package/lib/src/runtime/components/RuntimeValidatorManager.js.map +1 -0
  147. package/lib/src/runtime/components/graph-utils.d.ts +33 -0
  148. package/lib/src/runtime/components/graph-utils.d.ts.map +1 -0
  149. package/lib/src/runtime/components/graph-utils.js +292 -0
  150. package/lib/src/runtime/components/graph-utils.js.map +1 -0
  151. package/lib/src/runtime/components/interfaces.d.ts +54 -0
  152. package/lib/src/runtime/components/interfaces.d.ts.map +1 -0
  153. package/lib/src/runtime/components/interfaces.js +2 -0
  154. package/lib/src/runtime/components/interfaces.js.map +1 -0
  155. package/lib/src/runtime/components/types.d.ts +55 -0
  156. package/lib/src/runtime/components/types.d.ts.map +1 -0
  157. package/lib/src/runtime/components/types.js +2 -0
  158. package/lib/src/runtime/components/types.js.map +1 -0
  159. package/lib/src/runtime/utils.d.ts +67 -0
  160. package/lib/src/runtime/utils.d.ts.map +1 -0
  161. package/lib/src/runtime/utils.js +137 -0
  162. package/lib/src/runtime/utils.js.map +1 -0
  163. package/package.json +2 -2
@@ -0,0 +1,1122 @@
1
+ import { GraphBuilder } from "../builder/GraphBuilder";
2
+ import { Registry } from "../builder/Registry";
3
+ import { ComputeCategory } from "../plugins/compute";
4
+ // Helpers
5
+ const asArray = (v) => Array.isArray(v) ? v : [Number(v)];
6
+ const broadcast = (a, b) => {
7
+ const aa = asArray(a);
8
+ const bb = asArray(b);
9
+ if (aa.length === bb.length)
10
+ return [aa, bb];
11
+ if (aa.length === 1)
12
+ return [new Array(bb.length).fill(aa[0]), bb];
13
+ if (bb.length === 1)
14
+ return [aa, new Array(aa.length).fill(bb[0])];
15
+ const len = Math.max(aa.length, bb.length);
16
+ return [new Array(len).fill(aa[0] ?? 0), new Array(len).fill(bb[0] ?? 0)];
17
+ };
18
+ const asBoolArray = (v) => Array.isArray(v) ? v.map((x) => Boolean(x)) : [Boolean(v)];
19
+ const broadcastBool = (a, b) => {
20
+ const aa = asBoolArray(a);
21
+ const bb = asBoolArray(b);
22
+ if (aa.length === bb.length)
23
+ return [aa, bb];
24
+ if (aa.length === 1)
25
+ return [new Array(bb.length).fill(aa[0]), bb];
26
+ if (bb.length === 1)
27
+ return [aa, new Array(aa.length).fill(bb[0])];
28
+ const len = Math.max(aa.length, bb.length);
29
+ return [
30
+ new Array(len).fill(aa[0] ?? false),
31
+ new Array(len).fill(bb[0] ?? false),
32
+ ];
33
+ };
34
+ const clamp = (x, min, max) => Math.min(max, Math.max(min, x));
35
+ const lerp = (a, b, t) => a + (b - a) * t;
36
+ const lcg = (seed) => {
37
+ let s = seed >>> 0 || 1;
38
+ return () => (s = (s * 1664525 + 1013904223) >>> 0) / 0xffffffff;
39
+ };
40
+ // JSON Pointer helpers (RFC 6901 subset)
41
+ function jsonPointerGet(obj, pointer) {
42
+ if (!pointer || pointer === "/")
43
+ return obj;
44
+ if (!pointer.startsWith("/"))
45
+ return undefined;
46
+ const parts = pointer
47
+ .split("/")
48
+ .slice(1)
49
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
50
+ let cur = obj;
51
+ for (const key of parts) {
52
+ if (cur === undefined || cur === null)
53
+ return undefined;
54
+ cur = cur[key];
55
+ }
56
+ return cur;
57
+ }
58
+ function jsonPointerSet(obj, pointer, value) {
59
+ if (!pointer || pointer === "/")
60
+ return value;
61
+ if (!pointer.startsWith("/"))
62
+ return obj;
63
+ const parts = pointer
64
+ .split("/")
65
+ .slice(1)
66
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
67
+ const root = structuredClone(obj);
68
+ let cur = root;
69
+ for (let i = 0; i < parts.length; i++) {
70
+ const key = parts[i];
71
+ if (i === parts.length - 1) {
72
+ if (Array.isArray(cur) && key === "-")
73
+ cur.push(value);
74
+ else
75
+ cur[key] = value;
76
+ }
77
+ else {
78
+ const next = cur[key];
79
+ if (next === undefined || next === null) {
80
+ // create container heuristically
81
+ const nextKey = parts[i + 1];
82
+ cur[key] =
83
+ typeof nextKey === "string" && /^[0-9]+$/.test(nextKey) ? [] : {};
84
+ }
85
+ cur = cur[key];
86
+ }
87
+ }
88
+ return root;
89
+ }
90
+ function jsonPointerRemove(obj, pointer) {
91
+ if (!pointer || pointer === "/")
92
+ return undefined;
93
+ if (!pointer.startsWith("/"))
94
+ return obj;
95
+ const parts = pointer
96
+ .split("/")
97
+ .slice(1)
98
+ .map((p) => p.replace(/~1/g, "/").replace(/~0/g, "~"));
99
+ const root = structuredClone(obj);
100
+ let cur = root;
101
+ for (let i = 0; i < parts.length - 1; i++) {
102
+ const key = parts[i];
103
+ if (cur === undefined || cur === null)
104
+ return root;
105
+ cur = cur[key];
106
+ }
107
+ const last = parts[parts.length - 1];
108
+ if (Array.isArray(cur)) {
109
+ const idx = last === "-" ? cur.length - 1 : Number(last);
110
+ if (Number.isFinite(idx))
111
+ cur.splice(idx, 1);
112
+ }
113
+ else if (cur && typeof cur === "object") {
114
+ delete cur[last];
115
+ }
116
+ return root;
117
+ }
118
+ function deepMerge(a, b) {
119
+ if (Array.isArray(a) && Array.isArray(b))
120
+ return [...a, ...b];
121
+ if (isPlainObject(a) && isPlainObject(b)) {
122
+ const out = { ...a };
123
+ for (const [k, v] of Object.entries(b)) {
124
+ out[k] = k in out ? deepMerge(out[k], v) : v;
125
+ }
126
+ return out;
127
+ }
128
+ return b;
129
+ }
130
+ // JSON helpers
131
+ const isPlainObject = (v) => {
132
+ if (v === null || typeof v !== "object")
133
+ return false;
134
+ const proto = Object.getPrototypeOf(v);
135
+ return proto === Object.prototype || proto === null;
136
+ };
137
+ const isJson = (v) => {
138
+ if (v === null)
139
+ return true;
140
+ const t = typeof v;
141
+ if (t === "string" || t === "number" || t === "boolean")
142
+ return true;
143
+ if (Array.isArray(v))
144
+ return v.every(isJson);
145
+ if (isPlainObject(v))
146
+ return Object.values(v).every(isJson);
147
+ return false;
148
+ };
149
+ // Export operation constants for use in examples and tests
150
+ export const BaseMathOperation = {
151
+ Add: 0,
152
+ Subtract: 1,
153
+ Multiply: 2,
154
+ Divide: 3,
155
+ Min: 4,
156
+ Max: 5,
157
+ Modulo: 6,
158
+ Power: 7,
159
+ Round: 8,
160
+ Floor: 9,
161
+ Ceil: 10,
162
+ Abs: 11,
163
+ Sum: 12,
164
+ Avg: 13,
165
+ MinAll: 14,
166
+ MaxAll: 15,
167
+ Sin: 16,
168
+ Cos: 17,
169
+ Tan: 18,
170
+ Asin: 19,
171
+ Acos: 20,
172
+ Atan: 21,
173
+ Sqrt: 22,
174
+ Exp: 23,
175
+ Log: 24,
176
+ };
177
+ export const BaseCompareOperation = {
178
+ LessThan: 0,
179
+ LessThanOrEqual: 1,
180
+ GreaterThan: 2,
181
+ GreaterThanOrEqual: 3,
182
+ Equal: 4,
183
+ NotEqual: 5,
184
+ };
185
+ export const BaseLogicOperation = {
186
+ Not: 0,
187
+ And: 1,
188
+ Or: 2,
189
+ Xor: 3,
190
+ };
191
+ export function setupBasicGraphRegistry(id) {
192
+ const registry = new Registry(id);
193
+ registry.categories.register(ComputeCategory);
194
+ registry.registerType({
195
+ id: "base.float",
196
+ validate: (v) => typeof v === "number" && !Number.isNaN(v),
197
+ bakeTarget: { nodeTypeId: "base.input.number", inputHandle: "Value" },
198
+ }, { withArray: true, arrayPickFirstDefined: true });
199
+ registry.registerType({
200
+ id: "base.bool",
201
+ validate: (v) => typeof v === "boolean",
202
+ bakeTarget: { nodeTypeId: "base.input.bool", inputHandle: "Value" },
203
+ }, { withArray: true, arrayPickFirstDefined: true });
204
+ registry.registerType({
205
+ id: "base.string",
206
+ validate: (v) => typeof v === "string",
207
+ bakeTarget: { nodeTypeId: "base.input.string", inputHandle: "Value" },
208
+ }, { withArray: true, arrayPickFirstDefined: true });
209
+ // Generic object value (JSON-compatible; object/array/primitive/null)
210
+ registry.registerType({
211
+ id: "base.object",
212
+ validate: (v) => isJson(v),
213
+ bakeTarget: { nodeTypeId: "base.input.object", inputHandle: "Value" },
214
+ }, { withArray: false });
215
+ registry.registerType({
216
+ id: "base.vec3",
217
+ validate: (v) => Array.isArray(v) &&
218
+ v.length === 3 &&
219
+ v.every((x) => typeof x === "number"),
220
+ bakeTarget: { nodeTypeId: "base.input.vec3", inputHandle: "Value" },
221
+ }, { withArray: true, arrayPickFirstDefined: true });
222
+ // float -> vec3 : map x to [x,0,0]
223
+ registry.registerCoercion("base.float", "base.vec3", (v) => {
224
+ return [Number(v) || 0, 0, 0];
225
+ });
226
+ registry.registerCoercion("base.vec3", "base.float", (value) => {
227
+ const v = value;
228
+ return Math.hypot(Number(v[0] ?? 0), Number(v[1] ?? 0), Number(v[2] ?? 0));
229
+ });
230
+ registry.registerCoercion("base.bool", "base.float", (v) => (v ? 1 : 0));
231
+ registry.registerCoercion("base.float", "base.bool", (v) => !!v);
232
+ registry.registerCoercion("base.float", "base.string", (v) => String(v));
233
+ registry.registerCoercion("base.string", "base.float", (v) => Number(v));
234
+ // Object <-> String
235
+ registry.registerCoercion("base.string", "base.object", (v) => {
236
+ return v;
237
+ });
238
+ registry.registerCoercion("base.object", "base.string", (v) => {
239
+ if (typeof v === "string")
240
+ return v;
241
+ return undefined;
242
+ });
243
+ registry.registerCoercion("base.bool", "base.object", (v) => {
244
+ return v;
245
+ });
246
+ registry.registerCoercion("base.object", "base.bool", (v) => {
247
+ if (typeof v === "boolean")
248
+ return v;
249
+ return undefined;
250
+ });
251
+ registry.registerCoercion("base.float", "base.object", (v) => {
252
+ return v;
253
+ });
254
+ registry.registerCoercion("base.object", "base.float", (v) => {
255
+ if (typeof v === "number")
256
+ return v;
257
+ return undefined;
258
+ });
259
+ registry.registerCoercion("base.vec3", "base.object", (v) => {
260
+ return v;
261
+ });
262
+ registry.registerCoercion("base.object", "base.vec3", (v) => {
263
+ try {
264
+ if (Array.isArray(v) &&
265
+ v.length === 3 &&
266
+ v.every((x) => typeof x === "number")) {
267
+ return v;
268
+ }
269
+ return undefined;
270
+ }
271
+ catch {
272
+ return undefined;
273
+ }
274
+ });
275
+ // Enums: Math Operation
276
+ registry.registerEnum({
277
+ id: "enum:base.math.operation",
278
+ options: Object.entries(BaseMathOperation).map(([label, value]) => ({
279
+ value,
280
+ label,
281
+ })),
282
+ });
283
+ // Enums: Compare Operation
284
+ registry.registerEnum({
285
+ id: "enum:base.compare.operation",
286
+ options: Object.entries(BaseCompareOperation).map(([label, value]) => ({
287
+ value,
288
+ label,
289
+ })),
290
+ });
291
+ // Enums: Logic Operation
292
+ registry.registerEnum({
293
+ id: "enum:base.logic.operation",
294
+ options: Object.entries(BaseLogicOperation).map(([label, value]) => ({
295
+ value,
296
+ label,
297
+ })),
298
+ });
299
+ // Number
300
+ registry.registerNode({
301
+ id: "base.input.number",
302
+ categoryId: "compute",
303
+ inputs: { Value: "base.float" },
304
+ outputs: { Result: "base.float" },
305
+ policy: { autoRun: true },
306
+ impl: (ins) => ({ Result: Number(ins.Value) }),
307
+ });
308
+ registry.registerNode({
309
+ id: "base.input.string",
310
+ categoryId: "compute",
311
+ inputs: { Value: "base.string" },
312
+ outputs: { Result: "base.string" },
313
+ policy: { autoRun: true },
314
+ impl: (ins) => ({ Result: String(ins.Value) }),
315
+ });
316
+ registry.registerNode({
317
+ id: "base.input.bool",
318
+ categoryId: "compute",
319
+ inputs: { Value: "base.bool" },
320
+ outputs: { Result: "base.bool" },
321
+ policy: { autoRun: true },
322
+ impl: (ins) => ({ Result: Boolean(ins.Value) }),
323
+ });
324
+ registry.registerNode({
325
+ id: "base.input.object",
326
+ categoryId: "compute",
327
+ inputs: { Value: "base.object" },
328
+ outputs: { Result: "base.object" },
329
+ policy: { autoRun: true },
330
+ impl: (ins) => ({ Result: ins.Value }),
331
+ });
332
+ registry.registerNode({
333
+ id: "base.input.vec3",
334
+ categoryId: "compute",
335
+ inputs: { Value: "base.vec3" },
336
+ outputs: { Result: "base.vec3" },
337
+ policy: { autoRun: true },
338
+ impl: (ins) => ({ Result: ins.Value }),
339
+ });
340
+ // JSON parser node: base.stringToObject
341
+ registry.registerNode({
342
+ id: "base.string.toObject",
343
+ categoryId: "compute",
344
+ inputs: { Text: "base.string" },
345
+ outputs: { Object: "base.object" },
346
+ impl: (ins) => {
347
+ const t = ins.Text ?? "";
348
+ try {
349
+ const obj = JSON.parse(t);
350
+ return { Object: obj };
351
+ }
352
+ catch {
353
+ return { Object: undefined };
354
+ }
355
+ },
356
+ });
357
+ registry.registerNode({
358
+ id: "base.object.toString",
359
+ categoryId: "compute",
360
+ inputs: { Object: "base.object" },
361
+ outputs: { Text: "base.string" },
362
+ impl: (ins) => {
363
+ return { Text: JSON.stringify(ins.Object) };
364
+ },
365
+ });
366
+ // Clamp
367
+ registry.registerNode({
368
+ id: "base.clamp",
369
+ categoryId: "compute",
370
+ inputs: { Value: "base.float[]", Min: "base.float", Max: "base.float" },
371
+ outputs: { Value: "base.float[]" },
372
+ impl: (ins) => {
373
+ const vals = asArray(ins.Value);
374
+ const min = Number(ins.Min ?? 0);
375
+ const max = Number(ins.Max ?? 1);
376
+ return { Value: vals.map((v) => clamp(Number(v), min, max)) };
377
+ },
378
+ });
379
+ // Interpolate (lerp)
380
+ registry.registerNode({
381
+ id: "base.interpolate",
382
+ categoryId: "compute",
383
+ inputs: {
384
+ ValueA: "base.float[]",
385
+ ValueB: "base.float[]",
386
+ Factor: "base.float",
387
+ },
388
+ outputs: { Value: "base.float[]" },
389
+ inputDefaults: {
390
+ Factor: 0.5,
391
+ },
392
+ impl: (ins) => {
393
+ const [a, b] = broadcast(ins.ValueA, ins.ValueB);
394
+ const t = Number(ins.Factor ?? 0);
395
+ const len = Math.max(a.length, b.length);
396
+ const out = new Array(len)
397
+ .fill(0)
398
+ .map((_, i) => lerp(Number(a[i] ?? 0), Number(b[i] ?? 0), t));
399
+ return { Value: out };
400
+ },
401
+ });
402
+ // Map Range (linear)
403
+ registry.registerNode({
404
+ id: "base.mapRange",
405
+ categoryId: "compute",
406
+ inputs: {
407
+ Mode: "base.string",
408
+ Clamp: "base.bool",
409
+ Value: "base.float[]",
410
+ FromMin: "base.float",
411
+ FromMax: "base.float",
412
+ ToMin: "base.float",
413
+ ToMax: "base.float",
414
+ },
415
+ outputs: { Value: "base.float[]" },
416
+ impl: (ins) => {
417
+ const vals = asArray(ins.Value);
418
+ const fromMin = Number(ins.FromMin ?? 0);
419
+ const fromMax = Number(ins.FromMax ?? 1);
420
+ const toMin = Number(ins.ToMin ?? 0);
421
+ const toMax = Number(ins.ToMax ?? 1);
422
+ const doClamp = Boolean(ins.Clamp);
423
+ const out = vals.map((v) => {
424
+ const t = (Number(v) - fromMin) / (fromMax - fromMin || 1);
425
+ const r = toMin + t * (toMax - toMin);
426
+ return doClamp
427
+ ? clamp(r, Math.min(toMin, toMax), Math.max(toMin, toMax))
428
+ : r;
429
+ });
430
+ return { Value: out };
431
+ },
432
+ });
433
+ // Math (subset) - scalar version for simple examples
434
+ registry.registerNode({
435
+ id: "base.math",
436
+ categoryId: "compute",
437
+ inputs: {
438
+ Operation: "enum:base.math.operation",
439
+ A: "base.float[]",
440
+ B: "base.float[]",
441
+ },
442
+ outputs: { Result: "base.float[]" },
443
+ // Registry-level defaults: Add by default, A=[1], B=[1]
444
+ inputDefaults: { Operation: 0, A: [1], B: [1] },
445
+ impl: (ins) => {
446
+ const a = asArray(ins.A ?? []);
447
+ const b = asArray(ins.B ?? []);
448
+ const op = Number(ins.Operation ?? BaseMathOperation.Add);
449
+ const unaryByOp = {
450
+ [BaseMathOperation.Round]: (x) => Math.round(x),
451
+ [BaseMathOperation.Floor]: (x) => Math.floor(x),
452
+ [BaseMathOperation.Ceil]: (x) => Math.ceil(x),
453
+ [BaseMathOperation.Abs]: (x) => Math.abs(x),
454
+ [BaseMathOperation.Sin]: (x) => Math.sin(x),
455
+ [BaseMathOperation.Cos]: (x) => Math.cos(x),
456
+ [BaseMathOperation.Tan]: (x) => Math.tan(x),
457
+ [BaseMathOperation.Asin]: (x) => Math.asin(x),
458
+ [BaseMathOperation.Acos]: (x) => Math.acos(x),
459
+ [BaseMathOperation.Atan]: (x) => Math.atan(x),
460
+ [BaseMathOperation.Sqrt]: (x) => Math.sqrt(x),
461
+ [BaseMathOperation.Exp]: (x) => Math.exp(x),
462
+ [BaseMathOperation.Log]: (x) => Math.log(x),
463
+ };
464
+ if (unaryByOp[op])
465
+ return { Result: a.map((x) => unaryByOp[op](Number(x))) };
466
+ const aggregateByOp = {
467
+ [BaseMathOperation.Sum]: (arr) => arr.reduce((s, x) => s + Number(x || 0), 0),
468
+ [BaseMathOperation.Avg]: (arr) => {
469
+ const sum = arr.reduce((s, x) => s + Number(x || 0), 0);
470
+ return arr.length ? sum / arr.length : 0;
471
+ },
472
+ [BaseMathOperation.MinAll]: (arr) => arr.length ? Math.min(...arr.map((x) => Number(x))) : 0,
473
+ [BaseMathOperation.MaxAll]: (arr) => arr.length ? Math.max(...arr.map((x) => Number(x))) : 0,
474
+ };
475
+ if (aggregateByOp[op] !== undefined)
476
+ return { Result: [aggregateByOp[op](a)] };
477
+ const binaryByOp = {
478
+ [BaseMathOperation.Add]: (x, y) => x + y,
479
+ [BaseMathOperation.Subtract]: (x, y) => x - y,
480
+ [BaseMathOperation.Multiply]: (x, y) => x * y,
481
+ [BaseMathOperation.Divide]: (x, y) => x / (y || 1),
482
+ [BaseMathOperation.Min]: (x, y) => Math.min(x, y),
483
+ [BaseMathOperation.Max]: (x, y) => Math.max(x, y),
484
+ [BaseMathOperation.Modulo]: (x, y) => (y ? x % y : 0),
485
+ [BaseMathOperation.Power]: (x, y) => Math.pow(x, y),
486
+ };
487
+ const fn = binaryByOp[op] || binaryByOp[BaseMathOperation.Add];
488
+ const len = Math.max(a.length, b.length);
489
+ const out = new Array(len).fill(0).map((_, i) => {
490
+ const ax = a.length === 1 && len > 1 ? a[0] : a[i] ?? 0;
491
+ const bx = b.length === 1 && len > 1 ? b[0] : b[i] ?? 0;
492
+ return fn(Number(ax), Number(bx));
493
+ });
494
+ return { Result: out };
495
+ },
496
+ });
497
+ // Compare
498
+ registry.registerNode({
499
+ id: "base.compare",
500
+ categoryId: "compute",
501
+ inputs: {
502
+ Operation: "enum:base.compare.operation",
503
+ A: "base.float[]",
504
+ B: "base.float[]",
505
+ },
506
+ outputs: { Result: "base.bool[]" },
507
+ impl: (ins) => {
508
+ const [a, b] = broadcast(ins.A, ins.B);
509
+ const op = Number(ins.Operation ?? BaseCompareOperation.Equal);
510
+ const compareByOp = {
511
+ [BaseCompareOperation.LessThan]: (x, y) => x < y,
512
+ [BaseCompareOperation.LessThanOrEqual]: (x, y) => x <= y,
513
+ [BaseCompareOperation.GreaterThan]: (x, y) => x > y,
514
+ [BaseCompareOperation.GreaterThanOrEqual]: (x, y) => x >= y,
515
+ [BaseCompareOperation.Equal]: (x, y) => x === y,
516
+ [BaseCompareOperation.NotEqual]: (x, y) => x !== y,
517
+ };
518
+ const fn = compareByOp[op] || compareByOp[BaseCompareOperation.Equal];
519
+ return { Result: a.map((x, i) => fn(Number(x), Number(b[i] ?? 0))) };
520
+ },
521
+ });
522
+ // Logic
523
+ registry.registerNode({
524
+ id: "base.logic",
525
+ categoryId: "compute",
526
+ inputs: {
527
+ Operation: "enum:base.logic.operation",
528
+ A: "base.bool[]",
529
+ B: "base.bool[]",
530
+ },
531
+ outputs: { Result: "base.bool[]" },
532
+ inputDefaults: { Operation: BaseLogicOperation.Not },
533
+ impl: (ins) => {
534
+ const op = Number(ins.Operation ?? BaseLogicOperation.Not);
535
+ if (op === BaseLogicOperation.Not) {
536
+ const a = asBoolArray(ins.A ?? []);
537
+ return { Result: a.map((x) => !x) };
538
+ }
539
+ const [a, b] = broadcastBool(ins.A ?? [], ins.B ?? []);
540
+ const logicByOp = {
541
+ [BaseLogicOperation.And]: (x, y) => x && y,
542
+ [BaseLogicOperation.Or]: (x, y) => x || y,
543
+ [BaseLogicOperation.Xor]: (x, y) => Boolean(x ? !y : y),
544
+ };
545
+ const fn = logicByOp[op] || logicByOp[BaseLogicOperation.And];
546
+ return {
547
+ Result: a.map((x, i) => fn(Boolean(x), Boolean(b[i] ?? false))),
548
+ };
549
+ },
550
+ });
551
+ // Strings
552
+ registry.registerNode({
553
+ id: "base.string.length",
554
+ categoryId: "compute",
555
+ inputs: { Text: "base.string" },
556
+ outputs: { Length: "base.float" },
557
+ impl: (ins) => ({
558
+ Length: String(ins.Text || "").length,
559
+ }),
560
+ });
561
+ registry.registerNode({
562
+ id: "base.string.op",
563
+ categoryId: "compute",
564
+ inputs: { Op: "base.string", Text: "base.string" },
565
+ outputs: { Text: "base.string" },
566
+ impl: (ins) => {
567
+ const op = String(ins.Op || "trim").toLowerCase();
568
+ const t = String(ins.Text || "");
569
+ if (op === "lower")
570
+ return { Text: t.toLowerCase() };
571
+ if (op === "upper")
572
+ return { Text: t.toUpperCase() };
573
+ return { Text: t.trim() };
574
+ },
575
+ });
576
+ registry.registerNode({
577
+ id: "base.string.split",
578
+ categoryId: "compute",
579
+ inputs: { Text: "base.string", Sep: "base.string" },
580
+ outputs: { Parts: "base.string[]" },
581
+ impl: (ins) => {
582
+ const t = String(ins.Text || "");
583
+ const sep = String(ins.Sep || ",");
584
+ return { Parts: t.split(sep) };
585
+ },
586
+ });
587
+ registry.registerNode({
588
+ id: "base.string.join",
589
+ categoryId: "compute",
590
+ inputs: { Parts: "base.string[]", Sep: "base.string" },
591
+ outputs: { Text: "base.string" },
592
+ impl: (ins) => {
593
+ const parts = Array.isArray(ins.Parts) ? ins.Parts.map(String) : [];
594
+ const sep = String(ins.Sep || "");
595
+ return { Text: parts.join(sep) };
596
+ },
597
+ });
598
+ registry.registerNode({
599
+ id: "base.string.replace",
600
+ categoryId: "compute",
601
+ inputs: {
602
+ Text: "base.string",
603
+ Search: "base.string",
604
+ Replace: "base.string",
605
+ },
606
+ outputs: { Text: "base.string" },
607
+ impl: (ins) => {
608
+ const t = String(ins.Text || "");
609
+ const s = String(ins.Search || "");
610
+ const r = String(ins.Replace || "");
611
+ return { Text: t.split(s).join(r) };
612
+ },
613
+ });
614
+ // Arrays (carried as base.object)
615
+ registry.registerNode({
616
+ id: "base.array.length",
617
+ categoryId: "compute",
618
+ inputs: { Items: "base.object" },
619
+ outputs: { Length: "base.float" },
620
+ impl: (ins) => ({
621
+ Length: Array.isArray(ins.Items) ? ins.Items.length : 0,
622
+ }),
623
+ });
624
+ registry.registerNode({
625
+ id: "base.array.slice",
626
+ categoryId: "compute",
627
+ inputs: { Items: "base.object", Start: "base.float", End: "base.float" },
628
+ outputs: { Result: "base.object" },
629
+ impl: (ins) => {
630
+ const arr = Array.isArray(ins.Items) ? ins.Items : [];
631
+ const s = Math.trunc(Number(ins.Start ?? 0));
632
+ const e = Number.isFinite(Number(ins.End))
633
+ ? Math.trunc(Number(ins.End))
634
+ : undefined;
635
+ return { Result: arr.slice(s, e) };
636
+ },
637
+ });
638
+ // Compose array from dynamic item inputs
639
+ registry.registerNode({
640
+ id: "base.array.compose",
641
+ categoryId: "compute",
642
+ inputs: { Length: "base.float" },
643
+ outputs: { Items: "base.object" },
644
+ resolveHandles: ({ inputs }) => {
645
+ const maxLen = 64;
646
+ const raw = inputs?.Length ?? 0;
647
+ const n = Math.max(0, Math.min(maxLen, Math.trunc(Number(raw ?? 0))));
648
+ if (!Number.isFinite(n))
649
+ return { inputs: {} };
650
+ const dyn = {};
651
+ for (let i = 0; i < n; i++)
652
+ dyn[`Item${i}`] = { typeId: "base.object" };
653
+ return { inputs: dyn };
654
+ },
655
+ inputDefaults: { Length: 0 },
656
+ impl: (ins) => {
657
+ const length = Math.max(0, Math.trunc(Number(ins.Length ?? 0)));
658
+ if (!Number.isFinite(length))
659
+ return { Items: [] };
660
+ return { Items: Array.from({ length }, (_, i) => ins[`Item${i}`]) };
661
+ },
662
+ });
663
+ // Decompose array into dynamic item outputs
664
+ registry.registerNode({
665
+ id: "base.array.decompose",
666
+ categoryId: "compute",
667
+ inputs: { Items: "base.object" },
668
+ outputs: {},
669
+ resolveHandles: ({ inputs }) => {
670
+ const maxLen = 64;
671
+ const arr = Array.isArray(inputs?.Items) ? inputs?.Items : [];
672
+ const n = Math.max(0, Math.min(maxLen, arr.length));
673
+ const dyn = {};
674
+ for (let i = 0; i < n; i++)
675
+ dyn[`Item${i}`] = "base.object";
676
+ return { outputs: dyn };
677
+ },
678
+ impl: (ins) => {
679
+ const arr = Array.isArray(ins.Items) ? ins.Items : [];
680
+ const out = {};
681
+ const n = Math.max(0, Math.min(64, arr.length));
682
+ for (let i = 0; i < n; i++)
683
+ out[`Item${i}`] = arr[i];
684
+ return out;
685
+ },
686
+ });
687
+ // Select
688
+ registry.registerNode({
689
+ id: "base.select",
690
+ categoryId: "compute",
691
+ inputs: { Cond: "base.bool", Then: "base.object", Else: "base.object" },
692
+ outputs: { Result: "base.object" },
693
+ impl: (ins) => ({
694
+ Result: ins.Cond ? ins.Then : ins.Else,
695
+ }),
696
+ });
697
+ // Combine XYZ
698
+ registry.registerNode({
699
+ id: "base.compareXYZ",
700
+ categoryId: "compute",
701
+ inputs: { X: "base.float[]", Y: "base.float[]", Z: "base.float[]" },
702
+ outputs: { XYZ: "base.vec3[]" },
703
+ impl: (ins) => {
704
+ const [x, y] = broadcast(ins.X, ins.Y);
705
+ const [xx, z] = broadcast(x, ins.Z);
706
+ const len = Math.max(xx.length, z.length);
707
+ const out = new Array(len)
708
+ .fill(0)
709
+ .map((_, i) => [Number(xx[i] ?? 0), Number(y[i] ?? 0), Number(z[i] ?? 0)]);
710
+ return { XYZ: out };
711
+ },
712
+ });
713
+ // Separate XYZ
714
+ registry.registerNode({
715
+ id: "base.separateXYZ",
716
+ categoryId: "compute",
717
+ inputs: { XYZ: "base.vec3[]" },
718
+ outputs: { X: "base.float[]", Y: "base.float[]", Z: "base.float[]" },
719
+ impl: (ins) => {
720
+ const arr = ins.XYZ ?? [];
721
+ const X = arr.map((v) => Number(v?.[0] ?? 0));
722
+ const Y = arr.map((v) => Number(v?.[1] ?? 0));
723
+ const Z = arr.map((v) => Number(v?.[2] ?? 0));
724
+ return { X, Y, Z };
725
+ },
726
+ });
727
+ // Indices
728
+ registry.registerNode({
729
+ id: "base.indices",
730
+ categoryId: "compute",
731
+ inputs: { Domain: "base.float" },
732
+ outputs: { Indices: "base.float[]" },
733
+ impl: (ins) => {
734
+ const n = Math.trunc(ins.Domain);
735
+ return { Indices: Array.from({ length: n }, (_, i) => i) };
736
+ },
737
+ });
738
+ // Random Numbers
739
+ registry.registerNode({
740
+ id: "base.randomNumbers",
741
+ categoryId: "compute",
742
+ inputs: {
743
+ Domain: "base.float",
744
+ Min: "base.float",
745
+ Max: "base.float",
746
+ Seed: "base.float",
747
+ },
748
+ outputs: { Values: "base.float[]" },
749
+ impl: (ins) => {
750
+ const len = Math.trunc(ins.Domain);
751
+ const min = Number(ins.Min ?? 0);
752
+ const max = Number(ins.Max ?? 1);
753
+ const rng = lcg(Number(ins.Seed ?? 1));
754
+ const out = Array.from({ length: len }, () => min + rng() * (max - min));
755
+ return { Values: out };
756
+ },
757
+ });
758
+ // Random Vectors
759
+ registry.registerNode({
760
+ id: "base.randomXYZs",
761
+ categoryId: "compute",
762
+ inputs: {
763
+ Domain: "base.float",
764
+ Min: "base.vec3",
765
+ Max: "base.vec3",
766
+ Seed: "base.float",
767
+ },
768
+ outputs: { Values: "base.vec3[]" },
769
+ // Registry-level defaults for convenience
770
+ inputDefaults: { Domain: 10, Min: [0, 0, 0], Max: [1, 1, 1], Seed: 1 },
771
+ impl: (ins) => {
772
+ const len = Math.trunc(ins.Domain);
773
+ const min = ins.Min ?? [0, 0, 0];
774
+ const max = ins.Max ?? [1, 1, 1];
775
+ const rng = lcg(Number(ins.Seed ?? 1));
776
+ const out = Array.from({ length: len }, () => [
777
+ min[0] + rng() * (max[0] - min[0]),
778
+ min[1] + rng() * (max[1] - min[1]),
779
+ min[2] + rng() * (max[2] - min[2]),
780
+ ]);
781
+ return { Values: out };
782
+ },
783
+ });
784
+ // Timer
785
+ registry.registerNode({
786
+ id: "base.timer",
787
+ categoryId: "compute",
788
+ inputs: {
789
+ Enabled: "base.bool",
790
+ IntervalMs: "base.float",
791
+ Immediate: "base.bool",
792
+ },
793
+ outputs: { Now: "base.float", Count: "base.float" },
794
+ inputDefaults: { Enabled: true, IntervalMs: 1000, Immediate: true },
795
+ impl: (ins, ctx) => {
796
+ const enabled = Boolean(ins.Enabled);
797
+ const intervalMs = Math.max(1, Math.trunc(Number(ins.IntervalMs ?? 1000)));
798
+ const immediate = Boolean(ins.Immediate);
799
+ const stop = () => {
800
+ const id = ctx.state.timerId;
801
+ if (id !== undefined) {
802
+ clearInterval(id);
803
+ ctx.setState({ timerId: undefined });
804
+ }
805
+ };
806
+ if (!enabled) {
807
+ stop();
808
+ return;
809
+ }
810
+ // restart timer with new settings
811
+ stop();
812
+ let count = 0;
813
+ if (immediate) {
814
+ ctx.emit("Now", Date.now());
815
+ ctx.emit("Count", count);
816
+ count += 1;
817
+ }
818
+ const id = setInterval(() => {
819
+ ctx.emit("Now", Date.now());
820
+ ctx.emit("Count", count);
821
+ count += 1;
822
+ }, intervalMs);
823
+ ctx.setState({ timerId: id });
824
+ },
825
+ lifecycle: {
826
+ dispose: (ctx) => {
827
+ const id = ctx.state.timerId;
828
+ if (id !== undefined) {
829
+ clearInterval(id);
830
+ ctx.setState({ timerId: undefined });
831
+ }
832
+ },
833
+ },
834
+ });
835
+ // ------------------------- JSON/object utilities -------------------------
836
+ registry.registerNode({
837
+ id: "base.object.get",
838
+ categoryId: "compute",
839
+ inputs: { Object: "base.object", Pointers: "base.string[]" },
840
+ outputs: { Values: "base.object" },
841
+ impl: (ins) => {
842
+ const obj = ins.Object;
843
+ const pointers = (ins.Pointers || []).map(String);
844
+ const out = {};
845
+ for (const p of pointers)
846
+ out[p] = jsonPointerGet(obj, p);
847
+ return { Values: out };
848
+ },
849
+ });
850
+ registry.registerNode({
851
+ id: "base.object.set",
852
+ categoryId: "compute",
853
+ inputs: {
854
+ Object: "base.object",
855
+ Pointers: "base.string[]",
856
+ NewValues: "base.object",
857
+ },
858
+ outputs: { Result: "base.object" },
859
+ impl: (ins) => {
860
+ const pointers = (ins.Pointers || []).map(String);
861
+ const values = (ins.NewValues || []).map(String);
862
+ let cur = structuredClone(ins.Object);
863
+ for (let i = 0; i < pointers.length; i++) {
864
+ const p = pointers[i];
865
+ const raw = values[i];
866
+ let val = raw;
867
+ if (typeof raw === "string") {
868
+ try {
869
+ val = JSON.parse(raw);
870
+ }
871
+ catch {
872
+ /* keep as string */
873
+ }
874
+ }
875
+ cur = jsonPointerSet(cur, p, val);
876
+ }
877
+ return { Result: cur };
878
+ },
879
+ });
880
+ registry.registerNode({
881
+ id: "base.object.remove",
882
+ categoryId: "compute",
883
+ inputs: { Object: "base.object", Pointers: "base.string[]" },
884
+ outputs: { Result: "base.object" },
885
+ impl: (ins) => {
886
+ const pointers = (ins.Pointers || []).map(String);
887
+ let cur = structuredClone(ins.Object);
888
+ for (const p of pointers)
889
+ cur = jsonPointerRemove(cur, p);
890
+ return { Result: cur };
891
+ },
892
+ });
893
+ registry.registerNode({
894
+ id: "base.object.merge",
895
+ categoryId: "compute",
896
+ inputs: { A: "base.object", B: "base.object" },
897
+ outputs: { Result: "base.object" },
898
+ impl: (ins) => ({
899
+ Result: deepMerge(ins.A, ins.B),
900
+ }),
901
+ });
902
+ registry.registerNode({
903
+ id: "base.object.keys",
904
+ categoryId: "compute",
905
+ inputs: { Object: "base.object" },
906
+ outputs: { Keys: "base.string[]" },
907
+ impl: (ins) => {
908
+ const obj = ins.Object;
909
+ const keys = isPlainObject(obj)
910
+ ? Object.keys(obj)
911
+ : Array.isArray(obj)
912
+ ? Object.keys(obj)
913
+ : [];
914
+ return { Keys: keys };
915
+ },
916
+ });
917
+ registry.registerNode({
918
+ id: "base.object.values",
919
+ categoryId: "compute",
920
+ inputs: { Object: "base.object" },
921
+ outputs: { Values: "base.object" },
922
+ impl: (ins) => {
923
+ const obj = ins.Object;
924
+ const vals = isPlainObject(obj)
925
+ ? Object.values(obj)
926
+ : Array.isArray(obj)
927
+ ? Object.values(obj)
928
+ : [];
929
+ return { Values: vals };
930
+ },
931
+ });
932
+ registry.registerNode({
933
+ id: "base.object.patch",
934
+ categoryId: "compute",
935
+ inputs: { Object: "base.object", Ops: "base.object" },
936
+ outputs: { Result: "base.object" },
937
+ impl: (ins) => {
938
+ const root = structuredClone(ins.Object);
939
+ const opsRaw = ins.Ops;
940
+ const ops = Array.isArray(opsRaw)
941
+ ? opsRaw
942
+ : opsRaw
943
+ ? [opsRaw]
944
+ : [];
945
+ let cur = root;
946
+ for (const op of ops) {
947
+ if (!op || typeof op !== "object")
948
+ continue;
949
+ const kind = String(op.op || "");
950
+ const path = String(op.path || "");
951
+ if (kind === "add" || kind === "replace") {
952
+ cur = jsonPointerSet(cur, path, op.value);
953
+ }
954
+ else if (kind === "remove") {
955
+ cur = jsonPointerRemove(cur, path);
956
+ }
957
+ else if (kind === "test") {
958
+ const got = jsonPointerGet(cur, path);
959
+ const expected = op.value;
960
+ const ok = JSON.stringify(got) === JSON.stringify(expected);
961
+ if (!ok)
962
+ throw new Error(`objectPatch test failed at ${path}`);
963
+ }
964
+ }
965
+ return { Result: cur };
966
+ },
967
+ });
968
+ registry.registerNode({
969
+ id: "base.array.concat",
970
+ categoryId: "compute",
971
+ inputs: { A: "base.object", B: "base.object" },
972
+ outputs: { Result: "base.object" },
973
+ impl: (ins) => {
974
+ const a = Array.isArray(ins.A) ? ins.A : [];
975
+ const b = Array.isArray(ins.B) ? ins.B : [];
976
+ return { Result: [...a, ...b] };
977
+ },
978
+ });
979
+ registry.registerNode({
980
+ id: "base.array.flatten",
981
+ categoryId: "compute",
982
+ inputs: { Objects: "base.object" },
983
+ outputs: { Result: "base.object" },
984
+ impl: (ins) => {
985
+ const arr = Array.isArray(ins.Objects) ? ins.Objects : [];
986
+ const out = [];
987
+ for (const v of arr) {
988
+ if (Array.isArray(v))
989
+ out.push(...v);
990
+ else
991
+ out.push(v);
992
+ }
993
+ return { Result: out };
994
+ },
995
+ });
996
+ registry.registerNode({
997
+ id: "base.array.sortBy",
998
+ categoryId: "compute",
999
+ inputs: { Objects: "base.object", Pointers: "base.string[]" },
1000
+ outputs: { Result: "base.object" },
1001
+ impl: (ins) => {
1002
+ const arr = Array.isArray(ins.Objects) ? ins.Objects : [];
1003
+ const pointers = (ins.Pointers || []).map(String);
1004
+ const out = [...arr].sort((a, b) => {
1005
+ for (const p of pointers) {
1006
+ const av = jsonPointerGet(a, p);
1007
+ const bv = jsonPointerGet(b, p);
1008
+ if (av === bv)
1009
+ continue;
1010
+ if (av === undefined)
1011
+ return 1;
1012
+ if (bv === undefined)
1013
+ return -1;
1014
+ if (av < bv)
1015
+ return -1;
1016
+ if (av > bv)
1017
+ return 1;
1018
+ }
1019
+ return 0;
1020
+ });
1021
+ return { Result: out };
1022
+ },
1023
+ });
1024
+ return registry;
1025
+ }
1026
+ export function registerDelayNode(registry) {
1027
+ registry.registerNode({
1028
+ id: "async.delay",
1029
+ categoryId: "compute",
1030
+ inputs: { Value: "base.float", DelayMs: "base.float" },
1031
+ outputs: { Output: "base.float" },
1032
+ impl: async (ins, ctx) => {
1033
+ const ms = Number(ins.DelayMs ?? 200);
1034
+ const valueRaw = ins.Value;
1035
+ if (valueRaw === undefined ||
1036
+ valueRaw === null ||
1037
+ Number.isNaN(Number(valueRaw))) {
1038
+ return; // wait until x is present to avoid NaN emissions
1039
+ }
1040
+ await new Promise((resolve, reject) => {
1041
+ const id = setTimeout(resolve, ms);
1042
+ const onAbort = () => {
1043
+ clearTimeout(id);
1044
+ reject(new DOMException("Aborted", "AbortError"));
1045
+ };
1046
+ if (ctx.abortSignal.aborted)
1047
+ return onAbort();
1048
+ ctx.abortSignal.addEventListener("abort", onAbort, { once: true });
1049
+ });
1050
+ return { Output: Number(valueRaw) };
1051
+ },
1052
+ });
1053
+ }
1054
+ function sleepWithAbort(ms, signal) {
1055
+ return new Promise((resolve, reject) => {
1056
+ const id = setTimeout(() => {
1057
+ cleanup();
1058
+ resolve();
1059
+ }, ms);
1060
+ const onAbort = () => {
1061
+ clearTimeout(id);
1062
+ cleanup();
1063
+ reject(new DOMException("Aborted", "AbortError"));
1064
+ };
1065
+ const cleanup = () => {
1066
+ signal.removeEventListener("abort", onAbort);
1067
+ };
1068
+ if (signal.aborted)
1069
+ return onAbort();
1070
+ signal.addEventListener("abort", onAbort);
1071
+ });
1072
+ }
1073
+ export function registerProgressNodes(registry) {
1074
+ registry.registerNode({
1075
+ id: "async.progress",
1076
+ categoryId: "compute",
1077
+ inputs: {
1078
+ Steps: "base.float",
1079
+ DelayMs: "base.float",
1080
+ ShouldError: "base.bool",
1081
+ },
1082
+ outputs: { Done: "base.string" },
1083
+ impl: async (ins, ctx) => {
1084
+ const steps = Math.max(1, Math.trunc(Number(ins.Steps ?? 10)));
1085
+ const delayMs = Math.max(0, Math.trunc(Number(ins.DelayMs ?? 50)));
1086
+ const shouldError = Boolean(ins.ShouldError);
1087
+ for (let i = 0; i < steps; i++) {
1088
+ ctx.reportProgress?.(i / steps);
1089
+ await sleepWithAbort(delayMs, ctx.abortSignal);
1090
+ if (shouldError && i >= Math.floor(steps * 0.7)) {
1091
+ ctx.reportProgress?.(i / steps);
1092
+ throw new Error("progressWorker: simulated failure at 70% progress");
1093
+ }
1094
+ }
1095
+ ctx.reportProgress?.(1);
1096
+ return { Done: `Completed ${steps} steps` };
1097
+ },
1098
+ });
1099
+ }
1100
+ export function createRuntime(registry, def, opts) {
1101
+ const builder = new GraphBuilder(registry);
1102
+ const report = builder.validate(def);
1103
+ if (!report.ok)
1104
+ throw new Error("Validation failed: " +
1105
+ report.issues.map((i) => `${i.code}:${i.message}`).join(", "));
1106
+ return builder.build(def, opts);
1107
+ }
1108
+ export function generateId(prefix, used = new Set()) {
1109
+ let id;
1110
+ let attempts = 0;
1111
+ do {
1112
+ id = `${prefix}${Math.random().toString(36).slice(2, 8)}`;
1113
+ attempts++;
1114
+ if (attempts > 1000) {
1115
+ id = `${prefix}${Date.now().toString(36)}${Math.random()
1116
+ .toString(36)
1117
+ .slice(2, 4)}`;
1118
+ }
1119
+ } while (used.has(id));
1120
+ return id;
1121
+ }
1122
+ //# sourceMappingURL=base.js.map