@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
@@ -1,15 +1,16 @@
1
1
  import {
2
2
  browserAssetCapability
3
- } from "./chunk-53NMBJAO.js";
3
+ } from "./chunk-ZSCUZE6I.js";
4
4
  import {
5
5
  attachDefinition
6
- } from "./chunk-SGJAET2L.js";
6
+ } from "./chunk-BPODRH52.js";
7
7
  import {
8
- createGpuHost
9
- } from "./chunk-GFS6W3C5.js";
8
+ createGpuHost,
9
+ requestDeviceWithAdapterLimits
10
+ } from "./chunk-L6B6FUQP.js";
10
11
  import {
11
12
  validateNodeDefinition
12
- } from "./chunk-A4OAMOZH.js";
13
+ } from "./chunk-NJFMDXHR.js";
13
14
 
14
15
  // src/nodes/definition/browserShellCapability.ts
15
16
  var capability = null;
@@ -125,7 +126,7 @@ function createBrowserGpuHost() {
125
126
  "No WebGPU adapter is available \u2014 the browser reports WebGPU but the system offers no GPU to render on."
126
127
  );
127
128
  }
128
- const device = await adapter.requestDevice();
129
+ const device = await requestDeviceWithAdapterLimits(adapter);
129
130
  const info = adapter.info;
130
131
  return {
131
132
  device,
@@ -221,4 +222,4 @@ export {
221
222
  moduleNodeFunction,
222
223
  adaptModule
223
224
  };
224
- //# sourceMappingURL=chunk-RLCAJBVR.js.map
225
+ //# sourceMappingURL=chunk-TJW67QSI.js.map
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  validateNodeDefinition
3
- } from "./chunk-A4OAMOZH.js";
3
+ } from "./chunk-NJFMDXHR.js";
4
4
 
5
5
  // server/src/project.ts
6
6
  import fs5 from "fs/promises";
@@ -39,6 +39,19 @@ function resolveWithinRoot(root, ...segments) {
39
39
  }
40
40
  return full;
41
41
  }
