@field/cascade 0.11.0 → 0.12.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 (216) hide show
  1. package/AGENTS.md +7 -4
  2. package/ARCHITECTURE.md +5 -4
  3. package/CHANGELOG.md +195 -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-RQZ4SX5R.js → chunk-2JEU3MGB.js} +2 -2
  73. package/dist/cli/{chunk-WKURDVYP.js → chunk-3RXL5GHB.js} +28 -331
  74. package/dist/cli/chunk-53LGFPWX.js +138 -0
  75. package/dist/cli/chunk-5ORUQ6ES.js +267 -0
  76. package/dist/cli/chunk-73S4JZXK.js +336 -0
  77. package/dist/cli/{chunk-YAT5Y25U.js → chunk-7QE2ZNSN.js} +3 -3
  78. package/dist/cli/{chunk-SGJAET2L.js → chunk-BPODRH52.js} +8 -4
  79. package/dist/cli/{chunk-XWDSKG7N.js → chunk-BZ63XQ3I.js} +2 -2
  80. package/dist/cli/{chunk-FCDXJ6EK.js → chunk-CTPLRSWF.js} +2 -13
  81. package/dist/cli/chunk-EU4ZFZ7Y.js +122 -0
  82. package/dist/cli/{chunk-WIH4TEIS.js → chunk-EZRWJ4G2.js} +1 -1
  83. package/dist/cli/{chunk-BIJO5OFS.js → chunk-FWMSGKEH.js} +1 -1
  84. package/dist/cli/{chunk-POMMNKZZ.js → chunk-G23A2BZZ.js} +38 -5
  85. package/dist/cli/{chunk-35D343DC.js → chunk-GRIN2DDP.js} +19 -1
  86. package/dist/cli/chunk-JVOCK5W4.js +83 -0
  87. package/dist/cli/chunk-KBGUO27Q.js +15 -0
  88. package/dist/cli/{chunk-GFS6W3C5.js → chunk-L6B6FUQP.js} +54 -32
  89. package/dist/cli/chunk-LTXWBXOM.js +438 -0
  90. package/dist/cli/chunk-MI5JLQYA.js +220 -0
  91. package/dist/cli/{chunk-EUIAC3U3.js → chunk-MP653A5O.js} +884 -124
  92. package/dist/cli/{chunk-A4OAMOZH.js → chunk-NJFMDXHR.js} +13 -3
  93. package/dist/cli/{chunk-3JTJHF7V.js → chunk-OM6BEME6.js} +4 -4
  94. package/dist/cli/{chunk-DUIUFVU2.js → chunk-ST6LROUG.js} +49 -23
  95. package/dist/cli/{chunk-RLCAJBVR.js → chunk-TJW67QSI.js} +8 -7
  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-JJN5Z3ZC.js} +23 -17
  102. package/dist/cli/{commands-Q23M26QM.js → commands-PHYFYA3F.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-ORWQRNYP.js} +36 -6
  106. package/dist/cli/{edit-JHCPTQFT.js → edit-S5KEQ5RA.js} +3 -3
  107. package/dist/cli/editorCommands-NFR32JOY.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-EOUOBOIJ.js} +4 -4
  114. package/dist/cli/{graphFile-VPSQ6OF7.js → graphFile-YTGJB5F5.js} +25 -19
  115. package/dist/cli/{headlessCanvas-5XLDZT7M.js → headlessCanvas-ENRHC7GS.js} +3 -2
  116. package/dist/cli/{helpCommand-3VRE3KVO.js → helpCommand-PULH6ZYB.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-JVZFQOEX.js} +4 -4
  121. package/dist/cli/{parity-KINARCP2.js → parity-X6DEH2DV.js} +42 -36
  122. package/dist/cli/{pop-USDFXB7H.js → pop-B7SIIYKN.js} +5 -5
  123. package/dist/cli/project-VYGIA7JX.js +12 -0
  124. package/dist/cli/{projectDefinition-DMTZN2NE.js → projectDefinition-F3D7UIHJ.js} +8 -8
  125. package/dist/cli/{projectRuntime-Z5PJOH47.js → projectRuntime-2Q4JKEH3.js} +19 -12
  126. package/dist/cli/registerStandardNodes-BXJCB6E4.js +744 -0
  127. package/dist/cli/{render-YURAPPUI.js → render-PT4MWI6U.js} +24 -22
  128. package/dist/cli/{runCommand-PI764LEG.js → runCommand-HNPQVZMS.js} +3 -3
  129. package/dist/cli/runner-5XPXVW7J.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-MHF6TNRK.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-WPPMGGW7.js +44 -0
  138. package/dist/contracts/capabilities.d.ts +18 -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 +3 -2
  152. package/dist/player/editor.js +3 -2
  153. package/dist/player/editorEngine.js +3 -2
  154. package/dist/runtime/builtins/assert/checks.d.ts +18 -0
  155. package/dist/runtime/builtins/assert/checks.js +207 -0
  156. package/dist/runtime/builtins/assert/definitions.d.ts +130 -0
  157. package/dist/runtime/builtins/assert/definitions.js +54 -0
  158. package/dist/runtime/builtins/assert/index.d.ts +152 -0
  159. package/dist/runtime/builtins/assert/index.js +36 -0
  160. package/dist/runtime/builtins/image/contactSheet.d.ts +3 -165
  161. package/dist/runtime/builtins/image/contactSheet.js +0 -49
  162. package/dist/runtime/builtins/image/definitions.d.ts +950 -0
  163. package/dist/runtime/builtins/image/definitions.js +343 -0
  164. package/dist/runtime/builtins/image/file.d.ts +8 -0
  165. package/dist/runtime/builtins/image/file.js +37 -0
  166. package/dist/runtime/builtins/image/filters.d.ts +8 -0
  167. package/dist/runtime/builtins/image/filters.js +394 -0
  168. package/dist/runtime/builtins/image/generators.d.ts +7 -0
  169. package/dist/runtime/builtins/image/generators.js +144 -0
  170. package/dist/runtime/builtins/image/index.d.ts +947 -4
  171. package/dist/runtime/builtins/image/index.js +41 -6
  172. package/dist/runtime/builtins/image/io.d.ts +100 -0
  173. package/dist/runtime/builtins/image/io.js +142 -0
  174. package/dist/runtime/builtins/image/planar.d.ts +55 -0
  175. package/dist/runtime/builtins/image/planar.js +274 -0
  176. package/dist/runtime/builtins/image/simplex.d.ts +22 -0
  177. package/dist/runtime/builtins/image/simplex.js +92 -0
  178. package/dist/runtime/builtins/image/text.d.ts +4 -0
  179. package/dist/runtime/builtins/image/text.js +141 -0
  180. package/dist/runtime/builtins/index.d.ts +3 -0
  181. package/dist/runtime/builtins/index.js +7 -0
  182. package/dist/runtime/gpu.d.ts +25 -0
  183. package/dist/runtime/gpu.js +40 -0
  184. package/dist/runtime/runtime.d.ts +2 -0
  185. package/dist/runtime/runtime.js +268 -44
  186. package/dist/runtime/types.d.ts +30 -0
  187. package/doc/CLI_REFERENCE.md +21 -3
  188. package/doc/EDITOR_EXPORT.md +26 -0
  189. package/doc/MCP.md +2 -1
  190. package/doc/NODE_REFERENCE.md +286 -14
  191. package/doc/PROJECT_AUTHORING.md +98 -3
  192. package/package.json +4 -1
  193. package/dist/assets/CodePanel-BU-autmq.js +0 -2
  194. package/dist/assets/CustomNodeDialog-BRpDfOV5.js +0 -1
  195. package/dist/assets/OpenGraphDialog-BwbRlC0X.js +0 -1
  196. package/dist/assets/ProjectSettings-DLOXLxcA.js +0 -2
  197. package/dist/assets/definitions-GreEMXLV.js +0 -1
  198. package/dist/assets/geo-u0n_jOsD.js +0 -1
  199. package/dist/assets/image-cAa-Nu9O.js +0 -2062
  200. package/dist/assets/index-5ZtDAuy2.js +0 -220
  201. package/dist/assets/index-7vae6YFC.css +0 -1
  202. package/dist/assets/math-DioIQOPJ.js +0 -1
  203. package/dist/assets/pop-DC_TQG9t.js +0 -1
  204. package/dist/cli/chunk-36P5K3SD.js +0 -631
  205. package/dist/cli/chunk-4UDFYF5G.js +0 -132
  206. package/dist/cli/chunk-53NMBJAO.js +0 -35
  207. package/dist/cli/chunk-MIIEFUGG.js +0 -623
  208. package/dist/cli/chunk-QBBKRNGY.js +0 -90
  209. package/dist/cli/chunk-WPUDGEFY.js +0 -20
  210. package/dist/cli/editorCommands-UIF5YLUZ.js +0 -33
  211. package/dist/cli/project-K3QIPGBB.js +0 -12
  212. package/dist/cli/registerStandardNodes-S2VLMF6J.js +0 -3081
  213. package/dist/cli/runner-RA6UKEPD.js +0 -37
  214. package/dist/cli/webBuild-GT4PZT5D.js +0 -37
  215. package/dist/runtime/builtins/headless.d.ts +0 -19
  216. package/dist/runtime/builtins/headless.js +0 -25
