@womp/spark-agent 7.0.0 → 9.0.0

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 (39) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +3 -2
  3. package/dist/capabilities/geometry.d.ts +17 -5
  4. package/dist/capabilities/geometry.js +11 -12
  5. package/dist/generated/geometry-declarations.d.ts +1 -1
  6. package/dist/generated/geometry-declarations.js +1 -1
  7. package/dist/geometry/backendRegistry.d.ts +1 -0
  8. package/dist/geometry/common.d.ts +5 -2
  9. package/dist/geometry/common.js +18 -3
  10. package/dist/geometry/compiler.js +2 -2
  11. package/dist/geometry/diagnostics.d.ts +2 -7
  12. package/dist/geometry/diagnostics.js +3 -51
  13. package/dist/geometry/graph.d.ts +1 -1
  14. package/dist/geometry/graph.js +1 -0
  15. package/dist/geometry/index.d.ts +0 -1
  16. package/dist/geometry/index.js +0 -3
  17. package/dist/geometry/lift.js +105 -113
  18. package/dist/geometry/lower.js +29 -49
  19. package/dist/geometry/schemas/flowir-dialect.json +117 -117
  20. package/dist/geometry/writeParamData.js +4 -4
  21. package/dist/geometry.d.ts +2 -5
  22. package/dist/geometry.js +7 -11
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.js +0 -1
  25. package/dist/intent-bench.js +1 -1
  26. package/dist/kakapo.d.ts +1 -1
  27. package/dist/scene-operations.d.ts +2 -17
  28. package/dist/scene-operations.js +4 -40
  29. package/dist/sdk-geometry-host.d.ts +1 -1
  30. package/dist/sdk-geometry-host.js +252 -251
  31. package/dist/sdk-openscad-host.js +1 -3
  32. package/dist/sdk-scene-host.d.ts +3 -5
  33. package/dist/sdk-scene-host.js +5 -15
  34. package/dist/server/sqlite-store.d.ts +1 -5
  35. package/dist/server/sqlite-store.js +3 -40
  36. package/dist/server/standalone.js +0 -1
  37. package/dist/womp-scene-api.d.ts +9 -1
  38. package/dist/womp-scene-api.js +41 -32
  39. package/package.json +2 -2
