@field/cascade 0.10.0 → 0.11.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 (133) hide show
  1. package/AGENTS.md +20 -3
  2. package/CHANGELOG.md +187 -0
  3. package/README.md +1 -2
  4. package/dist/assets/{CodeEditor-DSZBpzdy.js → CodeEditor-4O7FQ__R.js} +4 -4
  5. package/dist/assets/{CodePanel-sY6sZa7B.js → CodePanel-BU-autmq.js} +2 -2
  6. package/dist/assets/{CustomNodeDialog-BRKnqZhp.js → CustomNodeDialog-BRpDfOV5.js} +1 -1
  7. package/dist/assets/{OpenGraphDialog-ZsgL-iwr.js → OpenGraphDialog-BwbRlC0X.js} +1 -1
  8. package/dist/assets/{ProjectSettings-Cw17nNe3.js → ProjectSettings-DLOXLxcA.js} +1 -1
  9. package/dist/assets/{SettingsDialog-p25h3tj0.js → SettingsDialog-Bz8JxISH.js} +1 -1
  10. package/dist/assets/{VersionHistoryDialog-DzFLUotB.js → VersionHistoryDialog-D5Z0nJ5-.js} +1 -1
  11. package/dist/assets/{core-BICrEt0y.js → core-Yqi0Ni61.js} +1 -1
  12. package/dist/assets/{cssMode-D7TXw57I.js → cssMode-CxUzc4vp.js} +1 -1
  13. package/dist/assets/{definitions-DUww2Rt-.js → definitions-GreEMXLV.js} +1 -1
  14. package/dist/assets/{editor.api-DvkCO3ko.js → editor.api-BNFb25CX.js} +1 -1
  15. package/dist/assets/{freemarker2-BA2lxsF1.js → freemarker2-0ZDnWxeM.js} +1 -1
  16. package/dist/assets/geo-u0n_jOsD.js +1 -0
  17. package/dist/assets/{handlebars-BcDiyPd8.js → handlebars-Cy18MoaD.js} +1 -1
  18. package/dist/assets/{html-C7O4TyhX.js → html-B-2Dx7nq.js} +1 -1
  19. package/dist/assets/{htmlMode-WaDGF3Mj.js → htmlMode-DUAc9B_d.js} +1 -1
  20. package/dist/assets/{image-DR4oeyJB.js → image-cAa-Nu9O.js} +1 -1
  21. package/dist/assets/{index-DyvDC6pS.js → index-5ZtDAuy2.js} +42 -41
  22. package/dist/assets/{javascript-CpIEK7tz.js → javascript-DeQsDqP5.js} +1 -1
  23. package/dist/assets/{jsonMode-B2FltW0f.js → jsonMode-BiFwJVSk.js} +1 -1
  24. package/dist/assets/{liquid-CN6FFHZE.js → liquid-BLoHH68a.js} +1 -1
  25. package/dist/assets/{lspLanguageFeatures-QhV9XtLp.js → lspLanguageFeatures-CNFgm1Kv.js} +1 -1
  26. package/dist/assets/math-DioIQOPJ.js +1 -0
  27. package/dist/assets/{mdx-DE89pafV.js → mdx-CFXQVNfs.js} +1 -1
  28. package/dist/assets/pop-DC_TQG9t.js +1 -0
  29. package/dist/assets/{python-ByTllTB9.js → python-Cf7S7p2E.js} +1 -1
  30. package/dist/assets/{razor-USYlRRCE.js → razor-CMHBcG-g.js} +1 -1
  31. package/dist/assets/{tsMode-BXpN8iUz.js → tsMode-BEih0GDj.js} +1 -1
  32. package/dist/assets/{typescript-DInBjGQQ.js → typescript-COEKYD_a.js} +1 -1
  33. package/dist/assets/{workers-C7Ye5B7D.js → workers-BNhAhMNn.js} +1 -1
  34. package/dist/assets/{xml-U0Uocqyo.js → xml-CPiMpnPl.js} +1 -1
  35. package/dist/assets/{yaml-CBw_PVYf.js → yaml-Bgp1ITLR.js} +1 -1
  36. package/dist/cli/Graph-ZJJTRQTM.js +2975 -0
  37. package/dist/cli/chunk-2KXB47FP.js +115 -0
  38. package/dist/cli/chunk-35D343DC.js +1447 -0
  39. package/dist/cli/chunk-36P5K3SD.js +631 -0
  40. package/dist/cli/chunk-3JTJHF7V.js +267 -0
  41. package/dist/cli/chunk-4UDFYF5G.js +132 -0
  42. package/dist/cli/chunk-53NMBJAO.js +35 -0
  43. package/dist/cli/chunk-A4OAMOZH.js +310 -0
  44. package/dist/cli/chunk-BIJO5OFS.js +71 -0
  45. package/dist/cli/chunk-BP7UKF36.js +376 -0
  46. package/dist/cli/chunk-CKS2FQWR.js +89 -0
  47. package/dist/cli/chunk-CO22XHNG.js +593 -0
  48. package/dist/cli/chunk-D6BUOHUG.js +634 -0
  49. package/dist/cli/chunk-DEQSEFFH.js +153 -0
  50. package/dist/cli/chunk-DUIUFVU2.js +241 -0
  51. package/dist/cli/chunk-EUIAC3U3.js +3006 -0
  52. package/dist/cli/chunk-FCDXJ6EK.js +2208 -0
  53. package/dist/cli/chunk-GFS6W3C5.js +308 -0
  54. package/dist/cli/chunk-HFHWYL55.js +139 -0
  55. package/dist/cli/chunk-HSNOBIVT.js +263 -0
  56. package/dist/cli/chunk-ICH7N5PN.js +26 -0
  57. package/dist/cli/chunk-J7VK7MNF.js +418 -0
  58. package/dist/cli/chunk-KH4MUBKF.js +1551 -0
  59. package/dist/cli/chunk-MIIEFUGG.js +623 -0
  60. package/dist/cli/chunk-OWWBYLHC.js +136 -0
  61. package/dist/cli/chunk-POMMNKZZ.js +672 -0
  62. package/dist/cli/chunk-QBBKRNGY.js +90 -0
  63. package/dist/cli/chunk-QQPA2ZHH.js +273 -0
  64. package/dist/cli/chunk-RLCAJBVR.js +224 -0
  65. package/dist/cli/chunk-RQZ4SX5R.js +1125 -0
  66. package/dist/cli/chunk-RUU3YWXU.js +99 -0
  67. package/dist/cli/chunk-RXDIMXN5.js +98 -0
  68. package/dist/cli/chunk-SGJAET2L.js +197 -0
  69. package/dist/cli/chunk-VVGSLVSF.js +151 -0
  70. package/dist/cli/chunk-WD62JIP6.js +118 -0
  71. package/dist/cli/chunk-WIH4TEIS.js +104 -0
  72. package/dist/cli/chunk-WKURDVYP.js +1119 -0
  73. package/dist/cli/chunk-WPUDGEFY.js +20 -0
  74. package/dist/cli/chunk-XWDSKG7N.js +1301 -0
  75. package/dist/cli/chunk-YAT5Y25U.js +121 -0
  76. package/dist/cli/chunk-ZL4JTG5Y.js +37 -0
  77. package/dist/cli/cliCommands-UQSFGEHF.js +3464 -0
  78. package/dist/cli/commands-Q23M26QM.js +1292 -0
  79. package/dist/cli/definitions-HF2FTHYU.js +32 -0
  80. package/dist/cli/doctorCommand-B7BPQMN6.js +76 -0
  81. package/dist/cli/edit-JHCPTQFT.js +28 -0
  82. package/dist/cli/editorCommands-UIF5YLUZ.js +33 -0
  83. package/dist/cli/encode-HAUZTYPT.js +17 -0
  84. package/dist/cli/frames-LSAUBZ7P.js +16 -0
  85. package/dist/cli/geo-4RNK3QVN.js +43 -0
  86. package/dist/cli/graphCommands-6BMAKGQE.js +184 -0
  87. package/dist/cli/graphFile-VPSQ6OF7.js +286 -0
  88. package/dist/cli/headlessCanvas-5XLDZT7M.js +15 -0
  89. package/dist/cli/helpCommand-3VRE3KVO.js +155 -0
  90. package/dist/cli/index.js +100 -30756
  91. package/dist/cli/math-ZRMLKU2D.js +16 -0
  92. package/dist/cli/mcp-ZLEEMYGF.js +164 -0
  93. package/dist/cli/parity-KINARCP2.js +328 -0
  94. package/dist/cli/pop-USDFXB7H.js +39 -0
  95. package/dist/cli/project-K3QIPGBB.js +12 -0
  96. package/dist/cli/projectDefinition-DMTZN2NE.js +27 -0
  97. package/dist/cli/projectRuntime-Z5PJOH47.js +48 -0
  98. package/dist/cli/registerStandardNodes-S2VLMF6J.js +3081 -0
  99. package/dist/cli/render-YURAPPUI.js +436 -0
  100. package/dist/cli/runCommand-PI764LEG.js +198 -0
  101. package/dist/cli/runner-RA6UKEPD.js +37 -0
  102. package/dist/cli/runtime/image.ts +277 -0
  103. package/dist/cli/runtime/stage.ts +4 -1
  104. package/dist/cli/stageRunner-KUNIMQKJ.js +10 -0
  105. package/dist/cli/verbs-RCE3K4VQ.js +19 -0
  106. package/dist/cli/webBuild-GT4PZT5D.js +37 -0
  107. package/dist/contracts/documentFormat.d.ts +18 -0
  108. package/dist/contracts/documentFormat.js +195 -0
  109. package/dist/contracts/editor.d.ts +17 -0
  110. package/dist/contracts/index.d.ts +1 -0
  111. package/dist/contracts/index.js +1 -0
  112. package/dist/image.d.ts +141 -0
  113. package/dist/image.js +127 -0
  114. package/dist/index.html +1 -1
  115. package/dist/player/editor.js +32 -7
  116. package/dist/runtime/builtins/headless.d.ts +19 -0
  117. package/dist/runtime/builtins/headless.js +25 -0
  118. package/dist/runtime/builtins/image/contactSheet.d.ts +226 -0
  119. package/dist/runtime/builtins/image/contactSheet.js +199 -0
  120. package/dist/runtime/builtins/image/index.d.ts +9 -0
  121. package/dist/runtime/builtins/image/index.js +9 -0
  122. package/dist/stage.js +4 -1
  123. package/doc/CLI_REFERENCE.md +73 -2
  124. package/doc/EDITOR_EXPORT.md +11 -2
  125. package/doc/MCP.md +17 -1
  126. package/doc/NODE_REFERENCE.md +142 -0
  127. package/doc/PROJECT_AUTHORING.md +127 -7
  128. package/package.json +16 -4
  129. package/dist/assets/ExportDialog-C7dVGua4.js +0 -358
  130. package/dist/assets/ExportDialog-DOq0flMV.css +0 -1
  131. package/dist/assets/geo-C2RHOVHC.js +0 -1
  132. package/dist/assets/math-DaDEgWKt.js +0 -1
  133. package/dist/assets/pop-pLH7L0PW.js +0 -1