@@ -0,0 +1,44 @@
1
+ import {
2
+ buildPlayer,
3
+ canonicalFuturePath,
4
+ exists,
5
+ inside,
6
+ installRoot,
7
+ playerFile,
8
+ staticRuntimePlugin
9
+ } from "./chunk-OM6BEME6.js";
10
+ import "./chunk-MP653A5O.js";
11
+ import "./chunk-MI5JLQYA.js";
12
+ import "./chunk-GRIN2DDP.js";
13
+ import "./chunk-X4WKAOQP.js";
14
+ import "./chunk-DEQSEFFH.js";
15
+ import "./chunk-RXDIMXN5.js";
16
+ import "./chunk-QQPA2ZHH.js";
17
+ import "./chunk-KH4MUBKF.js";
18
+ import "./chunk-HSNOBIVT.js";
19
+ import "./chunk-L6B6FUQP.js";
20
+ import "./chunk-HFHWYL55.js";
21
+ import "./chunk-KBGUO27Q.js";
22
+ import "./chunk-ICH7N5PN.js";
23
+ import "./chunk-CO22XHNG.js";
24
+ import "./chunk-LTXWBXOM.js";
25
+ import "./chunk-BZ63XQ3I.js";
26
+ import "./chunk-YH637PDL.js";
27
+ import "./chunk-X566FWBV.js";
28
+ import "./chunk-WD62JIP6.js";
29
+ import "./chunk-5ORUQ6ES.js";
30
+ import "./chunk-2JEU3MGB.js";
31
+ import "./chunk-NJFMDXHR.js";
32
+ import "./chunk-J7VK7MNF.js";
33
+ import "./chunk-FWMSGKEH.js";
34
+ import "./chunk-JVOCK5W4.js";
35
+ export {
36
+ buildPlayer,
37
+ canonicalFuturePath,
38
+ exists,
39
+ inside,
40
+ installRoot,
41
+ playerFile,
42
+ staticRuntimePlugin
43
+ };
44
+ //# sourceMappingURL=webBuild-WPPMGGW7.js.map
@@ -1,4 +1,4 @@
1
- import type { AssetRef, ImageRef, JsonValue } from "./values.js";
1
+ import type { AssetRef, ImagePixels, ImageRef, JsonValue } from "./values.js";
2
2
  export interface CascadeAbortSignal {
3
3
  readonly aborted: boolean;
4
4
  readonly reason?: unknown;
@@ -46,6 +46,23 @@ export interface AssetCapability {
46
46
  signal: CascadeAbortSignal;
47
47
  }): Promise<AssetRef>;