42
+ function canonicalPath(full) {
43
+ let existing = full;
44
+ while (!existsIncludingSymlink(existing)) {
45
+ const parent = path.dirname(existing);
46
+ if (parent === existing) return full;
47
+ existing = parent;
48
+ }
49
+ return path.join(canonical(existing, `path is not accessible: ${full}`), path.relative(existing, full));
50
+ }
51
+ function isWithin(base, candidate) {
52
+ const rel = path.relative(base, candidate);
53
+ return !rel.startsWith("..") && !path.isAbsolute(rel);
54
+ }
42
55
  function existsIncludingSymlink(candidate) {
43
56
  try {
44
57
  fs.lstatSync(candidate);
@@ -56,6 +69,101 @@ function canonical(candidate, message) {
56
69
  }
57
70
  }
58
71
 
72
+ // server/src/projectConfig.ts
73
+ import fs2 from "node:fs";
74
+ import path2 from "node:path";
75
+ var CREDENTIAL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
76
+ var SECRET_KEY = /^(?:api[_-]?key|apiKey|secret|password|access[_-]?token|refresh[_-]?token|credential|credentials)$/i;
77
+ function readProjectManifest(root) {
78
+ const raw = readRaw(root);
79
+ return normalizeManifest(raw);
80
+ }
81
+ function writeProjectManifest(root, input) {
82
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("project manifest must be an object");
83
+ const value = input;
84
+ const current = readRaw(root);
85
+ const next = {
86
+ ...current,
87
+ name: typeof value.name === "string" ? value.name.trim() : "",
88
+ commands: stringRecord(value.commands, "commands"),
89
+ credentials: credentialNames(value.credentials),
90
+ settings: settingsRecord(value.settings)
91
+ };
92
+ fs2.writeFileSync(path2.join(root, "cascade.json"), `${JSON.stringify(next, null, 2)}
93
+ `, "utf8");
94
+ return normalizeManifest(next);
95
+ }
96
+ function readRaw(root) {
97
+ try {
98
+ const value = JSON.parse(fs2.readFileSync(path2.join(root, "cascade.json"), "utf8"));
99
+ if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("root must be an object");
100
+ return value;
101
+ } catch (error) {
102
+ if (error.code === "ENOENT") return {};
103
+ throw new Error(`Cannot read cascade.json: ${error instanceof Error ? error.message : String(error)}`);
104
+ }
105
+ }
106
+ function normalizeManifest(raw) {
107
+ return Object.freeze({
108
+ name: typeof raw.name === "string" ? raw.name : "",
109
+ commands: Object.freeze(stringRecord(raw.commands, "commands")),
110
+ credentials: Object.freeze(credentialNames(raw.credentials)),
111
+ settings: Object.freeze(settingsRecord(raw.settings)),
112
+ reads: Object.freeze(relativeFolders(raw.reads, "reads", true)),
113
+ writes: Object.freeze(relativeFolders(raw.writes, "writes", false))
114
+ });
115
+ }
116
+ function relativeFolders(input, label, mayLeave) {
117
+ if (input === void 0) return [];
118
+ const shape = mayLeave ? '"../other/data"' : '"renders"';
119
+ if (!Array.isArray(input)) throw new Error(`cascade.json ${label} must be an array of relative folders such as [${shape}]`);
120
+ const folders = input.map((entry) => {
121
+ if (typeof entry !== "string" || !entry.trim() || entry.includes("\0") || path2.isAbsolute(entry) || /^~/.test(entry))
122
+ throw new Error(`cascade.json ${label}: ${JSON.stringify(entry)} is not a relative folder such as ${shape}`);
123
+ const folder = path2.posix.normalize(entry.split("\\").join("/")).replace(/\/+$/, "");
124
+ const leaves = folder === ".." || folder.startsWith("../");
125
+ if (!mayLeave && (leaves || folder === "." || /^(?:\.git|nodes|\.cascade)(?:\/|$)/.test(folder)))
126
+ throw new Error(`cascade.json writes: ${JSON.stringify(entry)} must be a folder inside the project, not its root, .git, .cascade or nodes`);
127
+ if (mayLeave && folder === ".") throw new Error('cascade.json reads: "." is the project itself, which is always readable');
128
+ return folder;
129
+ });
130
+ return [...new Set(folders)];
131
+ }
132
+ function stringRecord(input, label) {
133
+ if (input === void 0) return {};
134
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(`${label} must be an object of strings`);
135
+ const entries = Object.entries(input);
136
+ if (entries.some(([key, value]) => !CREDENTIAL_NAME.test(key) || typeof value !== "string" || value.includes("\0"))) {
137
+ throw new Error(`${label} must contain safe names and string values`);
138
+ }
139
+ return Object.fromEntries(entries);
140
+ }
141
+ function credentialNames(input) {
142
+ if (input === void 0) return [];
143
+ if (!Array.isArray(input) || input.some((name) => typeof name !== "string" || !CREDENTIAL_NAME.test(name))) {
144
+ throw new Error("credentials must be an array of safe names");
145
+ }
146
+ return [...new Set(input)];
147
+ }
148
+ function settingsRecord(input) {
149
+ if (input === void 0) return {};
150
+ if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("settings must be an object");
151
+ assertSafeSettings(input, 0);
152
+ return structuredClone(input);
153
+ }
154
+ function assertSafeSettings(value, depth) {
155
+ if (depth > 12) throw new Error("settings nesting is too deep");
156
+ if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return;
157
+ if (Array.isArray(value)) return value.forEach((item) => assertSafeSettings(item, depth + 1));
158
+ if (!value || typeof value !== "object") throw new Error("settings must contain JSON values");
159
+ for (const [key, item] of Object.entries(value)) {
160
+ if (key === "__proto__" || key === "constructor" || key === "prototype" || SECRET_KEY.test(key)) {
161
+ throw new Error(`settings key ${JSON.stringify(key)} looks secret or unsafe; credentials belong outside the project`);
162
+ }
163
+ assertSafeSettings(item, depth + 1);
164
+ }
165
+ }
166
+
59
167
  // server/src/shell/service.ts
60
168
  import fs4 from "node:fs";
61
169
  import os2 from "node:os";
@@ -64,11 +172,11 @@ import { execFile, spawn } from "node:child_process";
64
172
  import { randomUUID } from "node:crypto";
65
173
 
66
174
  // server/src/credentials.ts
67
- import fs2 from "node:fs";
175
+ import fs3 from "node:fs";
68
176
  import os from "node:os";
69
- import path2 from "node:path";
177
+ import path3 from "node:path";
70
178
  var CredentialStore = class {
71
- constructor(file = process.env.CASCADE_CREDENTIALS || path2.join(os.homedir(), ".cascade", "credentials.yaml")) {
179
+ constructor(file = process.env.CASCADE_CREDENTIALS || path3.join(os.homedir(), ".cascade", "credentials.yaml")) {
72
180
  this.file = file;
73
181
  }
74
182
  has(name) {
@@ -96,7 +204,7 @@ var CredentialStore = class {
96
204
  read() {
97
205
  let source;
98
206
  try {
99
- source = fs2.readFileSync(this.file, "utf8");
207
+ source = fs3.readFileSync(this.file, "utf8");
100
208
  } catch (error) {
101
209
  if (error.code === "ENOENT") return /* @__PURE__ */ new Map();
102
210
  throw error;
@@ -170,83 +278,6 @@ function safeEnvName(value) {
170
278
  return value && /^[A-Za-z_][A-Za-z0-9_]{0,127}$/.test(value) && !/^(?:PATH|NODE_OPTIONS|PYTHONPATH|LD_.*|DYLD_.*)$/i.test(value) ? value : null;
171
279
  }
172
280
 
173
- // server/src/projectConfig.ts
174
- import fs3 from "node:fs";
175
- import path3 from "node:path";
176
- var CREDENTIAL_NAME = /^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/;
177
- var SECRET_KEY = /^(?:api[_-]?key|apiKey|secret|password|access[_-]?token|refresh[_-]?token|credential|credentials)$/i;
178
- function readProjectManifest(root) {
179
- const raw = readRaw(root);
180
- return normalizeManifest(raw);
181
- }
182
- function writeProjectManifest(root, input) {
183
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("project manifest must be an object");
184
- const value = input;
185
- const current = readRaw(root);
186
- const next = {
187
- ...current,
188
- name: typeof value.name === "string" ? value.name.trim() : "",
189
- commands: stringRecord(value.commands, "commands"),
190
- credentials: credentialNames(value.credentials),
191
- settings: settingsRecord(value.settings)
192
- };
193
- fs3.writeFileSync(path3.join(root, "cascade.json"), `${JSON.stringify(next, null, 2)}
194
- `, "utf8");
195
- return normalizeManifest(next);
196
- }
197
- function readRaw(root) {
198
- try {
199
- const value = JSON.parse(fs3.readFileSync(path3.join(root, "cascade.json"), "utf8"));
200
- if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("root must be an object");
201
- return value;
202
- } catch (error) {
203
- if (error.code === "ENOENT") return {};
204
- throw new Error(`Cannot read cascade.json: ${error instanceof Error ? error.message : String(error)}`);
205
- }
206
- }
207
- function normalizeManifest(raw) {
208
- return Object.freeze({
209
- name: typeof raw.name === "string" ? raw.name : "",
210
- commands: Object.freeze(stringRecord(raw.commands, "commands")),
211
- credentials: Object.freeze(credentialNames(raw.credentials)),
212
- settings: Object.freeze(settingsRecord(raw.settings))
213
- });
214
- }
215
- function stringRecord(input, label) {
216
- if (input === void 0) return {};
217
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error(`${label} must be an object of strings`);
218
- const entries = Object.entries(input);
219
- if (entries.some(([key, value]) => !CREDENTIAL_NAME.test(key) || typeof value !== "string" || value.includes("\0"))) {
220
- throw new Error(`${label} must contain safe names and string values`);
221
- }
222
- return Object.fromEntries(entries);
223
- }
224
- function credentialNames(input) {
225
- if (input === void 0) return [];
226
- if (!Array.isArray(input) || input.some((name) => typeof name !== "string" || !CREDENTIAL_NAME.test(name))) {
227
- throw new Error("credentials must be an array of safe names");
228
- }
229
- return [...new Set(input)];
230
- }
231
- function settingsRecord(input) {
232
- if (input === void 0) return {};
233
- if (!input || typeof input !== "object" || Array.isArray(input)) throw new Error("settings must be an object");
234
- assertSafeSettings(input, 0);
235
- return structuredClone(input);
236
- }
237
- function assertSafeSettings(value, depth) {
238
- if (depth > 12) throw new Error("settings nesting is too deep");
239
- if (value === null || typeof value === "string" || typeof value === "boolean" || typeof value === "number" && Number.isFinite(value)) return;
240
- if (Array.isArray(value)) return value.forEach((item) => assertSafeSettings(item, depth + 1));
241
- if (!value || typeof value !== "object") throw new Error("settings must contain JSON values");
242
- for (const [key, item] of Object.entries(value)) {
243
- if (key === "__proto__" || key === "constructor" || key === "prototype" || SECRET_KEY.test(key)) {
244
- throw new Error(`settings key ${JSON.stringify(key)} looks secret or unsafe; credentials belong outside the project`);
245
- }
246
- assertSafeSettings(item, depth + 1);
247
- }
248
- }
249
-
250
281
  // server/src/shell/types.ts
251
282
  var ShellProcessError = class extends Error {
252
283
  constructor(kind, result, message) {
@@ -847,7 +878,48 @@ var ProjectRoot = class _ProjectRoot {
847
878
  if (normalized === "shared" || normalized.startsWith("shared/")) {
848
879
  return this.resolveShared(...normalized.split("/").slice(1));
849
880
  }
850
- return this.resolve(relativePath);
881
+ try {
882
+ return this.resolve(relativePath);
883
+ } catch (error) {
884
+ if (!(error instanceof PathSafetyError)) throw error;
885
+ const declared = this.resolveDeclaredRead(relativePath);
886
+ if (declared) return declared;
887
+ throw error;
888
+ }
889
+ }
890
+ /**
891
+ * A read outside the project, allowed only into a root the project declares
892
+ * in cascade.json `"reads"` (`["../other/data"]`). Reached by `..` or by a
893
+ * symlink alike: the canonical target must lie under the canonical read
894
+ * root. Never used for writes — `resolve()` still refuses those.
895
+ */
896
+ resolveDeclaredRead(relativePath) {
897
+ if (path5.isAbsolute(relativePath)) return null;
898
+ const reads = readProjectManifest(this.root).reads;
899
+ if (!reads.length) return null;
900
+ const target = canonicalPath(path5.resolve(this.root, relativePath));
901
+ for (const folder of reads) {
902
+ let base;
903
+ try {
904
+ base = fssync.realpathSync(path5.resolve(this.root, folder));
905
+ } catch {
906
+ continue;
907
+ }
908
+ if (isWithin(base, target)) return target;
909
+ }
910
+ return null;
911
+ }
912
+ /**
913
+ * Where a node may write a file under its own name: `.cascade-cache/`, or a
914
+ * folder the project declares in cascade.json `"writes"` (`["renders"]`).
915
+ * Returns the folder the normalised path falls in, or null. Containment is
916
+ * still `resolve()`'s, so a symlink out of a write folder is refused.
917
+ */
918
+ writeFolderOf(relativePath) {
919
+ const clean = path5.posix.normalize(relativePath.split("\\").join("/").replace(/^\.?\//, ""));
920
+ for (const folder of [".cascade-cache", ...readProjectManifest(this.root).writes])
921
+ if (clean.startsWith(`${folder}/`)) return folder;
922
+ return null;
851
923
  }
852
924
  /**
853
925
  * Whether this project is under version control, walking **up** rather than
@@ -1122,4 +1194,4 @@ export {
1122
1194
  extractNodeDefinition,
1123
1195
  ProjectRoot
1124
1196
  };
1125
- //# sourceMappingURL=chunk-RQZ4SX5R.js.map
1197
+ //# sourceMappingURL=chunk-W5PJ5RMP.js.map
@@ -0,0 +1,122 @@
1
+ import {
2
+ readProjectManifest
3
+ } from "./chunk-W5PJ5RMP.js";
4
+
5
+ // server/src/compile.ts
6
+ import * as esbuild from "esbuild";
7
+ import fs from "fs";
8
+ import path from "path";
9
+ import { fileURLToPath } from "url";
10
+ var __dirname_compile = path.dirname(fileURLToPath(import.meta.url));
11
+ function cascadeRuntimePlugin(project) {
12
+ return {
13
+ name: "cascade-runtime",
14
+ setup(build2) {
15
+ build2.onResolve({ filter: /^cascade\/(?:io|image|shell|net|config|stage)$/ }, (args) => ({
16
+ path: args.path,
17
+ namespace: "cascade-runtime"
18
+ }));
19
+ build2.onResolve({ filter: /^(?:@cascade|cascade)\/contracts$/ }, () => {
20
+ const candidates = [
21
+ path.join(__dirname_compile, "..", "contracts", "index.js"),
22
+ path.join(__dirname_compile, "..", "..", "packages", "contracts", "dist", "index.js")
23
+ ];
24
+ const found = candidates.find((c) => fs.existsSync(c));
25
+ if (!found) {
26
+ return { errors: [{ text: `@cascade/contracts not found (looked in ${candidates.join(", ")})` }] };
27
+ }
28
+ return { path: found };
29
+ });
30
+ build2.onLoad({ filter: /.*/, namespace: "cascade-runtime" }, (args) => {
31
+ if (args.path === "cascade/config") {
32
+ return { contents: configRuntimeSource(readProjectManifest(project.root).settings), loader: "ts" };
33
+ }
34
+ const candidates = [
35
+ path.join(__dirname_compile, "runtime", `${args.path.slice("cascade/".length)}.ts`),
36
+ path.join(__dirname_compile, "..", "src", "runtime", `${args.path.slice("cascade/".length)}.ts`)
37
+ ];
38
+ const found = candidates.find((c) => fs.existsSync(c));
39
+ if (!found) {
40
+ return { errors: [{ text: `${args.path} runtime not found (looked in ${candidates.join(", ")})` }] };
41
+ }
42
+ return { contents: fs.readFileSync(found, "utf-8"), loader: "ts", resolveDir: path.dirname(found) };
43
+ });
44
+ }
45
+ };
46
+ }
47
+ function commonOptions(project) {
48
+ return {
49
+ bundle: true,
50
+ format: "esm",
51
+ platform: "browser",
52
+ target: "es2022",
53
+ write: false,
54
+ logLevel: "silent",
55
+ plugins: [cascadeRuntimePlugin(project)]
56
+ };
57
+ }
58
+ async function compileProjectModule(project, moduleName) {
59
+ try {
60
+ await project.moduleRunsOn(moduleName);
61
+ return await compileEntry(project, project.resolve(`nodes/${moduleName}/index.ts`));
62
+ } catch (err) {
63
+ return { ok: false, code: "", errors: [errorMessage(err)] };
64
+ }
65
+ }
66
+ async function compileProjectPanel(project, panelName) {
67
+ try {
68
+ return await compileEntry(project, await project.resolvePanelEntry(panelName));
69
+ } catch (err) {
70
+ return { ok: false, code: "", errors: [errorMessage(err)] };
71
+ }
72
+ }
73
+ async function compileEmbedded(project, code) {
74
+ try {
75
+ const result = await esbuild.build({
76
+ ...commonOptions(project),
77
+ stdin: {
78
+ contents: code,
79
+ loader: "ts",
80
+ resolveDir: project.root,
81
+ sourcefile: "embedded.ts"
82
+ }
83
+ });
84
+ return toResult(result);
85
+ } catch (err) {
86
+ return { ok: false, code: "", errors: [errorMessage(err)] };
87
+ }
88
+ }
89
+ function toResult(result) {
90
+ const errors = result.errors.map((e) => e.text);
91
+ const code = result.outputFiles?.[0]?.text ?? "";
92
+ return { ok: errors.length === 0, code, errors };
93
+ }
94
+ async function compileEntry(project, entryPath) {
95
+ return toResult(await esbuild.build({ ...commonOptions(project), entryPoints: [entryPath] }));
96
+ }
97
+ function configRuntimeSource(settings) {
98
+ return `function deepFreeze(value) { if (value && typeof value === 'object') { for (const item of Object.values(value)) deepFreeze(item); Object.freeze(value); } return value; }
99
+ const values = deepFreeze(${JSON.stringify(settings)});
100
+ function value(name) { return Object.prototype.hasOwnProperty.call(values, name) ? values[name] : undefined; }
101
+ export const config = Object.freeze({
102
+ get(name, fallback) { return value(name) ?? fallback; },
103
+ string(name, fallback = '') { const item = value(name); return typeof item === 'string' ? item : fallback; },
104
+ number(name, fallback = 0) { const item = value(name); return typeof item === 'number' && Number.isFinite(item) ? item : fallback; },
105
+ boolean(name, fallback = false) { const item = value(name); return typeof item === 'boolean' ? item : fallback; },
106
+ has(name) { return value(name) !== undefined; }
107
+ });`;
108
+ }
109
+ function errorMessage(err) {
110
+ if (err && typeof err === "object" && "errors" in err) {
111
+ const errors = err.errors;
112
+ if (errors?.length) return errors.map((e) => e.text).join("\n");
113
+ }
114
+ return err instanceof Error ? err.message : String(err);
115
+ }
116
+
117
+ export {
118
+ compileProjectModule,
119
+ compileProjectPanel,
120
+ compileEmbedded
121
+ };
122
+ //# sourceMappingURL=chunk-W7OUAT7J.js.map