@@ -0,0 +1,90 @@
1
+ import {
2
+ PythonWorker,
3
+ callStage,
4
+ stageSources
5
+ } from "./chunk-MIIEFUGG.js";
6
+ import {
7
+ readStageConfig,
8
+ runProjectStage
9
+ } from "./chunk-VVGSLVSF.js";
10
+
11
+ // server/src/runtime/stage.ts
12
+ var BRIDGE_KEY = "__cascadeStageBridge";
13
+ function installStageBridge(bridge) {
14
+ const globals = globalThis;
15
+ const previous = globals[BRIDGE_KEY];
16
+ globals[BRIDGE_KEY] = bridge ?? void 0;
17
+ return () => {
18
+ globals[BRIDGE_KEY] = previous;
19
+ };
20
+ }
21
+
22
+ // src/cli/projectStages.ts
23
+ function installProjectStages(project, options = {}) {
24
+ let worker = null;
25
+ const warmWorker = (config) => {
26
+ if (options.worker === false || !config.worker) return null;
27
+ const sources = stageSources(project, config);
28
+ const key = `${config.python}\0${config.worker}\0${sources.roots.join("\0")}`;
29
+ if (worker?.key !== key) {
30
+ worker?.process.shutdown();
31
+ worker = {
32
+ key,
33
+ process: new PythonWorker(project, config.worker, config.python, process.env, {
34
+ sources,
35
+ // The one-shot path's bound, not Studio's two minutes: an offline
36
+ // render may run a stage for several.
37
+ requestTimeoutMs: 10 * 60 * 1e3,
38
+ // What a stage prints is shown with --verbose only, as before.
39
+ onStderr: options.onOutput ? (text) => {
40
+ for (const line of text.split("\n")) options.onOutput(null, "stderr", line);
41
+ } : options.verbose ? (text) => {
42
+ for (const line of text.split("\n")) console.warn(`[stage-worker] ${line}`);
43
+ } : () => {
44
+ }
45
+ })
46
+ };
47
+ }
48
+ return worker.process;
49
+ };
50
+ const dispose = installStageBridge(async (stage, args, callOptions) => {
51
+ const config = readStageConfig(project);
52
+ const warm = warmWorker(config);
53
+ if (warm) {
54
+ const { result: result2 } = await callStage(project, config, stage, args, (envelope) => warm.callWithMeta(stage, args, { signal: callOptions?.signal, cascade: envelope }), { onTrace: options.onTrace });
55
+ return result2;
56
+ }
57
+ const { result } = await callStage(project, config, stage, args, async (envelope) => ({
58
+ result: await runProjectStage(project, stage, args, {
59
+ // A superseded cook kills its subprocess rather than waiting for it.
60
+ signal: callOptions?.signal,
61
+ cascade: envelope,
62
+ codeHash: envelope.code,
63
+ /**
64
+ * A stage can log, and until 0.8 nobody could read it: `stderr` was
65
+ * consulted only on failure and `stdout` only for its last line. Behind
66
+ * `--verbose`, because a stage that prints per mark would bury the
67
+ * render it is part of; prefixed with the stage name, because a graph
68
+ * runs several.
69
+ */
70
+ onOutput: options.onOutput ? (stream, text) => {
71
+ for (const line of text.split("\n")) options.onOutput(stage, stream, line);
72
+ } : options.verbose ? (stream, text) => {
73
+ const write = stream === "stderr" ? console.warn : console.log;
74
+ for (const line of text.split("\n")) write(`[${stage}] ${line}`);
75
+ } : void 0
76
+ })
77
+ }), { onTrace: options.onTrace });
78
+ return result;
79
+ });
80
+ return () => {
81
+ dispose();
82
+ worker?.process.shutdown();
83
+ worker = null;
84
+ };
85
+ }
86
+
87
+ export {
88
+ installProjectStages
89
+ };
90
+ //# sourceMappingURL=chunk-QBBKRNGY.js.map
@@ -0,0 +1,273 @@
1
+ // packages/runtime/src/builtins/math/definitions.ts
2
+ function asComponents(value, label2) {
3
+ if (typeof value === "number") return [value];
4
+ if (Array.isArray(value) && value.every((part) => typeof part === "number"))
5
+ return value;
6
+ if (value === void 0 || value === null) return [0];
7
+ throw new Error(
8
+ `${label2} needs a number or a vector of numbers, and got ${describe(value)}`
9
+ );
10
+ }
11
+ function describe(value) {
12
+ if (Array.isArray(value)) return `an array of ${value.length}`;
13
+ return `a ${typeof value}`;
14
+ }
15
+ function combine(left, right, operate) {
16
+ const a = asComponents(left, "the first input");
17
+ const b = asComponents(right, "the second input");
18
+ if (a.length !== b.length && a.length !== 1 && b.length !== 1)
19
+ throw new Error(
20
+ `cannot combine a vector of ${a.length} with a vector of ${b.length}`
21
+ );
22
+ const width = Math.max(a.length, b.length);
23
+ const out = [];
24
+ for (let index = 0; index < width; index += 1)
25
+ out.push(operate(a[a.length === 1 ? 0 : index], b[b.length === 1 ? 0 : index]));
26
+ return width === 1 ? out[0] : out;
27
+ }
28
+ function map(value, operate) {
29
+ const components = asComponents(value, "the input");
30
+ const out = components.map(operate);
31
+ return out.length === 1 ? out[0] : out;
32
+ }
33
+ function divide(a, b) {
34
+ return b === 0 ? 0 : a / b;
35
+ }
36
+ var BINARY = {
37
+ add: (a, b) => a + b,
38
+ subtract: (a, b) => a - b,
39
+ multiply: (a, b) => a * b,
40
+ divide,
41
+ power: (a, b) => a ** b,
42
+ modulo: (a, b) => b === 0 ? 0 : a % b,
43
+ min: (a, b) => Math.min(a, b),
44
+ max: (a, b) => Math.max(a, b),
45
+ atan2: (a, b) => Math.atan2(a, b)
46
+ };
47
+ var DEG = Math.PI / 180;
48
+ var UNARY = {
49
+ abs: Math.abs,
50
+ negate: (a) => -a,
51
+ floor: Math.floor,
52
+ ceil: Math.ceil,
53
+ round: Math.round,
54
+ sqrt: (a) => a < 0 ? 0 : Math.sqrt(a),
55
+ sign: Math.sign,
56
+ // Trigonometry in RADIANS, matching the expression engine, where bare `sin`
57
+ // is radians too. `radians` and `degrees` are here for the conversion, and
58
+ // the divergence from `Transform`'s degrees is recorded in AGENTS.md.
59
+ sin: Math.sin,
60
+ cos: Math.cos,
61
+ tan: Math.tan,
62
+ asin: (a) => Math.asin(Math.max(-1, Math.min(1, a))),
63
+ acos: (a) => Math.acos(Math.max(-1, Math.min(1, a))),
64
+ exp: Math.exp,
65
+ log: (a) => a <= 0 ? 0 : Math.log(a),
66
+ radians: (a) => a * DEG,
67
+ degrees: (a) => a / DEG,
68
+ /** The fractional part, always positive — `fract(-0.25)` is `0.75`, which is
69
+ * what a repeating pattern wants and what GLSL's `fract` does. */
70
+ fract: (a) => a - Math.floor(a)
71
+ };
72
+ var COMPARE = {
73
+ greater: (a, b) => a > b,
74
+ "greater-or-equal": (a, b) => a >= b,
75
+ less: (a, b) => a < b,
76
+ "less-or-equal": (a, b) => a <= b,
77
+ equal: (a, b) => a === b,
78
+ "not-equal": (a, b) => a !== b
79
+ };
80
+ function options(table) {
81
+ return Object.keys(table).map((value) => ({ value, label: label(value) }));
82
+ }
83
+ function label(value) {
84
+ return value.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
85
+ }
86
+ var binaryDefinition = {
87
+ apiVersion: 1,
88
+ label: "Binary Math",
89
+ description: "Two values, one operation. Component-wise, and a scalar broadcasts against a vector.",
90
+ icon: "Plus",
91
+ runsOn: "portable",
92
+ inputs: {
93
+ a: { kind: "data", type: "any", default: 0, description: "First value." },
94
+ b: { kind: "data", type: "any", default: 0, description: "Second value." }
95
+ },
96
+ outputs: { value: { kind: "data", type: "any" } },
97
+ props: {
98
+ operation: {
99
+ type: "string",
100
+ default: "add",
101
+ control: "select",
102
+ options: options(BINARY),
103
+ label: "Operation"
104
+ }
105
+ }
106
+ };
107
+ function executeBinary(context) {
108
+ const operate = BINARY[context.props.operation];
109
+ if (!operate)
110
+ throw new Error(
111
+ `cascade.math.Binary has no operation "${context.props.operation}"`
112
+ );
113
+ context.outputs.value.set(combine(context.inputs.a, context.inputs.b, operate));
114
+ }
115
+ var unaryDefinition = {
116
+ apiVersion: 1,
117
+ label: "Unary Math",
118
+ description: "One value, one operation. Trigonometry is in radians.",
119
+ icon: "FunctionSquare",
120
+ runsOn: "portable",
121
+ inputs: {
122
+ value: { kind: "data", type: "any", default: 0 }
123
+ },
124
+ outputs: { value: { kind: "data", type: "any" } },
125
+ props: {
126
+ operation: {
127
+ type: "string",
128
+ default: "abs",
129
+ control: "select",
130
+ options: options(UNARY),
131
+ label: "Operation"
132
+ }
133
+ }
134
+ };
135
+ function executeUnary(context) {
136
+ const operate = UNARY[context.props.operation];
137
+ if (!operate)
138
+ throw new Error(
139
+ `cascade.math.Unary has no operation "${context.props.operation}"`
140
+ );
141
+ context.outputs.value.set(map(context.inputs.value, operate));
142
+ }
143
+ var clampDefinition = {
144
+ apiVersion: 1,
145
+ label: "Clamp",
146
+ description: "Hold a value between two bounds.",
147
+ icon: "Brackets",
148
+ runsOn: "portable",
149
+ inputs: {
150
+ value: { kind: "data", type: "any", default: 0 },
151
+ min: { kind: "data", type: "any", default: 0 },
152
+ max: { kind: "data", type: "any", default: 1 }
153
+ },
154
+ outputs: { value: { kind: "data", type: "any" } },
155
+ props: {}
156
+ };
157
+ function executeClamp(context) {
158
+ const lower = combine(context.inputs.value, context.inputs.min, Math.max);
159
+ context.outputs.value.set(combine(lower, context.inputs.max, Math.min));
160
+ }
161
+ var mixDefinition = {
162
+ apiVersion: 1,
163
+ label: "Mix",
164
+ description: "Blend between two values. Houdini's lerp.",
165
+ icon: "Blend",
166
+ runsOn: "portable",
167
+ inputs: {
168
+ a: { kind: "data", type: "any", default: 0, description: "The value at 0." },
169
+ b: { kind: "data", type: "any", default: 1, description: "The value at 1." },
170
+ bias: {
171
+ kind: "data",
172
+ type: "float",
173
+ default: 0.5,
174
+ min: 0,
175
+ max: 1,
176
+ step: 0.01,
177
+ description: "Not clamped: past 0..1 this extrapolates, which is useful."
178
+ }
179
+ },
180
+ outputs: { value: { kind: "data", type: "any" } },
181
+ props: {}
182
+ };
183
+ function executeMix(context) {
184
+ const bias = context.inputs.bias ?? 0.5;
185
+ const delta = combine(context.inputs.b, context.inputs.a, (b, a) => b - a);
186
+ context.outputs.value.set(
187
+ combine(context.inputs.a, delta, (a, d) => a + d * bias)
188
+ );
189
+ }
190
+ var compareDefinition = {
191
+ apiVersion: 1,
192
+ label: "Compare",
193
+ description: "Test two values. Outputs a bool, for a Switch.",
194
+ icon: "Equal",
195
+ runsOn: "portable",
196
+ inputs: {
197
+ a: { kind: "data", type: "float", default: 0 },
198
+ b: { kind: "data", type: "float", default: 0 }
199
+ },
200
+ outputs: { result: { kind: "data", type: "bool" } },
201
+ props: {
202
+ operation: {
203
+ type: "string",
204
+ default: "greater",
205
+ control: "select",
206
+ options: options(COMPARE),
207
+ label: "Test"
208
+ },
209
+ /**
210
+ * Scalars only, unlike the rest of this namespace.
211
+ *
212
+ * "Is this vector greater than that one" has no single right answer — all
213
+ * components, any component, or the length — and a node that picked one
214
+ * silently would be wrong for the other two readings. A comparison feeding
215
+ * a `Switch` wants one boolean, so the input is a `float` and the question
216
+ * does not arise.
217
+ */
218
+ tolerance: {
219
+ type: "float",
220
+ default: 0,
221
+ min: 0,
222
+ label: "Tolerance",
223
+ description: "Slack for Equal and Not Equal. Floating point rarely lands on exactly equal."
224
+ }
225
+ }
226
+ };
227
+ function executeCompare(context) {
228
+ const operation = context.props.operation;
229
+ const test = COMPARE[operation];
230
+ if (!test)
231
+ throw new Error(`cascade.math.Compare has no test "${context.props.operation}"`);
232
+ const a = context.inputs.a ?? 0;
233
+ const b = context.inputs.b ?? 0;
234
+ const tolerance = context.props.tolerance;
235
+ if (tolerance > 0 && (operation === "equal" || operation === "not-equal")) {
236
+ const near = Math.abs(a - b) <= tolerance;
237
+ context.outputs.result.set(operation === "equal" ? near : !near);
238
+ return;
239
+ }
240
+ context.outputs.result.set(test(a, b));
241
+ }
242
+ function registration(name, definition, execute) {
243
+ return {
244
+ kind: "definition-v1",
245
+ moduleId: `cascade.math.${name}`,
246
+ definition,
247
+ loadExecute: async () => execute
248
+ };
249
+ }
250
+ var binaryRegistration = registration("Binary", binaryDefinition, executeBinary);
251
+ var unaryRegistration = registration("Unary", unaryDefinition, executeUnary);
252
+ var clampRegistration = registration("Clamp", clampDefinition, executeClamp);
253
+ var mixRegistration = registration("Mix", mixDefinition, executeMix);
254
+ var compareRegistration = registration("Compare", compareDefinition, executeCompare);
255
+ var mathNodeRegistrations = Object.freeze([
256
+ binaryRegistration,
257
+ unaryRegistration,
258
+ clampRegistration,
259
+ mixRegistration,
260
+ compareRegistration
261
+ ]);
262
+ var mathNodeDefinitions = Object.freeze([
263
+ ["cascade.math.Binary", binaryDefinition],
264
+ ["cascade.math.Unary", unaryDefinition],
265
+ ["cascade.math.Clamp", clampDefinition],
266
+ ["cascade.math.Mix", mixDefinition],
267
+ ["cascade.math.Compare", compareDefinition]
268
+ ]);
269
+
270
+ export {
271
+ mathNodeRegistrations
272
+ };
273
+ //# sourceMappingURL=chunk-QQPA2ZHH.js.map
@@ -0,0 +1,224 @@
1
+ import {
2
+ browserAssetCapability
3
+ } from "./chunk-53NMBJAO.js";
4
+ import {
5
+ attachDefinition
6
+ } from "./chunk-SGJAET2L.js";
7
+ import {
8
+ createGpuHost
9
+ } from "./chunk-GFS6W3C5.js";
10
+ import {
11
+ validateNodeDefinition
12
+ } from "./chunk-A4OAMOZH.js";
13
+
14
+ // src/nodes/definition/browserShellCapability.ts
15
+ var capability = null;
16
+ function forgetShellCapability() {
17
+ capability = null;
18
+ }
19
+ async function token() {
20
+ if (capability) return capability;
21
+ const response = await fetch("/api/shell/capability", { cache: "no-store" });
22
+ if (!response.ok) {
23
+ throw new Error(
24
+ response.status === 404 ? "The shell capability is disabled on this server. It is available on a loopback server, or one started with an explicit trusted host." : `Could not obtain the shell capability (${response.status})`
25
+ );
26
+ }
27
+ const { capability: issued } = await response.json();
28
+ if (typeof issued !== "string" || !issued) {
29
+ throw new Error("Server issued no shell capability");
30
+ }
31
+ capability = issued;
32
+ return issued;
33
+ }
34
+ function domSignal(signal) {
35
+ if (!signal) return void 0;
36
+ const controller = new AbortController();
37
+ if (signal.aborted) controller.abort(signal.reason);
38
+ else signal.addEventListener("abort", () => controller.abort(signal.reason), { once: true });
39
+ return controller.signal;
40
+ }
41
+ async function post(body, signal) {
42
+ const bridged = domSignal(signal);
43
+ const send = async (issued) => fetch("/api/shell", {
44
+ method: "POST",
45
+ headers: {
46
+ "Content-Type": "application/json",
47
+ "X-Cascade-Shell-Capability": issued
48
+ },
49
+ body: JSON.stringify(body),
50
+ ...bridged ? { signal: bridged } : {}
51
+ });
52
+ let response = await send(await token());
53
+ if (response.status === 403) {
54
+ capability = null;
55
+ response = await send(await token());
56
+ }
57
+ return response;
58
+ }
59
+ function requestBody(command, args, options) {
60
+ const { signal: _signal, ...rest } = options ?? {};
61
+ return {
62
+ command,
63
+ ...args ? { args } : {},
64
+ // `/api/shell` has always accepted execution options at the top level;
65
+ // keep the Studio adapter on that wire contract. Nesting them under
66
+ // `options` silently drops stdin, timeout, cwd and env in the route parser.
67
+ ...rest
68
+ };
69
+ }
70
+ async function fail(response) {
71
+ let payload = null;
72
+ try {
73
+ payload = await response.json();
74
+ } catch {
75
+ }
76
+ const message = typeof payload?.error === "string" ? payload.error : `Shell request failed (${response.status})`;
77
+ if (payload?.code === "COMMAND_NOT_ALLOWED") {
78
+ throw new Error(`${message}. Add it under Allowed Commands in Project Settings.`);
79
+ }
80
+ throw new Error(message);
81
+ }
82
+ var browserShellCapability = {
83
+ async run(command, args, options) {
84
+ const response = await post(requestBody(command, args, options), options?.signal);
85
+ if (!response.ok) await fail(response);
86
+ const { ok: _ok, ...result } = await response.json();
87
+ return result;
88
+ },
89
+ async runJson(command, args, options) {
90
+ const result = await this.run(command, args, options);
91
+ try {
92
+ return JSON.parse(result.stdout);
93
+ } catch {
94
+ throw new Error(`${command} did not print JSON`);
95
+ }
96
+ }
97
+ };
98
+ async function shellRouteAvailable() {
99
+ const forget = () => {
100
+ forgetShellCapability();
101
+ return false;
102
+ };
103
+ if (typeof fetch !== "function") return forget();
104
+ try {
105
+ const response = await fetch("/api/shell/capability", { cache: "no-store" });
106
+ if (!response.ok) return forget();
107
+ const { capability: issued } = await response.json();
108
+ if (typeof issued !== "string" || !issued) return forget();
109
+ capability = issued;
110
+ return true;
111
+ } catch {
112
+ return forget();
113
+ }
114
+ }
115
+
116
+ // src/browser/gpu.ts
117
+ function createBrowserGpuHost() {
118
+ const gpu = globalThis.navigator?.gpu;
119
+ if (!gpu) return void 0;
120
+ return createGpuHost({
121
+ async acquire() {
122
+ const adapter = await gpu.requestAdapter();
123
+ if (!adapter) {
124
+ throw new Error(
125
+ "No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on."
126
+ );
127
+ }
128
+ const device = await adapter.requestDevice();
129
+ const info = adapter.info;
130
+ return {
131
+ device,
132
+ info: {
133
+ vendor: info?.vendor ?? "",
134
+ architecture: info?.architecture ?? "",
135
+ device: info?.device ?? "",
136
+ description: info?.description ?? ""
137
+ },
138
+ limits: device.limits ?? adapter.limits
139
+ };
140
+ }
141
+ });
142
+ }
143
+
144
+ // src/nodes/definition/projectDefinition.ts
145
+ var studioCapabilities = {
146
+ assets: browserAssetCapability,
147
+ // Installed only where the browser has WebGPU, and absent rather than
148
+ // stubbed otherwise. That is what makes the failure a sentence: the preflight
149
+ // reports `runtime/missing-capability` and the adapter throws "requires the
150
+ // Studio gpu capability", both naming what is missing. A stub that threw on
151
+ // first use would name nothing.
152
+ ...gpuCapabilityIfAvailable(),
153
+ /**
154
+ * Shell is present up front and withdrawn if the route turns out not to be
155
+ * there, which is the opposite way round from gpu and worth the sentence.
156
+ *
157
+ * `navigator.gpu` is a synchronous check, so gpu can be absent from the map
158
+ * before anything reads it. Whether the shell route exists takes a fetch, and
159
+ * this object is a module-level const read by three default parameters — so
160
+ * the choice is between delaying Studio's boot on a request, racing the
161
+ * probe, or installing and withdrawing. Installing is the one where the
162
+ * common case (a loopback server, where the route is always there) has no
163
+ * race at all and no cost.
164
+ *
165
+ * What it costs: on a server that does NOT offer the route, a cook in the
166
+ * first few milliseconds after boot sees the capability present and fails at
167
+ * the fetch instead of at the gate. That failure still names itself — *"the
168
+ * shell capability is disabled on this server"* — so it is a worse-shaped
169
+ * error rather than a silent one, and once the probe lands the preflight is
170
+ * correct from then on.
171
+ */
172
+ shell: browserShellCapability
173
+ };
174
+ void shellRouteAvailable().then((available) => {
175
+ if (!available) delete studioCapabilities.shell;
176
+ });
177
+ function gpuCapabilityIfAvailable() {
178
+ const gpu = createBrowserGpuHost();
179
+ return gpu ? { gpu } : {};
180
+ }
181
+ function declaresDefinitionV1(value) {
182
+ return typeof value === "object" && value !== null && value.apiVersion === 1;
183
+ }
184
+ function definitionAdapterError(value) {
185
+ const diagnostics = validateNodeDefinition(value);
186
+ if (!diagnostics.length) return null;
187
+ return diagnostics.map((item) => `${item.code}: ${item.message}`).join("\n");
188
+ }
189
+ function missingStudioCapabilities(definition, capabilities = studioCapabilities) {
190
+ const declared = definition?.capabilities;
191
+ if (!Array.isArray(declared)) return [];
192
+ return declared.filter((name) => typeof name === "string").filter((name) => !capabilities[name]);
193
+ }
194
+ function moduleNodeFunction(node, moduleId, modulePromise, capabilities = studioCapabilities) {
195
+ const prepared = modulePromise.then((module) => adaptModule(node, moduleId, module, capabilities));
196
+ prepared.catch(() => {
197
+ });
198
+ return (n, g) => prepared.then((fn) => fn(n, g));
199
+ }
200
+ function adaptModule(node, moduleId, module, capabilities = studioCapabilities) {
201
+ if (!declaresDefinitionV1(module.definition)) {
202
+ return (n, g) => module.execute(n, g);
203
+ }
204
+ const invalid = definitionAdapterError(module.definition);
205
+ if (invalid) throw new Error(`${moduleId} exports an invalid definition-v1 literal:
206
+ ${invalid}`);
207
+ const registration = {
208
+ kind: "definition-v1",
209
+ moduleId,
210
+ definition: module.definition,
211
+ loadExecute: async () => module.execute
212
+ };
213
+ return attachDefinition(node, registration, capabilities);
214
+ }
215
+
216
+ export {
217
+ studioCapabilities,
218
+ declaresDefinitionV1,
219
+ definitionAdapterError,
220
+ missingStudioCapabilities,
221
+ moduleNodeFunction,
222
+ adaptModule
223
+ };
224
+ //# sourceMappingURL=chunk-RLCAJBVR.js.map