48
48
  resolveUrl?(ref: AssetRef | ImageRef): Promise<ResourceLease<string>>;
49
+ /**
50
+ * Store an intermediate image from its pixels. Optional; a host without it
51
+ * gets encoded bytes through `write`. The host chooses how: the headless
52
+ * host holds the pixels in memory and writes a fast lossless PNG behind
53
+ * (zlib level 1), or encodes that PNG at once when the run does not keep
54
+ * pixels. The returned path is content-addressed like `write`'s and is a
55
+ * whole file before anything reads it from disk. Finals are not encoded
56
+ * here: a run's saved outputs are encoded at full compression when copied.
57
+ */
58
+ keepImage?(pixels: ImagePixels, metadata: {
59
+ suggestedName?: string;
60
+ }, options: {
61
+ signal: CascadeAbortSignal;
62
+ }): Promise<AssetRef>;
63
+ /** The pixels behind a stored image while the host still holds them in
64
+ * memory, so a reader skips the read and the decode. */
65
+ imagePixels?(ref: AssetRef | ImageRef): ImagePixels | undefined;
49
66
  }
50
67
  export interface MediaCapability {
51
68
  decodeImage(ref: ImageRef | AssetRef, options: {
@@ -57,6 +57,14 @@ export declare const Color: Readonly<{
57
57
  alpha(color: Color): number;
58
58
  /** The same colour at full alpha. */
59
59
  opaque(color: Color): Color;
60
+ /**
61
+ * A colour saved in an older shape, as the one colour: `{ r, g, b, a? }`
62
+ * (the class-based image nodes stored that until 2026-11-07) or three
63
+ * components, with alpha 1. Components above 1 are read as 0–255, the way
64
+ * those nodes read them. Anything else comes back unchanged, so a reader can
65
+ * apply this to every stored `color` value and validate the result.
66
+ */
67
+ upgrade(value: unknown): unknown;
60
68
  /**
61
69
  * Four unit components from anything indexable, clamping and defaulting a
62
70
  * missing alpha to 1. The reader for a `Cd` attribute's flat array, and the
@@ -70,6 +70,29 @@ export const Color = Object.freeze({
70
70
  opaque(color) {
71
71
  return [clamp(color[0]), clamp(color[1]), clamp(color[2]), 1];
72
72
  },
73
+ /**
74
+ * A colour saved in an older shape, as the one colour: `{ r, g, b, a? }`
75
+ * (the class-based image nodes stored that until 2026-11-07) or three
76
+ * components, with alpha 1. Components above 1 are read as 0–255, the way
77
+ * those nodes read them. Anything else comes back unchanged, so a reader can
78
+ * apply this to every stored `color` value and validate the result.
79
+ */
80
+ upgrade(value) {
81
+ const legacy = value !== null && typeof value === "object" && !Array.isArray(value) && "r" in value && "g" in value && "b" in value
82
+ ? value
83
+ : Array.isArray(value) && value.length === 3
84
+ ? { r: value[0], g: value[1], b: value[2] }
85
+ : null;
86
+ if (!legacy)
87
+ return value;
88
+ const parts = [legacy.r, legacy.g, legacy.b, legacy.a ?? 1];
89
+ if (!parts.every((part) => typeof part === "number" && Number.isFinite(part)))
90
+ return value;
91
+ const numbers = parts;
92
+ const unit = numbers.slice(0, 3).every((part) => part <= 1);
93
+ const read = (part) => (unit ? part : part / 255);
94
+ return [read(numbers[0]), read(numbers[1]), read(numbers[2]), legacy.a === undefined ? 1 : read(numbers[3])];
95
+ },
73
96
  /**
74
97
  * Four unit components from anything indexable, clamping and defaulting a
75
98
  * missing alpha to 1. The reader for a `Cd` attribute's flat array, and the
@@ -190,6 +190,16 @@ export interface NodeDefinitionForEnvironment<E extends RuntimeEnvironment> {
190
190
  */
191
191
  readonly container?: "subnet" | "feedback";
192
192
  readonly capabilities?: readonly CapabilityForEnvironment<E>[];
193
+ /**
194
+ * GPU limits this node cannot run without, by WebGPU name, for a node that
195
+ * declares `gpu`: `{ maxStorageBufferBindingSize: 512 * 2 ** 20 }`. The host
196
+ * already asks for everything the adapter allows; this makes a GPU that
197
+ * allows less fail before the cook, naming the limit, instead of with a
198
+ * validation error from inside it. `max*` limits are minimums to reach,
199
+ * `min*Alignment` ones maximums. Read `context.capabilities.gpu.limits` for
200
+ * what the device actually gives.
201
+ */
202
+ readonly gpuLimits?: Readonly<Record<string, number>>;
193
203
  /**
194
204
  * `live` marks a node that reads data from outside the graph — an archive,
195
205
  * a CLI, a cache, body data, the filesystem or a network. `cascade export`
@@ -1,6 +1,6 @@
1
1
  export declare const nodeDefinitionSchema: {
2
- readonly allOf: readonly [Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>];
3
2
  readonly $schema: "https://json-schema.org/draft/2020-12/schema";
4
3
  readonly $id: "https://cascade.dev/schema/node-definition-v1.json";
4
+ readonly allOf: readonly [Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>, Readonly<Record<string, unknown>>];
5
5
  };
6
6
  //# sourceMappingURL=schema.d.ts.map
@@ -88,6 +88,7 @@ export const nodeDefinitionSchema = {
88
88
  items: { type: "string" },
89
89
  uniqueItems: true,
90
90
  },
91
+ gpuLimits: record({ type: "number", minimum: 0 }),
91
92
  label: description,
92
93
  description,
93
94
  icon: description,
@@ -19,7 +19,7 @@ export const CAPABILITIES_BY_ENVIRONMENT = {
19
19
  };
20
20
  const ALLOWED = CAPABILITIES_BY_ENVIRONMENT;
21
21
  const fields = (names) => new Set(names.split(" "));
22
- const TOP_FIELDS = fields("apiVersion runsOn container capabilities source label description icon inputs outputs props");
22
+ const TOP_FIELDS = fields("apiVersion runsOn container capabilities gpuLimits source label description icon inputs outputs props");
23
23
  const DATA_FIELDS = fields("kind type default description variadic min max step accept control options schema fields");
24
24
  const OUTPUT_FIELDS = fields("kind type description schema fields");
25
25
  const PROP_FIELDS = fields("type default label description min max step accept control options expression action");
@@ -111,6 +111,16 @@ export function validateNodeDefinition(value) {
111
111
  add("duplicate-capability", `${String(capability)} is duplicated`, path);
112
112
  seen.add(capability);
113
113
  }
114
+ if (value.gpuLimits !== undefined) {
115
+ if (!seen.has("gpu"))
116
+ add("gpu-limits-without-gpu", "gpuLimits needs the gpu capability", "gpuLimits");
117
+ if (!isRecord(value.gpuLimits))
118
+ add("invalid-gpu-limits", "gpuLimits must be an object of WebGPU limit names to numbers", "gpuLimits");
119
+ else
120
+ for (const [name, limit] of Object.entries(value.gpuLimits))
121
+ if (typeof limit !== "number" || !Number.isFinite(limit) || limit < 0)
122
+ add("invalid-gpu-limits", `gpuLimits.${name} must be a non-negative number`, `gpuLimits.${name}`);
123
+ }
114
124
  // Two namespaces, not one. An output is addressed in the other direction from
115
125
  // an input, so `geometry` in and `geometry` out is not ambiguous anywhere: the
116
126
  // document format names the two endpoints of a connection separately, the
@@ -51,6 +51,18 @@ export type Mat4 = readonly [
51
51
  number,
52
52
  number
53
53
  ];
54
+ /**
55
+ * The value on an `image` port: a path naming stored pixels, plus what they
56
+ * are. The port carries the reference, never the pixels, so a value stays
57
+ * small to snapshot, hash, trace and send to a Python stage.
58
+ *
59
+ * The path is always readable as a file by whoever needs a file. A host may
60
+ * still hold the pixels in memory and write the file behind (see
61
+ * {@link ImagePixels} and `AssetCapability.keepImage`): a reader in the same
62
+ * process gets the pixels without a decode, and the host makes the file whole
63
+ * before anything reads it from disk (a final copy, a Python stage, the end of
64
+ * a run).
65
+ */
54
66
  export interface ImageRef {
55
67
  readonly path: string;
56
68
  readonly size: Vec2i;
@@ -58,6 +70,18 @@ export interface ImageRef {
58
70
  readonly depth: "u8" | "u16" | "f16" | "f32";
59
71
  readonly space: "srgb" | "linear";
60
72
  }
73
+ /**
74
+ * An image's pixels in memory: 8-bit RGBA, sRGB, unpremultiplied, row-major
75
+ * with the top row first. This is the layout of `ImageData` and of a PNG's
76
+ * decoded rows, so handing pixels on in memory and handing them on through a
77
+ * PNG give the next node the same numbers.
78
+ */
79
+ export interface ImagePixels {
80
+ readonly width: number;
81
+ readonly height: number;
82
+ /** `width * height * 4` bytes. Owned by the host once kept: never mutate. */
83
+ readonly data: Uint8ClampedArray;
84
+ }
61
85
  declare const textureHandleBrand: unique symbol;
62
86
  export interface TextureHandle {
63
87
  readonly [textureHandleBrand]: true;
package/dist/image.d.ts CHANGED
@@ -43,6 +43,8 @@
43
43
  * `cascade/io`'s bridge, because the node's copy of this module and the CLI's
44
44
  * copy are not always the same module instance.
45
45
  */
46
+ import { type ImagePixels } from './io.js';
47
+ export type { ImagePixels } from './io.js';
46
48
  /** An image port value. Mirrors Cascade's core `ImageRef`. */
47
49
  export interface ImageRef {
48
50
  path: string;
@@ -71,6 +73,19 @@ export interface ImageAssets {
71
73
  path: string;
72
74
  mediaType?: string;
73
75
  }>;
76
+ /** Optional: store an intermediate from its pixels (see core `AssetCapability.keepImage`). */
77
+ keepImage?(pixels: ImagePixels, metadata: {
78
+ suggestedName?: string;
79
+ }, options: {
80
+ signal: unknown;
81
+ }): Promise<{
82
+ path: string;
83
+ mediaType?: string;
84
+ }>;
85
+ /** Optional: the pixels behind a stored image while the host holds them in memory. */
86
+ imagePixels?(ref: {
87
+ path: string;
88
+ }): ImagePixels | undefined;
74
89
  }
75
90
  /**
76
91
  * The slice of a definition-v1 `NodeExecutionContext` these helpers need, so
package/dist/image.js CHANGED
@@ -1,3 +1,29 @@
1
+ // server/src/runtime/io.ts
2
+ function pixelsBitmap(pixels) {
3
+ const Canvas = globalThis.OffscreenCanvas;
4
+ if (!Canvas) throw new Error("cascade/io: this host has no OffscreenCanvas to hold the image");
5
+ const canvas = new Canvas(pixels.width, pixels.height);
6
+ const context = canvas.getContext("2d");
7
+ if (!context) throw new Error("cascade/io: the canvas has no 2D context");
8
+ const data = new ImageData(pixels.data, pixels.width, pixels.height);
9
+ context.putImageData(data, 0, 0);
10
+ const drawable = canvas;
11
+ drawable.close = () => {
12
+ };
13
+ drawable.naturalWidth = pixels.width;
14
+ drawable.naturalHeight = pixels.height;
15
+ return canvas;
16
+ }
17
+ function canvasPixels(source) {
18
+ const canvas = source;
19
+ if (typeof canvas?.getContext !== "function" || typeof canvas.width !== "number" || typeof canvas.height !== "number") return null;
20
+ if (!canvas.width || !canvas.height) return null;
21
+ const context = canvas.getContext("2d");
22
+ if (!context || typeof context.getImageData !== "function") return null;
23
+ const image = context.getImageData(0, 0, canvas.width, canvas.height);
24
+ return { width: image.width, height: image.height, data: image.data };
25
+ }
26
+
1
27
  // server/src/runtime/image.ts
2
28
  var HOST_KEY = "__cascadeImageHost";
3
29
  var NO_CANVAS = 'cascade/image: this host has no canvas. In a browser this needs OffscreenCanvas; headless, Cascade installs @napi-rs/canvas before a run (install it with "npm install @napi-rs/canvas" if it is missing).';
@@ -97,12 +123,20 @@ async function readBytes(context, ref) {
97
123
  return assetsOf(context).read(refOf(ref), { signal: context.signal ?? NEVER });
98
124
  }
99
125
  async function readImage(context, ref) {
126
+ const pixels = assetsOf(context).imagePixels?.(refOf(ref));
127
+ if (pixels) return pixelsBitmap(pixels);
100
128
  return decodeImage(await readBytes(context, ref));
101
129
  }
102
130
  async function writeImage(context, canvas, name, options = {}) {
103
131
  const type = options.type ?? "image/png";
104
- const bytes = await encodeImage(canvas, { type, ...options.quality === void 0 ? {} : { quality: options.quality } });
105
- const stored = await assetsOf(context).write(bytes, { mediaType: type, suggestedName: name }, { signal: context.signal ?? NEVER });
132
+ const assets = assetsOf(context);
133
+ const signal = context.signal ?? NEVER;
134
+ const pixels = type === "image/png" && assets.keepImage ? canvasPixels(canvas) : null;
135
+ const stored = pixels && assets.keepImage ? await assets.keepImage(pixels, { suggestedName: name }, { signal }) : await assets.write(
136
+ await encodeImage(canvas, { type, ...options.quality === void 0 ? {} : { quality: options.quality } }),
137
+ { mediaType: type, suggestedName: name },
138
+ { signal }
139
+ );
106
140
  return {
107
141
  path: stored.path,
108
142
  size: [canvas.width, canvas.height],
package/dist/index.html CHANGED
@@ -6,11 +6,17 @@
6
6
  <!-- Pinterest browser extension: no Save buttons over canvases and previews -->
7
7
  <meta name="pinterest" content="nopin">
8
8
  <title>Cascade</title>
9
- <script type="module" crossorigin src="/assets/index-5ZtDAuy2.js"></script>
9
+ <script type="module" crossorigin src="/assets/index-BWnrmIB9.js"></script>
10
10
  <link rel="modulepreload" crossorigin href="/assets/legacy-CTEeXeqF.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/index-client-DqYmKstU.js">
12
12
  <link rel="modulepreload" crossorigin href="/assets/nodeTypeUtils-BDJsc2Ks.js">
13
- <link rel="stylesheet" crossorigin href="/assets/index-7vae6YFC.css">
13
+ <link rel="modulepreload" crossorigin href="/assets/coreTypes-DtnDATZ7.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/expressions-X-Z-GrgW.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/DefinitionNode-CaRTXQ_b.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/builder-BmBKcDix.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/preload-helper-Czpn1I53.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/studioImages-CAyH-0Pl.js">
19
+ <link rel="stylesheet" crossorigin href="/assets/index-DIaxEONI.css">
14
20
  </head>
15
21
  <body>
16
22
  <div id="app"></div>
package/dist/io.d.ts CHANGED
@@ -33,6 +33,24 @@ export interface IoBridge {
33
33
  write(path: string, data: Uint8Array): Promise<string>;
34
34
  /** Host-owned display URL, valid until the host releases its stored asset. */
35
35
  url?(path: string, options?: MediaOptions): string;
36
+ /**
37
+ * Store an intermediate image from its pixels and return the stored path,
38
+ * as `write` would. Optional. The headless host has it: it writes a fast
39
+ * lossless PNG, and in a run that keeps pixels it holds them in memory and
40
+ * writes behind (`pixels` below). Without it `saveImage` encodes and writes.
41
+ */
42
+ keep?(path: string, pixels: ImagePixels): Promise<string>;
43
+ /** The pixels behind a path `keep` returned, while the host holds them. */
44
+ pixels?(path: string): ImagePixels | undefined;
45
+ }
46
+ /**
47
+ * An image's pixels in memory. Mirrors Cascade's core `ImagePixels`: 8-bit
48
+ * RGBA, sRGB, unpremultiplied, top row first — `ImageData`'s layout.
49
+ */
50
+ export interface ImagePixels {
51
+ readonly width: number;
52
+ readonly height: number;
53
+ readonly data: Uint8ClampedArray;
36
54
  }
37
55
  /** Install the local transport. Returns a disposer, so a test can take it down. */
38
56
  export declare function installIoBridge(bridge: IoBridge | null): () => void;
@@ -47,6 +65,11 @@ export declare function loadBytes(path: string, options?: MediaOptions): Promise
47
65
  * to hand it. Pass a width explicitly to work at a smaller size on purpose.
48
66
  */
49
67
  export declare function loadImage(path: string, options?: MediaOptions): Promise<HTMLImageElement>;
68
+ /**
69
+ * `ImagePixels` as a drawable: a canvas holding them, with an `ImageBitmap`'s
70
+ * `width`, `height` and `close()` (and an image's `naturalWidth`/`naturalHeight`).
71
+ */
72
+ export declare function pixelsBitmap(pixels: ImagePixels): ImageBitmap;
50
73
  /** Same, as an ImageBitmap — the right input for `texImage2D`. */
51
74
  export declare function loadBitmap(path: string, options?: MediaOptions): Promise<ImageBitmap>;
52
75
  /**
@@ -83,6 +106,12 @@ export interface ImageSource {
83
106
  * node.out('path').setValue(out);
84
107
  */
85
108
  export declare function saveImage(source: HTMLCanvasElement | OffscreenCanvas | Blob | ImageSource, path: string, options?: SaveOptions): Promise<string>;
109
+ /**
110
+ * A canvas's pixels as `ImagePixels`, or null for anything that is not a 2D
111
+ * canvas (a Blob, an encoder-only source, a WebGL canvas, whose `getContext('2d')`
112
+ * is null).
113
+ */
114
+ export declare function canvasPixels(source: unknown): ImagePixels | null;
86
115
  /** Write any bytes into the project cache and return the path. */
87
116
  export declare function saveBytes(data: Blob | ArrayBuffer | Uint8Array, path: string): Promise<string>;
88
117
  /**
package/dist/io.js CHANGED
@@ -40,6 +40,8 @@ async function loadBytes(path, options = { raw: true }) {
40
40
  return await response.arrayBuffer();
41
41
  }
42
42
  async function loadImage(path, options = { raw: true }) {
43
+ const held = heldBitmap(path);
44
+ if (held) return held;
43
45
  const image = new Image();
44
46
  const bridge = localIo();
45
47
  let temporaryUrl;
@@ -65,7 +67,29 @@ async function loadImage(path, options = { raw: true }) {
65
67
  if (temporaryUrl) URL.revokeObjectURL(temporaryUrl);
66
68
  }
67
69
  }
70
+ function heldBitmap(path) {
71
+ if (/^(https?:|data:|blob:)/.test(path)) return null;
72
+ const pixels = localIo()?.pixels?.(path.replace(/^\.?\//, ""));
73
+ return pixels ? pixelsBitmap(pixels) : null;
74
+ }
75
+ function pixelsBitmap(pixels) {
76
+ const Canvas = globalThis.OffscreenCanvas;
77
+ if (!Canvas) throw new Error("cascade/io: this host has no OffscreenCanvas to hold the image");
78
+ const canvas = new Canvas(pixels.width, pixels.height);
79
+ const context = canvas.getContext("2d");
80
+ if (!context) throw new Error("cascade/io: the canvas has no 2D context");
81
+ const data = new ImageData(pixels.data, pixels.width, pixels.height);
82
+ context.putImageData(data, 0, 0);
83
+ const drawable = canvas;
84
+ drawable.close = () => {
85
+ };
86
+ drawable.naturalWidth = pixels.width;
87
+ drawable.naturalHeight = pixels.height;
88
+ return canvas;
89
+ }
68
90
  async function loadBitmap(path, options = { raw: true }) {
91
+ const held = heldBitmap(path);
92
+ if (held) return held;
69
93
  const bridge = localIo();
70
94
  if (bridge?.url) return createImageBitmap(await loadImage(path, options));
71
95
  if (bridge) return createImageBitmap(new Blob([await loadBytes(path, options)]));
@@ -80,6 +104,11 @@ async function saveImage(source, path, options = {}) {
80
104
  const format = options.format ?? "png";
81
105
  const type = `image/${format}`;
82
106
  let blob;
107
+ const bridge = localIo();
108
+ if (format === "png" && bridge?.keep) {
109
+ const pixels = canvasPixels(source);
110
+ if (pixels) return bridge.keep(path.replace(/^\.?\//, ""), pixels);
111
+ }
83
112
  if (source instanceof Blob) {
84
113
  blob = source;
85
114
  } else if (typeof source.convertToBlob === "function") {
@@ -99,6 +128,15 @@ async function saveImage(source, path, options = {}) {
99
128
  }
100
129
  return saveBytes(blob, path);
101
130
  }
131
+ function canvasPixels(source) {
132
+ const canvas = source;
133
+ if (typeof canvas?.getContext !== "function" || typeof canvas.width !== "number" || typeof canvas.height !== "number") return null;
134
+ if (!canvas.width || !canvas.height) return null;
135
+ const context = canvas.getContext("2d");
136
+ if (!context || typeof context.getImageData !== "function") return null;
137
+ const image = context.getImageData(0, 0, canvas.width, canvas.height);
138
+ return { width: image.width, height: image.height, data: image.data };
139
+ }
102
140
  async function saveBytes(data, path) {
103
141
  const clean = path.replace(/^\.?\//, "");
104
142
  const bridge = localIo();
@@ -302,6 +340,7 @@ async function textureToImage(ref, path, options = {}) {
302
340
  }
303
341
  export {
304
342
  cachePath,
343
+ canvasPixels,
305
344
  createTexture,
306
345
  installIoBridge,
307
346
  loadBitmap,
@@ -310,6 +349,7 @@ export {
310
349
  loadFloats,
311
350
  loadImage,
312
351
  mediaUrl,
352
+ pixelsBitmap,
313
353
  saveBytes,
314
354
  saveFloats,
315
355
  saveImage,
@@ -12,7 +12,8 @@ import { installIoBridge } from "@field/cascade/io";
12
12
 
13
13
  // src/browser/gpu.ts
14
14
  import {
15
- createGpuHost
15
+ createGpuHost,
16
+ requestDeviceWithAdapterLimits
16
17
  } from "@field/cascade/runtime";
17
18
  function createBrowserGpuHost() {
18
19
  const gpu = globalThis.navigator?.gpu;
@@ -25,7 +26,7 @@ function createBrowserGpuHost() {
25
26
  "No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on."
26
27
  );
27
28
  }
28
- const device = await adapter.requestDevice();
29
+ const device = await requestDeviceWithAdapterLimits(adapter);
29
30
  const info = adapter.info;
30
31
  return {
31
32
  device,
@@ -28,7 +28,8 @@ import { installIoBridge } from "@field/cascade/io";
28
28
 
29
29
  // src/browser/gpu.ts
30
30
  import {
31
- createGpuHost
31
+ createGpuHost,
32
+ requestDeviceWithAdapterLimits
32
33
  } from "@field/cascade/runtime";
33
34
  function createBrowserGpuHost() {
34
35
  const gpu = globalThis.navigator?.gpu;
@@ -41,7 +42,7 @@ function createBrowserGpuHost() {
41
42
  "No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on."
42
43
  );
43
44
  }
44
- const device = await adapter.requestDevice();
45
+ const device = await requestDeviceWithAdapterLimits(adapter);
45
46
  const info = adapter.info;
46
47
  return {
47
48
  device,
@@ -5,7 +5,8 @@ import { installIoBridge as installIoBridge2 } from "@field/cascade/io";
5
5
 
6
6
  // src/browser/gpu.ts
7
7
  import {
8
- createGpuHost
8
+ createGpuHost,
9
+ requestDeviceWithAdapterLimits
9
10
  } from "@field/cascade/runtime";
10
11
  function createBrowserGpuHost() {
11
12
  const gpu = globalThis.navigator?.gpu;
@@ -18,7 +19,7 @@ function createBrowserGpuHost() {
18
19
  "No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on."
19
20
  );
20
21
  }
21
- const device = await adapter.requestDevice();
22
+ const device = await requestDeviceWithAdapterLimits(adapter);
22
23
  const info = adapter.info;
23
24
  return {
24
25
  device,
@@ -0,0 +1,18 @@
1
+ import type { NodeExecutionContext } from "@field/cascade/contracts";
2
+ import type { equalsDefinition, notBlankDefinition, rangeDefinition, trueDefinition } from "./definitions.js";
3
+ export declare function executeRange({ nodeId, inputs, props, outputs }: NodeExecutionContext<typeof rangeDefinition>): void;
4
+ export declare function executeEquals({ nodeId, inputs, props, outputs }: NodeExecutionContext<typeof equalsDefinition>): void;
5
+ export declare function executeTrue({ nodeId, inputs, props, outputs }: NodeExecutionContext<typeof trueDefinition>): void;
6
+ export declare function executeNotBlank({ nodeId, inputs, props, outputs, capabilities, signal }: NodeExecutionContext<typeof notBlankDefinition>): Promise<void>;
7
+ export interface Pixels {
8
+ readonly width: number;
9
+ readonly height: number;
10
+ /** Channels per pixel: 1 grey, 2 grey+alpha, 3 RGB, 4 RGBA. */
11
+ readonly channels: number;
12
+ readonly data: ArrayLike<number>;
13
+ }
14
+ /** 'one flat colour', 'fully transparent', or null when the image has content. */
15
+ export declare function blankness(pixels: Pixels, tolerance?: number): string | null;
16
+ /** 8-bit non-interlaced PNG (grey, grey+alpha, RGB, RGBA) or binary PPM/PGM. */
17
+ export declare function decodeImage(bytes: Uint8Array): Promise<Pixels | null>;
18
+ //# sourceMappingURL=checks.d.ts.map