@field/cascade 0.5.1 → 0.6.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 (44) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/dist/assets/{CodeEditor-dOrU3aQg.js → CodeEditor-BLpahZw6.js} +7 -7
  3. package/dist/assets/{CodePanel-Bqy85qen.js → CodePanel-BqYKqBMH.js} +2 -2
  4. package/dist/assets/{CustomNodeDialog-DkZlOgjs.js → CustomNodeDialog-CYr9y7dG.js} +1 -1
  5. package/dist/assets/{ProjectSettings-D192mr6g.js → ProjectSettings-CnYfrkKh.js} +2 -2
  6. package/dist/assets/{SettingsDialog-BTeeIE3T.js → SettingsDialog-mSS6KUyS.js} +1 -1
  7. package/dist/assets/{VersionHistoryDialog-TZQKkn3Y.js → VersionHistoryDialog-DZuwNG6s.js} +1 -1
  8. package/dist/assets/{core-DwbcGL0o.js → core-BOGrwBxU.js} +2 -2
  9. package/dist/assets/{cssMode-Dcjn356x.js → cssMode-0Z3pyDOW.js} +1 -1
  10. package/dist/assets/definitions-Cckh442l.js +1 -0
  11. package/dist/assets/{editor.api-D7Q83606.js → editor.api-D-_7pnnj.js} +1 -1
  12. package/dist/assets/{freemarker2-DCCiHlV6.js → freemarker2-DE0JRkF7.js} +1 -1
  13. package/dist/assets/geo-DcAVygkw.js +1 -0
  14. package/dist/assets/{handlebars-BCxC8xu1.js → handlebars-B_Shirzj.js} +1 -1
  15. package/dist/assets/{html-COwepzSd.js → html-DI5Qj4gd.js} +1 -1
  16. package/dist/assets/{htmlMode-Dpg7H0rx.js → htmlMode-CkHZy0mE.js} +1 -1
  17. package/dist/assets/{image-CMfMMxlC.js → image-B6RU2frM.js} +3 -3
  18. package/dist/assets/{index-De1cIZ9N.js → index-DTyzl6YV.js} +36 -36
  19. package/dist/assets/{javascript-CSwWxfLr.js → javascript-zxQfbzS_.js} +1 -1
  20. package/dist/assets/{jsonMode-BXRriTpj.js → jsonMode-DSQ0uLqA.js} +1 -1
  21. package/dist/assets/{liquid-Cmkfyogc.js → liquid-0zs2oOuA.js} +1 -1
  22. package/dist/assets/{lspLanguageFeatures-xH9JbZGO.js → lspLanguageFeatures-7VWaq5lc.js} +1 -1
  23. package/dist/assets/math-BA6juJD7.js +1 -0
  24. package/dist/assets/{mdx-BzrNjNvK.js → mdx-BFKlF5Xh.js} +1 -1
  25. package/dist/assets/pop-Bkzzmp57.js +1 -0
  26. package/dist/assets/{python-D4E9jbx7.js → python-BgexpWZh.js} +1 -1
  27. package/dist/assets/{razor-B3x9cIEM.js → razor-E1kWyvkT.js} +1 -1
  28. package/dist/assets/{tsMode-dhQYPNMZ.js → tsMode-CTmJLLW5.js} +1 -1
  29. package/dist/assets/{typescript-CITkDxAE.js → typescript-BheZLRDM.js} +1 -1
  30. package/dist/assets/{workers-BQvF9bbO.js → workers-KIoho05H.js} +1 -1
  31. package/dist/assets/{xml-DvHlU3A8.js → xml-DaVLg9zI.js} +1 -1
  32. package/dist/assets/{yaml-uznsLgs8.js → yaml-D6XGTfOj.js} +1 -1
  33. package/dist/cli/index.js +632 -289
  34. package/dist/index.html +1 -1
  35. package/dist/runtime/builtins/index.js +2 -0
  36. package/dist/runtime/builtins/math/definitions.d.ts +845 -0
  37. package/dist/runtime/builtins/math/definitions.js +285 -0
  38. package/dist/runtime/builtins/math/index.d.ts +2 -0
  39. package/dist/runtime/builtins/math/index.js +2 -0
  40. package/doc/NODE_REFERENCE.md +115 -0
  41. package/package.json +1 -1
  42. package/dist/assets/definitions-C_SY6Bhg.js +0 -1
  43. package/dist/assets/geo-CT-wtvaE.js +0 -1
  44. package/dist/assets/pop-BdKEHslJ.js +0 -1
package/dist/cli/index.js CHANGED
@@ -1653,8 +1653,8 @@ function frameRange(start, end, step2 = 1) {
1653
1653
  for (let i = 0; i < count; i++) frames[i] = start + i * step2;
1654
1654
  return frames;
1655
1655
  }
