@field/cascade 0.11.0 → 0.13.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 (220) hide show
  1. package/AGENTS.md +8 -5
  2. package/ARCHITECTURE.md +5 -4
  3. package/CHANGELOG.md +245 -0
  4. package/README.md +1 -1
  5. package/dist/assets/AgentPanel-1JugFjE4.css +1 -0
  6. package/dist/assets/AgentPanel-BDL5MddA.js +5 -0
  7. package/dist/assets/{CodeEditor-4O7FQ__R.js → CodeEditor-C-xYG-Wn.js} +120 -120
  8. package/dist/assets/CodePanel-CtXd50m6.js +2 -0
  9. package/dist/assets/CookInfoPanel-B3NMhKJF.css +1 -0
  10. package/dist/assets/CookInfoPanel-DG4_TRsX.js +2 -0
  11. package/dist/assets/CustomNodeDialog-CHfZUs-r.js +1 -0
  12. package/dist/assets/DefinitionNode-CaRTXQ_b.js +1 -0
  13. package/dist/assets/DefinitionPanel-C3CxZY9r.js +1 -0
  14. package/dist/assets/DefinitionPanel-VNLnAiQD.css +1 -0
  15. package/dist/assets/OpenGraphDialog-BLdr6k_q.js +1 -0
  16. package/dist/assets/ProjectSettings-ZnvmIAir.js +2 -0
  17. package/dist/assets/{SettingsDialog-Bz8JxISH.js → SettingsDialog-CBX3Wa87.js} +1 -1
  18. package/dist/assets/TimelinePanel-CDeR0arY.css +1 -0
  19. package/dist/assets/TimelinePanel-CWgnFH1e.js +1 -0
  20. package/dist/assets/{VersionHistoryDialog-D5Z0nJ5-.js → VersionHistoryDialog-BOSoW1R-.js} +1 -1
  21. package/dist/assets/assert-BO2_erPD.js +1 -0
  22. package/dist/assets/assert-hvykGCnP.js +1 -0
  23. package/dist/assets/builder-BmBKcDix.js +1 -0
  24. package/dist/assets/camera-DFF7YA3F.js +1 -0
  25. package/dist/assets/checks-DAvyO9Gf.js +1 -0
  26. package/dist/assets/contactSheet-N1NcaWxw.js +2 -0
  27. package/dist/assets/{core-Yqi0Ni61.js → core-DB-MkGjj.js} +3 -4
  28. package/dist/assets/coreTypes-DtnDATZ7.js +1 -0
  29. package/dist/assets/{cssMode-CxUzc4vp.js → cssMode-DPVckSOc.js} +1 -1
  30. package/dist/assets/definitions-B227e7f_.js +1 -0
  31. package/dist/assets/definitions-D0WOUJd7.js +1 -0
  32. package/dist/assets/{editor.api-BNFb25CX.js → editor.api-Dkutg_Z1.js} +1 -1
  33. package/dist/assets/expressions-X-Z-GrgW.js +5 -0
  34. package/dist/assets/file-DAL6IfjV.js +1 -0
  35. package/dist/assets/filters-D3k1LRMa.js +1 -0
  36. package/dist/assets/{freemarker2-0ZDnWxeM.js → freemarker2-CLiLKk3B.js} +1 -1
  37. package/dist/assets/generators-BhDxRAFC.js +1 -0
  38. package/dist/assets/geo-BsMImJIJ.js +1 -0
  39. package/dist/assets/geo-C4YngDob.js +2 -0
  40. package/dist/assets/{handlebars-Cy18MoaD.js → handlebars-CcD3aMeL.js} +1 -1
  41. package/dist/assets/{html-B-2Dx7nq.js → html-DvZWGfqb.js} +1 -1
  42. package/dist/assets/{htmlMode-DUAc9B_d.js → htmlMode-DUD3VaoN.js} +1 -1
  43. package/dist/assets/image-0SfH-snh.js +1 -0
  44. package/dist/assets/image-Ck-RdGU4.js +2 -0
  45. package/dist/assets/index-BWnrmIB9.js +137 -0
  46. package/dist/assets/index-DIaxEONI.css +1 -0
  47. package/dist/assets/io-BGmt2g0D.js +1 -0
  48. package/dist/assets/{javascript-DeQsDqP5.js → javascript-Cap2ahgh.js} +1 -1
  49. package/dist/assets/{jsonMode-BiFwJVSk.js → jsonMode-B0uLjp9C.js} +1 -1
  50. package/dist/assets/{liquid-BLoHH68a.js → liquid-CnxwhfLn.js} +1 -1
  51. package/dist/assets/{lspLanguageFeatures-CNFgm1Kv.js → lspLanguageFeatures-Dn9hlKX6.js} +1 -1
  52. package/dist/assets/math-GeULS-sk.js +1 -0
  53. package/dist/assets/{mdx-CFXQVNfs.js → mdx-BV3jbrVa.js} +1 -1
  54. package/dist/assets/pop-KMF_qloi.js +1 -0
  55. package/dist/assets/preload-helper-Czpn1I53.js +1 -0
  56. package/dist/assets/{python-Cf7S7p2E.js → python-CGyFk4XK.js} +1 -1
  57. package/dist/assets/{razor-CMHBcG-g.js → razor-CQfzX3-N.js} +1 -1
  58. package/dist/assets/remap-5SjXQU-q.js +1 -0
  59. package/dist/assets/renderMarkdown-ork0Oir7.js +67 -0
  60. package/dist/assets/studioImages-CAyH-0Pl.js +1 -0
  61. package/dist/assets/text-ZpX7qNkx.js +2 -0
  62. package/dist/assets/time-P_OiBhcf.js +1 -0
  63. package/dist/assets/{tsMode-BEih0GDj.js → tsMode-BOeXzO9H.js} +1 -1
  64. package/dist/assets/{typescript-COEKYD_a.js → typescript-J1SnE_Tu.js} +1 -1
  65. package/dist/assets/{workers-BNhAhMNn.js → workers-CYwwTiVF.js} +1 -1
  66. package/dist/assets/{xml-CPiMpnPl.js → xml-DnPRNzK9.js} +1 -1
  67. package/dist/assets/{yaml-Bgp1ITLR.js → yaml-BmmYBNa9.js} +1 -1
  68. package/dist/cli/DefinitionNode-P7AA4TAU.js +15 -0
  69. package/dist/cli/{Graph-ZJJTRQTM.js → Graph-OB6EWSGL.js} +12 -12
  70. package/dist/cli/assert-GROGQNCQ.js +25 -0
  71. package/dist/cli/checks-3WB44XBL.js +189 -0
  72. package/dist/cli/{chunk-3JTJHF7V.js → chunk-2L34ERS2.js} +4 -4
  73. package/dist/cli/chunk-53LGFPWX.js +138 -0
  74. package/dist/cli/chunk-5ORUQ6ES.js +267 -0
  75. package/dist/cli/{chunk-YAT5Y25U.js → chunk-7QE2ZNSN.js} +3 -3
  76. package/dist/cli/{chunk-SGJAET2L.js → chunk-BPODRH52.js} +8 -4
  77. package/dist/cli/{chunk-XWDSKG7N.js → chunk-BZ63XQ3I.js} +2 -2
  78. package/dist/cli/{chunk-FCDXJ6EK.js → chunk-CTPLRSWF.js} +2 -13
  79. package/dist/cli/{chunk-WIH4TEIS.js → chunk-EZRWJ4G2.js} +1 -1
  80. package/dist/cli/{chunk-BIJO5OFS.js → chunk-FWMSGKEH.js} +1 -1
  81. package/dist/cli/{chunk-POMMNKZZ.js → chunk-G23A2BZZ.js} +38 -5
  82. package/dist/cli/{chunk-35D343DC.js → chunk-GRIN2DDP.js} +19 -1
  83. package/dist/cli/{chunk-WKURDVYP.js → chunk-JEW5MPGI.js} +28 -331
  84. package/dist/cli/chunk-JVOCK5W4.js +83 -0
  85. package/dist/cli/chunk-KBGUO27Q.js +15 -0
  86. package/dist/cli/{chunk-GFS6W3C5.js → chunk-L6B6FUQP.js} +54 -32
  87. package/dist/cli/chunk-LTXWBXOM.js +438 -0
  88. package/dist/cli/chunk-MI5JLQYA.js +220 -0
  89. package/dist/cli/{chunk-A4OAMOZH.js → chunk-NJFMDXHR.js} +13 -3
  90. package/dist/cli/{chunk-DUIUFVU2.js → chunk-OBZAP3GG.js} +49 -23
  91. package/dist/cli/chunk-QAGATJGE.js +336 -0
  92. package/dist/cli/{chunk-EUIAC3U3.js → chunk-QKL7CCRS.js} +966 -132
  93. package/dist/cli/{chunk-RLCAJBVR.js → chunk-TJW67QSI.js} +8 -7
  94. package/dist/cli/{chunk-RQZ4SX5R.js → chunk-W5PJ5RMP.js} +156 -84
  95. package/dist/cli/chunk-W7OUAT7J.js +122 -0
  96. package/dist/cli/chunk-WGKPJZDY.js +364 -0
  97. package/dist/cli/{chunk-D6BUOHUG.js → chunk-X4WKAOQP.js} +4 -4
  98. package/dist/cli/{chunk-VVGSLVSF.js → chunk-X566FWBV.js} +6 -1
  99. package/dist/cli/chunk-YH637PDL.js +317 -0
  100. package/dist/cli/chunk-ZSCUZE6I.js +112 -0
  101. package/dist/cli/{cliCommands-UQSFGEHF.js → cliCommands-XVFRD7CF.js} +23 -17
  102. package/dist/cli/{commands-Q23M26QM.js → commands-M2XLTPTZ.js} +129 -47
  103. package/dist/cli/contactSheet-2PQVRUFW.js +11 -0
  104. package/dist/cli/{definitions-HF2FTHYU.js → definitions-BPOPW52L.js} +7 -7
  105. package/dist/cli/{doctorCommand-B7BPQMN6.js → doctorCommand-RAEI7GVY.js} +36 -6
  106. package/dist/cli/{edit-JHCPTQFT.js → edit-S5KEQ5RA.js} +3 -3
  107. package/dist/cli/editorCommands-5BBG4S6V.js +41 -0
  108. package/dist/cli/{encode-HAUZTYPT.js → encode-ICPEPMH5.js} +2 -2
  109. package/dist/cli/file-3HTQHPYQ.js +41 -0
  110. package/dist/cli/filters-6ODFWYK4.js +379 -0
  111. package/dist/cli/generators-YU7GB4MD.js +210 -0
  112. package/dist/cli/{geo-4RNK3QVN.js → geo-6ZPGKZRU.js} +7 -7
  113. package/dist/cli/{graphCommands-6BMAKGQE.js → graphCommands-MULQ7VD2.js} +4 -4
  114. package/dist/cli/{graphFile-VPSQ6OF7.js → graphFile-CITF36ZM.js} +25 -19
  115. package/dist/cli/{headlessCanvas-5XLDZT7M.js → headlessCanvas-ENRHC7GS.js} +3 -2
  116. package/dist/cli/{helpCommand-3VRE3KVO.js → helpCommand-X2QDANM3.js} +9 -7
  117. package/dist/cli/image-TXELZZK5.js +55 -0
  118. package/dist/cli/index.js +14 -14
  119. package/dist/cli/{math-ZRMLKU2D.js → math-RSYRQ3C6.js} +5 -5
  120. package/dist/cli/{mcp-ZLEEMYGF.js → mcp-YRIGRLYQ.js} +4 -4
  121. package/dist/cli/{parity-KINARCP2.js → parity-3N3HTKWX.js} +42 -36
  122. package/dist/cli/{pop-USDFXB7H.js → pop-B7SIIYKN.js} +5 -5
  123. package/dist/cli/project-SJF5ZF3F.js +12 -0
  124. package/dist/cli/{projectDefinition-DMTZN2NE.js → projectDefinition-F3D7UIHJ.js} +8 -8
  125. package/dist/cli/{projectRuntime-Z5PJOH47.js → projectRuntime-NY4XMDFH.js} +19 -12
  126. package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
  127. package/dist/cli/{render-YURAPPUI.js → render-Y23W6MY6.js} +29 -22
  128. package/dist/cli/{runCommand-PI764LEG.js → runCommand-XOG7AVC5.js} +3 -3
  129. package/dist/cli/runner-DNJO5WO2.js +43 -0
  130. package/dist/cli/runtime/image.ts +27 -2
  131. package/dist/cli/runtime/io.ts +78 -0
  132. package/dist/cli/share-B7HTCOJX.js +166 -0
  133. package/dist/cli/stageCache-TY2BFZJR.js +15 -0
  134. package/dist/cli/{stageRunner-KUNIMQKJ.js → stageRunner-KY2EJP7X.js} +2 -2
  135. package/dist/cli/text-MB3EKR4H.js +126 -0
  136. package/dist/cli/{verbs-RCE3K4VQ.js → verbs-PI2JH5FI.js} +2 -2
  137. package/dist/cli/webBuild-2K4COCGY.js +44 -0
  138. package/dist/contracts/capabilities.d.ts +39 -1
  139. package/dist/contracts/color.d.ts +8 -0
  140. package/dist/contracts/color.js +23 -0
  141. package/dist/contracts/definition.d.ts +10 -0
  142. package/dist/contracts/schema.d.ts +1 -1
  143. package/dist/contracts/schema.js +1 -0
  144. package/dist/contracts/validate.js +11 -1
  145. package/dist/contracts/values.d.ts +24 -0
  146. package/dist/image.d.ts +15 -0
  147. package/dist/image.js +36 -2
  148. package/dist/index.html +8 -2
  149. package/dist/io.d.ts +29 -0
  150. package/dist/io.js +40 -0
  151. package/dist/player/app.js +11 -7
  152. package/dist/player/editor.js +3 -2
  153. package/dist/player/editorEngine.js +3 -2
  154. package/dist/player/embed.js +2 -0
  155. package/dist/player/types.d.ts +6 -0
  156. package/dist/runtime/builtins/assert/checks.d.ts +18 -0
  157. package/dist/runtime/builtins/assert/checks.js +207 -0
  158. package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
  159. package/dist/runtime/builtins/assert/definitions.js +54 -0
  160. package/dist/runtime/builtins/assert/index.d.ts +152 -0
  161. package/dist/runtime/builtins/assert/index.js +36 -0
  162. package/dist/runtime/builtins/image/contactSheet.d.ts +3 -165
  163. package/dist/runtime/builtins/image/contactSheet.js +0 -49
  164. package/dist/runtime/builtins/image/definitions.d.ts +950 -0
  165. package/dist/runtime/builtins/image/definitions.js +343 -0
  166. package/dist/runtime/builtins/image/file.d.ts +8 -0
  167. package/dist/runtime/builtins/image/file.js +37 -0
  168. package/dist/runtime/builtins/image/filters.d.ts +8 -0
  169. package/dist/runtime/builtins/image/filters.js +394 -0
  170. package/dist/runtime/builtins/image/generators.d.ts +7 -0
  171. package/dist/runtime/builtins/image/generators.js +144 -0
  172. package/dist/runtime/builtins/image/index.d.ts +947 -4
  173. package/dist/runtime/builtins/image/index.js +41 -6
  174. package/dist/runtime/builtins/image/io.d.ts +100 -0
  175. package/dist/runtime/builtins/image/io.js +142 -0
  176. package/dist/runtime/builtins/image/planar.d.ts +55 -0
  177. package/dist/runtime/builtins/image/planar.js +274 -0
  178. package/dist/runtime/builtins/image/simplex.d.ts +22 -0
  179. package/dist/runtime/builtins/image/simplex.js +92 -0
  180. package/dist/runtime/builtins/image/text.d.ts +4 -0
  181. package/dist/runtime/builtins/image/text.js +141 -0
  182. package/dist/runtime/builtins/index.d.ts +3 -0
  183. package/dist/runtime/builtins/index.js +7 -0
  184. package/dist/runtime/definition/architecture.js +89 -0
  185. package/dist/runtime/gpu.d.ts +25 -0
  186. package/dist/runtime/gpu.js +40 -0
  187. package/dist/runtime/runtime.d.ts +2 -0
  188. package/dist/runtime/runtime.js +268 -44
  189. package/dist/runtime/types.d.ts +30 -0
  190. package/doc/CLI_REFERENCE.md +21 -3
  191. package/doc/EDITOR_EXPORT.md +26 -0
  192. package/doc/MCP.md +2 -1
  193. package/doc/NODE_REFERENCE.md +286 -14
  194. package/doc/PROJECT_AUTHORING.md +158 -4
  195. package/doc/WEB_PLAYER.md +9 -1
  196. package/package.json +4 -1
  197. package/dist/assets/CodePanel-BU-autmq.js +0 -2
  198. package/dist/assets/CustomNodeDialog-BRpDfOV5.js +0 -1
  199. package/dist/assets/OpenGraphDialog-BwbRlC0X.js +0 -1
  200. package/dist/assets/ProjectSettings-DLOXLxcA.js +0 -2
  201. package/dist/assets/definitions-GreEMXLV.js +0 -1
  202. package/dist/assets/geo-u0n_jOsD.js +0 -1
  203. package/dist/assets/image-cAa-Nu9O.js +0 -2062
  204. package/dist/assets/index-5ZtDAuy2.js +0 -220
  205. package/dist/assets/index-7vae6YFC.css +0 -1
  206. package/dist/assets/math-DioIQOPJ.js +0 -1
  207. package/dist/assets/pop-DC_TQG9t.js +0 -1
  208. package/dist/cli/chunk-36P5K3SD.js +0 -631
  209. package/dist/cli/chunk-4UDFYF5G.js +0 -132
  210. package/dist/cli/chunk-53NMBJAO.js +0 -35
  211. package/dist/cli/chunk-MIIEFUGG.js +0 -623
  212. package/dist/cli/chunk-QBBKRNGY.js +0 -90
  213. package/dist/cli/chunk-WPUDGEFY.js +0 -20
  214. package/dist/cli/editorCommands-UIF5YLUZ.js +0 -33
  215. package/dist/cli/project-K3QIPGBB.js +0 -12
  216. package/dist/cli/registerStandardNodes-S2VLMF6J.js +0 -3081
  217. package/dist/cli/runner-RA6UKEPD.js +0 -37
  218. package/dist/cli/webBuild-GT4PZT5D.js +0 -37
  219. package/dist/runtime/builtins/headless.d.ts +0 -19
  220. package/dist/runtime/builtins/headless.js +0 -25