@@ -1,11 +1,42 @@
1
- import { VALUE_BACKEND_NAMES, titleToAstName, isArrayPortType, buildIncomingMap, defaultScalar, } from "./common.js";
2
- const POS = { line: 0, column: 0 };
3
- function decimal(value) {
4
- const text = value.toLocaleString("en-US", {
5
- useGrouping: false,
6
- maximumSignificantDigits: 21,
1
+ import { VALUE_BACKEND_NAMES, backendTypeId, isArrayPortType, paramName, storedSymbol, buildIncomingMap, defaultScalar, } from "./common.js";
2
+ const NO_SOURCE_POSITION = { line: 0, column: 0 };
3
+ function uniqueSymbol(prefix, start, taken) {
4
+ let index = start;
5
+ while (taken.has(`${prefix}${index}`))
6
+ index += 1;
7
+ taken.add(`${prefix}${index}`);
8
+ return `${prefix}${index}`;
9
+ }
10
+ function resolveNames(valueNodes, paramInputBid, dialectBids, graphExt) {
11
+ const names = new Map();
12
+ const taken = new Set();
13
+ const keep = (bid, name) => {
14
+ if (name === undefined)
15
+ return;
16
+ names.set(bid, name);
17
+ taken.add(name);
18
+ };
19
+ for (const vNode of valueNodes) {
20
+ const inputBid = paramInputBid(vNode);
21
+ keep(vNode.bid, inputBid
22
+ ? paramName(graphExt, vNode.bid, inputBid)
23
+ : storedSymbol(graphExt, vNode.bid));
24
+ }
25
+ for (const bid of dialectBids)
26
+ keep(bid, storedSymbol(graphExt, bid));
27
+ let paramIndex = 0;
28
+ let constIndex = 0;
29
+ for (const vNode of valueNodes) {
30
+ const isParam = paramInputBid(vNode) !== undefined;
31
+ const index = isParam ? paramIndex++ : constIndex++;
32
+ if (!names.has(vNode.bid))
33
+ names.set(vNode.bid, uniqueSymbol(isParam ? "p" : "c", index, taken));
34
+ }
35
+ dialectBids.forEach((bid, index) => {
36
+ if (!names.has(bid))
37
+ names.set(bid, uniqueSymbol("n", index, taken));
7
38
  });
8
- return text.includes(".") ? text : `${text}.0`;
39
+ return names;
9
40
  }
10
41
  function isVec3Object(v) {
11
42
  if (!v || typeof v !== "object")
@@ -105,70 +136,47 @@ function decodeScalarValue(raw, typeName) {
105
136
  return {
106
137
  kind: "vec3",
107
138
  value: [vec.x, vec.y, vec.z],
108
- raw: `Vec3(${decimal(vec.x)}, ${decimal(vec.y)}, ${decimal(vec.z)})`,
109
- pos: POS,
139
+ ...NO_SOURCE_POSITION,
110
140
  };
111
141
  }
112
- if (typeName === "Bool" && typeof raw === "boolean") {
113
- return { kind: "bool", value: raw, pos: POS };
114
- }
115
142
  if (typeName === "Int" && typeof raw === "number") {
116
143
  const v = Math.trunc(raw);
117
- return { kind: "int", value: v, raw: String(v), pos: POS };
144
+ return { kind: "int", value: v, ...NO_SOURCE_POSITION };
118
145
  }
119
146
  const num = typeof raw === "number" ? raw : 0;
120
- return { kind: "float", value: num, raw: decimal(num), pos: POS };
147
+ return { kind: "float", value: num, ...NO_SOURCE_POSITION };
121
148
  }
122
149
  function decodeBackendValue(raw, backendPort, dialectPort, flowirRegistry) {
123
- if (dialectPort.enum && typeof raw === "number") {
124
- const values = flowirRegistry.getEnumValues(dialectPort.enum);
125
- const name = values[raw];
126
- if (name)
127
- return { kind: "ident", name, pos: POS };
128
- }
129
- if (backendPort.enumValues?.length && typeof raw === "number") {
130
- const name = backendPort.enumValues[raw];
131
- if (name)
132
- return { kind: "ident", name, pos: POS };
150
+ if (flowirRegistry.isEnum(dialectPort.type)) {
151
+ const name = typeof raw === "number"
152
+ ? flowirRegistry.getEnumValues(dialectPort.type)[raw]
153
+ : undefined;
154
+ return name ? { kind: "ident", name, ...NO_SOURCE_POSITION } : null;
133
155
  }
134
156
  const unwrapped = isArrayPortType(backendPort.type) && Array.isArray(raw) && raw.length === 1
135
157
  ? raw[0]
136
158
  : raw;
137
- if (isVec3Object(unwrapped)) {
138
- return {
139
- kind: "vec3",
140
- value: [unwrapped.x, unwrapped.y, unwrapped.z],
141
- raw: `Vec3(${decimal(unwrapped.x)}, ${decimal(unwrapped.y)}, ${decimal(unwrapped.z)})`,
142
- pos: POS,
143
- };
144
- }
159
+ if (isVec3Object(unwrapped))
160
+ return decodeScalarValue(unwrapped, "Vec3");
145
161
  if (typeof unwrapped === "boolean") {
146
- return { kind: "bool", value: unwrapped, pos: POS };
162
+ return { kind: "bool", value: unwrapped, ...NO_SOURCE_POSITION };
147
163
  }
148
164
  if (typeof unwrapped === "number") {
149
165
  if (dialectPort.type === "Int" || dialectPort.type === "Int[]") {
150
166
  const v = Math.trunc(unwrapped);
151
- return { kind: "int", value: v, raw: String(v), pos: POS };
167
+ return { kind: "int", value: v, ...NO_SOURCE_POSITION };
152
168
  }
153
- return {
154
- kind: "float",
155
- value: unwrapped,
156
- raw: decimal(unwrapped),
157
- pos: POS,
158
- };
169
+ return { kind: "float", value: unwrapped, ...NO_SOURCE_POSITION };
159
170
  }
160
171
  return null;
161
172
  }
162
- function liftParam(vNode, gipBid, paramCounter, ctx) {
173
+ function liftParam(vNode, gipBid, name, ctx) {
163
174
  const typeName = vNode.valueTypeName ?? "Float";
164
- const extName = ctx.graphExt.nodes?.[gipBid]?.name;
165
- const name = extName ? titleToAstName(extName) : `p${paramCounter}`;
166
175
  const rawDefault = getData(ctx.graph, vNode.bid, "0");
167
- const paramTypeName = typeName === "Bool" ? "Float" : typeName;
168
176
  return {
169
177
  kind: "param",
170
178
  name,
171
- typeName: paramTypeName,
179
+ typeName,
172
180
  value: rawDefault != null
173
181
  ? decodeScalarValue(rawDefault, typeName)
174
182
  : decodeScalarValue(typeName === "Vec3" ? { x: 0, y: 0, z: 0 } : 0, typeName),
@@ -177,53 +185,50 @@ function liftParam(vNode, gipBid, paramCounter, ctx) {
177
185
  max: getDataAsNumber(ctx.graph, gipBid, "1"),
178
186
  step: getDataAsNumber(ctx.graph, gipBid, "2"),
179
187
  },
180
- pos: POS,
188
+ ...NO_SOURCE_POSITION,
181
189
  };
182
190
  }
183
- function liftConst(vNode, constCounter, ctx) {
191
+ function liftConst(vNode, name, ctx) {
184
192
  const typeName = vNode.valueTypeName ?? "Float";
185
193
  const rawDefault = getData(ctx.graph, vNode.bid, "0");
186
194
  return {
187
195
  kind: "const",
188
- name: `c${constCounter}`,
189
- typeName: typeName,
196
+ name,
197
+ typeName,
190
198
  value: rawDefault != null
191
199
  ? decodeScalarValue(rawDefault, typeName)
192
200
  : decodeScalarValue(typeName === "Vec3" ? { x: 0, y: 0, z: 0 } : 0, typeName),
193
- pos: POS,
201
+ ...NO_SOURCE_POSITION,
194
202
  };
195
203
  }
196
204
  function resolveInputBinding(bid, idx, dialectPort, backendPort, ctx) {
197
205
  const connEdge = ctx.incoming.get(bid)?.find((e) => e.toPort === idx);
198
206
  if (connEdge) {
199
207
  const sourceNode = ctx.classMap.get(connEdge.fromBid);
200
- if (!sourceNode)
208
+ const sourceName = ctx.names.get(connEdge.fromBid);
209
+ if (!sourceNode || !sourceName)
201
210
  return null;
202
- const paramName = ctx.paramBids.get(connEdge.fromBid);
203
- if (paramName) {
211
+ if (sourceNode.cls === "value") {
204
212
  return {
205
213
  name: dialectPort.name,
206
- value: { kind: "ident", name: paramName, pos: POS },
207
- pos: POS,
214
+ value: { kind: "ident", name: sourceName, ...NO_SOURCE_POSITION },
215
+ ...NO_SOURCE_POSITION,
208
216
  };
209
217
  }
210
218
  if (sourceNode.cls === "dialect" && sourceNode.dialectName) {
211
219
  const sourceDialect = ctx.flowirRegistry.getNode(sourceNode.dialectName);
212
- const sourceStmtId = ctx.dialectStmtIds.get(connEdge.fromBid);
213
- if (sourceDialect && sourceStmtId) {
214
- const outputPort = sourceDialect.outputs[connEdge.fromPort];
215
- if (outputPort) {
216
- return {
217
- name: dialectPort.name,
218
- value: {
219
- kind: "ref",
220
- nodeId: sourceStmtId,
221
- port: outputPort.name,
222
- pos: POS,
223
- },
224
- pos: POS,
225
- };
226
- }
220
+ const outputPort = sourceDialect?.outputs[connEdge.fromPort];
221
+ if (outputPort) {
222
+ return {
223
+ name: dialectPort.name,
224
+ value: {
225
+ kind: "ref",
226
+ nodeId: sourceName,
227
+ port: outputPort.name,
228
+ ...NO_SOURCE_POSITION,
229
+ },
230
+ ...NO_SOURCE_POSITION,
231
+ };
227
232
  }
228
233
  }
229
234
  return null;
@@ -234,11 +239,11 @@ function resolveInputBinding(bid, idx, dialectPort, backendPort, ctx) {
234
239
  const decoded = decodeBackendValue(rawData, backendPort, dialectPort, ctx.flowirRegistry);
235
240
  if (!decoded)
236
241
  return null;
237
- return { name: dialectPort.name, value: decoded, pos: POS };
242
+ return { name: dialectPort.name, value: decoded, ...NO_SOURCE_POSITION };
238
243
  }
239
244
  function liftNodeStmt(bid, cNode, ctx) {
240
245
  const dialectName = cNode.dialectName;
241
- const stmtId = ctx.dialectStmtIds.get(bid);
246
+ const stmtId = ctx.names.get(bid);
242
247
  const dialectNode = ctx.flowirRegistry.getNode(dialectName);
243
248
  if (!dialectNode)
244
249
  return null;
@@ -254,7 +259,13 @@ function liftNodeStmt(bid, cNode, ctx) {
254
259
  if (binding)
255
260
  inputs.push(binding);
256
261
  }
257
- return { kind: "node", id: stmtId, nodeType: dialectName, inputs, pos: POS };
262
+ return {
263
+ kind: "node",
264
+ id: stmtId,
265
+ nodeType: dialectName,
266
+ inputs,
267
+ ...NO_SOURCE_POSITION,
268
+ };
258
269
  }
259
270
  function liftOutStmt(oNode, ctx) {
260
271
  const geoEdge = ctx.incoming.get(oNode.bid)?.find((e) => e.toPort === 1);
@@ -264,7 +275,7 @@ function liftOutStmt(oNode, ctx) {
264
275
  if (!sourceNode || sourceNode.cls !== "dialect" || !sourceNode.dialectName)
265
276
  return null;
266
277
  const sourceDialect = ctx.flowirRegistry.getNode(sourceNode.dialectName);
267
- const sourceStmtId = ctx.dialectStmtIds.get(geoEdge.fromBid);
278
+ const sourceStmtId = ctx.names.get(geoEdge.fromBid);
268
279
  if (!sourceDialect || !sourceStmtId)
269
280
  return null;
270
281
  const outputPort = sourceDialect.outputs[geoEdge.fromPort];
@@ -277,20 +288,16 @@ function liftOutStmt(oNode, ctx) {
277
288
  kind: "ref",
278
289
  nodeId: sourceStmtId,
279
290
  port: outputPort.name,
280
- pos: POS,
291
+ ...NO_SOURCE_POSITION,
281
292
  },
282
- pos: POS,
293
+ ...NO_SOURCE_POSITION,
283
294
  };
284
295
  }
285
296
  export function lift(graph, graphExt, flowirRegistry, backendRegistry) {
286
297
  const reverseMap = new Map();
287
298
  for (const name of flowirRegistry.listNodeNames()) {
288
- const id = flowirRegistry.getNodeTypeId(name);
289
- if (id == null)
290
- continue;
299
+ const id = backendTypeId(flowirRegistry, name);
291
300
  const outputs = backendRegistry.getSchema(id)?.outputs;
292
- // Dialect aliases can share a backend ID. Lift the native output type,
293
- // so vector math is not accidentally narrowed to its scalar alias.
294
301
  const matches = flowirRegistry
295
302
  .getNode(name)
296
303
  ?.outputs.every((port, index) => port.type ===
@@ -313,52 +320,37 @@ export function lift(graph, graphExt, flowirRegistry, backendRegistry) {
313
320
  const classMap = new Map();
314
321
  for (const node of classified)
315
322
  classMap.set(node.bid, node);
323
+ const valueNodes = classified.filter((n) => n.cls === "value");
324
+ const paramInputBid = (vNode) => incoming
325
+ .get(vNode.bid)
326
+ ?.find((e) => classMap.get(e.fromBid)?.cls === "gip" && e.toPort === 0)
327
+ ?.fromBid;
328
+ const dialectBids = toposort(classified.filter((n) => n.cls === "dialect").map((n) => n.bid), incoming);
316
329
  const ctx = {
317
330
  graph,
318
- graphExt,
319
331
  flowirRegistry,
320
332
  backendRegistry,
321
333
  incoming,
322
334
  classMap,
323
- paramBids: new Map(),
324
- dialectStmtIds: new Map(),
335
+ names: resolveNames(valueNodes, paramInputBid, dialectBids, graphExt),
325
336
  };
326
337
  const statements = [];
327
338
  const nodeIds = {};
328
- let paramCounter = 0;
329
- let constCounter = 0;
330
- const valueNodes = classified.filter((n) => n.cls === "value");
331
339
  for (const vNode of valueNodes) {
332
- const edges = incoming.get(vNode.bid);
333
- const gipEdge = edges?.find((e) => classMap.get(e.fromBid)?.cls === "gip" && e.toPort === 0);
334
- if (gipEdge) {
335
- statements.push(liftParam(vNode, gipEdge.fromBid, paramCounter, ctx));
336
- const stmt = statements[statements.length - 1];
337
- ctx.paramBids.set(vNode.bid, stmt.name);
338
- nodeIds[`param:${stmt.name}`] = vNode.bid;
339
- nodeIds[`param-input:${stmt.name}`] = gipEdge.fromBid;
340
- paramCounter++;
340
+ const name = ctx.names.get(vNode.bid);
341
+ const inputBid = paramInputBid(vNode);
342
+ if (inputBid) {
343
+ statements.push(liftParam(vNode, inputBid, name, ctx));
344
+ nodeIds[`param:${name}`] = vNode.bid;
345
+ nodeIds[`param-input:${name}`] = inputBid;
341
346
  }
342
347
  else {
343
- statements.push(liftConst(vNode, constCounter, ctx));
344
- const stmt = statements[statements.length - 1];
345
- ctx.paramBids.set(vNode.bid, stmt.name);
346
- nodeIds[`const:${stmt.name}`] = vNode.bid;
347
- constCounter++;
348
+ statements.push(liftConst(vNode, name, ctx));
349
+ nodeIds[`const:${name}`] = vNode.bid;
348
350
  }
349
351
  }
350
- const dialectBids = classified
351
- .filter((n) => n.cls === "dialect")
352
- .map((n) => n.bid);
353
- const sortedBids = toposort(dialectBids, incoming);
354
- let nodeCounter = 0;
355
- for (const bid of sortedBids) {
356
- ctx.dialectStmtIds.set(bid, `n${nodeCounter}`);
357
- nodeCounter++;
358
- }
359
- for (const bid of sortedBids) {
360
- const cNode = classMap.get(bid);
361
- const stmt = liftNodeStmt(bid, cNode, ctx);
352
+ for (const bid of dialectBids) {
353
+ const stmt = liftNodeStmt(bid, classMap.get(bid), ctx);
362
354
  if (stmt) {
363
355
  statements.push(stmt);
364
356
  nodeIds[`node:${stmt.id}`] = bid;
@@ -1,4 +1,4 @@
1
- import { astNameToTitle, defaultScalar, valueBackendName, isArrayPortType, } from "./common.js";
1
+ import { astNameToTitle, backendTypeId, defaultScalar, valueBackendName, isArrayPortType, } from "./common.js";
2
2
  class GraphBuilder {
3
3
  nextId = 0;
4
4
  nodes = {};
@@ -28,10 +28,7 @@ class GraphBuilder {
28
28
  }
29
29
  }
30
30
  function resolveBackendSchema(nodeType, flowirRegistry, backendRegistry) {
31
- const typeId = flowirRegistry.getNodeTypeId(nodeType);
32
- if (typeId === null)
33
- return null;
34
- return backendRegistry.getSchema(typeId);
31
+ return backendRegistry.getSchema(backendTypeId(flowirRegistry, nodeType));
35
32
  }
36
33
  function toLiteral(value) {
37
34
  if (value.kind === "int")
@@ -45,10 +42,7 @@ function toLiteral(value) {
45
42
  return null;
46
43
  }
47
44
  function isVecPortType(type) {
48
- return (type === "Vec3" ||
49
- type === "Vec" ||
50
- type === "Vec3[]" ||
51
- type === "VecArray");
45
+ return type === "Vec" || type === "VecArray";
52
46
  }
53
47
  function isZeroVec(v) {
54
48
  if (!v || typeof v !== "object")
@@ -56,11 +50,9 @@ function isZeroVec(v) {
56
50
  const vec = v;
57
51
  return vec.x === 0 && vec.y === 0 && vec.z === 0;
58
52
  }
59
- function isDefaultValue(value, port, dialectPort) {
53
+ function isDefaultValue(value, port) {
60
54
  if (port.enumValues?.length)
61
55
  return false;
62
- if (dialectPort?.enum)
63
- return false;
64
56
  if (defaultScalar(port) === null)
65
57
  return false;
66
58
  if (value === 0)
@@ -91,9 +83,9 @@ function encodeLiteralForPort(literal, port) {
91
83
  }
92
84
  return isArray ? [scalar] : scalar;
93
85
  }
94
- function trySetInput(bid, portIndex, literal, port, dialectPort, graph) {
86
+ function trySetInput(bid, portIndex, literal, port, graph) {
95
87
  const encoded = encodeLiteralForPort(literal, port);
96
- if (!isDefaultValue(encoded, port, dialectPort)) {
88
+ if (!isDefaultValue(encoded, port)) {
97
89
  graph.setInput(bid, portIndex, encoded);
98
90
  }
99
91
  }
@@ -101,27 +93,19 @@ function encodeValue(value, port, dialectPort, flowirRegistry) {
101
93
  const literal = toLiteral(value);
102
94
  if (literal)
103
95
  return encodeLiteralForPort(literal, port);
104
- if (value.kind === "ident") {
105
- if (dialectPort?.enum) {
106
- const idx = flowirRegistry
107
- .getEnumValues(dialectPort.enum)
108
- .indexOf(value.name);
109
- if (idx >= 0)
110
- return idx;
111
- }
112
- if (port.enumValues?.length) {
113
- const idx = port.enumValues.indexOf(value.name);
114
- if (idx >= 0)
115
- return idx;
116
- }
117
- }
118
- return null;
96
+ if (value.kind !== "ident" || !flowirRegistry.isEnum(dialectPort.type))
97
+ return null;
98
+ const index = flowirRegistry
99
+ .getEnumValues(dialectPort.type)
100
+ .indexOf(value.name);
101
+ return index >= 0 ? index : null;
119
102
  }
120
103
  function valueNodeSchema(typeName, backendRegistry) {
121
104
  const backendName = valueBackendName(typeName);
122
- if (!backendName)
123
- return null;
124
- return backendRegistry.getSchemaByName(backendName);
105
+ const schema = backendName ? backendRegistry.getSchemaByName(backendName) : null;
106
+ if (!schema)
107
+ throw new Error(`Geometry has no value node for ${typeName}.`);
108
+ return schema;
125
109
  }
126
110
  function lowerFloatExpr(expr, ctx) {
127
111
  if (expr.kind === "float_lit")
@@ -157,7 +141,7 @@ function lowerFloatExpr(expr, ctx) {
157
141
  if (left.kind === "literal") {
158
142
  const port = mathFloat.inputs[1];
159
143
  if (port)
160
- trySetInput(bid, 1, { kind: "float", value: left.value }, port, null, ctx.graph);
144
+ trySetInput(bid, 1, { kind: "float", value: left.value }, port, ctx.graph);
161
145
  }
162
146
  else {
163
147
  ctx.graph.connect(left.backendNodeId, left.portIndex, bid, 1);
@@ -165,7 +149,7 @@ function lowerFloatExpr(expr, ctx) {
165
149
  if (right.kind === "literal") {
166
150
  const port = mathFloat.inputs[2];
167
151
  if (port)
168
- trySetInput(bid, 2, { kind: "float", value: right.value }, port, null, ctx.graph);
152
+ trySetInput(bid, 2, { kind: "float", value: right.value }, port, ctx.graph);
169
153
  }
170
154
  else {
171
155
  ctx.graph.connect(right.backendNodeId, right.portIndex, bid, 2);
@@ -187,7 +171,7 @@ function lowerVec3Ctor(ctor, targetBid, targetPortIndex, targetPort, ctx) {
187
171
  if (result.kind === "literal") {
188
172
  const port = combineXyz.inputs[idx];
189
173
  if (port)
190
- trySetInput(combineBid, idx, { kind: "float", value: result.value }, port, null, ctx.graph);
174
+ trySetInput(combineBid, idx, { kind: "float", value: result.value }, port, ctx.graph);
191
175
  }
192
176
  else {
193
177
  ctx.graph.connect(result.backendNodeId, result.portIndex, combineBid, idx);
@@ -198,22 +182,19 @@ function lowerVec3Ctor(ctor, targetBid, targetPortIndex, targetPort, ctx) {
198
182
  }
199
183
  function lowerConst(stmt, ctx) {
200
184
  const schema = valueNodeSchema(stmt.typeName, ctx.backendRegistry);
201
- if (!schema)
202
- return;
203
185
  const lit = toLiteral(stmt.value);
204
186
  if (!lit)
205
187
  return;
206
188
  const bid = ctx.graph.addNode(schema.typeId);
207
189
  const backendPort = schema.inputs[0];
208
190
  if (backendPort)
209
- trySetInput(bid, 0, lit, backendPort, null, ctx.graph);
191
+ trySetInput(bid, 0, lit, backendPort, ctx.graph);
210
192
  ctx.paramNodes.set(stmt.name, bid);
211
193
  ctx.nodeIds[`const:${stmt.name}`] = bid;
194
+ ctx.graphExt.nodes[bid] = { flowirSymbol: stmt.name };
212
195
  }
213
196
  function lowerParam(stmt, ctx) {
214
197
  const schema = valueNodeSchema(stmt.typeName, ctx.backendRegistry);
215
- if (!schema)
216
- return;
217
198
  const lit = toLiteral(stmt.value);
218
199
  if (!lit)
219
200
  return;
@@ -222,17 +203,18 @@ function lowerParam(stmt, ctx) {
222
203
  return;
223
204
  const gipBid = ctx.graph.addNode(gipSchema.typeId);
224
205
  ctx.nodeIds[`param-input:${stmt.name}`] = gipBid;
225
- ctx.nodeNames.set(gipBid, astNameToTitle(stmt.name));
206
+ ctx.graphExt.nodes[gipBid] = { name: astNameToTitle(stmt.name) };
226
207
  ctx.graph.setInput(gipBid, 0, stmt.range.min);
227
208
  ctx.graph.setInput(gipBid, 1, stmt.range.max);
228
209
  ctx.graph.setInput(gipBid, 2, stmt.range.step);
229
210
  const bid = ctx.graph.addNode(schema.typeId);
230
211
  const backendPort = schema.inputs[0];
231
212
  if (backendPort)
232
- trySetInput(bid, 0, lit, backendPort, null, ctx.graph);
213
+ trySetInput(bid, 0, lit, backendPort, ctx.graph);
233
214
  ctx.graph.connect(gipBid, 0, bid, 0);
234
215
  ctx.paramNodes.set(stmt.name, bid);
235
216
  ctx.nodeIds[`param:${stmt.name}`] = bid;
217
+ ctx.graphExt.nodes[bid] = { flowirSymbol: stmt.name };
236
218
  }
237
219
  function lowerNodeStmt(stmt, ctx, pending) {
238
220
  const schema = resolveBackendSchema(stmt.nodeType, ctx.flowirRegistry, ctx.backendRegistry);
@@ -241,6 +223,7 @@ function lowerNodeStmt(stmt, ctx, pending) {
241
223
  const bid = ctx.graph.addNode(schema.typeId);
242
224
  ctx.backendIds.set(stmt.id, bid);
243
225
  ctx.nodeIds[`node:${stmt.id}`] = bid;
226
+ ctx.graphExt.nodes[bid] = { flowirSymbol: stmt.id };
244
227
  ctx.nodeTypes.set(stmt.id, stmt.nodeType);
245
228
  ctx.nodeSchemas.set(stmt.id, schema);
246
229
  for (const input of stmt.inputs) {
@@ -272,7 +255,7 @@ function lowerNodeStmt(stmt, ctx, pending) {
272
255
  if (input.value.kind === "vec3ctor") {
273
256
  const lowered = lowerVec3Ctor(input.value, bid, resolved.index, backendPort, ctx);
274
257
  if (lowered !== null &&
275
- !isDefaultValue(lowered, backendPort, resolved.port)) {
258
+ !isDefaultValue(lowered, backendPort)) {
276
259
  ctx.graph.setInput(bid, resolved.index, lowered);
277
260
  }
278
261
  continue;
@@ -280,7 +263,7 @@ function lowerNodeStmt(stmt, ctx, pending) {
280
263
  const encoded = encodeValue(input.value, backendPort, resolved.port, ctx.flowirRegistry);
281
264
  if (encoded === null)
282
265
  continue;
283
- if (isDefaultValue(encoded, backendPort, resolved.port))
266
+ if (isDefaultValue(encoded, backendPort))
284
267
  continue;
285
268
  ctx.graph.setInput(bid, resolved.index, encoded);
286
269
  }
@@ -337,7 +320,7 @@ export function lower(program, flowirRegistry, backendRegistry) {
337
320
  backendIds: new Map(),
338
321
  nodeTypes: new Map(),
339
322
  nodeSchemas: new Map(),
340
- nodeNames: new Map(),
323
+ graphExt: { nodes: {} },
341
324
  nodeIds: {},
342
325
  };
343
326
  const pending = [];
@@ -358,8 +341,5 @@ export function lower(program, flowirRegistry, backendRegistry) {
358
341
  }
359
342
  }
360
343
  resolvePendingConnections(pending, ctx);
361
- const graphExt = {
362
- nodes: Object.fromEntries([...ctx.nodeNames.entries()].map(([bid, name]) => [bid, { name }])),
363
- };
364
- return { graph: graph.toPayload(), graphExt, nodeIds: ctx.nodeIds };
344
+ return { graph: graph.toPayload(), graphExt: ctx.graphExt, nodeIds: ctx.nodeIds };
365
345
  }