1656
- async function runFrameRange(clock2, options) {
1657
- const { start, end, step: step2 = 1, onFrame, signal, restoreFrame = true } = options;
1656
+ async function runFrameRange(clock2, options2) {
1657
+ const { start, end, step: step2 = 1, onFrame, signal, restoreFrame = true } = options2;
1658
1658
  const frames = frameRange(start, end, step2);
1659
1659
  const previousFrame = clock2.getFrameFraction();
1660
1660
  const visited = [];
@@ -1842,7 +1842,7 @@ var init_params = __esm({
1842
1842
  function isGpuHost(value) {
1843
1843
  return !!value && typeof value.ensure === "function" && typeof value.forNode === "function" && typeof value.dispose === "function";
1844
1844
  }
1845
- function createGpuHost(options) {
1845
+ function createGpuHost(options2) {
1846
1846
  let active = null;
1847
1847
  let starting = null;
1848
1848
  let disposal = null;
@@ -1905,7 +1905,7 @@ function createGpuHost(options) {
1905
1905
  });
1906
1906
  }
1907
1907
  async function start() {
1908
- const result = await options.acquire();
1908
+ const result = await options2.acquire();
1909
1909
  const acquired = {
1910
1910
  device: result.device,
1911
1911
  info: {
@@ -2323,7 +2323,7 @@ var init_builder = __esm({
2323
2323
  init_src();
2324
2324
  init_attributes();
2325
2325
  GeometryBuilder = class {
2326
- constructor(options = {}) {
2326
+ constructor(options2 = {}) {
2327
2327
  this.positions = [];
2328
2328
  this.vertexPoints = [];
2329
2329
  this.offsets = [0];
@@ -2333,8 +2333,8 @@ var init_builder = __esm({
2333
2333
  this.detail = {};
2334
2334
  this.pointGroups = /* @__PURE__ */ new Map();
2335
2335
  this.primitiveGroups = /* @__PURE__ */ new Map();
2336
- this.size = options.positionSize ?? 2;
2337
- this.storage = options.positionStorage ?? "f64";
2336
+ this.size = options2.positionSize ?? 2;
2337
+ this.storage = options2.positionStorage ?? "f64";
2338
2338
  }
2339
2339
  get pointCount() {
2340
2340
  return this.positions.length / this.size;
@@ -2369,7 +2369,7 @@ var init_builder = __esm({
2369
2369
  * closed primitive does not repeat its first point: `closed` is topology, not
2370
2370
  * a duplicated vertex.
2371
2371
  */
2372
- addPrimitive(points, options = {}) {
2372
+ addPrimitive(points, options2 = {}) {
2373
2373
  const index = this.primitiveCount;
2374
2374
  for (const point of points) {
2375
2375
  if (!Number.isInteger(point) || point < 0 || point >= this.pointCount)
@@ -2380,17 +2380,17 @@ var init_builder = __esm({
2380
2380
  this.vertexPoints.push(point);
2381
2381
  }
2382
2382
  this.offsets.push(this.vertexPoints.length);
2383
- this.kinds.push(PRIMITIVE_KINDS.indexOf(options.kind ?? "poly"));
2384
- this.closed.push(options.closed ? 1 : 0);
2383
+ this.kinds.push(PRIMITIVE_KINDS.indexOf(options2.kind ?? "poly"));
2384
+ this.closed.push(options2.closed ? 1 : 0);
2385
2385
  return index;
2386
2386
  }
2387
2387
  /** Append points and one primitive over them, the common generator case. */
2388
- addPolygon(flat, options = {}) {
2388
+ addPolygon(flat, options2 = {}) {
2389
2389
  const first = this.addPoints(flat);
2390
2390
  const points = [];
2391
2391
  for (let index = first; index < this.pointCount; index += 1)
2392
2392
  points.push(index);
2393
- return this.addPrimitive(points, options);
2393
+ return this.addPrimitive(points, options2);
2394
2394
  }
2395
2395
  setAttribute(level, name, attribute) {
2396
2396
  if (level === "point" && name === POSITION_ATTRIBUTE)
@@ -2711,20 +2711,20 @@ var init_transform = __esm({
2711
2711
  });
2712
2712
 
2713
2713
  // packages/runtime/src/geometry/copy.ts
2714
- function copyToPoints(source, target, options = {}) {
2714
+ function copyToPoints(source, target, options2 = {}) {
2715
2715
  const width = positionSize(source) === 3 ? 3 : 2;
2716
- const selection = options.targetGroup === void 0 || options.targetGroup === "" ? void 0 : groupMask(target, "point", options.targetGroup) ?? geometryError(
2716
+ const selection = options2.targetGroup === void 0 || options2.targetGroup === "" ? void 0 : groupMask(target, "point", options2.targetGroup) ?? geometryError(
2717
2717
  "missing-group",
2718
- `point group ${options.targetGroup} is not present on the target`
2718
+ `point group ${options2.targetGroup} is not present on the target`
2719
2719
  );
2720
2720
  const indices = selection === void 0 ? range(target.pointCount) : groupIndices(selection);
2721
2721
  if (indices.length === 0 || source.pointCount === 0)
2722
2722
  return emptyGeometry(width);
2723
- const orient = options.useTargetOrientations ?? true;
2723
+ const orient = options2.useTargetOrientations ?? true;
2724
2724
  const pscale = orient ? numericAttribute(target.point.pscale, "point.pscale", [1]) : void 0;
2725
2725
  const normal = orient ? numericAttribute(target.point.N, "point.N", [2, 3]) : void 0;
2726
- const copyNumber = options.copyNumberAttribute ?? "copynum";
2727
- const transfer = options.transferTargetAttributes ?? true ? Object.keys(target.point).filter(
2726
+ const copyNumber = options2.copyNumberAttribute ?? "copynum";
2727
+ const transfer = options2.transferTargetAttributes ?? true ? Object.keys(target.point).filter(
2728
2728
  (name) => name !== POSITION_ATTRIBUTE && !Object.prototype.hasOwnProperty.call(source.point, name)
2729
2729
  ) : [];
2730
2730
  const copies = [];
@@ -2794,9 +2794,9 @@ function finitePair(value, code) {
2794
2794
  if (!Number.isFinite(value[0]) || !Number.isFinite(value[1]))
2795
2795
  geometryError(code, "both components must be finite");
2796
2796
  }
2797
- function rectangleGeometry(options = {}) {
2798
- const [width, height] = options.size ?? [1, 1];
2799
- const [cx, cy] = options.center ?? [0, 0];
2797
+ function rectangleGeometry(options2 = {}) {
2798
+ const [width, height] = options2.size ?? [1, 1];
2799
+ const [cx, cy] = options2.center ?? [0, 0];
2800
2800
  finitePair([width, height], "rectangle-size");
2801
2801
  finitePair([cx, cy], "rectangle-center");
2802
2802
  if (width < 0 || height < 0)
@@ -2809,16 +2809,16 @@ function rectangleGeometry(options = {}) {
2809
2809
  builder.addPolygon([x0, y0, x1, y0, x1, y1, x0, y1], { closed: true });
2810
2810
  return builder.build();
2811
2811
  }
2812
- function circleGeometry(options = {}) {
2813
- const [cx, cy] = options.center ?? [0, 0];
2814
- const [rx, ry] = options.radius ?? [1, 1];
2812
+ function circleGeometry(options2 = {}) {
2813
+ const [cx, cy] = options2.center ?? [0, 0];
2814
+ const [rx, ry] = options2.radius ?? [1, 1];
2815
2815
  finitePair([cx, cy], "circle-center");
2816
2816
  finitePair([rx, ry], "circle-radius");
2817
2817
  if (rx < 0 || ry < 0)
2818
2818
  geometryError("circle-radius", "circle radius must not be negative");
2819
2819
  const builder = new GeometryBuilder();
2820
- if ((options.type ?? "bezier") === "poly") {
2821
- const divisions = options.divisions ?? 32;
2820
+ if ((options2.type ?? "bezier") === "poly") {
2821
+ const divisions = options2.divisions ?? 32;
2822
2822
  if (!Number.isSafeInteger(divisions) || divisions < 3)
2823
2823
  geometryError(
2824
2824
  "circle-divisions",
@@ -2956,10 +2956,10 @@ var init_resample = __esm({
2956
2956
  });
2957
2957
 
2958
2958
  // packages/runtime/src/geometry/svg.ts
2959
- function geometryToSvg(geometry, options = {}) {
2960
- validateOptions(options);
2961
- const margin = options.margin ?? 0;
2962
- const explicit = options.bounds;
2959
+ function geometryToSvg(geometry, options2 = {}) {
2960
+ validateOptions(options2);
2961
+ const margin = options2.margin ?? 0;
2962
+ const explicit = options2.bounds;
2963
2963
  const bounds = explicit ? void 0 : geometryBounds(geometry);
2964
2964
  const minX = explicit ? explicit[0] - margin : bounds === void 0 ? 0 : bounds.min[0] - margin;
2965
2965
  const minY = explicit ? explicit[1] - margin : bounds === void 0 ? 0 : bounds.min[1] - margin;
@@ -2967,14 +2967,14 @@ function geometryToSvg(geometry, options = {}) {
2967
2967
  const maxY = explicit ? explicit[3] + margin : bounds === void 0 ? 1 : bounds.max[1] + margin;
2968
2968
  const spanX = Math.max(maxX - minX, Number.EPSILON);
2969
2969
  const spanY = Math.max(maxY - minY, Number.EPSILON);
2970
- const width = options.width && options.width > 0 ? options.width : spanX;
2971
- const height = options.height && options.height > 0 ? options.height : spanY;
2972
- const precision = options.precision ?? 3;
2970
+ const width = options2.width && options2.width > 0 ? options2.width : spanX;
2971
+ const height = options2.height && options2.height > 0 ? options2.height : spanY;
2972
+ const precision = options2.precision ?? 3;
2973
2973
  const number = (value) => format(value, precision);
2974
- const stroke = options.stroke ?? BLACK;
2975
- const strokeWidth = options.strokeWidth ?? 1;
2976
- const fill = options.fill ?? BLACK;
2977
- const filled = (options.fillMode ?? "none") === "solid";
2974
+ const stroke = options2.stroke ?? BLACK;
2975
+ const strokeWidth = options2.strokeWidth ?? 1;
2976
+ const fill = options2.fill ?? BLACK;
2977
+ const filled = (options2.fillMode ?? "none") === "solid";
2978
2978
  const colour = geometry.primitive.Cd;
2979
2979
  const strokeWidths = numericAttribute(geometry.primitive.width, "primitive.width", [1]);
2980
2980
  const opacities = numericAttribute(geometry.primitive.opacity, "primitive.opacity", [1]);
@@ -3037,7 +3037,7 @@ function geometryToSvg(geometry, options = {}) {
3037
3037
  const position = positionAttribute(geometry);
3038
3038
  for (let point = 0; point < geometry.pointCount; point += 1) {
3039
3039
  if (loose[point] !== 1) continue;
3040
- const radius = pointScale === void 0 ? options.pointRadius ?? 0.5 : readComponent(pointScale, point);
3040
+ const radius = pointScale === void 0 ? options2.pointRadius ?? 0.5 : readComponent(pointScale, point);
3041
3041
  const paint = paintOf(pointColour, point, "point") ?? stroke;
3042
3042
  const transparent = Color.alpha(paint) < 1;
3043
3043
  body.push(
@@ -3051,13 +3051,13 @@ function geometryToSvg(geometry, options = {}) {
3051
3051
  }
3052
3052
  const root = [
3053
3053
  `<svg xmlns="http://www.w3.org/2000/svg" width="${number(width)}" height="${number(height)}" viewBox="${number(minX)} ${number(-maxY)} ${number(spanX)} ${number(spanY)}">`,
3054
- ...options.background ? [indent(
3055
- `<rect x="${number(minX)}" y="${number(-maxY)}" width="${number(spanX)}" height="${number(spanY)}" fill="${Color.toHex(options.background)}" />`,
3054
+ ...options2.background ? [indent(
3055
+ `<rect x="${number(minX)}" y="${number(-maxY)}" width="${number(spanX)}" height="${number(spanY)}" fill="${Color.toHex(options2.background)}" />`,
3056
3056
  1
3057
3057
  )] : [],
3058
3058
  indent("<!-- Geometry is +Y up; SVG is +Y down. This is the one flip. -->", 1),
3059
3059
  indent(
3060
- `<g transform="scale(1,-1)" fill="${filled ? Color.toHex(fill) : "none"}"${filled && Color.alpha(fill) < 1 ? ` fill-opacity="${number(Color.alpha(fill))}"` : ""} stroke="${Color.toHex(stroke)}"${Color.alpha(stroke) < 1 ? ` stroke-opacity="${number(Color.alpha(stroke))}"` : ""} stroke-width="${number(strokeWidth)}"${options.opacity !== void 0 && options.opacity !== 1 ? ` opacity="${number(options.opacity)}"` : ""}>`,
3060
+ `<g transform="scale(1,-1)" fill="${filled ? Color.toHex(fill) : "none"}"${filled && Color.alpha(fill) < 1 ? ` fill-opacity="${number(Color.alpha(fill))}"` : ""} stroke="${Color.toHex(stroke)}"${Color.alpha(stroke) < 1 ? ` stroke-opacity="${number(Color.alpha(stroke))}"` : ""} stroke-width="${number(strokeWidth)}"${options2.opacity !== void 0 && options2.opacity !== 1 ? ` opacity="${number(options2.opacity)}"` : ""}>`,
3061
3061
  1
3062
3062
  ),
3063
3063
  ...body,
@@ -3110,25 +3110,25 @@ function format(value, precision) {
3110
3110
  const rounded = Number.parseFloat(value.toFixed(precision));
3111
3111
  return Object.is(rounded, -0) ? "0" : String(rounded);
3112
3112
  }
3113
- function validateOptions(options) {
3113
+ function validateOptions(options2) {
3114
3114
  for (const [name, value] of [
3115
- ["width", options.width],
3116
- ["height", options.height],
3117
- ["margin", options.margin],
3118
- ["strokeWidth", options.strokeWidth],
3119
- ["opacity", options.opacity],
3120
- ["pointRadius", options.pointRadius]
3115
+ ["width", options2.width],
3116
+ ["height", options2.height],
3117
+ ["margin", options2.margin],
3118
+ ["strokeWidth", options2.strokeWidth],
3119
+ ["opacity", options2.opacity],
3120
+ ["pointRadius", options2.pointRadius]
3121
3121
  ]) {
3122
3122
  if (value !== void 0 && !Number.isFinite(value))
3123
3123
  geometryError("svg-option", `${name} must be finite`);
3124
3124
  }
3125
- if ((options.width ?? 0) < 0 || (options.height ?? 0) < 0)
3125
+ if ((options2.width ?? 0) < 0 || (options2.height ?? 0) < 0)
3126
3126
  geometryError("svg-option", "width and height must not be negative");
3127
- if ((options.strokeWidth ?? 0) < 0 || (options.pointRadius ?? 0) < 0)
3127
+ if ((options2.strokeWidth ?? 0) < 0 || (options2.pointRadius ?? 0) < 0)
3128
3128
  geometryError("svg-option", "strokeWidth and pointRadius must not be negative");
3129
- if (options.opacity !== void 0 && (options.opacity < 0 || options.opacity > 1))
3129
+ if (options2.opacity !== void 0 && (options2.opacity < 0 || options2.opacity > 1))
3130
3130
  geometryError("svg-option", "opacity must be between 0 and 1");
3131
- const precision = options.precision ?? 3;
3131
+ const precision = options2.precision ?? 3;
3132
3132
  if (!Number.isSafeInteger(precision) || precision < 0 || precision > 15)
3133
3133
  geometryError("svg-precision", "precision must be an integer from 0 to 15");
3134
3134
  }
@@ -3842,7 +3842,7 @@ var init_core = __esm({
3842
3842
  previousRegistration
3843
3843
  ]);
3844
3844
  registrationsById = new Map(
3845
- coreNodeRegistrations.map((registration) => [registration.moduleId, registration])
3845
+ coreNodeRegistrations.map((registration2) => [registration2.moduleId, registration2])
3846
3846
  );
3847
3847
  }
3848
3848
  });
@@ -4223,24 +4223,24 @@ function multiply(matrix, x, y, z) {
4223
4223
  (row) => matrix[row] * x + matrix[4 + row] * y + matrix[8 + row] * z + matrix[12 + row]
4224
4224
  );
4225
4225
  }
4226
- function renderScene(geometry, options) {
4227
- const [width, height] = options.size;
4228
- const canvas = options.surface(
4226
+ function renderScene(geometry, options2) {
4227
+ const [width, height] = options2.size;
4228
+ const canvas = options2.surface(
4229
4229
  Math.max(1, Math.round(width)),
4230
4230
  Math.max(1, Math.round(height))
4231
4231
  );
4232
4232
  const context = canvas.getContext("2d");
4233
4233
  if (!context) throw new Error("no 2D context on this surface");
4234
- if (options.background && options.background[3] > 0) {
4235
- context.fillStyle = css(options.background);
4234
+ if (options2.background && options2.background[3] > 0) {
4235
+ context.fillStyle = css(options2.background);
4236
4236
  context.fillRect(0, 0, canvas.width, canvas.height);
4237
4237
  }
4238
- for (const one of geometry) drawGeometry(context, canvas, one, options);
4238
+ for (const one of geometry) drawGeometry(context, canvas, one, options2);
4239
4239
  return canvas;
4240
4240
  }
4241
- function drawGeometry(context, canvas, geometry, options) {
4242
- const view = viewMatrix(options.camera);
4243
- const projection = projectionMatrix(options.camera);
4241
+ function drawGeometry(context, canvas, geometry, options2) {
4242
+ const view = viewMatrix(options2.camera);
4243
+ const projection = projectionMatrix(options2.camera);
4244
4244
  const position = geometry.point[POSITION_ATTRIBUTE];
4245
4245
  if (!position || position.storage === "string") return;
4246
4246
  const size = position.size;
@@ -4265,15 +4265,15 @@ function drawGeometry(context, canvas, geometry, options) {
4265
4265
  const widths = numericAttribute(geometry.primitive.width, "primitive.width", [1]);
4266
4266
  context.lineCap = "round";
4267
4267
  context.lineJoin = "round";
4268
- context.globalAlpha = options.opacity;
4268
+ context.globalAlpha = options2.opacity;
4269
4269
  const { vertexPoints, offsets, closed } = geometry.topology;
4270
4270
  const drawn = /* @__PURE__ */ new Set();
4271
4271
  for (let primitive = 0; primitive < geometry.primitiveCount; primitive += 1) {
4272
4272
  const from = offsets[primitive];
4273
4273
  const to = offsets[primitive + 1];
4274
4274
  if (to - from < 2) continue;
4275
- context.strokeStyle = colours ? css(readElement(colours, primitive)) : css(options.stroke);
4276
- context.lineWidth = widths ? readElement(widths, primitive)[0] : options.strokeWidth;
4275
+ context.strokeStyle = colours ? css(readElement(colours, primitive)) : css(options2.stroke);
4276
+ context.lineWidth = widths ? readElement(widths, primitive)[0] : options2.strokeWidth;
4277
4277
  context.beginPath();
4278
4278
  let open = false;
4279
4279
  for (let vertex = from; vertex < to; vertex += 1) {
@@ -4296,14 +4296,14 @@ function drawGeometry(context, canvas, geometry, options) {
4296
4296
  }
4297
4297
  context.stroke();
4298
4298
  }
4299
- if (options.drawPoints && options.pointRadius > 0) {
4300
- context.fillStyle = css(options.stroke);
4299
+ if (options2.drawPoints && options2.pointRadius > 0) {
4300
+ context.fillStyle = css(options2.stroke);
4301
4301
  for (let index = 0; index < projected.length; index += 1) {
4302
4302
  if (drawn.has(index)) continue;
4303
4303
  const at = projected[index];
4304
4304
  if (at.w <= 0) continue;
4305
4305
  context.beginPath();
4306
- context.arc(at.x, at.y, options.pointRadius, 0, Math.PI * 2);
4306
+ context.arc(at.x, at.y, options2.pointRadius, 0, Math.PI * 2);
4307
4307
  context.fill();
4308
4308
  }
4309
4309
  }
@@ -4485,17 +4485,300 @@ var init_geo = __esm({
4485
4485
  svgExportRegistration
4486
4486
  ]);
4487
4487
  registrationsById2 = new Map(
4488
- geoNodeRegistrations.map((registration) => [
4489
- registration.moduleId,
4490
- registration
4488
+ geoNodeRegistrations.map((registration2) => [
4489
+ registration2.moduleId,
4490
+ registration2
4491
4491
  ])
4492
4492
  );
4493
4493
  }
4494
4494
  });
4495
4495
 
4496
+ // packages/runtime/src/builtins/math/definitions.ts
4497
+ function asComponents(value, label2) {
4498
+ if (typeof value === "number") return [value];
4499
+ if (Array.isArray(value) && value.every((part) => typeof part === "number"))
4500
+ return value;
4501
+ if (value === void 0 || value === null) return [0];
4502
+ throw new Error(
4503
+ `${label2} needs a number or a vector of numbers, and got ${describe(value)}`
4504
+ );
4505
+ }
4506
+ function describe(value) {
4507
+ if (Array.isArray(value)) return `an array of ${value.length}`;
4508
+ return `a ${typeof value}`;
4509
+ }
4510
+ function combine(left, right, operate) {
4511
+ const a = asComponents(left, "the first input");
4512
+ const b = asComponents(right, "the second input");
4513
+ if (a.length !== b.length && a.length !== 1 && b.length !== 1)
4514
+ throw new Error(
4515
+ `cannot combine a vector of ${a.length} with a vector of ${b.length}`
4516
+ );
4517
+ const width = Math.max(a.length, b.length);
4518
+ const out = [];
4519
+ for (let index = 0; index < width; index += 1)
4520
+ out.push(operate(a[a.length === 1 ? 0 : index], b[b.length === 1 ? 0 : index]));
4521
+ return width === 1 ? out[0] : out;
4522
+ }
4523
+ function map(value, operate) {
4524
+ const components = asComponents(value, "the input");
4525
+ const out = components.map(operate);
4526
+ return out.length === 1 ? out[0] : out;
4527
+ }
4528
+ function divide(a, b) {
4529
+ return b === 0 ? 0 : a / b;
4530
+ }
4531
+ function options(table) {
4532
+ return Object.keys(table).map((value) => ({ value, label: label(value) }));
4533
+ }
4534
+ function label(value) {
4535
+ return value.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
4536
+ }
4537
+ function executeBinary(context) {
4538
+ const operate = BINARY[context.props.operation];
4539
+ if (!operate)
4540
+ throw new Error(
4541
+ `cascade.math.Binary has no operation "${context.props.operation}"`
4542
+ );
4543
+ context.outputs.value.set(combine(context.inputs.a, context.inputs.b, operate));
4544
+ }
4545
+ function executeUnary(context) {
4546
+ const operate = UNARY[context.props.operation];
4547
+ if (!operate)
4548
+ throw new Error(
4549
+ `cascade.math.Unary has no operation "${context.props.operation}"`
4550
+ );
4551
+ context.outputs.value.set(map(context.inputs.value, operate));
4552
+ }
4553
+ function executeClamp(context) {
4554
+ const lower = combine(context.inputs.value, context.inputs.min, Math.max);
4555
+ context.outputs.value.set(combine(lower, context.inputs.max, Math.min));
4556
+ }
4557
+ function executeMix(context) {
4558
+ const bias = context.inputs.bias ?? 0.5;
4559
+ const delta = combine(context.inputs.b, context.inputs.a, (b, a) => b - a);
4560
+ context.outputs.value.set(
4561
+ combine(context.inputs.a, delta, (a, d) => a + d * bias)
4562
+ );
4563
+ }
4564
+ function executeCompare(context) {
4565
+ const operation = context.props.operation;
4566
+ const test = COMPARE[operation];
4567
+ if (!test)
4568
+ throw new Error(`cascade.math.Compare has no test "${context.props.operation}"`);
4569
+ const a = context.inputs.a ?? 0;
4570
+ const b = context.inputs.b ?? 0;
4571
+ const tolerance = context.props.tolerance;
4572
+ if (tolerance > 0 && (operation === "equal" || operation === "not-equal")) {
4573
+ const near = Math.abs(a - b) <= tolerance;
4574
+ context.outputs.result.set(operation === "equal" ? near : !near);
4575
+ return;
4576
+ }
4577
+ context.outputs.result.set(test(a, b));
4578
+ }
4579
+ function registration(name, definition, execute2) {
4580
+ return {
4581
+ kind: "definition-v1",
4582
+ moduleId: `cascade.math.${name}`,
4583
+ definition,
4584
+ loadExecute: async () => execute2
4585
+ };
4586
+ }
4587
+ var BINARY, DEG2, UNARY, COMPARE, binaryDefinition, unaryDefinition, clampDefinition, mixDefinition, compareDefinition, binaryRegistration, unaryRegistration, clampRegistration, mixRegistration, compareRegistration, mathNodeRegistrations, mathNodeDefinitions;
4588
+ var init_definitions2 = __esm({
4589
+ "packages/runtime/src/builtins/math/definitions.ts"() {
4590
+ "use strict";
4591
+ BINARY = {
4592
+ add: (a, b) => a + b,
4593
+ subtract: (a, b) => a - b,
4594
+ multiply: (a, b) => a * b,
4595
+ divide,
4596
+ power: (a, b) => a ** b,
4597
+ modulo: (a, b) => b === 0 ? 0 : a % b,
4598
+ min: (a, b) => Math.min(a, b),
4599
+ max: (a, b) => Math.max(a, b),
4600
+ atan2: (a, b) => Math.atan2(a, b)
4601
+ };
4602
+ DEG2 = Math.PI / 180;
4603
+ UNARY = {
4604
+ abs: Math.abs,
4605
+ negate: (a) => -a,
4606
+ floor: Math.floor,
4607
+ ceil: Math.ceil,
4608
+ round: Math.round,
4609
+ sqrt: (a) => a < 0 ? 0 : Math.sqrt(a),
4610
+ sign: Math.sign,
4611
+ // Trigonometry in RADIANS, matching the expression engine, where bare `sin`
4612
+ // is radians too. `radians` and `degrees` are here for the conversion, and
4613
+ // the divergence from `Transform`'s degrees is recorded in AGENTS.md.
4614
+ sin: Math.sin,
4615
+ cos: Math.cos,
4616
+ tan: Math.tan,
4617
+ asin: (a) => Math.asin(Math.max(-1, Math.min(1, a))),
4618
+ acos: (a) => Math.acos(Math.max(-1, Math.min(1, a))),
4619
+ exp: Math.exp,
4620
+ log: (a) => a <= 0 ? 0 : Math.log(a),
4621
+ radians: (a) => a * DEG2,
4622
+ degrees: (a) => a / DEG2,
4623
+ /** The fractional part, always positive — `fract(-0.25)` is `0.75`, which is
4624
+ * what a repeating pattern wants and what GLSL's `fract` does. */
4625
+ fract: (a) => a - Math.floor(a)
4626
+ };
4627
+ COMPARE = {
4628
+ greater: (a, b) => a > b,
4629
+ "greater-or-equal": (a, b) => a >= b,
4630
+ less: (a, b) => a < b,
4631
+ "less-or-equal": (a, b) => a <= b,
4632
+ equal: (a, b) => a === b,
4633
+ "not-equal": (a, b) => a !== b
4634
+ };
4635
+ binaryDefinition = {
4636
+ apiVersion: 1,
4637
+ label: "Binary Math",
4638
+ description: "Two values, one operation. Component-wise, and a scalar broadcasts against a vector.",
4639
+ icon: "Plus",
4640
+ runsOn: "portable",
4641
+ inputs: {
4642
+ a: { kind: "data", type: "any", default: 0, description: "First value." },
4643
+ b: { kind: "data", type: "any", default: 0, description: "Second value." }
4644
+ },
4645
+ outputs: { value: { kind: "data", type: "any" } },
4646
+ props: {
4647
+ operation: {
4648
+ type: "string",
4649
+ default: "add",
4650
+ control: "select",
4651
+ options: options(BINARY),
4652
+ label: "Operation"
4653
+ }
4654
+ }
4655
+ };
4656
+ unaryDefinition = {
4657
+ apiVersion: 1,
4658
+ label: "Unary Math",
4659
+ description: "One value, one operation. Trigonometry is in radians.",
4660
+ icon: "FunctionSquare",
4661
+ runsOn: "portable",
4662
+ inputs: {
4663
+ value: { kind: "data", type: "any", default: 0 }
4664
+ },
4665
+ outputs: { value: { kind: "data", type: "any" } },
4666
+ props: {
4667
+ operation: {
4668
+ type: "string",
4669
+ default: "abs",
4670
+ control: "select",
4671
+ options: options(UNARY),
4672
+ label: "Operation"
4673
+ }
4674
+ }
4675
+ };
4676
+ clampDefinition = {
4677
+ apiVersion: 1,
4678
+ label: "Clamp",
4679
+ description: "Hold a value between two bounds.",
4680
+ icon: "Brackets",
4681
+ runsOn: "portable",
4682
+ inputs: {
4683
+ value: { kind: "data", type: "any", default: 0 },
4684
+ min: { kind: "data", type: "any", default: 0 },
4685
+ max: { kind: "data", type: "any", default: 1 }
4686
+ },
4687
+ outputs: { value: { kind: "data", type: "any" } },
4688
+ props: {}
4689
+ };
4690
+ mixDefinition = {
4691
+ apiVersion: 1,
4692
+ label: "Mix",
4693
+ description: "Blend between two values. Houdini's lerp.",
4694
+ icon: "Blend",
4695
+ runsOn: "portable",
4696
+ inputs: {
4697
+ a: { kind: "data", type: "any", default: 0, description: "The value at 0." },
4698
+ b: { kind: "data", type: "any", default: 1, description: "The value at 1." },
4699
+ bias: {
4700
+ kind: "data",
4701
+ type: "float",
4702
+ default: 0.5,
4703
+ min: 0,
4704
+ max: 1,
4705
+ step: 0.01,
4706
+ description: "Not clamped: past 0..1 this extrapolates, which is useful."
4707
+ }
4708
+ },
4709
+ outputs: { value: { kind: "data", type: "any" } },
4710
+ props: {}
4711
+ };
4712
+ compareDefinition = {
4713
+ apiVersion: 1,
4714
+ label: "Compare",
4715
+ description: "Test two values. Outputs a bool, for a Switch.",
4716
+ icon: "Equal",
4717
+ runsOn: "portable",
4718
+ inputs: {
4719
+ a: { kind: "data", type: "float", default: 0 },
4720
+ b: { kind: "data", type: "float", default: 0 }
4721
+ },
4722
+ outputs: { result: { kind: "data", type: "bool" } },
4723
+ props: {
4724
+ operation: {
4725
+ type: "string",
4726
+ default: "greater",
4727
+ control: "select",
4728
+ options: options(COMPARE),
4729
+ label: "Test"
4730
+ },
4731
+ /**
4732
+ * Scalars only, unlike the rest of this namespace.
4733
+ *
4734
+ * "Is this vector greater than that one" has no single right answer — all
4735
+ * components, any component, or the length — and a node that picked one
4736
+ * silently would be wrong for the other two readings. A comparison feeding
4737
+ * a `Switch` wants one boolean, so the input is a `float` and the question
4738
+ * does not arise.
4739
+ */
4740
+ tolerance: {
4741
+ type: "float",
4742
+ default: 0,
4743
+ min: 0,
4744
+ label: "Tolerance",
4745
+ description: "Slack for Equal and Not Equal. Floating point rarely lands on exactly equal."
4746
+ }
4747
+ }
4748
+ };
4749
+ binaryRegistration = registration("Binary", binaryDefinition, executeBinary);
4750
+ unaryRegistration = registration("Unary", unaryDefinition, executeUnary);
4751
+ clampRegistration = registration("Clamp", clampDefinition, executeClamp);
4752
+ mixRegistration = registration("Mix", mixDefinition, executeMix);
4753
+ compareRegistration = registration("Compare", compareDefinition, executeCompare);
4754
+ mathNodeRegistrations = Object.freeze([
4755
+ binaryRegistration,
4756
+ unaryRegistration,
4757
+ clampRegistration,
4758
+ mixRegistration,
4759
+ compareRegistration
4760
+ ]);
4761
+ mathNodeDefinitions = Object.freeze([
4762
+ ["cascade.math.Binary", binaryDefinition],
4763
+ ["cascade.math.Unary", unaryDefinition],
4764
+ ["cascade.math.Clamp", clampDefinition],
4765
+ ["cascade.math.Mix", mixDefinition],
4766
+ ["cascade.math.Compare", compareDefinition]
4767
+ ]);
4768
+ }
4769
+ });
4770
+
4771
+ // packages/runtime/src/builtins/math/index.ts
4772
+ var init_math2 = __esm({
4773
+ "packages/runtime/src/builtins/math/index.ts"() {
4774
+ "use strict";
4775
+ init_definitions2();
4776
+ }
4777
+ });
4778
+
4496
4779
  // packages/runtime/src/pop/state.ts
4497
- function step(state, forces, options) {
4498
- const { timestep, maxSpeed = 0, wrap } = options;
4780
+ function step(state, forces, options2) {
4781
+ const { timestep, maxSpeed = 0, wrap } = options2;
4499
4782
  const { count, size } = state;
4500
4783
  if (count === 0) return state;
4501
4784
  const acceleration = new Float32Array(count * size);
@@ -4856,8 +5139,8 @@ function drag(airresist) {
4856
5139
  }
4857
5140
  };
4858
5141
  }
4859
- function noiseField(options) {
4860
- const { seed, frequency, amplitude, evolve = 0 } = options;
5142
+ function noiseField(options2) {
5143
+ const { seed, frequency, amplitude, evolve = 0 } = options2;
4861
5144
  return {
4862
5145
  label: "noiseField",
4863
5146
  accelerate(state, size) {
@@ -4873,8 +5156,8 @@ function noiseField(options) {
4873
5156
  }
4874
5157
  };
4875
5158
  }
4876
- function separation(options) {
4877
- const { radius, strength } = options;
5159
+ function separation(options2) {
5160
+ const { radius, strength } = options2;
4878
5161
  return {
4879
5162
  label: "separation",
4880
5163
  accelerate(state, size) {
@@ -4945,8 +5228,8 @@ function hash(seed, x, y, z) {
4945
5228
  const n = Math.sin(seed * 12.9898 + x * 78.233 + y * 37.719 + z * 19.371) * 43758.5453;
4946
5229
  return n - Math.floor(n);
4947
5230
  }
4948
- function attract(options) {
4949
- const { targets, targetSize, targetCount, amplitude, radius } = options;
5231
+ function attract(options2) {
5232
+ const { targets, targetSize, targetCount, amplitude, radius } = options2;
4950
5233
  const cell = radius > 0 ? radius : 0;
4951
5234
  const buckets = /* @__PURE__ */ new Map();
4952
5235
  if (cell > 0) {
@@ -5008,7 +5291,7 @@ function attract(options) {
5008
5291
  function bucketKey(x, y, cell) {
5009
5292
  return `${Math.floor(x / cell)}:${Math.floor(y / cell)}`;
5010
5293
  }
5011
- function flowField(options) {
5294
+ function flowField(options2) {
5012
5295
  const {
5013
5296
  positions,
5014
5297
  positionSize: positionSize2,
@@ -5020,7 +5303,7 @@ function flowField(options) {
5020
5303
  radius,
5021
5304
  level = 0,
5022
5305
  hold = 0
5023
- } = options;
5306
+ } = options2;
5024
5307
  const cell = radius > 0 ? radius : 1;
5025
5308
  const buckets = /* @__PURE__ */ new Map();
5026
5309
  for (let sample = 0; sample < count; sample += 1) {
@@ -5135,8 +5418,8 @@ function trailFrame(geometry) {
5135
5418
  size: position.size
5136
5419
  };
5137
5420
  }
5138
- function buildTrails(history, current, options = {}) {
5139
- const { result = "polylines", length = 12, increment = 1, velocityscale = 1 } = options;
5421
+ function buildTrails(history, current, options2 = {}) {
5422
+ const { result = "polylines", length = 12, increment = 1, velocityscale = 1 } = options2;
5140
5423
  if (result === "preserve") return current;
5141
5424
  const frames = sampled(history, length, increment);
5142
5425
  const size = frames[0]?.size ?? 2;
@@ -5552,7 +5835,7 @@ function executeSimulate(context) {
5552
5835
  );
5553
5836
  }
5554
5837
  var sourceDefinition, solverDefinition, noiseForceDefinition, separateDefinition, fieldForceDefinition, trailDefinition, simulateDefinition, simulateRegistration, sourceRegistration, solverRegistration, noiseForceRegistration, separateRegistration, fieldForceRegistration, trailRegistration, popNodeRegistrations, popNodeDefinitions;
5555
- var init_definitions2 = __esm({
5838
+ var init_definitions3 = __esm({
5556
5839
  "packages/runtime/src/builtins/pop/definitions.ts"() {
5557
5840
  "use strict";
5558
5841
  init_src();
@@ -5945,7 +6228,7 @@ var init_definitions2 = __esm({
5945
6228
  var init_pop2 = __esm({
5946
6229
  "packages/runtime/src/builtins/pop/index.ts"() {
5947
6230
  "use strict";
5948
- init_definitions2();
6231
+ init_definitions3();
5949
6232
  }
5950
6233
  });
5951
6234
 
@@ -5991,12 +6274,12 @@ function executeScene(context) {
5991
6274
  return;
5992
6275
  }
5993
6276
  throw new Error(
5994
- `cascade.scene.Scene input ${index} is not geometry, a camera, a light or a scene (received ${describe(value)}). A scene can only hold those.`
6277
+ `cascade.scene.Scene input ${index} is not geometry, a camera, a light or a scene (received ${describe2(value)}). A scene can only hold those.`
5995
6278
  );
5996
6279
  });
5997
6280
  context.outputs.scene.set({ geometry, camera, lights });
5998
6281
  }
5999
- function describe(value) {
6282
+ function describe2(value) {
6000
6283
  if (Array.isArray(value)) return `an array of ${value.length}`;
6001
6284
  if (typeof value === "object" && value !== null) {
6002
6285
  const kind = value.kind;
@@ -6016,7 +6299,7 @@ function executeLight(context) {
6016
6299
  });
6017
6300
  }
6018
6301
  var SCENE_NAMESPACE, sceneDefinition, lightDefinition, sceneRegistration, lightRegistration, sceneNodeRegistrations, sceneNodeDefinitions;
6019
- var init_definitions3 = __esm({
6302
+ var init_definitions4 = __esm({
6020
6303
  "packages/runtime/src/builtins/scene/definitions.ts"() {
6021
6304
  "use strict";
6022
6305
  init_src();
@@ -6124,7 +6407,7 @@ var init_definitions3 = __esm({
6124
6407
  var init_scene3 = __esm({
6125
6408
  "packages/runtime/src/builtins/scene/index.ts"() {
6126
6409
  "use strict";
6127
- init_definitions3();
6410
+ init_definitions4();
6128
6411
  }
6129
6412
  });
6130
6413
 
@@ -6138,36 +6421,38 @@ var init_builtins = __esm({
6138
6421
  "use strict";
6139
6422
  init_core();
6140
6423
  init_geo();
6424
+ init_math2();
6141
6425
  init_pop2();
6142
6426
  init_scene3();
6143
6427
  builtinNodeRegistrations = Object.freeze([
6144
6428
  ...coreNodeRegistrations,
6145
6429
  ...geoNodeRegistrations,
6430
+ ...mathNodeRegistrations,
6146
6431
  ...popNodeRegistrations,
6147
6432
  ...sceneNodeRegistrations
6148
6433
  ]);
6149
6434
  registrationsById3 = new Map(
6150
- builtinNodeRegistrations.map((registration) => [
6151
- registration.moduleId,
6152
- registration
6435
+ builtinNodeRegistrations.map((registration2) => [
6436
+ registration2.moduleId,
6437
+ registration2
6153
6438
  ])
6154
6439
  );
6155
6440
  }
6156
6441
  });
6157
6442
 
6158
6443
  // packages/runtime/src/runtime.ts
6159
- function createRuntime(options) {
6160
- return new Runtime(options.host, [...builtinNodeRegistrations, ...options.nodes ?? []]);
6444
+ function createRuntime(options2) {
6445
+ return new Runtime(options2.host, [...builtinNodeRegistrations, ...options2.nodes ?? []]);
6161
6446
  }
6162
6447
  function bindShellToRun(shell, runSignal) {
6163
6448
  if (!shell) return void 0;
6164
- const options = (value) => ({
6449
+ const options2 = (value) => ({
6165
6450
  ...value,
6166
6451
  signal: combineAbortSignals(runSignal, value?.signal)
6167
6452
  });
6168
6453
  return Object.freeze({
6169
- run: (command, args, value) => shell.run(command, args, options(value)),
6170
- runJson: (command, args, value) => shell.runJson(command, args, options(value))
6454
+ run: (command, args, value) => shell.run(command, args, options2(value)),
6455
+ runJson: (command, args, value) => shell.runJson(command, args, options2(value))
6171
6456
  });
6172
6457
  }
6173
6458
  function combineAbortSignals(first, second) {
@@ -6180,7 +6465,7 @@ function combineAbortSignals(first, second) {
6180
6465
  get reason() {
6181
6466
  return first.aborted ? first.reason : second.reason;
6182
6467
  },
6183
- addEventListener(_type, listener, options) {
6468
+ addEventListener(_type, listener, options2) {
6184
6469
  if (first.aborted || second.aborted) {
6185
6470
  listener();
6186
6471
  return;
@@ -6192,8 +6477,8 @@ function combineAbortSignals(first, second) {
6192
6477
  listener();
6193
6478
  };
6194
6479
  wrappers.set(listener, wrapped);
6195
- first.addEventListener("abort", wrapped, options);
6196
- second.addEventListener("abort", wrapped, options);
6480
+ first.addEventListener("abort", wrapped, options2);
6481
+ second.addEventListener("abort", wrapped, options2);
6197
6482
  },
6198
6483
  removeEventListener(_type, listener) {
6199
6484
  const wrapped = wrappers.get(listener);
@@ -6204,11 +6489,11 @@ function combineAbortSignals(first, second) {
6204
6489
  }
6205
6490
  };
6206
6491
  }
6207
- function materialize(authored, registration) {
6492
+ function materialize(authored, registration2) {
6208
6493
  const inputs = {};
6209
6494
  const props = {};
6210
6495
  for (const [name, definition] of Object.entries(
6211
- registration.definition.inputs ?? {}
6496
+ registration2.definition.inputs ?? {}
6212
6497
  ))
6213
6498
  if (definition.kind === "data" && "default" in definition)
6214
6499
  inputs[name] = runtimeValue(
@@ -6219,7 +6504,7 @@ function materialize(authored, registration) {
6219
6504
  );
6220
6505
  if (Array.isArray(authored.inputs))
6221
6506
  for (const saved of authored.inputs) {
6222
- const definition = registration.definition.inputs?.[saved.name];
6507
+ const definition = registration2.definition.inputs?.[saved.name];
6223
6508
  if (definition?.kind === "data") {
6224
6509
  inputs[saved.name] = runtimeValue(
6225
6510
  "defaultValue" in saved ? saved.defaultValue : saved.value,
@@ -6231,7 +6516,7 @@ function materialize(authored, registration) {
6231
6516
  }
6232
6517
  else if (authored.inputs)
6233
6518
  for (const [name, saved] of Object.entries(authored.inputs)) {
6234
- const definition = registration.definition.inputs?.[name];
6519
+ const definition = registration2.definition.inputs?.[name];
6235
6520
  if (definition?.kind === "data")
6236
6521
  inputs[name] = runtimeValue(
6237
6522
  saved,
@@ -6241,7 +6526,7 @@ function materialize(authored, registration) {
6241
6526
  );
6242
6527
  }
6243
6528
  for (const [name, definition] of Object.entries(
6244
- registration.definition.props ?? {}
6529
+ registration2.definition.props ?? {}
6245
6530
  ))
6246
6531
  props[name] = runtimeValue(
6247
6532
  definition.default,
@@ -6257,7 +6542,7 @@ function materialize(authored, registration) {
6257
6542
  const bindings = {};
6258
6543
  const storedProps = authored.props ?? {};
6259
6544
  for (const [name, definition] of Object.entries(
6260
- registration.definition.props ?? {}
6545
+ registration2.definition.props ?? {}
6261
6546
  )) {
6262
6547
  if (!definition.expression || name in storedProps) continue;
6263
6548
  bindings[name] = {
@@ -6266,7 +6551,7 @@ function materialize(authored, registration) {
6266
6551
  };
6267
6552
  }
6268
6553
  for (const [name, saved] of Object.entries(storedProps)) {
6269
- const definition = registration.definition.props?.[name];
6554
+ const definition = registration2.definition.props?.[name];
6270
6555
  if (!definition) continue;
6271
6556
  const object = saved && typeof saved === "object" && "value" in saved ? saved : null;
6272
6557
  const value = runtimeValue(
@@ -6291,26 +6576,26 @@ function materialize(authored, registration) {
6291
6576
  }
6292
6577
  return {
6293
6578
  id: authored.id,
6294
- moduleId: registration.moduleId,
6579
+ moduleId: registration2.moduleId,
6295
6580
  ...authored.parent ? { parent: authored.parent } : {},
6296
- registration,
6297
- definition: registration.definition,
6581
+ registration: registration2,
6582
+ definition: registration2.definition,
6298
6583
  inputs,
6299
6584
  props,
6300
6585
  bindings,
6301
- strayParams: strayParams(authored, registration),
6302
- unknownProps: unknownProps(authored, registration),
6586
+ strayParams: strayParams(authored, registration2),
6587
+ unknownProps: unknownProps(authored, registration2),
6303
6588
  outputs: /* @__PURE__ */ new Map()
6304
6589
  };
6305
6590
  }
6306
- function unknownProps(authored, registration) {
6591
+ function unknownProps(authored, registration2) {
6307
6592
  const props = authored.props;
6308
6593
  if (!props || Array.isArray(props)) return [];
6309
- const declared = registration.definition.props ?? {};
6594
+ const declared = registration2.definition.props ?? {};
6310
6595
  return Object.keys(props).filter((name) => !(name in declared));
6311
6596
  }
6312
- function strayParams(authored, registration) {
6313
- const definition = registration.definition;
6597
+ function strayParams(authored, registration2) {
6598
+ const definition = registration2.definition;
6314
6599
  const declared = /* @__PURE__ */ new Set([
6315
6600
  ...Object.keys(definition.props ?? {}),
6316
6601
  ...Object.keys(definition.inputs ?? {})
@@ -6320,14 +6605,14 @@ function strayParams(authored, registration) {
6320
6605
  (name) => typeof name === "string" && declared.has(name)
6321
6606
  );
6322
6607
  }
6323
- function specializeCoreRegistration(registration, authored, nodes) {
6324
- if (registration.moduleId === "cascade.core.Input" || registration.moduleId === "cascade.core.Output") {
6608
+ function specializeCoreRegistration(registration2, authored, nodes) {
6609
+ if (registration2.moduleId === "cascade.core.Input" || registration2.moduleId === "cascade.core.Output") {
6325
6610
  const type = savedDataType(authored.props?.dataType);
6326
- const inputName2 = registration.moduleId === "cascade.core.Input" ? "value" : "input";
6611
+ const inputName2 = registration2.moduleId === "cascade.core.Input" ? "value" : "input";
6327
6612
  return Object.freeze({
6328
- ...registration,
6613
+ ...registration2,
6329
6614
  definition: Object.freeze({
6330
- ...registration.definition,
6615
+ ...registration2.definition,
6331
6616
  inputs: Object.freeze({
6332
6617
  [inputName2]: dataInputDefinition(type)
6333
6618
  }),
@@ -6337,14 +6622,14 @@ function specializeCoreRegistration(registration, authored, nodes) {
6337
6622
  })
6338
6623
  });
6339
6624
  }
6340
- if (registration.moduleId !== "cascade.core.Subnet") return registration;
6625
+ if (registration2.moduleId !== "cascade.core.Subnet") return registration2;
6341
6626
  const children = nodes.filter((candidate) => candidate.parent === authored.id);
6342
6627
  const inputs = indexedBoundaryPorts(children, "cascade.core.Input", "inputIndex", "input");
6343
6628
  const outputs = indexedBoundaryPorts(children, "cascade.core.Output", "outputIndex", "output");
6344
6629
  return Object.freeze({
6345
- ...registration,
6630
+ ...registration2,
6346
6631
  definition: Object.freeze({
6347
- ...registration.definition,
6632
+ ...registration2.definition,
6348
6633
  ...Object.keys(inputs).length ? { inputs: Object.freeze(inputs) } : {},
6349
6634
  ...Object.keys(outputs).length ? { outputs: Object.freeze(outputs) } : {}
6350
6635
  })
@@ -6516,11 +6801,11 @@ function validateHierarchy(nodes, byId) {
6516
6801
  }
6517
6802
  }
6518
6803
  }
6519
- function snapshotRegistration(registration) {
6520
- if (registration.kind === "legacy-dynamic")
6521
- return Object.freeze({ ...registration });
6522
- const definition = snapshot(registration.definition);
6523
- return Object.freeze({ ...registration, definition });
6804
+ function snapshotRegistration(registration2) {
6805
+ if (registration2.kind === "legacy-dynamic")
6806
+ return Object.freeze({ ...registration2 });
6807
+ const definition = snapshot(registration2.definition);
6808
+ return Object.freeze({ ...registration2, definition });
6524
6809
  }
6525
6810
  function isGated(node) {
6526
6811
  return Object.values(node.definition.inputs ?? {}).some(
@@ -6764,9 +7049,9 @@ var init_runtime = __esm({
6764
7049
  this.aborted = false;
6765
7050
  this.listeners = /* @__PURE__ */ new Set();
6766
7051
  }
6767
- addEventListener(_type, listener, options) {
7052
+ addEventListener(_type, listener, options2) {
6768
7053
  if (this.aborted) this.call(listener);
6769
- else if (options?.once) {
7054
+ else if (options2?.once) {
6770
7055
  const once = () => {
6771
7056
  this.listeners.delete(once);
6772
7057
  listener();
@@ -6803,22 +7088,22 @@ var init_runtime = __esm({
6803
7088
  this.graphs = /* @__PURE__ */ new Set();
6804
7089
  this.sealed = false;
6805
7090
  this.disposed = false;
6806
- for (const registration of registrations) this.registerNode(registration);
7091
+ for (const registration2 of registrations) this.registerNode(registration2);
6807
7092
  }
6808
- registerNode(registration) {
7093
+ registerNode(registration2) {
6809
7094
  if (this.sealed || this.disposed)
6810
7095
  misuse(
6811
7096
  "runtime/registry-sealed",
6812
7097
  "Node registrations are sealed after the first load"
6813
7098
  );
6814
- if (this.registrations.has(registration.moduleId))
7099
+ if (this.registrations.has(registration2.moduleId))
6815
7100
  misuse(
6816
7101
  "runtime/duplicate-registration",
6817
- `Duplicate registration ${registration.moduleId}`
7102
+ `Duplicate registration ${registration2.moduleId}`
6818
7103
  );
6819
7104
  this.registrations.set(
6820
- registration.moduleId,
6821
- snapshotRegistration(registration)
7105
+ registration2.moduleId,
7106
+ snapshotRegistration(registration2)
6822
7107
  );
6823
7108
  }
6824
7109
  async load(document2) {
@@ -6853,18 +7138,18 @@ var init_runtime = __esm({
6853
7138
  "runtime/legacy-adapter-required",
6854
7139
  `Legacy module ${moduleId2} requires the explicit legacy adapter`
6855
7140
  );
6856
- const registration = specializeCoreRegistration(
7141
+ const registration2 = specializeCoreRegistration(
6857
7142
  snapshot2,
6858
7143
  authored,
6859
7144
  document2.nodes
6860
7145
  );
6861
- const diagnostics = validateNodeDefinition(registration.definition);
7146
+ const diagnostics = validateNodeDefinition(registration2.definition);
6862
7147
  if (diagnostics.length)
6863
7148
  throw new CascadeRuntimeError(
6864
7149
  "runtime/definition-invalid",
6865
7150
  diagnostics
6866
7151
  );
6867
- nodes.push(materialize(authored, registration));
7152
+ nodes.push(materialize(authored, registration2));
6868
7153
  }
6869
7154
  const byId = new Map(nodes.map((node) => [node.id, node]));
6870
7155
  validateHierarchy(nodes, byId);
@@ -7184,17 +7469,17 @@ var init_runtime = __esm({
7184
7469
  getFps() {
7185
7470
  return this.fps;
7186
7471
  }
7187
- run(options = {}) {
7188
- if (options.fps !== void 0) this.setFps(options.fps);
7189
- if (options.frame !== void 0) this.setFrame(options.frame);
7472
+ run(options2 = {}) {
7473
+ if (options2.fps !== void 0) this.setFps(options2.fps);
7474
+ if (options2.frame !== void 0) this.setFrame(options2.frame);
7190
7475
  else this.animator.resolveAll();
7191
7476
  return this.trackRun(
7192
- options.target ?? { kind: "all" },
7193
- options.signal,
7194
- options.runId
7477
+ options2.target ?? { kind: "all" },
7478
+ options2.signal,
7479
+ options2.runId
7195
7480
  );
7196
7481
  }
7197
- trigger(nodeId, inputName2, payload, options = {}) {
7482
+ trigger(nodeId, inputName2, payload, options2 = {}) {
7198
7483
  const node = this.requireNode(nodeId);
7199
7484
  if (node.definition.inputs?.[inputName2]?.kind !== "trigger")
7200
7485
  return Promise.reject(
@@ -7205,8 +7490,8 @@ var init_runtime = __esm({
7205
7490
  );
7206
7491
  return this.trackRun(
7207
7492
  { kind: "node", nodeId },
7208
- options.signal,
7209
- options.runId,
7493
+ options2.signal,
7494
+ options2.runId,
7210
7495
  { nodeId, inputName: inputName2, payload }
7211
7496
  );
7212
7497
  }
@@ -7922,7 +8207,7 @@ var init_src2 = __esm({
7922
8207
  });
7923
8208
 
7924
8209
  // packages/runtime/src/node.ts
7925
- function createNodeRuntimeHost(options) {
8210
+ function createNodeRuntimeHost(options2) {
7926
8211
  const {
7927
8212
  modules,
7928
8213
  legacyShell,
@@ -7930,7 +8215,7 @@ function createNodeRuntimeHost(options) {
7930
8215
  report = () => {
7931
8216
  },
7932
8217
  ...capabilities
7933
- } = options;
8218
+ } = options2;
7934
8219
  return {
7935
8220
  environment: "server",
7936
8221
  modules,
@@ -8481,12 +8766,12 @@ function normalizeManifest(raw) {
8481
8766
  settings: Object.freeze(settingsRecord(raw.settings))
8482
8767
  });
8483
8768
  }
8484
- function stringRecord(input, label) {
8769
+ function stringRecord(input, label2) {
8485
8770
  if (input === void 0) return {};
8486
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(`${label} must be an object of strings`);
8771
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(`${label2} must be an object of strings`);
8487
8772
  const entries2 = Object.entries(input);
8488
8773
  if (entries2.some(([key2, value]) => !CREDENTIAL_NAME.test(key2) || typeof value !== "string" || value.includes("\0"))) {
8489
- throw new Error(`${label} must contain safe names and string values`);
8774
+ throw new Error(`${label2} must contain safe names and string values`);
8490
8775
  }
8491
8776
  return Object.fromEntries(entries2);
8492
8777
  }
@@ -8630,7 +8915,7 @@ function within(root, candidate) {
8630
8915
  function emptyResult() {
8631
8916
  return { stdout: "", stderr: "", code: null, timedOut: false, cancelled: false, outputLimited: false };
8632
8917
  }
8633
- function execute(executable, args, options) {
8918
+ function execute(executable, args, options2) {
8634
8919
  return new Promise((resolve2, reject) => {
8635
8920
  let terminal = null;
8636
8921
  let spawnError;
@@ -8640,8 +8925,8 @@ function execute(executable, args, options) {
8640
8925
  let stdoutBytes = 0;
8641
8926
  let stderrBytes = 0;
8642
8927
  const child = spawn(executable, args, {
8643
- cwd: options.cwd,
8644
- env: options.env,
8928
+ cwd: options2.cwd,
8929
+ env: options2.env,
8645
8930
  detached: process.platform !== "win32",
8646
8931
  shell: false,
8647
8932
  stdio: ["pipe", "pipe", "pipe"]
@@ -8651,9 +8936,9 @@ function execute(executable, args, options) {
8651
8936
  terminal = cause;
8652
8937
  if (cause === "timeout" || cause === "cancelled" || cause === "output-limit") termination = terminateTree(child);
8653
8938
  };
8654
- const timer = setTimeout(() => finishCause("timeout"), options.timeout);
8939
+ const timer = setTimeout(() => finishCause("timeout"), options2.timeout);
8655
8940
  const abort = () => finishCause("cancelled");
8656
- options.signal?.addEventListener("abort", abort, { once: true });
8941
+ options2.signal?.addEventListener("abort", abort, { once: true });
8657
8942
  child.stdout.on("data", (chunk) => {
8658
8943
  stdoutBytes += chunk.length;
8659
8944
  if (stdoutBytes <= MAX_BUFFER) stdout.push(chunk);
@@ -8671,7 +8956,7 @@ function execute(executable, args, options) {
8671
8956
  child.once("exit", (code, signal) => finishCause(code === null || signal ? "signal" : "exit"));
8672
8957
  child.once("close", async (code, signal) => {
8673
8958
  clearTimeout(timer);
8674
- options.signal?.removeEventListener("abort", abort);
8959
+ options2.signal?.removeEventListener("abort", abort);
8675
8960
  await termination?.beforeSettle();
8676
8961
  const output = { stdout: Buffer.concat(stdout).toString("utf8"), stderr: Buffer.concat(stderr).toString("utf8") };
8677
8962
  if (terminal === "timeout" || terminal === "cancelled" || terminal === "output-limit") {
@@ -8684,9 +8969,9 @@ function execute(executable, args, options) {
8684
8969
  resolve2({ ...output, code, timedOut: false, cancelled: false, outputLimited: false });
8685
8970
  }
8686
8971
  });
8687
- if (options.stdin !== void 0) child.stdin.end(options.stdin);
8972
+ if (options2.stdin !== void 0) child.stdin.end(options2.stdin);
8688
8973
  else child.stdin.end();
8689
- if (options.signal?.aborted) abort();
8974
+ if (options2.signal?.aborted) abort();
8690
8975
  });
8691
8976
  }
8692
8977
  function terminateTree(child) {
@@ -8758,7 +9043,7 @@ var init_service = __esm({
8758
9043
  setAuditSink(sink) {
8759
9044
  this.auditSink = sink;
8760
9045
  }
8761
- async run(command, args = [], options = {}) {
9046
+ async run(command, args = [], options2 = {}) {
8762
9047
  const started = Date.now();
8763
9048
  const id = randomUUID();
8764
9049
  let cwd = ".";
@@ -8767,14 +9052,14 @@ var init_service = __esm({
8767
9052
  try {
8768
9053
  this.reloadConfig();
8769
9054
  const executable = this.resolveCommand(command);
8770
- const validated = this.validate(args, options);
9055
+ const validated = this.validate(args, options2);
8771
9056
  cwd = validated.relativeCwd;
8772
9057
  result = await execute(executable, validated.args, {
8773
9058
  cwd: validated.cwd,
8774
9059
  timeout: validated.timeout,
8775
9060
  stdin: validated.stdin,
8776
9061
  env: validated.env,
8777
- signal: options.signal
9062
+ signal: options2.signal
8778
9063
  });
8779
9064
  return result;
8780
9065
  } catch (error) {
@@ -8810,8 +9095,8 @@ var init_service = __esm({
8810
9095
  this.executables.set(alias, resolveExecutable(this.project.root, target));
8811
9096
  }
8812
9097
  }
8813
- async runJson(command, args = [], options = {}) {
8814
- const result = await this.run(command, args, options);
9098
+ async runJson(command, args = [], options2 = {}) {
9099
+ const result = await this.run(command, args, options2);
8815
9100
  if (result.code !== 0) throw new ShellProcessError("nonzero", result, result.stderr || `Command exited with code ${result.code}`);
8816
9101
  const lines = result.stdout.split(/\r?\n/).filter((value) => value.trim());
8817
9102
  const line2 = lines[lines.length - 1] ?? "";
@@ -8833,15 +9118,15 @@ var init_service = __esm({
8833
9118
  if (!executable) throw new ShellRequestError("MISSING_EXECUTABLE", `Configured command "${command}" is not available`);
8834
9119
  return executable;
8835
9120
  }
8836
- validate(args, options) {
9121
+ validate(args, options2) {
8837
9122
  if (!Array.isArray(args) || args.length > MAX_ARGS || args.some((arg) => typeof arg !== "string" || Buffer.byteLength(arg) > MAX_ARG_BYTES || arg.includes("\0"))) {
8838
9123
  throw new ShellRequestError("INVALID_REQUEST", "args must be an array of bounded strings");
8839
9124
  }
8840
- if (options.stdin !== void 0 && (typeof options.stdin !== "string" || Buffer.byteLength(options.stdin) > MAX_STDIN_BYTES || options.stdin.includes("\0"))) {
9125
+ if (options2.stdin !== void 0 && (typeof options2.stdin !== "string" || Buffer.byteLength(options2.stdin) > MAX_STDIN_BYTES || options2.stdin.includes("\0"))) {
8841
9126
  throw new ShellRequestError("INVALID_REQUEST", "stdin exceeds the 16 MiB limit or is invalid");
8842
9127
  }
8843
- const { cwd, relativeCwd } = resolveCwd(this.project.root, options.cwd ?? ".");
8844
- const requestEnv = options.env ?? {};
9128
+ const { cwd, relativeCwd } = resolveCwd(this.project.root, options2.cwd ?? ".");
9129
+ const requestEnv = options2.env ?? {};
8845
9130
  const entries2 = Object.entries(requestEnv);
8846
9131
  if (entries2.length > MAX_ENV_KEYS || entries2.reduce((sum2, [key2, value]) => sum2 + Buffer.byteLength(key2) + Buffer.byteLength(String(value)), 0) > MAX_ENV_BYTES) {
8847
9132
  throw new ShellRequestError("INVALID_REQUEST", "request environment exceeds its limit");
@@ -8851,17 +9136,17 @@ var init_service = __esm({
8851
9136
  throw new ShellRequestError("INVALID_REQUEST", `request environment variable ${JSON.stringify(key2)} is not allowed`);
8852
9137
  }
8853
9138
  }
8854
- if (options.timeout !== void 0 && (typeof options.timeout !== "number" || !Number.isFinite(options.timeout))) {
9139
+ if (options2.timeout !== void 0 && (typeof options2.timeout !== "number" || !Number.isFinite(options2.timeout))) {
8855
9140
  throw new ShellRequestError("INVALID_REQUEST", "timeout must be a finite number");
8856
9141
  }
8857
- const timeout = Math.min(6e5, Math.max(1, Math.trunc(options.timeout ?? this.config.shell.timeout)));
9142
+ const timeout = Math.min(6e5, Math.max(1, Math.trunc(options2.timeout ?? this.config.shell.timeout)));
8858
9143
  const credentialEnv = new CredentialStore().environment(readProjectManifest(this.project.root).credentials);
8859
9144
  return {
8860
9145
  args: [...args],
8861
9146
  cwd,
8862
9147
  relativeCwd,
8863
9148
  timeout,
8864
- stdin: options.stdin,
9149
+ stdin: options2.stdin,
8865
9150
  env: { ...process.env, ...this.config.shell.env, ...requestEnv, ...credentialEnv }
8866
9151
  };
8867
9152
  }
@@ -9543,7 +9828,7 @@ async function runDeterministicProjectGraph(file, document2, entryNode) {
9543
9828
  }
9544
9829
  }
9545
9830
  }
9546
- async function renderDeterministicProjectFrames(file, document2, options) {
9831
+ async function renderDeterministicProjectFrames(file, document2, options2) {
9547
9832
  const prepared = await prepare(file, document2, true);
9548
9833
  reportUnresolved(prepared);
9549
9834
  if (!usesDeterministicHost(document2, prepared)) return null;
@@ -9554,7 +9839,7 @@ async function renderDeterministicProjectFrames(file, document2, options) {
9554
9839
  nodes: prepared.registrations
9555
9840
  });
9556
9841
  let graph;
9557
- const width = sequenceWidth(options.end);
9842
+ const width = sequenceWidth(options2.end);
9558
9843
  const frames = [];
9559
9844
  const files = [];
9560
9845
  let aborted = false;
@@ -9563,24 +9848,24 @@ async function renderDeterministicProjectFrames(file, document2, options) {
9563
9848
  try {
9564
9849
  graph = await runtime.load(document2);
9565
9850
  reportPreflightWarnings(host, classifyPreflight(graph.preflight()).warnings);
9566
- const outDirectory = project.resolve(options.out);
9851
+ const outDirectory = project.resolve(options2.out);
9567
9852
  await fs7.mkdir(outDirectory, { recursive: true });
9568
- for (const frame of frameRange(options.start, options.end, options.step ?? 1)) {
9569
- if (options.signal?.aborted) {
9853
+ for (const frame of frameRange(options2.start, options2.end, options2.step ?? 1)) {
9854
+ if (options2.signal?.aborted) {
9570
9855
  aborted = true;
9571
9856
  break;
9572
9857
  }
9573
9858
  const result = await graph.run({
9574
9859
  frame,
9575
- ...options.fps === void 0 ? {} : { fps: options.fps },
9576
- ...options.entryNode ? { target: { kind: "node", nodeId: options.entryNode } } : {}
9860
+ ...options2.fps === void 0 ? {} : { fps: options2.fps },
9861
+ ...options2.entryNode ? { target: { kind: "node", nodeId: options2.entryNode } } : {}
9577
9862
  });
9578
9863
  if (result.status !== "completed") {
9579
9864
  throw new Error(
9580
9865
  `frame ${frame} ${result.status} \u2014 ${result.diagnostics.map((item) => item.message).join("; ") || "no diagnostics"}`
9581
9866
  );
9582
9867
  }
9583
- const outputs = imageOutputs(graph, options.entryNode);
9868
+ const outputs = imageOutputs(graph, options2.entryNode);
9584
9869
  if (!outputs.length) {
9585
9870
  throw new Error(
9586
9871
  "No image output to save: the graph has no unconsumed image port. Name the node to render with --entry-node."
@@ -9591,10 +9876,10 @@ async function renderDeterministicProjectFrames(file, document2, options) {
9591
9876
  if (!source) continue;
9592
9877
  const name = sequenceFileName(nodeId, frame, source, width);
9593
9878
  await fs7.copyFile(project.resolveMedia(source.replace(/^\.?\//, "")), path9.join(outDirectory, name));
9594
- files.push(path9.posix.join(options.out, name));
9879
+ files.push(path9.posix.join(options2.out, name));
9595
9880
  }
9596
9881
  frames.push(frame);
9597
- if (options.verbose) console.log(` frame ${frame}`);
9882
+ if (options2.verbose) console.log(` frame ${frame}`);
9598
9883
  }
9599
9884
  try {
9600
9885
  gpu = { renderer: "dawn", adapter: host.capabilities.gpu.adapterInfo };
@@ -9835,7 +10120,7 @@ function jsonValue(value) {
9835
10120
  }
9836
10121
  }
9837
10122
  function resolver(registrations) {
9838
- const modules = new Map(registrations.map((registration) => [registration.moduleId, registration]));
10123
+ const modules = new Map(registrations.map((registration2) => [registration2.moduleId, registration2]));
9839
10124
  return { resolve: async (id) => modules.get(id) ?? null };
9840
10125
  }
9841
10126
  function formatDiagnostic(diagnostic2) {
@@ -10038,7 +10323,7 @@ function readStageConfig(project) {
10038
10323
  }
10039
10324
  return { python, entrypoint, ...worker ? { worker } : {} };
10040
10325
  }
10041
- async function runProjectStage(project, stage, args, options = {}) {
10326
+ async function runProjectStage(project, stage, args, options2 = {}) {
10042
10327
  if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(stage)) {
10043
10328
  throw new Error("stage contains unsupported characters");
10044
10329
  }
@@ -10051,10 +10336,10 @@ async function runProjectStage(project, stage, args, options = {}) {
10051
10336
  [script, "--stage", stage, "--args", "-"],
10052
10337
  {
10053
10338
  cwd: project.root,
10054
- timeout: options.timeout ?? TIMEOUT_MS,
10339
+ timeout: options2.timeout ?? TIMEOUT_MS,
10055
10340
  maxBuffer: MAX_BUFFER2,
10056
- env: options.env ?? process.env,
10057
- signal: options.signal
10341
+ env: options2.env ?? process.env,
10342
+ signal: options2.signal
10058
10343
  },
10059
10344
  (error, out, err) => {
10060
10345
  if (error) {
@@ -10088,7 +10373,7 @@ var init_stageRunner = __esm({
10088
10373
  });
10089
10374
 
10090
10375
  // packages/runtime/src/browser.ts
10091
- function createBrowserRuntimeHost(options) {
10376
+ function createBrowserRuntimeHost(options2) {
10092
10377
  const {
10093
10378
  modules,
10094
10379
  legacyShell,
@@ -10096,7 +10381,7 @@ function createBrowserRuntimeHost(options) {
10096
10381
  report = () => {
10097
10382
  },
10098
10383
  ...capabilities
10099
- } = options;
10384
+ } = options2;
10100
10385
  return {
10101
10386
  environment: "browser",
10102
10387
  modules,
@@ -10123,10 +10408,10 @@ import { builtinModules } from "node:module";
10123
10408
  import { fileURLToPath as fileURLToPath2 } from "node:url";
10124
10409
  import { build as build3 } from "esbuild";
10125
10410
  import ts3 from "typescript";
10126
- async function buildPlayer(file, options) {
10127
- if (!options.out) throw new Error("cascade build requires --out <new-directory>");
10411
+ async function buildPlayer(file, options2) {
10412
+ if (!options2.out) throw new Error("cascade build requires --out <new-directory>");
10128
10413
  const project = new ProjectRoot(path14.dirname(path14.resolve(file)));
10129
- const out = path14.resolve(project.root, options.out);
10414
+ const out = path14.resolve(project.root, options2.out);
10130
10415
  if (await exists2(out)) throw new Error(`Player output already exists: ${out}. Choose a new directory.`);
10131
10416
  const canonicalOut = await canonicalFuturePath(out);
10132
10417
  for (const sourceDirectory of ["assets", "nodes"]) {
@@ -10169,7 +10454,7 @@ ${extracted.diagnostics.map((d) => `${d.code}: ${d.message}`).join("\n")}`);
10169
10454
  assets: {},
10170
10455
  gpu: {}
10171
10456
  }), nodes: registrations });
10172
- const assets = new Set(options.assets ?? []);
10457
+ const assets = new Set(options2.assets ?? []);
10173
10458
  try {
10174
10459
  const graph = await runtime.load(document2);
10175
10460
  const diagnostics = graph.preflight();
@@ -11124,12 +11409,12 @@ function isLoopbackHost(host) {
11124
11409
  const normalized = host.toLowerCase().replace(/^\[|\]$/g, "");
11125
11410
  return normalized === "127.0.0.1" || normalized === "localhost" || normalized === "::1";
11126
11411
  }
11127
- function allowsSensitiveCapabilities(options) {
11128
- return options.sensitiveCapabilities;
11412
+ function allowsSensitiveCapabilities(options2) {
11413
+ return options2.sensitiveCapabilities;
11129
11414
  }
11130
- function createProjectRequestBoundary(options) {
11131
- const origins = new Set(options.trustedOrigins.map((origin) => validateOrigin(origin, "Project")));
11132
- const authorities = allowedAuthorities(options, origins);
11415
+ function createProjectRequestBoundary(options2) {
11416
+ const origins = new Set(options2.trustedOrigins.map((origin) => validateOrigin(origin, "Project")));
11417
+ const authorities = allowedAuthorities(options2, origins);
11133
11418
  return {
11134
11419
  guard(req, res, next) {
11135
11420
  const host = req.get("Host");
@@ -11150,9 +11435,9 @@ function createProjectRequestBoundary(options) {
11150
11435
  }
11151
11436
  };
11152
11437
  }
11153
- function createBrowserCapabilityBoundary(options, label, header) {
11154
- const origins = new Set(options.trustedOrigins.map((origin) => validateOrigin(origin, label)));
11155
- const authorities = allowedAuthorities(options, origins);
11438
+ function createBrowserCapabilityBoundary(options2, label2, header) {
11439
+ const origins = new Set(options2.trustedOrigins.map((origin) => validateOrigin(origin, label2)));
11440
+ const authorities = allowedAuthorities(options2, origins);
11156
11441
  const capability2 = randomBytes(32).toString("base64url");
11157
11442
  const expectedBytes = Buffer.from(capability2);
11158
11443
  return {
@@ -11160,7 +11445,7 @@ function createBrowserCapabilityBoundary(options, label, header) {
11160
11445
  const origin = req.get("Origin");
11161
11446
  const host = req.get("Host");
11162
11447
  if (!host || !authorities.has(host.toLowerCase()) || origin === "null" || origin && !origins.has(origin)) {
11163
- res.status(403).json({ ok: false, error: `${label} access denied` });
11448
+ res.status(403).json({ ok: false, error: `${label2} access denied` });
11164
11449
  return;
11165
11450
  }
11166
11451
  if (origin) setCorsOrigin(res, origin);
@@ -11180,23 +11465,23 @@ function createBrowserCapabilityBoundary(options, label, header) {
11180
11465
  const supplied = req.get(header);
11181
11466
  const suppliedBytes = supplied ? Buffer.from(supplied) : Buffer.alloc(0);
11182
11467
  if (suppliedBytes.length !== expectedBytes.length || !timingSafeEqual(suppliedBytes, expectedBytes)) {
11183
- res.status(403).json({ ok: false, error: `${label} capability required` });
11468
+ res.status(403).json({ ok: false, error: `${label2} capability required` });
11184
11469
  return;
11185
11470
  }
11186
11471
  next();
11187
11472
  }
11188
11473
  };
11189
11474
  }
11190
- function allowedAuthorities(options, origins) {
11475
+ function allowedAuthorities(options2, origins) {
11191
11476
  const authorities = new Set([...origins].map((origin) => new URL(origin).host.toLowerCase()));
11192
- if (isLoopbackHost(options.host)) {
11193
- authorities.add(`127.0.0.1:${options.port}`);
11194
- authorities.add(`localhost:${options.port}`);
11195
- } else if (!isWildcardHost(options.host)) {
11196
- authorities.add(authority(options.host, options.port));
11197
- }
11198
- for (const host of options.trustedHosts) {
11199
- const value = trustedAuthority(host, options.port);
11477
+ if (isLoopbackHost(options2.host)) {
11478
+ authorities.add(`127.0.0.1:${options2.port}`);
11479
+ authorities.add(`localhost:${options2.port}`);
11480
+ } else if (!isWildcardHost(options2.host)) {
11481
+ authorities.add(authority(options2.host, options2.port));
11482
+ }
11483
+ for (const host of options2.trustedHosts) {
11484
+ const value = trustedAuthority(host, options2.port);
11200
11485
  authorities.add(value);
11201
11486
  const bare = value.replace(/:(?:80|443)$/, "");
11202
11487
  if (bare !== value) authorities.add(bare);
@@ -11232,10 +11517,10 @@ function setCorsOrigin(res, origin) {
11232
11517
  res.setHeader("Access-Control-Allow-Origin", origin);
11233
11518
  res.setHeader("Vary", "Origin");
11234
11519
  }
11235
- function validateOrigin(value, label) {
11520
+ function validateOrigin(value, label2) {
11236
11521
  const url = new URL(value);
11237
11522
  if (!/^https?:$/.test(url.protocol) || url.username || url.password || url.pathname !== "/" || url.search || url.hash) {
11238
- throw new Error(`Invalid trusted ${label.toLowerCase()} origin: ${value}`);
11523
+ throw new Error(`Invalid trusted ${label2.toLowerCase()} origin: ${value}`);
11239
11524
  }
11240
11525
  return url.origin;
11241
11526
  }
@@ -11367,10 +11652,10 @@ var init_exec = __esm({
11367
11652
 
11368
11653
  // server/src/routes/shell.ts
11369
11654
  import { Router as Router6, json as json2 } from "express";
11370
- function createShellRouter(shell, options) {
11655
+ function createShellRouter(shell, options2) {
11371
11656
  const router = Router6();
11372
- if (!allowsSensitiveCapabilities(options)) return router.use((_req, res) => res.status(404).end());
11373
- const boundary = createBrowserCapabilityBoundary(options, "Shell", "X-Cascade-Shell-Capability");
11657
+ if (!allowsSensitiveCapabilities(options2)) return router.use((_req, res) => res.status(404).end());
11658
+ const boundary = createBrowserCapabilityBoundary(options2, "Shell", "X-Cascade-Shell-Capability");
11374
11659
  router.use(boundary.guardOrigin);
11375
11660
  router.options("{*path}", boundary.preflight);
11376
11661
  router.get("/capability", boundary.issueCapability);
@@ -13477,19 +13762,19 @@ var Node = class _Node {
13477
13762
  else if (!promoted && index >= 0) parent.promotions.splice(index, 1);
13478
13763
  }
13479
13764
  // ============ Port Creation ============
13480
- in(name, defaultValue, options = {}) {
13765
+ in(name, defaultValue, options2 = {}) {
13481
13766
  const portType = name === "trigger" ? "trigger" : "param";
13482
13767
  const existingPort = this.inputs.find((p) => p.name === name);
13483
13768
  if (existingPort) {
13484
13769
  this.portsUsedDuringSetup.add(`input_${name}`);
13485
- if (options.type) existingPort.dataType = options.type;
13770
+ if (options2.type) existingPort.dataType = options2.type;
13486
13771
  if (defaultValue !== void 0 && existingPort.defaultValue !== defaultValue) {
13487
13772
  existingPort.defaultValue = defaultValue;
13488
13773
  if (existingPort.value === void 0 || existingPort.value === existingPort.defaultValue) {
13489
13774
  existingPort.value = defaultValue;
13490
13775
  }
13491
13776
  }
13492
- existingPort.options = Object.assign(existingPort.options ?? {}, options);
13777
+ existingPort.options = Object.assign(existingPort.options ?? {}, options2);
13493
13778
  return existingPort;
13494
13779
  }
13495
13780
  const portIndex = this.inputs.length;
@@ -13497,10 +13782,10 @@ var Node = class _Node {
13497
13782
  id: `${this.id}_in_${portIndex}`,
13498
13783
  name,
13499
13784
  portType,
13500
- dataType: options.type || "any",
13785
+ dataType: options2.type || "any",
13501
13786
  value: defaultValue,
13502
13787
  defaultValue,
13503
- options,
13788
+ options: options2,
13504
13789
  connections: []
13505
13790
  };
13506
13791
  this.inputs.push(port);
@@ -13514,13 +13799,13 @@ var Node = class _Node {
13514
13799
  * only ever sees `any` on one side, and the port draws in the neutral colour
13515
13800
  * whatever it actually carries.
13516
13801
  */
13517
- out(name, portType = "param", options = {}) {
13802
+ out(name, portType = "param", options2 = {}) {
13518
13803
  const existingPort = this.outputs.find((p) => p.name === name);
13519
13804
  if (existingPort) {
13520
13805
  this.portsUsedDuringSetup.add(`output_${name}`);
13521
13806
  if (existingPort.portType !== portType) existingPort.portType = portType;
13522
- if (options.type) existingPort.dataType = options.type;
13523
- existingPort.options = { ...existingPort.options ?? {}, ...options };
13807
+ if (options2.type) existingPort.dataType = options2.type;
13808
+ existingPort.options = { ...existingPort.options ?? {}, ...options2 };
13524
13809
  return existingPort;
13525
13810
  }
13526
13811
  const portIndex = this.outputs.length;
@@ -13528,10 +13813,10 @@ var Node = class _Node {
13528
13813
  id: `${this.id}_out_${portIndex}`,
13529
13814
  name,
13530
13815
  portType,
13531
- dataType: options.type || "any",
13816
+ dataType: options2.type || "any",
13532
13817
  value: void 0,
13533
13818
  connections: [],
13534
- options,
13819
+ options: options2,
13535
13820
  setValue: (value) => this.setOutputValue(port, value),
13536
13821
  trigger: (props) => {
13537
13822
  port.connections.forEach((conn) => {
@@ -13877,26 +14162,26 @@ var Node = class _Node {
13877
14162
  * say. Promotion is per parameter and off by default: making every setting a
13878
14163
  * pin is how the two got conflated in the first place.
13879
14164
  */
13880
- param(name, defaultValue, options = {}) {
14165
+ param(name, defaultValue, options2 = {}) {
13881
14166
  let parameter = this.parameters.find((p) => p.name === name);
13882
14167
  if (!parameter) {
13883
14168
  parameter = {
13884
14169
  name,
13885
14170
  value: defaultValue,
13886
14171
  defaultValue,
13887
- dataType: options.type || inferParamType(defaultValue),
14172
+ dataType: options2.type || inferParamType(defaultValue),
13888
14173
  promoted: false,
13889
- options
14174
+ options: options2
13890
14175
  };
13891
14176
  this.parameters.push(parameter);
13892
14177
  } else {
13893
- if (options.type) parameter.dataType = options.type;
14178
+ if (options2.type) parameter.dataType = options2.type;
13894
14179
  parameter.defaultValue = defaultValue;
13895
- Object.assign(parameter.options, options);
14180
+ Object.assign(parameter.options, options2);
13896
14181
  }
13897
14182
  this.parametersUsedDuringSetup.add(name);
13898
- this.reconcileParameter(parameter, defaultValue, options);
13899
- this.bindParameterProp(parameter, name, options);
14183
+ this.reconcileParameter(parameter, defaultValue, options2);
14184
+ this.bindParameterProp(parameter, name, options2);
13900
14185
  if (parameter.promoted) {
13901
14186
  const port = this.in(name, parameter.value, {
13902
14187
  ...parameter.options,
@@ -13920,7 +14205,7 @@ var Node = class _Node {
13920
14205
  * rather than replaced for the same reason the Inspector does not re-key a
13921
14206
  * row on commit — replacing it mid-drag destroys the control in use.
13922
14207
  */
13923
- bindParameterProp(parameter, name, options) {
14208
+ bindParameterProp(parameter, name, options2) {
13924
14209
  const descriptor = Object.getOwnPropertyDescriptor(parameter, "value");
13925
14210
  const seeded = descriptor && !descriptor.get ? descriptor.value : void 0;
13926
14211
  let prop = this.props[name];
@@ -13929,10 +14214,10 @@ var Node = class _Node {
13929
14214
  this.props[name] = prop;
13930
14215
  this.props = { ...this.props };
13931
14216
  }
13932
- if (options.defaultExpression && !this.defaultExpressionsApplied.has(name)) {
14217
+ if (options2.defaultExpression && !this.defaultExpressionsApplied.has(name)) {
13933
14218
  this.defaultExpressionsApplied.add(name);
13934
14219
  if (!this.plainPropOverrides.has(name) && !prop.expression) {
13935
- prop.expression = options.defaultExpression;
14220
+ prop.expression = options2.defaultExpression;
13936
14221
  this.props = { ...this.props };
13937
14222
  this.updateTimeDependent();
13938
14223
  }
@@ -13942,9 +14227,9 @@ var Node = class _Node {
13942
14227
  const control = paramControlType(parameter.dataType);
13943
14228
  if (control) prop.type = control;
13944
14229
  const params = { ...prop.params ?? {} };
13945
- if (options.min !== void 0) params.min = options.min;
13946
- if (options.max !== void 0) params.max = options.max;
13947
- if (options.step !== void 0) params.step = options.step;
14230
+ if (options2.min !== void 0) params.min = options2.min;
14231
+ if (options2.max !== void 0) params.max = options2.max;
14232
+ if (options2.step !== void 0) params.step = options2.step;
13948
14233
  if (normalizeType(parameter.dataType ?? "any") === "int") params.integer = true;
13949
14234
  prop.params = params;
13950
14235
  if (descriptor?.get) return;
@@ -14177,13 +14462,13 @@ var Node = class _Node {
14177
14462
  return carry.report;
14178
14463
  }
14179
14464
  /** Called from param() for every declaration the new definition makes. */
14180
- reconcileParameter(parameter, defaultValue, options) {
14465
+ reconcileParameter(parameter, defaultValue, options2) {
14181
14466
  const carry = this.carryOver;
14182
14467
  if (!carry) return;
14183
14468
  const key2 = `parameter:${parameter.name}`;
14184
14469
  if (carry.resolved.has(key2)) return;
14185
14470
  carry.resolved.add(key2);
14186
- const declared = options.type ?? inferParamType(defaultValue);
14471
+ const declared = options2.type ?? inferParamType(defaultValue);
14187
14472
  parameter.dataType = declared;
14188
14473
  const declaredType = carryType(declared);
14189
14474
  const crossKey = `prop:${parameter.name}`;
@@ -14244,10 +14529,10 @@ var Node = class _Node {
14244
14529
  }
14245
14530
  }
14246
14531
  // ============ Variadic Inputs ============
14247
- setVariadic(defaultValue = null, options = {}) {
14532
+ setVariadic(defaultValue = null, options2 = {}) {
14248
14533
  this.variadic = true;
14249
14534
  this.variadicDefault = defaultValue;
14250
- this.variadicOptions = options;
14535
+ this.variadicOptions = options2;
14251
14536
  this.syncVariadicPorts();
14252
14537
  }
14253
14538
  getVariadicInputs() {
@@ -15996,8 +16281,8 @@ function parameterOptions(definition) {
15996
16281
  promotable: false
15997
16282
  };
15998
16283
  }
15999
- function attachDefinition(node, registration, capabilities) {
16000
- const definition = registration.definition;
16284
+ function attachDefinition(node, registration2, capabilities) {
16285
+ const definition = registration2.definition;
16001
16286
  const pendingTriggers = /* @__PURE__ */ new Map();
16002
16287
  let triggerSequence = 0;
16003
16288
  const container = definition.container;
@@ -16060,10 +16345,10 @@ function attachDefinition(node, registration, capabilities) {
16060
16345
  if (hasTriggerInputs && pendingTriggers.size === 0) return;
16061
16346
  for (const name of definition.capabilities ?? []) {
16062
16347
  if (!capabilities[name]) {
16063
- throw new Error(`${registration.moduleId} requires the Studio ${name} capability`);
16348
+ throw new Error(`${registration2.moduleId} requires the Studio ${name} capability`);
16064
16349
  }
16065
16350
  }
16066
- const execute2 = await (executePromise ??= registration.loadExecute("browser"));
16351
+ const execute2 = await (executePromise ??= registration2.loadExecute("browser"));
16067
16352
  const inputValues = {};
16068
16353
  for (const [name, input] of Object.entries(definition.inputs ?? {})) {
16069
16354
  inputValues[name] = input.kind === "trigger" ? pendingTriggers.get(name) : input.variadic ? node.getVariadicInputs().map((port) => port.value ?? void 0) : node.inputs.find((port) => port.name === name)?.value;
@@ -16071,7 +16356,7 @@ function attachDefinition(node, registration, capabilities) {
16071
16356
  pendingTriggers.clear();
16072
16357
  const outputs = Object.fromEntries(Object.entries(definition.outputs ?? {}).map(([name, output]) => {
16073
16358
  const port = node.outputs.find((candidate) => candidate.name === name);
16074
- if (!port) throw new Error(`Missing output ${registration.moduleId}.${name}`);
16359
+ if (!port) throw new Error(`Missing output ${registration2.moduleId}.${name}`);
16075
16360
  return [name, output.kind === "trigger" ? { trigger: (payload) => port.trigger(payload) } : { set: (value) => port.setValue(value) }];
16076
16361
  }));
16077
16362
  let nodeCapabilities = capabilities;
@@ -16171,8 +16456,8 @@ async function post(body, signal) {
16171
16456
  }
16172
16457
  return response;
16173
16458
  }
16174
- function requestBody(command, args, options) {
16175
- const { signal: _signal, ...rest } = options ?? {};
16459
+ function requestBody(command, args, options2) {
16460
+ const { signal: _signal, ...rest } = options2 ?? {};
16176
16461
  return {
16177
16462
  command,
16178
16463
  ...args ? { args } : {},
@@ -16192,14 +16477,14 @@ async function fail2(response) {
16192
16477
  throw new Error(message2);
16193
16478
  }
16194
16479
  var browserShellCapability = {
16195
- async run(command, args, options) {
16196
- const response = await post(requestBody(command, args, options), options?.signal);
16480
+ async run(command, args, options2) {
16481
+ const response = await post(requestBody(command, args, options2), options2?.signal);
16197
16482
  if (!response.ok) await fail2(response);
16198
16483
  const { ok: _ok, ...result } = await response.json();
16199
16484
  return result;
16200
16485
  },
16201
- async runJson(command, args, options) {
16202
- const result = await this.run(command, args, options);
16486
+ async runJson(command, args, options2) {
16487
+ const result = await this.run(command, args, options2);
16203
16488
  try {
16204
16489
  return JSON.parse(result.stdout);
16205
16490
  } catch {
@@ -16312,13 +16597,13 @@ function adaptModule(node, moduleId2, module, capabilities = studioCapabilities)
16312
16597
  const invalid = definitionAdapterError(module.definition);
16313
16598
  if (invalid) throw new Error(`${moduleId2} exports an invalid definition-v1 literal:
16314
16599
  ${invalid}`);
16315
- const registration = {
16600
+ const registration2 = {
16316
16601
  kind: "definition-v1",
16317
16602
  moduleId: moduleId2,
16318
16603
  definition: module.definition,
16319
16604
  loadExecute: async () => module.execute
16320
16605
  };
16321
- return attachDefinition(node, registration, capabilities);
16606
+ return attachDefinition(node, registration2, capabilities);
16322
16607
  }
16323
16608
 
16324
16609
  // src/nodes/CookScheduler.ts
@@ -16466,8 +16751,20 @@ var CookScheduler = class {
16466
16751
  elapsed: 0
16467
16752
  });
16468
16753
  const blocked = /* @__PURE__ */ new Set();
16754
+ const notReady = this.graph.pendingConnectionTargets();
16469
16755
  let superseded = false;
16470
16756
  for (const node of ordered) {
16757
+ if (notReady.has(node.id)) {
16758
+ node.setCookState("stale");
16759
+ blocked.add(node.id);
16760
+ this.setStatus({
16761
+ ...this._status,
16762
+ completed: this._status.completed + 1,
16763
+ currentNode: null,
16764
+ elapsed: performance.now() - startedAt
16765
+ });
16766
+ continue;
16767
+ }
16471
16768
  const upstreamBlocked = node.inputs.some((input) => input.connections.some(
16472
16769
  (connection) => staleIds.has(connection.from.nodeId) && (blocked.has(connection.from.nodeId) || this.graph.getNode(connection.from.nodeId)?.cookState === "error")
16473
16770
  ));
@@ -16538,9 +16835,9 @@ var CookScheduler = class {
16538
16835
  * interactive queue, not to weaken the preemption; the preemption is the
16539
16836
  * only thing making the lane honest.
16540
16837
  */
16541
- async runBackground(task, options = {}) {
16838
+ async runBackground(task, options2 = {}) {
16542
16839
  this.preemptBackground("superseded by a newer background task");
16543
- await this.whenSettled(options.settleMs ?? this.debounceMs * 4);
16840
+ await this.whenSettled(options2.settleMs ?? this.debounceMs * 4);
16544
16841
  const abort = new AbortController();
16545
16842
  this.backgroundAbort = abort;
16546
16843
  try {
@@ -16654,6 +16951,16 @@ var Graph = class _Graph {
16654
16951
  this.executionState = "idle";
16655
16952
  // Cached topological order (invalidated when graph structure changes)
16656
16953
  this.cachedTopologicalOrder = null;
16954
+ /**
16955
+ * Whether the load-time connection restore has stopped making progress.
16956
+ *
16957
+ * Set by whoever drives the warm-up passes — Studio's `cookGraph`, the CLI's
16958
+ * runner — when the pending list stops shrinking. The pending list itself is
16959
+ * kept, so a connection can still bind later if a bypass is lifted or a
16960
+ * module finally compiles; what ends is treating it as a reason to hold a
16961
+ * node back.
16962
+ */
16963
+ this.connectionsSettled = false;
16657
16964
  this.assetManager = assetManager || new AssetManager();
16658
16965
  this.packageManager = packageManager || new PackageManager();
16659
16966
  this.moduleResolver = moduleResolver || createModuleResolver();
@@ -16945,13 +17252,13 @@ var Graph = class _Graph {
16945
17252
  * them the result is `deferred: true` — a module's parameters are only known
16946
17253
  * once its code declares them, which happens on the next cook.
16947
17254
  */
16948
- retargetDefinition(nodeId, modulePath, source, code, options = {}) {
17255
+ retargetDefinition(nodeId, modulePath, source, code, options2 = {}) {
16949
17256
  const blank = () => ({ kept: [], defaulted: [], dropped: [], retyped: [] });
16950
17257
  const node = this.getNode(nodeId);
16951
17258
  if (!node) {
16952
17259
  return { ok: false, deferred: false, ...blank(), error: `no node with id ${nodeId}` };
16953
17260
  }
16954
- const preserve = options.preserveParameters !== false;
17261
+ const preserve = options2.preserveParameters !== false;
16955
17262
  if (preserve) node.beginParameterCarryOver();
16956
17263
  node.modulePath = modulePath;
16957
17264
  node.sourceType = source;
@@ -16963,8 +17270,8 @@ var Graph = class _Graph {
16963
17270
  console.warn("Failed to compile " + modulePath + ":", error);
16964
17271
  });
16965
17272
  node.markDirty();
16966
- if (preserve && options.parameters) {
16967
- const report = node.applyParameterDeclarations(options.parameters) ?? blank();
17273
+ if (preserve && options2.parameters) {
17274
+ const report = node.applyParameterDeclarations(options2.parameters) ?? blank();
16968
17275
  return { ok: true, deferred: false, ...report };
16969
17276
  }
16970
17277
  return { ok: true, deferred: preserve, ...blank() };
@@ -18019,12 +18326,47 @@ var Graph = class _Graph {
18019
18326
  * still unresolved stays pending, so calling this again after another cook
18020
18327
  * finishes the job.
18021
18328
  */
18329
+ /**
18330
+ * Node ids that a loaded file wires an input to, and whose connection has
18331
+ * not bound yet.
18332
+ *
18333
+ * The reason this is public: **an input that is about to be connected is not
18334
+ * an input that is missing**, and until now nothing could tell the two
18335
+ * apart. On load a node's ports only exist once its code has run, so the
18336
+ * first cook passes reach a node whose upstream edge has no port to attach
18337
+ * to — the node runs with `undefined` on that input, throws whatever its
18338
+ * author wrote for the unwired case, and a later pass then succeeds.
18339
+ *
18340
+ * The graph ends up correct and the log does not: those warm-up throws are
18341
+ * real errors from the node's point of view, they are recorded, and they
18342
+ * never clear. A user reported `volume-field` as broken on exactly this,
18343
+ * having verified from the cache files that the output was in fact right.
18344
+ *
18345
+ * So the scheduler asks this before cooking, and treats a node with a
18346
+ * pending inbound edge as *not ready* rather than as failed.
18347
+ */
18348
+ pendingConnectionTargets() {
18349
+ const pending = this._connectionsToRestore;
18350
+ const targets = /* @__PURE__ */ new Set();
18351
+ if (this.connectionsSettled) return targets;
18352
+ if (!Array.isArray(pending)) return targets;
18353
+ for (const connection of pending) {
18354
+ const target = Array.isArray(connection?.[1]) ? connection[1][0] : void 0;
18355
+ if (typeof target === "string") targets.add(target);
18356
+ }
18357
+ return targets;
18358
+ }
18359
+ /** Called when the warm-up passes stop making progress. */
18360
+ markConnectionsSettled() {
18361
+ this.connectionsSettled = true;
18362
+ }
18022
18363
  restoreConnections() {
18023
18364
  const connectionsToRestore = this._connectionsToRestore || [];
18024
18365
  if (connectionsToRestore.length === 0) return;
18025
18366
  const stillPending = connectionsToRestore.filter(
18026
18367
  (connData) => !this.tryRestoreConnection(connData, true)
18027
18368
  );
18369
+ if (stillPending.length < connectionsToRestore.length) this.connectionsSettled = false;
18028
18370
  if (stillPending.length > 0) {
18029
18371
  this._connectionsToRestore = stillPending;
18030
18372
  } else {
@@ -18100,10 +18442,10 @@ async function installHeadlessCanvas() {
18100
18442
  const canvas = await loadCanvasModule();
18101
18443
  const globals2 = globalThis;
18102
18444
  class HeadlessOffscreenCanvas extends canvas.Canvas {
18103
- async convertToBlob(options = {}) {
18104
- const type = options.type ?? "image/png";
18445
+ async convertToBlob(options2 = {}) {
18446
+ const type = options2.type ?? "image/png";
18105
18447
  const format2 = type.replace(/^image\//, "");
18106
- const quality = options.quality === void 0 ? void 0 : Math.round(options.quality * 100);
18448
+ const quality = options2.quality === void 0 ? void 0 : Math.round(options2.quality * 100);
18107
18449
  return new Blob([await this.encode(format2, quality)], { type });
18108
18450
  }
18109
18451
  }
@@ -18399,8 +18741,8 @@ var cascade = new CascadeContext();
18399
18741
 
18400
18742
  // src/engine/animation/frameRange.ts
18401
18743
  init_animation2();
18402
- async function evaluateFrameRange(options) {
18403
- const { fps, context = cascade, ...rest } = options;
18744
+ async function evaluateFrameRange(options2) {
18745
+ const { fps, context = cascade, ...rest } = options2;
18404
18746
  const previousFps = context.fps();
18405
18747
  if (fps !== void 0) context.setFps(fps);
18406
18748
  try {
@@ -18440,8 +18782,8 @@ function outputPorts(graph, entryNode) {
18440
18782
  function outputSnapshot(graph) {
18441
18783
  return JSON.stringify(graph.nodes.map((node) => [node.id, node.outputs.map((port) => port.value ?? null)]));
18442
18784
  }
18443
- async function cookUntilSettled(graph, entryNode, options = {}) {
18444
- const { fixpoint = false, passes = 8 } = options;
18785
+ async function cookUntilSettled(graph, entryNode, options2 = {}) {
18786
+ const { fixpoint = false, passes = 8 } = options2;
18445
18787
  const dirtyAll = () => {
18446
18788
  for (const node of graph.nodes) node.markDirty?.();
18447
18789
  };
@@ -18463,8 +18805,8 @@ async function cookUntilSettled(graph, entryNode, options = {}) {
18463
18805
  }
18464
18806
  throw new Error(`graph did not settle after ${passes} cook passes`);
18465
18807
  }
18466
- async function renderFrameRange(options) {
18467
- const { graph, project, out, start, end, step: step2, fps, entryNode, verbose, signal } = options;
18808
+ async function renderFrameRange(options2) {
18809
+ const { graph, project, out, start, end, step: step2, fps, entryNode, verbose, signal } = options2;
18468
18810
  cascade.setGraph(graph);
18469
18811
  const outDirectory = project.resolve(out);
18470
18812
  await fs10.mkdir(outDirectory, { recursive: true });
@@ -21572,8 +21914,8 @@ function reportMissingCanvas(messages, canvasError) {
21572
21914
  if (canvasError && messages.some(looksLikeCanvasFailure)) console.error(`
21573
21915
  ${MISSING_CANVAS_MESSAGE}`);
21574
21916
  }
21575
- async function runGraph(options) {
21576
- const { file, entryNode, validateOnly, checkOnly, inspectOnly, verbose, frames, fps, out } = options;
21917
+ async function runGraph(options2) {
21918
+ const { file, entryNode, validateOnly, checkOnly, inspectOnly, verbose, frames, fps, out } = options2;
21577
21919
  if (verbose) {
21578
21920
  console.log(`Loading graph from: ${file}`);
21579
21921
  }
@@ -21653,11 +21995,11 @@ async function runGraph(options) {
21653
21995
  });
21654
21996
  if (rendered) {
21655
21997
  releaseHost();
21656
- if (options.json) process.stdout.write(JSON.stringify({ status: "completed", graph: path13.resolve(file), ...rendered }) + "\n");
21998
+ if (options2.json) process.stdout.write(JSON.stringify({ status: "completed", graph: path13.resolve(file), ...rendered }) + "\n");
21657
21999
  else console.log(`Rendered ${rendered.frames.length} frames, ${rendered.files.length} files -> ${out ?? "renders"}/`);
21658
22000
  return;
21659
22001
  }
21660
- if (options.json) throw new Error("--json rendering requires a fully definition-v1 graph");
22002
+ if (options2.json) throw new Error("--json rendering requires a fully definition-v1 graph");
21661
22003
  } else if (await runDeterministicProjectGraph(file, graphData, entryNode)) {
21662
22004
  if (verbose) console.log("Graph execution completed");
21663
22005
  releaseHost();
@@ -21698,6 +22040,7 @@ async function runGraph(options) {
21698
22040
  }
21699
22041
  }
21700
22042
  graph.restoreConnections();
22043
+ graph.markConnectionsSettled();
21701
22044
  const validation = graph.validate();
21702
22045
  if (validation.errors.length > 0) {
21703
22046
  console.error("Graph validation failed:");
@@ -21942,7 +22285,7 @@ image output nothing downstream consumes, or per output of --entry-node.
21942
22285
  console.error(`Error: File not found: ${fileArg}`);
21943
22286
  process.exit(1);
21944
22287
  }
21945
- const options = {
22288
+ const options2 = {
21946
22289
  file: fileArg,
21947
22290
  validateOnly: command === "validate",
21948
22291
  checkOnly: command === "check",
@@ -21950,11 +22293,11 @@ image output nothing downstream consumes, or per output of --entry-node.
21950
22293
  };
21951
22294
  const verboseIndex = args.indexOf("--verbose") !== -1 ? args.indexOf("--verbose") : args.indexOf("-v");
21952
22295
  if (verboseIndex !== -1) {
21953
- options.verbose = true;
22296
+ options2.verbose = true;
21954
22297
  }
21955
22298
  const entryNodeIndex = args.indexOf("--entry-node");
21956
22299
  if (entryNodeIndex !== -1 && args[entryNodeIndex + 1]) {
21957
- options.entryNode = args[entryNodeIndex + 1];
22300
+ options2.entryNode = args[entryNodeIndex + 1];
21958
22301
  }
21959
22302
  const valueOf = (flag) => {
21960
22303
  const index = args.indexOf(flag);
@@ -21967,7 +22310,7 @@ image output nothing downstream consumes, or per output of --entry-node.
21967
22310
  return value;
21968
22311
  };
21969
22312
  const frames = valueOf("--frames");
21970
- if (frames) options.frames = frames;
22313
+ if (frames) options2.frames = frames;
21971
22314
  const fps = valueOf("--fps");
21972
22315
  if (fps !== void 0) {
21973
22316
  const rate = Number(fps);
@@ -21975,13 +22318,13 @@ image output nothing downstream consumes, or per output of --entry-node.
21975
22318
  console.error(`Error: --fps must be a positive number (got "${fps}")`);
21976
22319
  process.exit(1);
21977
22320
  }
21978
- options.fps = rate;
22321
+ options2.fps = rate;
21979
22322
  }
21980
22323
  const out = valueOf("--out");
21981
- if (out) options.out = out;
22324
+ if (out) options2.out = out;
21982
22325
  if (args.includes("--json")) {
21983
- if (command !== "run" || !options.frames) throw new Error("--json requires run --frames (use --frames 1 for a still)");
21984
- options.json = true;
22326
+ if (command !== "run" || !options2.frames) throw new Error("--json requires run --frames (use --frames 1 for a still)");
22327
+ options2.json = true;
21985
22328
  }
21986
22329
  const timeout = valueOf("--timeout");
21987
22330
  if (timeout !== void 0) {
@@ -21996,11 +22339,11 @@ image output nothing downstream consumes, or per output of --entry-node.
21996
22339
  return;
21997
22340
  }
21998
22341
  try {
21999
- if (options.json) console.log = console.error;
22000
- await runGraph(options);
22342
+ if (options2.json) console.log = console.error;
22343
+ await runGraph(options2);
22001
22344
  } catch (error) {
22002
22345
  console.error(`Error: ${error.message}`);
22003
- if (options.verbose && error.stack) {
22346
+ if (options2.verbose && error.stack) {
22004
22347
  console.error(error.stack);
22005
22348
  }
22006
22349
  process.exit(1);