@@ -0,0 +1,267 @@
1
+ // server/src/stageCache.ts
2
+ import { createHash as createHash2 } from "node:crypto";
3
+ import fs3 from "node:fs";
4
+ import path2 from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
+
7
+ // server/src/atomicWrite.ts
8
+ import fs from "node:fs";
9
+ import fsp from "node:fs/promises";
10
+ var temporaryFor = (file) => `${file}.${process.pid}.tmp`;
11
+ async function writeFileAtomic(file, data, options = {}) {
12
+ const temporary = temporaryFor(file);
13
+ await fsp.writeFile(temporary, data, options.mode === void 0 ? void 0 : { mode: options.mode });
14
+ await fsp.rename(temporary, file);
15
+ }
16
+ function writeFileAtomicSync(file, data) {
17
+ const temporary = temporaryFor(file);
18
+ fs.writeFileSync(temporary, data);
19
+ fs.renameSync(temporary, file);
20
+ }
21
+
22
+ // server/src/stageSources.ts
23
+ import { createHash } from "node:crypto";
24
+ import fs2 from "node:fs";
25
+ import path from "node:path";
26
+ var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set(["__pycache__", "node_modules", "venv", ".venv", ".git", ".cascade", ".cascade-cache"]);
27
+ var digests = /* @__PURE__ */ new Map();
28
+ function contentDigest(fullPath) {
29
+ let stat;
30
+ try {
31
+ stat = fs2.statSync(fullPath, { bigint: true });
32
+ } catch {
33
+ return null;
34
+ }
35
+ if (!stat.isFile()) return null;
36
+ const stamp = `${stat.size}:${stat.mtimeNs}:${stat.ctimeNs}`;
37
+ const known = digests.get(fullPath);
38
+ if (known?.stamp === stamp) return known.digest;
39
+ const hash = createHash("sha256");
40
+ const handle = fs2.openSync(fullPath, "r");
41
+ try {
42
+ const buffer = Buffer.allocUnsafe(1 << 20);
43
+ let read;
44
+ while ((read = fs2.readSync(handle, buffer, 0, buffer.length, null)) > 0) hash.update(buffer.subarray(0, read));
45
+ } finally {
46
+ fs2.closeSync(handle);
47
+ }
48
+ const digest = hash.digest("hex");
49
+ digests.set(fullPath, { stamp, digest });
50
+ return digest;
51
+ }
52
+ var StageSources = class {
53
+ constructor(project, roots) {
54
+ this.project = project;
55
+ this.roots = [...new Set(roots)].sort();
56
+ }
57
+ /** The default roots for a stage configuration without `sources`. */
58
+ static defaultRoots(project, entrypoint, worker) {
59
+ const roots = [path.posix.dirname(entrypoint)];
60
+ if (worker) roots.push(path.posix.dirname(worker));
61
+ if (fs2.existsSync(project.resolve("src"))) roots.push("src");
62
+ return roots.map((root) => root === "" ? "." : root);
63
+ }
64
+ /** Every source file, project-relative and sorted. */
65
+ files() {
66
+ const found = /* @__PURE__ */ new Set();
67
+ for (const root of this.roots) {
68
+ let full;
69
+ try {
70
+ full = this.project.resolveMedia(root);
71
+ } catch {
72
+ continue;
73
+ }
74
+ let stat;
75
+ try {
76
+ stat = fs2.statSync(full);
77
+ } catch {
78
+ continue;
79
+ }
80
+ if (stat.isFile()) found.add(root);
81
+ else if (stat.isDirectory()) this.walk(full, root, found);
82
+ }
83
+ return [...found].sort();
84
+ }
85
+ walk(directory, relative, found) {
86
+ let entries;
87
+ try {
88
+ entries = fs2.readdirSync(directory, { withFileTypes: true });
89
+ } catch {
90
+ return;
91
+ }
92
+ for (const entry of entries) {
93
+ const child = relative === "." ? entry.name : path.posix.join(relative, entry.name);
94
+ if (entry.isDirectory()) {
95
+ if (SKIPPED_DIRECTORIES.has(entry.name) || entry.name.startsWith(".")) continue;
96
+ this.walk(path.join(directory, entry.name), child, found);
97
+ } else if (entry.isFile() && entry.name.endsWith(".py")) {
98
+ found.add(child);
99
+ }
100
+ }
101
+ }
102
+ digest(relative) {
103
+ return contentDigest(this.project.resolveMedia(relative)) ?? "missing";
104
+ }
105
+ /**
106
+ * The code hash, 16 hex characters. A file added, removed or edited changes
107
+ * it. Cheap to call per request: one directory walk and one stat per file,
108
+ * with a file read only when its stat changed.
109
+ */
110
+ codeHash() {
111
+ const hash = createHash("sha256");
112
+ for (const file of this.files()) hash.update(`${file}\0${this.digest(file)}
113
+ `);
114
+ return hash.digest("hex").slice(0, 16);
115
+ }
116
+ };
117
+
118
+ // server/src/stageCache.ts
119
+ var CACHE_FORMAT = 1;
120
+ var CACHE_DIRECTORY = ".cascade/cache/stages";
121
+ var version;
122
+ function cascadeVersion() {
123
+ if (version) return version;
124
+ let directory = path2.dirname(fileURLToPath(import.meta.url));
125
+ for (let depth = 0; depth < 6; depth++) {
126
+ try {
127
+ const manifest = JSON.parse(fs3.readFileSync(path2.join(directory, "package.json"), "utf8"));
128
+ if (manifest.name === "@field/cascade" && manifest.version) return version = manifest.version;
129
+ } catch {
130
+ }
131
+ directory = path2.dirname(directory);
132
+ }
133
+ return version = "unknown";
134
+ }
135
+ function projectFile(project, value) {
136
+ if (!value || value.length > 1024 || value.includes("\0") || /^[a-z][a-z0-9+.-]*:/i.test(value)) return null;
137
+ if (path2.isAbsolute(value) || !/[./]/.test(value)) return null;
138
+ try {
139
+ return project.resolveMedia(value.replace(/^\.\//, ""));
140
+ } catch {
141
+ return null;
142
+ }
143
+ }
144
+ function replaceFiles(project, value, at, skip, onFile) {
145
+ if (typeof value === "string") {
146
+ const full = projectFile(project, value);
147
+ const digest = full ? contentDigest(full) : null;
148
+ if (!digest) return value;
149
+ onFile(at, value, digest);
150
+ return `file:${digest}`;
151
+ }
152
+ if (Array.isArray(value)) return value.map((item, index) => replaceFiles(project, item, `${at}[${index}]`, skip, onFile));
153
+ if (value && typeof value === "object") {
154
+ const out = {};
155
+ for (const name of Object.keys(value).sort()) {
156
+ const child = value[name];
157
+ const where = at ? `${at}.${name}` : name;
158
+ out[name] = !at && skip.has(name) ? child : replaceFiles(project, child, where, skip, onFile);
159
+ }
160
+ return out;
161
+ }
162
+ return value;
163
+ }
164
+ function stageCacheKey(project, stage, args, code, outputs = []) {
165
+ const inputs = {};
166
+ const params = replaceFiles(project, args, "", new Set(outputs), (where, _relative, digest) => {
167
+ inputs[where] = digest.slice(0, 16);
168
+ });
169
+ const cascade = cascadeVersion();
170
+ const blob = JSON.stringify({ format: CACHE_FORMAT, stage, code, cascade, params });
171
+ return { key: createHash2("sha256").update(blob).digest("hex").slice(0, 32), code, cascade, inputs };
172
+ }
173
+ function entryPath(project, stage, key) {
174
+ return project.resolve(path2.posix.join(CACHE_DIRECTORY, stage, `${key}.json`));
175
+ }
176
+ function resultFiles(project, result) {
177
+ const files = {};
178
+ replaceFiles(project, result, "", /* @__PURE__ */ new Set(), (_where, relative, digest) => {
179
+ files[relative] = digest;
180
+ });
181
+ return files;
182
+ }
183
+ function readEntry(project, stage, key) {
184
+ let entry;
185
+ try {
186
+ entry = JSON.parse(fs3.readFileSync(entryPath(project, stage, key), "utf8"));
187
+ } catch {
188
+ return { why: "no entry" };
189
+ }
190
+ if (entry.format !== CACHE_FORMAT || entry.key !== key) return { why: "no entry" };
191
+ for (const [relative, digest] of Object.entries(entry.outputs ?? {})) {
192
+ const full = projectFile(project, relative);
193
+ if (!full || contentDigest(full) !== digest) return { why: `output changed: ${relative}` };
194
+ }
195
+ return { result: entry.result };
196
+ }
197
+ function writeEntry(project, stage, key, result) {
198
+ const file = entryPath(project, stage, key);
199
+ const entry = { format: CACHE_FORMAT, stage, key, result, outputs: resultFiles(project, result) };
200
+ fs3.mkdirSync(path2.dirname(file), { recursive: true });
201
+ writeFileAtomicSync(file, JSON.stringify(entry));
202
+ }
203
+ function cacheMode(policy, stage, caller, env) {
204
+ if (env.CASCADE_STAGE_CACHE === "off") return { use: false, bypass: true, why: "CASCADE_STAGE_CACHE=off" };
205
+ if (!policy.enabled) return { use: false, bypass: false, why: "not enabled" };
206
+ if (policy.exclude.includes(stage)) return { use: false, bypass: false, why: "excluded" };
207
+ if (policy.scope === "cook" && caller !== "cook") return { use: false, bypass: false, why: "not a cook call" };
208
+ return { use: true, bypass: false };
209
+ }
210
+ function stageSources(project, config) {
211
+ return new StageSources(project, config.sources ?? StageSources.defaultRoots(project, config.entrypoint, config.worker));
212
+ }
213
+ async function callStage(project, config, stage, args, invoke, options = {}) {
214
+ const started = performance.now();
215
+ const code = stageSources(project, config).codeHash();
216
+ const identity = stageCacheKey(project, stage, args, code, config.cache.outputs);
217
+ const mode = cacheMode(config.cache, stage, options.caller ?? "cook", options.env ?? process.env);
218
+ const finish = (result, trace) => {
219
+ const full = {
220
+ stage,
221
+ key: identity.key,
222
+ ...trace,
223
+ code: identity.code,
224
+ cascade: identity.cascade,
225
+ inputs: identity.inputs,
226
+ ms: Math.round(performance.now() - started)
227
+ };
228
+ options.onTrace?.(full);
229
+ return { result, trace: full };
230
+ };
231
+ let why = mode.why;
232
+ if (mode.use) {
233
+ const stored = readEntry(project, stage, identity.key);
234
+ if ("result" in stored) return finish(stored.result, { cache: "hit" });
235
+ why = stored.why;
236
+ }
237
+ const answer = await invoke({ key: identity.key, code, cache: mode.bypass ? "off" : "use" });
238
+ if (mode.use) {
239
+ try {
240
+ writeEntry(project, stage, identity.key, answer.result);
241
+ } catch {
242
+ }
243
+ }
244
+ return finish(answer.result, {
245
+ cache: mode.use ? "miss" : "off",
246
+ ...why ? { why } : {},
247
+ ...answer.projectCache ? { project: answer.projectCache } : {},
248
+ ...answer.restarted ? { restarted: true } : {}
249
+ });
250
+ }
251
+ function formatStageTrace(trace) {
252
+ const reason = trace.why ? ` (${trace.why})` : "";
253
+ const project = trace.project ? ` project:${trace.project}` : "";
254
+ const restarted = trace.restarted ? " worker restarted for new code" : "";
255
+ return `${trace.stage}: ${trace.cache}${reason}${project} key ${trace.key.slice(0, 12)} code ${trace.code} ${trace.ms} ms${restarted}`;
256
+ }
257
+
258
+ export {
259
+ writeFileAtomic,
260
+ writeFileAtomicSync,
261
+ cascadeVersion,
262
+ stageCacheKey,
263
+ stageSources,
264
+ callStage,
265
+ formatStageTrace
266
+ };
267
+ //# sourceMappingURL=chunk-5ORUQ6ES.js.map
@@ -52,7 +52,7 @@ async function encodeSequence(options) {
52
52
  if (frames[index] !== frames[index - 1] + 1) throw new Error("--mp4 needs a contiguous range of whole frames (no step)");
53
53
  }
54
54
  const { prefix, width, extension } = options.sequence;
55
- const pattern = path.join(options.root, `${prefix}.%0${width}d${extension}`);
55
+ const pattern = path.resolve(options.root, `${prefix}.%0${width}d${extension}`);
56
56
  const output = path.resolve(options.root, options.file);
57
57
  await fs.mkdir(path.dirname(output), { recursive: true });
58
58
  const size = scale === 1 ? "pad=ceil(iw/2)*2:ceil(ih/2)*2" : `scale=trunc(iw*${scale}/2)*2:trunc(ih*${scale}/2)*2:flags=lanczos`;
@@ -60,7 +60,7 @@ async function encodeSequence(options) {
60
60
  let pixelFormat;
61
61
  let codecArgs;
62
62
  if (options.codec === "prores") {
63
- const first = path.join(options.root, `${prefix}.${String(frames[0]).padStart(width, "0")}${extension}`);
63
+ const first = path.resolve(options.root, `${prefix}.${String(frames[0]).padStart(width, "0")}${extension}`);
64
64
  const alpha = await hasTransparency(first);
65
65
  pixelFormat = alpha ? "yuva444p10le" : "yuv422p10le";
66
66
  codecArgs = ["-c:v", "prores_ks", "-profile:v", alpha ? "4" : "3", "-vendor", "apl0", "-pix_fmt", pixelFormat];
@@ -118,4 +118,4 @@ export {
118
118
  hasFfmpeg,
119
119
  encodeSequence
120
120
  };
121
- //# sourceMappingURL=chunk-YAT5Y25U.js.map
121
+ //# sourceMappingURL=chunk-7QE2ZNSN.js.map
@@ -3,15 +3,16 @@ import {
3
3
  packagePathToType,
4
4
  registerNodeClasses,
5
5
  registerNodeMetadata
6
- } from "./chunk-FCDXJ6EK.js";
6
+ } from "./chunk-CTPLRSWF.js";
7
7
  import {
8
8
  bundleValueProblem,
9
+ gpuLimitShortfall,
9
10
  graphSeed,
10
11
  isGpuHost,
11
12
  nodeSeed,
12
13
  seededRandom,
13
14
  selectBundleFields
14
- } from "./chunk-GFS6W3C5.js";
15
+ } from "./chunk-L6B6FUQP.js";
15
16
 
16
17
  // src/nodes/definition/DefinitionNode.ts
17
18
  var silentProgress = { report: () => {
@@ -136,7 +137,10 @@ function attachDefinition(node, registration, capabilities) {
136
137
  const gpuHost = (definition.capabilities ?? []).includes("gpu") && isGpuHost(capabilities.gpu) ? capabilities.gpu : null;
137
138
  if (gpuHost) {
138
139
  await gpuHost.ensure();
139
- nodeCapabilities = { ...capabilities, gpu: gpuHost.forNode(node.id) };
140
+ const gpu = gpuHost.forNode(node.id);
141
+ const shortfall = gpuLimitShortfall(node.id, definition.gpuLimits, gpu);
142
+ if (shortfall) throw new Error(shortfall);
143
+ nodeCapabilities = { ...capabilities, gpu };
140
144
  }
141
145
  const seed = nodeSeed(graphSeed({ seed: node.documentSeed }), node.id);
142
146
  const context = {
@@ -194,4 +198,4 @@ export {
194
198
  attachDefinition,
195
199
  registerDefinitionNodes
196
200
  };
197
- //# sourceMappingURL=chunk-SGJAET2L.js.map
201
+ //# sourceMappingURL=chunk-BPODRH52.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  canConnectTypes
3
- } from "./chunk-BIJO5OFS.js";
3
+ } from "./chunk-FWMSGKEH.js";
4
4
 
5
5
  // packages/runtime/src/edit/document.ts
6
6
  var SUBNET = "cascade.core.Subnet";
@@ -1298,4 +1298,4 @@ export {
1298
1298
  applyOperation,
1299
1299
  readGraph
1300
1300
  };
1301
- //# sourceMappingURL=chunk-XWDSKG7N.js.map
1301
+ //# sourceMappingURL=chunk-BZ63XQ3I.js.map
@@ -16,7 +16,7 @@ import {
16
16
  CORE_TYPES,
17
17
  canConnectTypes,
18
18
  normalizeCascadeType
19
- } from "./chunk-BIJO5OFS.js";
19
+ } from "./chunk-FWMSGKEH.js";
20
20
 
21
21
  // src/utils/nodeTypeUtils.ts
22
22
  var nodeClassRegistries = /* @__PURE__ */ new Map();
@@ -136,16 +136,6 @@ function normalizeColor(input) {
136
136
  }
137
137
  return { r: 0, g: 0, b: 0, a: 1 };
138
138
  }
139
- function colorToCss(color) {
140
- const r = Math.round(color.r * 255);
141
- const g = Math.round(color.g * 255);
142
- const b = Math.round(color.b * 255);
143
- const a = color.a !== void 0 ? color.a : 1;
144
- if (a < 1) {
145
- return `rgba(${r},${g},${b},${a})`;
146
- }
147
- return `rgb(${r},${g},${b})`;
148
- }
149
139
  function isColorValue(value) {
150
140
  if (typeof value === "string") {
151
141
  return value.startsWith("#") || value.startsWith("rgb(");
@@ -2197,7 +2187,6 @@ export {
2197
2187
  isStandardLibraryNode,
2198
2188
  getNodeClass,
2199
2189
  normalizeColor,
2200
- colorToCss,
2201
2190
  isColorValue,
2202
2191
  CORE_TYPES2 as CORE_TYPES,
2203
2192
  isCoreType,
@@ -2205,4 +2194,4 @@ export {
2205
2194
  canConnect,
2206
2195
  Node
2207
2196
  };
2208
- //# sourceMappingURL=chunk-FCDXJ6EK.js.map
2197
+ //# sourceMappingURL=chunk-CTPLRSWF.js.map
@@ -101,4 +101,4 @@ export {
101
101
  canvasAvailable,
102
102
  installHeadlessCanvas
103
103
  };
104
- //# sourceMappingURL=chunk-WIH4TEIS.js.map
104
+ //# sourceMappingURL=chunk-EZRWJ4G2.js.map
@@ -68,4 +68,4 @@ export {
68
68
  normalizeCascadeType,
69
69
  canConnectTypes
70
70
  };
71
- //# sourceMappingURL=chunk-BIJO5OFS.js.map
71
+ //# sourceMappingURL=chunk-FWMSGKEH.js.map
@@ -118,6 +118,34 @@ var VERBS = [
118
118
  call: { kind: "cli", argv: (args, resolve) => ["export", resolve(args.graph), ...args.out ? ["--out", resolve(args.out)] : ["--plan"]] }
119
119
  }]
120
120
  },
121
+ {
122
+ ...editorVerb,
123
+ name: "share",
124
+ json: "keep",
125
+ usage: "<graph-file | site-directory> [--name <slug>] [--scope <team>] [--protected]",
126
+ summary: "Export and deploy to Vercel as cascade-<slug>; print the link and record it in shares.json",
127
+ mcp: [{
128
+ name: "share",
129
+ description: "Share a graph's editor, or a static directory with an index.html, as one link (cascade share): export, deploy to the Vercel project cascade-<name> and return `url`, recorded in the project's shares.json. Public unless `protected`.",
130
+ inputSchema: object({
131
+ graph: { type: "string", description: "The .cascade file or site directory, relative to the project directory" },
132
+ name: { type: "string", description: "Slug; the Vercel project is cascade-<name> (default: the editor id or directory name)" },
133
+ scope: { type: "string", description: "Vercel team (default field-io)" },
134
+ protected: { type: "boolean", description: "Require Vercel Authentication instead of a public link" }
135
+ }, ["graph"]),
136
+ call: {
137
+ kind: "cli",
138
+ argv: (args, resolve) => [
139
+ "share",
140
+ resolve(args.graph),
141
+ ...args.name ? ["--name", String(args.name)] : [],
142
+ ...args.scope ? ["--scope", String(args.scope)] : [],
143
+ ...args.protected ? ["--protected"] : []
144
+ ],
145
+ timeoutMs: () => 10 * 6e4
146
+ }
147
+ }]
148
+ },
121
149
  { ...editorVerb, name: "publish", usage: "<graph-file> <node>.<prop> [--label ...]", summary: "Publish a node prop as an editor parameter" },
122
150
  { ...editorVerb, name: "freeze", usage: "<graph-file> <node> --fields <a,b>", summary: "Snapshot chosen fields of a live data source for export" },
123
151
  { ...editorVerb, name: "scene", usage: "<graph-file> <scene.json> --out <new-graph-file>", summary: "Apply a scene file to a copy of the graph" },
@@ -434,11 +462,11 @@ var VERBS = [
434
462
  {
435
463
  ...lockVerb,
436
464
  name: "ladder",
437
- usage: "<graph-file> <node> --name <name> --vary <node.prop>=<a,b,c> | --seeds <a,b,c>",
438
- summary: "Cook 3-5 variants, write a labelled contact sheet and ladders/<id>.json",
465
+ usage: "<graph-file> <node> --name <name> --vary <node.prop>=<a,b,c> | --seeds <a,b,c> [--cairn <vault dir>]",
466
+ summary: "Cook 3-5 variants, write a labelled contact sheet and ladders/<id>.json; --cairn makes a tap-to-pick card",
439
467
  mcp: [{
440
468
  name: "ladder",
441
- description: "Cook `node` once per variant (cascade ladder) and return the contact sheet, each variant image and the record ladders/<id>.json. `vary` maps node.prop to a list of values (zipped; one value applies to all); `seeds` varies the graph seed. Three to five variants is the usual.",
469
+ description: 'Cook `node` once per variant (cascade ladder) and return the contact sheet, each variant image and the record ladders/<id>.json. `vary` maps node.prop to a list of values (zipped; one value applies to all); `seeds` varies the graph seed. Three to five variants is the usual. `cairn` is a directory in the Substrate vault (the space\'s _attachments/ladders/<id>): each variant is copied there and `data.cairnChoice` is a cairn:choice fence to post as is (at most 8 variants); a tap comes back as "Picked: <label> (pick:<id>:<n>)", then call `pick` with ladder <id> and variant <n>.',
442
470
  inputSchema: object({
443
471
  graph,
444
472
  node: { type: "string" },
@@ -447,7 +475,9 @@ var VERBS = [
447
475
  seeds: { type: "array", items: { type: "integer" } },
448
476
  columns: { type: "integer" },
449
477
  note: { type: "string" },
450
- live: { type: "boolean" }
478
+ live: { type: "boolean" },
479
+ cairn: { type: "string", description: "Absolute vault directory for the variant images; returns data.cairnChoice" },
480
+ vault: { type: "string", description: "Absolute vault root the cairnChoice image paths are relative to (default: nearest .obsidian or .git above `cairn`)" }
451
481
  }, ["graph", "node", "name"]),
452
482
  call: {
453
483
  kind: "cli",
@@ -457,6 +487,9 @@ var VERBS = [
457
487
  args.node,
458
488
  "--name",
459
489
  args.name,
490
+ // Outside the project by design (the vault), so passed as given: absolute.
491
+ ...args.cairn ? ["--cairn", String(args.cairn)] : [],
492
+ ...args.vault ? ["--vault", String(args.vault)] : [],
460
493
  ...Object.entries(args.vary ?? {}).flatMap(([key, values]) => [
461
494
  "--vary",
462
495
  `${key}=${(Array.isArray(values) ? values : [values]).map((value) => JSON.stringify(value)).join(",")}`
@@ -669,4 +702,4 @@ export {
669
702
  graphCommandsHelp,
670
703
  mainHelp
671
704
  };
672
- //# sourceMappingURL=chunk-POMMNKZZ.js.map
705
+ //# sourceMappingURL=chunk-G23A2BZZ.js.map
@@ -100,6 +100,23 @@ var Color = Object.freeze({
100
100
  opaque(color) {
101
101
  return [clamp(color[0]), clamp(color[1]), clamp(color[2]), 1];
102
102
  },
103
+ /**
104
+ * A colour saved in an older shape, as the one colour: `{ r, g, b, a? }`
105
+ * (the class-based image nodes stored that until 2026-11-07) or three
106
+ * components, with alpha 1. Components above 1 are read as 0–255, the way
107
+ * those nodes read them. Anything else comes back unchanged, so a reader can
108
+ * apply this to every stored `color` value and validate the result.
109
+ */
110
+ upgrade(value) {
111
+ const legacy = value !== null && typeof value === "object" && !Array.isArray(value) && "r" in value && "g" in value && "b" in value ? value : Array.isArray(value) && value.length === 3 ? { r: value[0], g: value[1], b: value[2] } : null;
112
+ if (!legacy) return value;
113
+ const parts = [legacy.r, legacy.g, legacy.b, legacy.a ?? 1];
114
+ if (!parts.every((part) => typeof part === "number" && Number.isFinite(part))) return value;
115
+ const numbers = parts;
116
+ const unit2 = numbers.slice(0, 3).every((part) => part <= 1);
117
+ const read = (part) => unit2 ? part : part / 255;
118
+ return [read(numbers[0]), read(numbers[1]), read(numbers[2]), legacy.a === void 0 ? 1 : read(numbers[3])];
119
+ },
103
120
  /**
104
121
  * Four unit components from anything indexable, clamping and defaulting a
105
122
  * missing alpha to 1. The reader for a `Cd` attribute's flat array, and the
@@ -1438,10 +1455,11 @@ var registrationsById = new Map(
1438
1455
  );
1439
1456
 
1440
1457
  export {
1458
+ Color,
1441
1459
  LIGHT_DEFAULTS,
1442
1460
  isLight,
1443
1461
  isScene,
1444
1462
  geoNodeDefinitions,
1445
1463
  geoNodeRegistrations
1446
1464
  };
1447
- //# sourceMappingURL=chunk-35D343DC.js.map
1465
+ //# sourceMappingURL=chunk-GRIN2DDP.js.map