@field/cascade 0.2.22

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 (319) hide show
  1. package/AGENTS.md +168 -0
  2. package/ARCHITECTURE.md +137 -0
  3. package/DESIGN.md +161 -0
  4. package/LICENSE +21 -0
  5. package/README.md +229 -0
  6. package/dist/assets/CodeEditor-DR7u0q7p.css +1 -0
  7. package/dist/assets/CodeEditor-DoaNEUaM.js +641 -0
  8. package/dist/assets/CodePanel-B0_2XJ-t.js +2 -0
  9. package/dist/assets/CodePanel-IT1secyI.css +1 -0
  10. package/dist/assets/CustomNodeDialog-026lzecD.js +1 -0
  11. package/dist/assets/CustomNodeDialog-JGYnWY6m.css +1 -0
  12. package/dist/assets/ExportDialog-DOq0flMV.css +1 -0
  13. package/dist/assets/ExportDialog-DWN4nhXI.js +358 -0
  14. package/dist/assets/ProjectSettings-BzT164Yx.js +2 -0
  15. package/dist/assets/ProjectSettings-DdO9xoQz.css +1 -0
  16. package/dist/assets/SettingsDialog-CeK7ytpY.css +1 -0
  17. package/dist/assets/SettingsDialog-DmHb1V0r.js +1 -0
  18. package/dist/assets/VersionHistoryDialog-CWNmHQ-P.css +1 -0
  19. package/dist/assets/VersionHistoryDialog-hH6VsXLX.js +3 -0
  20. package/dist/assets/abap-08VXUWAP.js +1 -0
  21. package/dist/assets/apex-BWPQTe0t.js +1 -0
  22. package/dist/assets/azcli-Bc_sGQ0U.js +1 -0
  23. package/dist/assets/bat-i0X4ZdIN.js +1 -0
  24. package/dist/assets/bicep-B5-_aFwp.js +2 -0
  25. package/dist/assets/cameligo-DMUM7wLl.js +1 -0
  26. package/dist/assets/cascade.jpg +0 -0
  27. package/dist/assets/clojure-Cm7r79vr.js +1 -0
  28. package/dist/assets/codicon-Brq4_Ui5.ttf +0 -0
  29. package/dist/assets/coffee-Ba7i2nA0.js +1 -0
  30. package/dist/assets/core-Bji4aZUU.js +965 -0
  31. package/dist/assets/cpp-C7h46wYY.js +1 -0
  32. package/dist/assets/csharp-BKxtCVv1.js +1 -0
  33. package/dist/assets/csp-bTuwJoIa.js +1 -0
  34. package/dist/assets/css-DIMkf-bt.js +3 -0
  35. package/dist/assets/css.worker-B3ciXF_0.js +93 -0
  36. package/dist/assets/cssMode-B6Z_Q1Kh.js +1 -0
  37. package/dist/assets/cypher-CVaqCwHa.js +1 -0
  38. package/dist/assets/dart-onAF5SnQ.js +1 -0
  39. package/dist/assets/dockerfile-DZFCIeNp.js +1 -0
  40. package/dist/assets/ecl-D05T4iGw.js +1 -0
  41. package/dist/assets/editor-1FQrmyyI.css +1 -0
  42. package/dist/assets/editor.api-jMoW_43X.js +847 -0
  43. package/dist/assets/editor.worker-q-txB4vs.js +30 -0
  44. package/dist/assets/elixir-6RTg0lbw.js +1 -0
  45. package/dist/assets/flow9-C5_-GSwl.js +1 -0
  46. package/dist/assets/freemarker2-DvvPV6m5.js +3 -0
  47. package/dist/assets/fsharp-C8Ef5oNN.js +1 -0
  48. package/dist/assets/geo-CXmCtzMM.js +2 -0
  49. package/dist/assets/go-C-y9NEjX.js +1 -0
  50. package/dist/assets/graphql-fmXr3nnJ.js +1 -0
  51. package/dist/assets/handlebars-B8rfuSIa.js +1 -0
  52. package/dist/assets/hcl-CpzslTdj.js +1 -0
  53. package/dist/assets/html-DyHm6JCp.js +1 -0
  54. package/dist/assets/html.worker-C93Ht9o9.js +506 -0
  55. package/dist/assets/htmlMode-E9XLWE5u.js +1 -0
  56. package/dist/assets/image-BUcQC7PG.js +2076 -0
  57. package/dist/assets/index-BXYXSdgh.js +224 -0
  58. package/dist/assets/index-CrNke7SW.css +1 -0
  59. package/dist/assets/index-client-DN_FvjN8.js +1 -0
  60. package/dist/assets/ini-sBoK_t0W.js +1 -0
  61. package/dist/assets/java-BEtHBSE6.js +1 -0
  62. package/dist/assets/javascript-C_bM5PCN.js +1 -0
  63. package/dist/assets/json.worker-B2V3pomh.js +62 -0
  64. package/dist/assets/jsonMode-BdyVS7G4.js +7 -0
  65. package/dist/assets/julia-Bri6UV-V.js +1 -0
  66. package/dist/assets/kotlin-BOotOW0E.js +1 -0
  67. package/dist/assets/legacy-DIu3XRwl.js +2 -0
  68. package/dist/assets/less-B9JPFI3C.js +2 -0
  69. package/dist/assets/lexon-CfSJPG6W.js +1 -0
  70. package/dist/assets/liquid-CDkn3spA.js +1 -0
  71. package/dist/assets/lspLanguageFeatures-AB5DMAsr.js +4 -0
  72. package/dist/assets/lua-CsQS60Ue.js +1 -0
  73. package/dist/assets/m3-D-oSqn_W.js +1 -0
  74. package/dist/assets/markdown-Cimd5fb3.js +1 -0
  75. package/dist/assets/mdx-BvH_NjDN.js +1 -0
  76. package/dist/assets/mips-CIPQ_RoX.js +1 -0
  77. package/dist/assets/msdax-DauUninz.js +1 -0
  78. package/dist/assets/mysql-SOo6toE5.js +1 -0
  79. package/dist/assets/nodeTypeUtils-CweA3NHd.js +1 -0
  80. package/dist/assets/objective-c-FvmIjYaQ.js +1 -0
  81. package/dist/assets/pascal-DrH0SRf2.js +1 -0
  82. package/dist/assets/pascaligo-D-ptJ9y-.js +1 -0
  83. package/dist/assets/perl-oz_6vUea.js +1 -0
  84. package/dist/assets/pgsql-DTj74zXo.js +1 -0
  85. package/dist/assets/php-nr791fC2.js +1 -0
  86. package/dist/assets/pla-CopQ2nXW.js +1 -0
  87. package/dist/assets/postiats-43DmfD33.js +1 -0
  88. package/dist/assets/powerquery-D3hlyOfw.js +1 -0
  89. package/dist/assets/powershell-DmHpPYUd.js +1 -0
  90. package/dist/assets/projectSettingsApi-C1WvIwfP.js +1 -0
  91. package/dist/assets/protobuf-C531GsRP.js +2 -0
  92. package/dist/assets/pug-Z5eAx3Zn.js +1 -0
  93. package/dist/assets/python-B7uii4fi.js +1 -0
  94. package/dist/assets/qsharp-DkqhCAOL.js +1 -0
  95. package/dist/assets/quill-DjZ69wL_.js +121 -0
  96. package/dist/assets/r-BwWrilGY.js +1 -0
  97. package/dist/assets/razor-Z48fWdJc.js +1 -0
  98. package/dist/assets/redis-ClamHrr6.js +1 -0
  99. package/dist/assets/redshift-DT7zqm-g.js +1 -0
  100. package/dist/assets/restructuredtext-BYgofb2h.js +1 -0
  101. package/dist/assets/ruby-DezsRK8O.js +1 -0
  102. package/dist/assets/rust-DdL9SqIa.js +1 -0
  103. package/dist/assets/sb-CcwsVR0C.js +1 -0
  104. package/dist/assets/scala-DHpiXF5c.js +1 -0
  105. package/dist/assets/scheme-BeGwcela.js +1 -0
  106. package/dist/assets/scss-gp-XZpBa.js +3 -0
  107. package/dist/assets/shell-CC2rA5mh.js +1 -0
  108. package/dist/assets/solidity-BEEn4gHE.js +1 -0
  109. package/dist/assets/sophia-CRfGWb83.js +1 -0
  110. package/dist/assets/sparql-D_Lu-MrJ.js +1 -0
  111. package/dist/assets/sql-NEE52Syq.js +1 -0
  112. package/dist/assets/st-DbInun42.js +1 -0
  113. package/dist/assets/swift-Bxkupp3x.js +1 -0
  114. package/dist/assets/systemverilog-Bz4Y3fRF.js +1 -0
  115. package/dist/assets/tcl-DISqw1ZD.js +1 -0
  116. package/dist/assets/ts.worker-D7T1-Ig5.js +67738 -0
  117. package/dist/assets/tsMode-BDgacVG4.js +11 -0
  118. package/dist/assets/twig-De2hgUGE.js +1 -0
  119. package/dist/assets/typescript-Derb6Oqa.js +1 -0
  120. package/dist/assets/typespec-B8J7ngcE.js +1 -0
  121. package/dist/assets/vb-DV3o63ZY.js +1 -0
  122. package/dist/assets/wgsl-DpFanUEy.js +298 -0
  123. package/dist/assets/workers-BtoMX_pm.js +1 -0
  124. package/dist/assets/xml-B1CaFSrG.js +1 -0
  125. package/dist/assets/yaml-HmIeA0KH.js +1 -0
  126. package/dist/cli/index.js +11377 -0
  127. package/dist/cli/index.js.map +7 -0
  128. package/dist/cli/runtime/io.ts +445 -0
  129. package/dist/cli/runtime/net.ts +19 -0
  130. package/dist/cli/runtime/shell.ts +46 -0
  131. package/dist/cli/runtime/stage.ts +132 -0
  132. package/dist/contracts/capabilities.d.ts +109 -0
  133. package/dist/contracts/capabilities.d.ts.map +1 -0
  134. package/dist/contracts/capabilities.js +11 -0
  135. package/dist/contracts/capabilities.js.map +1 -0
  136. package/dist/contracts/color.d.ts +67 -0
  137. package/dist/contracts/color.d.ts.map +1 -0
  138. package/dist/contracts/color.js +83 -0
  139. package/dist/contracts/color.js.map +1 -0
  140. package/dist/contracts/definition.d.ts +118 -0
  141. package/dist/contracts/definition.d.ts.map +1 -0
  142. package/dist/contracts/definition.js +2 -0
  143. package/dist/contracts/definition.js.map +1 -0
  144. package/dist/contracts/document.d.ts +50 -0
  145. package/dist/contracts/document.d.ts.map +1 -0
  146. package/dist/contracts/document.js +2 -0
  147. package/dist/contracts/document.js.map +1 -0
  148. package/dist/contracts/geometry.d.ts +301 -0
  149. package/dist/contracts/geometry.d.ts.map +1 -0
  150. package/dist/contracts/geometry.js +604 -0
  151. package/dist/contracts/geometry.js.map +1 -0
  152. package/dist/contracts/index.d.ts +8 -0
  153. package/dist/contracts/index.d.ts.map +1 -0
  154. package/dist/contracts/index.js +8 -0
  155. package/dist/contracts/index.js.map +1 -0
  156. package/dist/contracts/schema.d.ts +6 -0
  157. package/dist/contracts/schema.d.ts.map +1 -0
  158. package/dist/contracts/schema.js +85 -0
  159. package/dist/contracts/schema.js.map +1 -0
  160. package/dist/contracts/validate.d.ts +13 -0
  161. package/dist/contracts/validate.d.ts.map +1 -0
  162. package/dist/contracts/validate.js +202 -0
  163. package/dist/contracts/validate.js.map +1 -0
  164. package/dist/contracts/values.d.ts +100 -0
  165. package/dist/contracts/values.d.ts.map +1 -0
  166. package/dist/contracts/values.js +59 -0
  167. package/dist/contracts/values.js.map +1 -0
  168. package/dist/graphs/default-orig.cascade +128 -0
  169. package/dist/graphs/default.cascade +21 -0
  170. package/dist/index.html +19 -0
  171. package/dist/runtime/browser.d.ts +3 -0
  172. package/dist/runtime/browser.d.ts.map +1 -0
  173. package/dist/runtime/browser.js +12 -0
  174. package/dist/runtime/browser.js.map +1 -0
  175. package/dist/runtime/builtins/core/index.d.ts +9 -0
  176. package/dist/runtime/builtins/core/index.d.ts.map +1 -0
  177. package/dist/runtime/builtins/core/index.js +21 -0
  178. package/dist/runtime/builtins/core/index.js.map +1 -0
  179. package/dist/runtime/builtins/core/random.d.ts +66 -0
  180. package/dist/runtime/builtins/core/random.d.ts.map +1 -0
  181. package/dist/runtime/builtins/core/random.js +46 -0
  182. package/dist/runtime/builtins/core/random.js.map +1 -0
  183. package/dist/runtime/builtins/core/remap.d.ts +105 -0
  184. package/dist/runtime/builtins/core/remap.d.ts.map +1 -0
  185. package/dist/runtime/builtins/core/remap.js +43 -0
  186. package/dist/runtime/builtins/core/remap.js.map +1 -0
  187. package/dist/runtime/builtins/core/routing.d.ts +117 -0
  188. package/dist/runtime/builtins/core/routing.d.ts.map +1 -0
  189. package/dist/runtime/builtins/core/routing.js +114 -0
  190. package/dist/runtime/builtins/core/routing.js.map +1 -0
  191. package/dist/runtime/builtins/core/structural.d.ts +86 -0
  192. package/dist/runtime/builtins/core/structural.d.ts.map +1 -0
  193. package/dist/runtime/builtins/core/structural.js +63 -0
  194. package/dist/runtime/builtins/core/structural.js.map +1 -0
  195. package/dist/runtime/builtins/geo/copyToPoints.d.ts +58 -0
  196. package/dist/runtime/builtins/geo/copyToPoints.d.ts.map +1 -0
  197. package/dist/runtime/builtins/geo/copyToPoints.js +35 -0
  198. package/dist/runtime/builtins/geo/copyToPoints.js.map +1 -0
  199. package/dist/runtime/builtins/geo/definitions.d.ts +526 -0
  200. package/dist/runtime/builtins/geo/definitions.d.ts.map +1 -0
  201. package/dist/runtime/builtins/geo/definitions.js +182 -0
  202. package/dist/runtime/builtins/geo/definitions.js.map +1 -0
  203. package/dist/runtime/builtins/geo/generators.d.ts +94 -0
  204. package/dist/runtime/builtins/geo/generators.d.ts.map +1 -0
  205. package/dist/runtime/builtins/geo/generators.js +41 -0
  206. package/dist/runtime/builtins/geo/generators.js.map +1 -0
  207. package/dist/runtime/builtins/geo/index.d.ts +23 -0
  208. package/dist/runtime/builtins/geo/index.d.ts.map +1 -0
  209. package/dist/runtime/builtins/geo/index.js +35 -0
  210. package/dist/runtime/builtins/geo/index.js.map +1 -0
  211. package/dist/runtime/builtins/geo/merge.d.ts +40 -0
  212. package/dist/runtime/builtins/geo/merge.d.ts.map +1 -0
  213. package/dist/runtime/builtins/geo/merge.js +24 -0
  214. package/dist/runtime/builtins/geo/merge.js.map +1 -0
  215. package/dist/runtime/builtins/geo/namespace.d.ts +15 -0
  216. package/dist/runtime/builtins/geo/namespace.d.ts.map +1 -0
  217. package/dist/runtime/builtins/geo/namespace.js +17 -0
  218. package/dist/runtime/builtins/geo/namespace.js.map +1 -0
  219. package/dist/runtime/builtins/geo/svgExport.d.ts +130 -0
  220. package/dist/runtime/builtins/geo/svgExport.d.ts.map +1 -0
  221. package/dist/runtime/builtins/geo/svgExport.js +50 -0
  222. package/dist/runtime/builtins/geo/svgExport.js.map +1 -0
  223. package/dist/runtime/builtins/geo/transform.d.ts +60 -0
  224. package/dist/runtime/builtins/geo/transform.d.ts.map +1 -0
  225. package/dist/runtime/builtins/geo/transform.js +32 -0
  226. package/dist/runtime/builtins/geo/transform.js.map +1 -0
  227. package/dist/runtime/builtins/index.d.ts +9 -0
  228. package/dist/runtime/builtins/index.d.ts.map +1 -0
  229. package/dist/runtime/builtins/index.js +16 -0
  230. package/dist/runtime/builtins/index.js.map +1 -0
  231. package/dist/runtime/definition/architecture.d.ts +8 -0
  232. package/dist/runtime/definition/architecture.d.ts.map +1 -0
  233. package/dist/runtime/definition/architecture.js +112 -0
  234. package/dist/runtime/definition/architecture.js.map +1 -0
  235. package/dist/runtime/definition/extract.d.ts +12 -0
  236. package/dist/runtime/definition/extract.d.ts.map +1 -0
  237. package/dist/runtime/definition/extract.js +151 -0
  238. package/dist/runtime/definition/extract.js.map +1 -0
  239. package/dist/runtime/error.d.ts +8 -0
  240. package/dist/runtime/error.d.ts.map +1 -0
  241. package/dist/runtime/error.js +11 -0
  242. package/dist/runtime/error.js.map +1 -0
  243. package/dist/runtime/geometry/attributes.d.ts +65 -0
  244. package/dist/runtime/geometry/attributes.d.ts.map +1 -0
  245. package/dist/runtime/geometry/attributes.js +201 -0
  246. package/dist/runtime/geometry/attributes.js.map +1 -0
  247. package/dist/runtime/geometry/builder.d.ts +57 -0
  248. package/dist/runtime/geometry/builder.d.ts.map +1 -0
  249. package/dist/runtime/geometry/builder.js +153 -0
  250. package/dist/runtime/geometry/builder.js.map +1 -0
  251. package/dist/runtime/geometry/copy.d.ts +36 -0
  252. package/dist/runtime/geometry/copy.d.ts.map +1 -0
  253. package/dist/runtime/geometry/copy.js +68 -0
  254. package/dist/runtime/geometry/copy.js.map +1 -0
  255. package/dist/runtime/geometry/generate.d.ts +43 -0
  256. package/dist/runtime/geometry/generate.d.ts.map +1 -0
  257. package/dist/runtime/geometry/generate.js +76 -0
  258. package/dist/runtime/geometry/generate.js.map +1 -0
  259. package/dist/runtime/geometry/groups.d.ts +18 -0
  260. package/dist/runtime/geometry/groups.d.ts.map +1 -0
  261. package/dist/runtime/geometry/groups.js +69 -0
  262. package/dist/runtime/geometry/groups.js.map +1 -0
  263. package/dist/runtime/geometry/index.d.ts +23 -0
  264. package/dist/runtime/geometry/index.d.ts.map +1 -0
  265. package/dist/runtime/geometry/index.js +23 -0
  266. package/dist/runtime/geometry/index.js.map +1 -0
  267. package/dist/runtime/geometry/matrix.d.ts +26 -0
  268. package/dist/runtime/geometry/matrix.d.ts.map +1 -0
  269. package/dist/runtime/geometry/matrix.js +27 -0
  270. package/dist/runtime/geometry/matrix.js.map +1 -0
  271. package/dist/runtime/geometry/merge.d.ts +33 -0
  272. package/dist/runtime/geometry/merge.d.ts.map +1 -0
  273. package/dist/runtime/geometry/merge.js +185 -0
  274. package/dist/runtime/geometry/merge.js.map +1 -0
  275. package/dist/runtime/geometry/primitives.d.ts +41 -0
  276. package/dist/runtime/geometry/primitives.d.ts.map +1 -0
  277. package/dist/runtime/geometry/primitives.js +131 -0
  278. package/dist/runtime/geometry/primitives.js.map +1 -0
  279. package/dist/runtime/geometry/promote.d.ts +44 -0
  280. package/dist/runtime/geometry/promote.d.ts.map +1 -0
  281. package/dist/runtime/geometry/promote.js +203 -0
  282. package/dist/runtime/geometry/promote.js.map +1 -0
  283. package/dist/runtime/geometry/resample.d.ts +29 -0
  284. package/dist/runtime/geometry/resample.d.ts.map +1 -0
  285. package/dist/runtime/geometry/resample.js +165 -0
  286. package/dist/runtime/geometry/resample.js.map +1 -0
  287. package/dist/runtime/geometry/svg.d.ts +91 -0
  288. package/dist/runtime/geometry/svg.d.ts.map +1 -0
  289. package/dist/runtime/geometry/svg.js +216 -0
  290. package/dist/runtime/geometry/svg.js.map +1 -0
  291. package/dist/runtime/geometry/transform.d.ts +18 -0
  292. package/dist/runtime/geometry/transform.d.ts.map +1 -0
  293. package/dist/runtime/geometry/transform.js +56 -0
  294. package/dist/runtime/geometry/transform.js.map +1 -0
  295. package/dist/runtime/index.d.ts +5 -0
  296. package/dist/runtime/index.d.ts.map +1 -0
  297. package/dist/runtime/index.js +4 -0
  298. package/dist/runtime/index.js.map +1 -0
  299. package/dist/runtime/node.d.ts +4 -0
  300. package/dist/runtime/node.d.ts.map +1 -0
  301. package/dist/runtime/node.js +12 -0
  302. package/dist/runtime/node.js.map +1 -0
  303. package/dist/runtime/runtime.d.ts +3 -0
  304. package/dist/runtime/runtime.d.ts.map +1 -0
  305. package/dist/runtime/runtime.js +1198 -0
  306. package/dist/runtime/runtime.js.map +1 -0
  307. package/dist/runtime/types.d.ts +241 -0
  308. package/dist/runtime/types.d.ts.map +1 -0
  309. package/dist/runtime/types.js +2 -0
  310. package/dist/runtime/types.js.map +1 -0
  311. package/dist/shell.d.ts +8 -0
  312. package/dist/shell.js +54 -0
  313. package/dist/shell.js.map +7 -0
  314. package/dist/stage.d.ts +7 -0
  315. package/dist/stage.js +84 -0
  316. package/dist/stage.js.map +7 -0
  317. package/dist/studio/panel.d.ts +48 -0
  318. package/doc/PROJECT_AUTHORING.md +360 -0
  319. package/package.json +126 -0
@@ -0,0 +1,1198 @@
1
+ import { canConnectTypes, validateNodeDefinition, } from "cascade/contracts";
2
+ import { CascadeRuntimeError } from "./error.js";
3
+ import { builtinNodeRegistrations } from "./builtins/index.js";
4
+ class MutableAbortSignal {
5
+ report;
6
+ aborted = false;
7
+ reason;
8
+ listeners = new Set();
9
+ constructor(report) {
10
+ this.report = report;
11
+ }
12
+ addEventListener(_type, listener, options) {
13
+ if (this.aborted)
14
+ this.call(listener);
15
+ else if (options?.once) {
16
+ const once = () => {
17
+ this.listeners.delete(once);
18
+ listener();
19
+ };
20
+ this.listeners.add(once);
21
+ }
22
+ else
23
+ this.listeners.add(listener);
24
+ }
25
+ removeEventListener(_type, listener) {
26
+ this.listeners.delete(listener);
27
+ }
28
+ abort(reason) {
29
+ if (this.aborted)
30
+ return;
31
+ this.aborted = true;
32
+ this.reason = reason;
33
+ for (const listener of [...this.listeners])
34
+ this.call(listener);
35
+ this.listeners.clear();
36
+ }
37
+ call(listener) {
38
+ try {
39
+ listener();
40
+ }
41
+ catch (error) {
42
+ this.report({
43
+ phase: "run",
44
+ code: "runtime/abort-listener-failed",
45
+ message: error instanceof Error ? error.message : String(error),
46
+ });
47
+ }
48
+ }
49
+ }
50
+ export function createRuntime(options) {
51
+ return new Runtime(options.host, [...builtinNodeRegistrations, ...(options.nodes ?? [])]);
52
+ }
53
+ class Runtime {
54
+ host;
55
+ registrations = new Map();
56
+ graphs = new Set();
57
+ sealed = false;
58
+ disposed = false;
59
+ constructor(host, registrations) {
60
+ this.host = host;
61
+ for (const registration of registrations)
62
+ this.registerNode(registration);
63
+ }
64
+ registerNode(registration) {
65
+ if (this.sealed || this.disposed)
66
+ misuse("runtime/registry-sealed", "Node registrations are sealed after the first load");
67
+ if (this.registrations.has(registration.moduleId))
68
+ misuse("runtime/duplicate-registration", `Duplicate registration ${registration.moduleId}`);
69
+ this.registrations.set(registration.moduleId, snapshotRegistration(registration));
70
+ }
71
+ async load(document) {
72
+ if (this.disposed)
73
+ misuse("runtime/disposed", "Runtime is disposed");
74
+ this.sealed = true;
75
+ const nodes = [];
76
+ const ids = new Set();
77
+ for (const authored of document.nodes) {
78
+ if (!authored.id || ids.has(authored.id))
79
+ misuse("runtime/invalid-node-id", `Invalid or duplicate node id ${authored.id}`);
80
+ ids.add(authored.id);
81
+ const moduleId = authored.module ?? authored.type;
82
+ if (!moduleId)
83
+ misuse("runtime/missing-module", `Node ${authored.id} has no module`);
84
+ const resolved = this.registrations.get(moduleId) ??
85
+ (await this.host.modules.resolve(moduleId));
86
+ if (!resolved)
87
+ misuse("runtime/module-not-found", `Module ${moduleId} was not registered`);
88
+ if (resolved.moduleId !== moduleId)
89
+ misuse("runtime/module-identity", `Resolver returned ${resolved.moduleId} for ${moduleId}`);
90
+ const snapshot = snapshotRegistration(resolved);
91
+ if (snapshot.kind === "legacy-dynamic")
92
+ misuse("runtime/legacy-adapter-required", `Legacy module ${moduleId} requires the explicit legacy adapter`);
93
+ const registration = specializeCoreRegistration(snapshot, authored, document.nodes);
94
+ const diagnostics = validateNodeDefinition(registration.definition);
95
+ if (diagnostics.length)
96
+ throw new CascadeRuntimeError("runtime/definition-invalid", diagnostics);
97
+ nodes.push(materialize(authored, registration));
98
+ }
99
+ const byId = new Map(nodes.map((node) => [node.id, node]));
100
+ validateHierarchy(nodes, byId);
101
+ const connections = (document.connections ?? []).map((connection, index) => materializeConnection(connection, index, byId));
102
+ validateCardinality(byId, connections);
103
+ validateTopology(nodes, connections);
104
+ const graph = new Graph(this.host, nodes, connections, () => this.graphs.delete(graph), document.annotations);
105
+ this.graphs.add(graph);
106
+ return graph;
107
+ }
108
+ async dispose() {
109
+ if (this.disposed)
110
+ return;
111
+ this.disposed = true;
112
+ await Promise.all([...this.graphs].map((graph) => graph.dispose()));
113
+ await this.host.legacyShell?.dispose?.();
114
+ }
115
+ }
116
+ class Graph {
117
+ host;
118
+ nodes;
119
+ connections;
120
+ onDispose;
121
+ graphState = "ready";
122
+ byId;
123
+ listeners = new Set();
124
+ diagnostics = [];
125
+ active;
126
+ activeRun;
127
+ disposal;
128
+ runCounter = 0;
129
+ authoredAnnotations;
130
+ graphInputs;
131
+ graphOutputs;
132
+ constructor(host, nodes, connections, onDispose, annotations = []) {
133
+ this.host = host;
134
+ this.nodes = nodes;
135
+ this.connections = connections;
136
+ this.onDispose = onDispose;
137
+ this.byId = new Map(nodes.map((node) => [node.id, node]));
138
+ this.graphInputs = graphBoundaries(nodes, "cascade.core.Input", "inputName");
139
+ this.graphOutputs = graphBoundaries(nodes, "cascade.core.Output", "outputName");
140
+ this.authoredAnnotations = Object.freeze(annotations.map((annotation) => snapshot(annotation)));
141
+ }
142
+ get state() {
143
+ return this.graphState;
144
+ }
145
+ inspect() {
146
+ const nodes = this.nodes.map((node) => ({
147
+ id: node.id,
148
+ moduleId: node.moduleId,
149
+ runsOn: node.definition.runsOn,
150
+ ...(node.parent ? { parent: node.parent } : {}),
151
+ inputs: Object.fromEntries(Object.entries(node.definition.inputs ?? {}).map(([name, definition]) => [
152
+ name,
153
+ {
154
+ kind: definition.kind,
155
+ type: definition.kind === "trigger" ? "trigger" : definition.type,
156
+ connected: this.connections.some((connection) => connection.target.nodeId === node.id &&
157
+ connection.target.inputName === name),
158
+ ...(definition.kind === "data"
159
+ ? { value: snapshot(node.inputs[name]) }
160
+ : {}),
161
+ },
162
+ ])),
163
+ outputs: Object.fromEntries(Object.entries(node.definition.outputs ?? {}).map(([name, definition]) => [
164
+ name,
165
+ {
166
+ kind: definition.kind,
167
+ type: definition.kind === "trigger" ? "trigger" : definition.type,
168
+ connected: this.connections.some((connection) => connection.source.nodeId === node.id &&
169
+ connection.source.outputName === name),
170
+ ...(definition.kind === "data" && node.outputs.has(name)
171
+ ? { value: snapshot(node.outputs.get(name)) }
172
+ : {}),
173
+ },
174
+ ])),
175
+ props: Object.fromEntries(Object.entries(node.definition.props ?? {}).map(([name, definition]) => [
176
+ name,
177
+ { type: definition.type, value: snapshot(node.props[name]) },
178
+ ])),
179
+ }));
180
+ return Object.freeze({
181
+ state: this.graphState,
182
+ interface: Object.freeze({
183
+ inputs: boundaryInspection(this.graphInputs, "output"),
184
+ outputs: boundaryInspection(this.graphOutputs, "output"),
185
+ }),
186
+ nodes: Object.freeze(nodes),
187
+ connections: Object.freeze(this.connections.map((connection) => Object.freeze({
188
+ source: Object.freeze({ ...connection.source }),
189
+ target: Object.freeze({ ...connection.target }),
190
+ }))),
191
+ diagnostics: Object.freeze([...this.diagnostics]),
192
+ });
193
+ }
194
+ async setInput(nodeId, inputName, value) {
195
+ this.requireReady();
196
+ const node = this.requireNode(nodeId);
197
+ const definition = node.definition.inputs?.[inputName];
198
+ if (!definition || definition.kind !== "data")
199
+ misuse("runtime/invalid-input", `${nodeId}.${inputName} is not a data input`);
200
+ node.inputs[inputName] = runtimeValue(value, definition.type, !("default" in definition), `${nodeId}.${inputName}`);
201
+ }
202
+ setGraphInput(name, value) {
203
+ const node = this.graphInputs.get(name);
204
+ if (!node)
205
+ return Promise.reject(runtimeError("runtime/graph-input-not-found", `Unknown graph input ${name}`));
206
+ return this.setInput(node.id, "value", value);
207
+ }
208
+ async setProp(nodeId, propName, value) {
209
+ this.requireReady();
210
+ const node = this.requireNode(nodeId);
211
+ if (isStructuralProp(node, propName))
212
+ misuse("runtime/structural-prop", `${nodeId}.${propName} is structural and requires a graph reload`);
213
+ if (!node.definition.props?.[propName])
214
+ misuse("runtime/invalid-prop", `${nodeId}.${propName} is not a prop`);
215
+ node.props[propName] = runtimeValue(value, node.definition.props[propName].type, false, `${nodeId}.${propName}`);
216
+ }
217
+ async applyPreset(preset) {
218
+ this.requireReady();
219
+ if (preset.version !== 1)
220
+ misuse("runtime/invalid-preset", "Preset version must be 1");
221
+ const inputDrafts = [];
222
+ const propDrafts = [];
223
+ for (const [nodeId, values] of Object.entries(preset.nodes)) {
224
+ const node = this.requireNode(nodeId);
225
+ for (const [name, value] of Object.entries(values.inputs ?? {})) {
226
+ const definition = node.definition.inputs?.[name];
227
+ if (!definition || definition.kind !== "data")
228
+ misuse("runtime/invalid-input", `${nodeId}.${name} is not a data input`);
229
+ inputDrafts.push([
230
+ node,
231
+ name,
232
+ runtimeValue(value, definition.type, !("default" in definition), `${nodeId}.${name}`),
233
+ ]);
234
+ }
235
+ for (const [name, value] of Object.entries(values.props ?? {})) {
236
+ const definition = node.definition.props?.[name];
237
+ if (!definition)
238
+ misuse("runtime/invalid-prop", `${nodeId}.${name} is not a prop`);
239
+ if (isStructuralProp(node, name))
240
+ misuse("runtime/structural-prop", `${nodeId}.${name} is structural and cannot be changed by a preset`);
241
+ propDrafts.push([
242
+ node,
243
+ name,
244
+ runtimeValue(value, definition.type, false, `${nodeId}.${name}`),
245
+ ]);
246
+ }
247
+ }
248
+ for (const [node, name, value] of inputDrafts)
249
+ node.inputs[name] = value;
250
+ for (const [node, name, value] of propDrafts)
251
+ node.props[name] = value;
252
+ }
253
+ run(options = {}) {
254
+ return this.trackRun(options.target ?? { kind: "all" }, options.signal, options.runId);
255
+ }
256
+ trigger(nodeId, inputName, payload, options = {}) {
257
+ const node = this.requireNode(nodeId);
258
+ if (node.definition.inputs?.[inputName]?.kind !== "trigger")
259
+ return Promise.reject(runtimeError("runtime/invalid-trigger", `${nodeId}.${inputName} is not a trigger input`));
260
+ return this.trackRun({ kind: "node", nodeId }, options.signal, options.runId, { nodeId, inputName, payload });
261
+ }
262
+ cancel(reason = "Cancelled") {
263
+ if (!this.active || this.active.signal.aborted)
264
+ return;
265
+ this.graphState = "cancelling";
266
+ this.active.reason = reason;
267
+ this.active.signal.abort(reason);
268
+ this.emit({ type: "run:cancel", runId: this.active.runId, reason });
269
+ }
270
+ subscribe(listener) {
271
+ this.listeners.add(listener);
272
+ return () => this.listeners.delete(listener);
273
+ }
274
+ getOutput(nodeId, outputName) {
275
+ return snapshot(this.requireNode(nodeId).outputs.get(outputName));
276
+ }
277
+ getGraphOutput(name) {
278
+ const node = this.graphOutputs.get(name);
279
+ if (!node)
280
+ misuse("runtime/graph-output-not-found", `Unknown graph output ${name}`);
281
+ return this.getOutput(node.id, "output");
282
+ }
283
+ getOutputs(nodeId) {
284
+ return immutableMap([...this.requireNode(nodeId).outputs].map(([name, value]) => [
285
+ name,
286
+ snapshot(value),
287
+ ]));
288
+ }
289
+ dispose() {
290
+ this.disposal ??= this.performDispose();
291
+ return this.disposal;
292
+ }
293
+ trackRun(target, signal, runId, injection) {
294
+ const running = this.executeRun(target, signal, runId, injection);
295
+ this.activeRun = running;
296
+ const clear = () => {
297
+ if (this.activeRun === running)
298
+ this.activeRun = undefined;
299
+ };
300
+ void running.then(clear, clear);
301
+ return running;
302
+ }
303
+ async performDispose() {
304
+ if (this.graphState === "disposed")
305
+ return;
306
+ this.cancel("Graph disposed");
307
+ try {
308
+ await this.activeRun;
309
+ }
310
+ catch {
311
+ // API/preflight failures are already exposed to the run caller.
312
+ }
313
+ this.graphState = "disposed";
314
+ this.listeners.clear();
315
+ this.onDispose();
316
+ }
317
+ async executeRun(target, externalSignal, requestedId, injection) {
318
+ this.requireReady();
319
+ const runId = requestedId ?? `run-${this.runCounter++}`;
320
+ const signal = new MutableAbortSignal((diagnostic) => this.host.report(diagnostic));
321
+ this.active = { runId, signal };
322
+ this.graphState = "running";
323
+ const startedAt = this.host.now();
324
+ const runDiagnostics = [];
325
+ this.emit({ type: "run:start", runId, target });
326
+ const relay = () => this.cancel(typeof externalSignal?.reason === "string"
327
+ ? externalSignal.reason
328
+ : "Cancelled by external signal");
329
+ if (externalSignal?.aborted)
330
+ relay();
331
+ else
332
+ externalSignal?.addEventListener("abort", relay, { once: true });
333
+ let executionStarted = false;
334
+ try {
335
+ const selection = injection
336
+ ? {
337
+ reachable: this.triggerReachable(injection.nodeId),
338
+ initial: this.triggerDataAncestors(injection.nodeId),
339
+ }
340
+ : this.runSelection(target);
341
+ const { reachable, initial } = selection;
342
+ this.preflight(reachable);
343
+ if (!signal.aborted)
344
+ await Promise.all(reachable
345
+ .filter((node) => !this.isRemote(node))
346
+ .map((node) => this.loadExecute(node)));
347
+ executionStarted = true;
348
+ const queue = [];
349
+ let sequence = 0;
350
+ const enqueue = (nodeId, inputName, payload, source) => {
351
+ const event = snapshot({
352
+ id: `${runId}:${sequence}`,
353
+ runId,
354
+ sequence: sequence++,
355
+ ...(payload === undefined
356
+ ? {}
357
+ : { payload: snapshot(payload) }),
358
+ source,
359
+ });
360
+ queue.push({ nodeId, inputName, event });
361
+ this.emit({ type: "trigger:queued", runId, nodeId, inputName, event });
362
+ };
363
+ const ordered = topological(this.nodes, this.connections).filter((node) => initial.includes(node));
364
+ for (const node of ordered) {
365
+ if (signal.aborted)
366
+ break;
367
+ await this.executeNode(node, runId, signal, {}, enqueue);
368
+ }
369
+ if (injection && !signal.aborted)
370
+ enqueue(injection.nodeId, injection.inputName, injection.payload, {
371
+ kind: "external",
372
+ });
373
+ while (queue.length && !signal.aborted) {
374
+ const next = queue.shift();
375
+ this.emit({
376
+ type: "trigger:delivered",
377
+ runId,
378
+ nodeId: next.nodeId,
379
+ inputName: next.inputName,
380
+ event: next.event,
381
+ });
382
+ await this.executeNode(this.requireNode(next.nodeId), runId, signal, { [next.inputName]: next.event }, enqueue);
383
+ }
384
+ return this.finish(runId, signal.aborted ? "cancelled" : "completed", startedAt, runDiagnostics);
385
+ }
386
+ catch (error) {
387
+ if (!executionStarted && error instanceof CascadeRuntimeError)
388
+ throw error;
389
+ const diagnostic = {
390
+ phase: "run",
391
+ code: "runtime/execution-failed",
392
+ message: error instanceof Error ? error.message : String(error),
393
+ };
394
+ runDiagnostics.push(diagnostic);
395
+ this.diagnostics.push(diagnostic);
396
+ this.emit({ type: "diagnostic", runId, diagnostic });
397
+ return this.finish(runId, signal.aborted ? "cancelled" : "failed", startedAt, runDiagnostics);
398
+ }
399
+ finally {
400
+ externalSignal?.removeEventListener("abort", relay);
401
+ if (!this.isDisposed())
402
+ this.graphState = "ready";
403
+ this.active = undefined;
404
+ }
405
+ }
406
+ async executeNode(node, runId, signal, triggers, enqueue) {
407
+ this.emit({ type: "node:start", runId, nodeId: node.id });
408
+ const inputs = Object.fromEntries(Object.entries(node.definition.inputs ?? {}).map(([name, definition]) => {
409
+ if (definition.kind === "trigger")
410
+ return [name, triggers[name]];
411
+ if (node.moduleId === "cascade.core.Input" &&
412
+ name === "value" &&
413
+ node.parent) {
414
+ const parent = this.requireNode(node.parent);
415
+ const index = integerProp(node.props.inputIndex);
416
+ return [name, this.dataInputValue(parent, `input_${index}`)];
417
+ }
418
+ const connections = this.connections.filter((item) => item.kind === "data" &&
419
+ item.target.nodeId === node.id &&
420
+ item.target.inputName === name).sort((first, second) => (first.variadicIndex ?? first.order) -
421
+ (second.variadicIndex ?? second.order));
422
+ if (definition.variadic) {
423
+ const values = connections.map((connection) => this.requireNode(connection.source.nodeId).outputs.get(connection.source.outputName));
424
+ if (!values.length && node.inputs[name] !== undefined)
425
+ values.push(node.inputs[name]);
426
+ return [name, Object.freeze(values)];
427
+ }
428
+ const connection = connections[0];
429
+ return [
430
+ name,
431
+ connection
432
+ ? this.requireNode(connection.source.nodeId).outputs.get(connection.source.outputName)
433
+ : node.inputs[name],
434
+ ];
435
+ }));
436
+ Object.freeze(inputs);
437
+ if (this.isRemote(node)) {
438
+ const bridge = this.host.capabilities.serverBridge;
439
+ if (!bridge)
440
+ throw runtimeError("runtime/bridge-missing", `No server bridge for ${node.moduleId}`);
441
+ const progress = {
442
+ report: (event) => this.emit({
443
+ type: "node:progress",
444
+ runId,
445
+ nodeId: node.id,
446
+ ...event,
447
+ }),
448
+ };
449
+ const response = await bridge.execute({
450
+ moduleId: node.moduleId,
451
+ nodeId: node.id,
452
+ inputs: inputs,
453
+ props: Object.freeze({ ...node.props }),
454
+ }, { signal, progress });
455
+ for (const diagnostic of response.diagnostics) {
456
+ this.diagnostics.push(diagnostic);
457
+ this.emit({ type: "diagnostic", runId, diagnostic });
458
+ }
459
+ for (const name of Object.keys(node.definition.outputs ?? {}))
460
+ if (name in response.outputs) {
461
+ const definition = node.definition.outputs?.[name];
462
+ if (!definition || definition.kind !== "data")
463
+ continue;
464
+ const value = runtimeValue(response.outputs[name], definition.type, false, `${node.id}.${name}`);
465
+ node.outputs.set(name, value);
466
+ this.emit({
467
+ type: "node:output",
468
+ runId,
469
+ nodeId: node.id,
470
+ outputName: name,
471
+ value,
472
+ });
473
+ }
474
+ this.emit({ type: "node:complete", runId, nodeId: node.id });
475
+ return;
476
+ }
477
+ if (!node.execute)
478
+ throw runtimeError("runtime/executor-missing", `Executor ${node.moduleId} was not loaded`);
479
+ const pendingData = new Map();
480
+ const pendingTriggers = [];
481
+ const outputs = Object.fromEntries(Object.entries(node.definition.outputs ?? {}).map(([name, definition]) => [
482
+ name,
483
+ definition.kind === "trigger"
484
+ ? {
485
+ trigger: (payload) => pendingTriggers.push({
486
+ name,
487
+ ...(payload === undefined
488
+ ? {}
489
+ : { payload: snapshot(payload) }),
490
+ }),
491
+ }
492
+ : {
493
+ set: (value) => pendingData.set(name, runtimeValue(value, definition.type, false, `${node.id}.${name}`)),
494
+ },
495
+ ]));
496
+ const capabilities = Object.freeze(Object.fromEntries((node.definition.capabilities ?? []).map((name) => [
497
+ name,
498
+ name === "shell"
499
+ ? bindShellToRun(this.host.capabilities.shell, signal)
500
+ : this.host.capabilities[name],
501
+ ])));
502
+ const props = Object.freeze({ ...node.props });
503
+ const progress = {
504
+ report: (event) => this.emit({ type: "node:progress", runId, nodeId: node.id, ...event }),
505
+ };
506
+ await node.execute({
507
+ inputs,
508
+ outputs,
509
+ props,
510
+ capabilities,
511
+ signal,
512
+ progress,
513
+ });
514
+ if (signal.aborted)
515
+ return;
516
+ if (node.moduleId === "cascade.core.Subnet") {
517
+ for (const child of this.nodes) {
518
+ if (child.parent !== node.id ||
519
+ child.moduleId !== "cascade.core.Output")
520
+ continue;
521
+ const name = `output_${integerProp(child.props.outputIndex)}`;
522
+ if (node.definition.outputs?.[name]?.kind === "data")
523
+ pendingData.set(name, child.outputs.get("output"));
524
+ }
525
+ }
526
+ for (const name of Object.keys(node.definition.outputs ?? {}))
527
+ if (pendingData.has(name)) {
528
+ const value = pendingData.get(name);
529
+ node.outputs.set(name, value);
530
+ this.emit({
531
+ type: "node:output",
532
+ runId,
533
+ nodeId: node.id,
534
+ outputName: name,
535
+ value: snapshot(value),
536
+ });
537
+ }
538
+ for (const emitted of pendingTriggers)
539
+ for (const connection of this.connections) {
540
+ if (connection.kind === "trigger" &&
541
+ connection.source.nodeId === node.id &&
542
+ connection.source.outputName === emitted.name)
543
+ enqueue(connection.target.nodeId, connection.target.inputName, emitted.payload, { nodeId: node.id, outputName: emitted.name });
544
+ }
545
+ this.emit({ type: "node:complete", runId, nodeId: node.id });
546
+ }
547
+ preflight(nodes) {
548
+ for (const node of nodes) {
549
+ const remote = this.isRemote(node);
550
+ const locationWorks = node.definition.runsOn === "portable" ||
551
+ node.definition.runsOn === this.host.environment ||
552
+ remote;
553
+ if (!locationWorks)
554
+ throw runtimeError("runtime/preflight-environment", `${node.moduleId} requires ${node.definition.runsOn}`);
555
+ if (remote) {
556
+ const hasTriggers = [
557
+ ...Object.values(node.definition.inputs ?? {}),
558
+ ...Object.values(node.definition.outputs ?? {}),
559
+ ].some((port) => port.kind === "trigger");
560
+ if (hasTriggers)
561
+ throw runtimeError("runtime/preflight-bridge-trigger", `${node.moduleId} cannot send triggers over the server bridge`);
562
+ const hasTexture = [
563
+ ...Object.values(node.definition.inputs ?? {}),
564
+ ...Object.values(node.definition.outputs ?? {}),
565
+ ].some((port) => port.kind === "data" && port.type === "texture");
566
+ if (hasTexture)
567
+ throw runtimeError("runtime/preflight-bridge-texture", `${node.moduleId} cannot send textures over the server bridge`);
568
+ }
569
+ else
570
+ for (const capability of node.definition.capabilities ?? [])
571
+ if (!this.host.capabilities[capability])
572
+ throw runtimeError("runtime/missing-capability", `Missing capability ${capability} for ${node.moduleId}`);
573
+ }
574
+ }
575
+ isRemote(node) {
576
+ return (this.host.environment === "browser" &&
577
+ node.definition.runsOn === "server" &&
578
+ !!this.host.capabilities.serverBridge);
579
+ }
580
+ async loadExecute(node) {
581
+ node.execute ??= (await node.registration.loadExecute(this.host.environment));
582
+ }
583
+ runSelection(target) {
584
+ if (target.kind === "all") {
585
+ const initial = this.nodes.filter((node) => !isGated(node) && !this.hasGatedDataAncestor(node.id));
586
+ const ids = new Set(initial.map((node) => node.id));
587
+ let changed = true;
588
+ while (changed) {
589
+ changed = false;
590
+ for (const connection of this.connections) {
591
+ if (connection.kind === "trigger" &&
592
+ ids.has(connection.source.nodeId) &&
593
+ !ids.has(connection.target.nodeId)) {
594
+ ids.add(connection.target.nodeId);
595
+ for (const dependency of this.dataClosure(connection.target.nodeId))
596
+ ids.add(dependency.id);
597
+ changed = true;
598
+ }
599
+ }
600
+ }
601
+ return {
602
+ initial,
603
+ reachable: this.nodes.filter((node) => ids.has(node.id)),
604
+ };
605
+ }
606
+ const root = this.requireNode(target.nodeId);
607
+ if (target.kind === "output" &&
608
+ !root.definition.outputs?.[target.outputName])
609
+ misuse("runtime/invalid-target", `Unknown output ${target.nodeId}.${target.outputName}`);
610
+ const reachable = this.dataClosure(root.id);
611
+ if (reachable.some(isGated))
612
+ misuse("runtime/invalid-target", `Target ${root.id} depends on a trigger event`);
613
+ return { reachable, initial: reachable };
614
+ }
615
+ dataClosure(nodeId) {
616
+ const ids = new Set([nodeId]);
617
+ const dependencies = dataDependencies(this.nodes, this.connections);
618
+ let changed = true;
619
+ while (changed) {
620
+ changed = false;
621
+ for (const dependency of dependencies)
622
+ if (ids.has(dependency.targetId) &&
623
+ !ids.has(dependency.sourceId)) {
624
+ ids.add(dependency.sourceId);
625
+ changed = true;
626
+ }
627
+ }
628
+ return this.nodes.filter((node) => ids.has(node.id));
629
+ }
630
+ hasGatedDataAncestor(nodeId) {
631
+ return this.dataClosure(nodeId).some(isGated);
632
+ }
633
+ triggerDataAncestors(nodeId) {
634
+ const dependencies = this.dataClosure(nodeId).filter((node) => node.id !== nodeId);
635
+ if (dependencies.some(isGated))
636
+ misuse("runtime/invalid-target", `Trigger target ${nodeId} depends on another trigger event`);
637
+ return dependencies;
638
+ }
639
+ triggerReachable(nodeId) {
640
+ const ids = new Set([this.requireNode(nodeId).id]);
641
+ const dependencies = dataDependencies(this.nodes, this.connections);
642
+ let changed = true;
643
+ while (changed) {
644
+ changed = false;
645
+ for (const connection of this.connections) {
646
+ if (connection.kind === "trigger" &&
647
+ ids.has(connection.source.nodeId) &&
648
+ !ids.has(connection.target.nodeId)) {
649
+ ids.add(connection.target.nodeId);
650
+ changed = true;
651
+ }
652
+ }
653
+ for (const dependency of dependencies)
654
+ if (ids.has(dependency.targetId) &&
655
+ !ids.has(dependency.sourceId)) {
656
+ ids.add(dependency.sourceId);
657
+ changed = true;
658
+ }
659
+ }
660
+ return this.nodes.filter((node) => ids.has(node.id));
661
+ }
662
+ finish(runId, status, startedAt, diagnostics) {
663
+ const finishedAt = this.host.now();
664
+ const outputs = immutableMap(this.nodes.map((node) => [node.id, this.getOutputs(node.id)]));
665
+ const result = Object.freeze({
666
+ runId,
667
+ status,
668
+ outputs,
669
+ diagnostics: Object.freeze([...diagnostics]),
670
+ timings: Object.freeze({
671
+ startedAt,
672
+ finishedAt,
673
+ durationMs: finishedAt - startedAt,
674
+ }),
675
+ });
676
+ this.emit({ type: "run:complete", result });
677
+ return result;
678
+ }
679
+ emit(event) {
680
+ for (const listener of [...this.listeners])
681
+ try {
682
+ listener(event);
683
+ }
684
+ catch (error) {
685
+ this.host.report({
686
+ phase: "event",
687
+ code: "runtime/listener-failed",
688
+ message: error instanceof Error ? error.message : String(error),
689
+ });
690
+ }
691
+ }
692
+ requireReady() {
693
+ if (this.graphState !== "ready")
694
+ misuse("runtime/invalid-state", `Graph is ${this.graphState}`);
695
+ }
696
+ isDisposed() {
697
+ return this.graphState === "disposed";
698
+ }
699
+ requireNode(id) {
700
+ const node = this.byId.get(id);
701
+ if (!node)
702
+ misuse("runtime/node-not-found", `Unknown node ${id}`);
703
+ return node;
704
+ }
705
+ dataInputValue(node, inputName) {
706
+ const connection = this.connections.find((item) => item.kind === "data" &&
707
+ item.target.nodeId === node.id &&
708
+ item.target.inputName === inputName);
709
+ return connection
710
+ ? this.requireNode(connection.source.nodeId).outputs.get(connection.source.outputName)
711
+ : node.inputs[inputName];
712
+ }
713
+ }
714
+ function bindShellToRun(shell, runSignal) {
715
+ if (!shell)
716
+ return undefined;
717
+ const options = (value) => ({
718
+ ...value,
719
+ signal: combineAbortSignals(runSignal, value?.signal),
720
+ });
721
+ return Object.freeze({
722
+ run: (command, args, value) => shell.run(command, args, options(value)),
723
+ runJson: (command, args, value) => shell.runJson(command, args, options(value)),
724
+ });
725
+ }
726
+ function combineAbortSignals(first, second) {
727
+ if (!second || first === second)
728
+ return first;
729
+ const wrappers = new Map();
730
+ return {
731
+ get aborted() { return first.aborted || second.aborted; },
732
+ get reason() { return first.aborted ? first.reason : second.reason; },
733
+ addEventListener(_type, listener, options) {
734
+ if (first.aborted || second.aborted) {
735
+ listener();
736
+ return;
737
+ }
738
+ const wrapped = () => {
739
+ first.removeEventListener("abort", wrapped);
740
+ second.removeEventListener("abort", wrapped);
741
+ wrappers.delete(listener);
742
+ listener();
743
+ };
744
+ wrappers.set(listener, wrapped);
745
+ first.addEventListener("abort", wrapped, options);
746
+ second.addEventListener("abort", wrapped, options);
747
+ },
748
+ removeEventListener(_type, listener) {
749
+ const wrapped = wrappers.get(listener);
750
+ if (!wrapped)
751
+ return;
752
+ first.removeEventListener("abort", wrapped);
753
+ second.removeEventListener("abort", wrapped);
754
+ wrappers.delete(listener);
755
+ },
756
+ };
757
+ }
758
+ function materialize(authored, registration) {
759
+ const inputs = {};
760
+ const props = {};
761
+ for (const [name, definition] of Object.entries(registration.definition.inputs ?? {}))
762
+ if (definition.kind === "data" && "default" in definition)
763
+ inputs[name] = runtimeValue(definition.default, definition.type, false, `${authored.id}.${name}`);
764
+ if (Array.isArray(authored.inputs))
765
+ for (const saved of authored.inputs) {
766
+ const definition = registration.definition.inputs?.[saved.name];
767
+ if (definition?.kind === "data") {
768
+ inputs[saved.name] = runtimeValue("defaultValue" in saved ? saved.defaultValue : saved.value, definition.type, !("default" in definition), `${authored.id}.${saved.name}`);
769
+ }
770
+ }
771
+ else if (authored.inputs)
772
+ for (const [name, saved] of Object.entries(authored.inputs)) {
773
+ const definition = registration.definition.inputs?.[name];
774
+ if (definition?.kind === "data")
775
+ inputs[name] = runtimeValue(saved, definition.type, !("default" in definition), `${authored.id}.${name}`);
776
+ }
777
+ for (const [name, definition] of Object.entries(registration.definition.props ?? {}))
778
+ props[name] = runtimeValue(definition.default, definition.type, false, `${authored.id}.${name}`);
779
+ for (const [name, saved] of Object.entries(authored.props ?? {})) {
780
+ const definition = registration.definition.props?.[name];
781
+ if (definition)
782
+ props[name] = runtimeValue(saved && typeof saved === "object" && "value" in saved
783
+ ? saved.value
784
+ : saved, definition.type, false, `${authored.id}.${name}`);
785
+ }
786
+ return {
787
+ id: authored.id,
788
+ moduleId: registration.moduleId,
789
+ ...(authored.parent ? { parent: authored.parent } : {}),
790
+ registration,
791
+ definition: registration.definition,
792
+ inputs,
793
+ props,
794
+ outputs: new Map(),
795
+ };
796
+ }
797
+ function specializeCoreRegistration(registration, authored, nodes) {
798
+ if (registration.moduleId === "cascade.core.Input" ||
799
+ registration.moduleId === "cascade.core.Output") {
800
+ const type = savedDataType(authored.props?.dataType);
801
+ const inputName = registration.moduleId === "cascade.core.Input" ? "value" : "input";
802
+ return Object.freeze({
803
+ ...registration,
804
+ definition: Object.freeze({
805
+ ...registration.definition,
806
+ inputs: Object.freeze({
807
+ [inputName]: dataInputDefinition(type),
808
+ }),
809
+ outputs: Object.freeze({
810
+ output: { kind: "data", type },
811
+ }),
812
+ }),
813
+ });
814
+ }
815
+ if (registration.moduleId !== "cascade.core.Subnet")
816
+ return registration;
817
+ const children = nodes.filter((candidate) => candidate.parent === authored.id);
818
+ const inputs = indexedBoundaryPorts(children, "cascade.core.Input", "inputIndex", "input");
819
+ const outputs = indexedBoundaryPorts(children, "cascade.core.Output", "outputIndex", "output");
820
+ return Object.freeze({
821
+ ...registration,
822
+ definition: Object.freeze({
823
+ ...registration.definition,
824
+ ...(Object.keys(inputs).length ? { inputs: Object.freeze(inputs) } : {}),
825
+ ...(Object.keys(outputs).length ? { outputs: Object.freeze(outputs) } : {}),
826
+ }),
827
+ });
828
+ }
829
+ function indexedBoundaryPorts(nodes, moduleId, propName, prefix) {
830
+ const boundaries = nodes
831
+ .filter((node) => (node.module ?? node.type) === moduleId)
832
+ .map((node) => ({
833
+ index: savedIntegerProp(node.props?.[propName]),
834
+ type: savedDataType(node.props?.dataType),
835
+ }))
836
+ .filter((boundary) => boundary.index >= 0);
837
+ const max = boundaries.length ? Math.max(...boundaries.map(({ index }) => index)) : -1;
838
+ const types = new Map(boundaries.map(({ index, type }) => [index, type]));
839
+ return Object.fromEntries(Array.from({ length: max + 1 }, (_, index) => [
840
+ `${prefix}_${index}`,
841
+ prefix === "input"
842
+ ? dataInputDefinition(types.get(index) ?? "any")
843
+ : { kind: "data", type: types.get(index) ?? "any" },
844
+ ]));
845
+ }
846
+ function dataInputDefinition(type) {
847
+ return type === "any"
848
+ ? { kind: "data", type, default: null }
849
+ : { kind: "data", type };
850
+ }
851
+ function savedDataType(value) {
852
+ const type = savedStringProp(value);
853
+ return (type || "any");
854
+ }
855
+ function savedStringProp(value) {
856
+ const unwrapped = value && typeof value === "object" && "value" in value
857
+ ? value.value
858
+ : value;
859
+ return typeof unwrapped === "string" ? unwrapped : "";
860
+ }
861
+ function savedIntegerProp(value) {
862
+ const unwrapped = value && typeof value === "object" && "value" in value
863
+ ? value.value
864
+ : value;
865
+ return typeof unwrapped === "number" && Number.isInteger(unwrapped)
866
+ ? unwrapped
867
+ : 0;
868
+ }
869
+ function integerProp(value) {
870
+ return typeof value === "number" && Number.isInteger(value) ? value : 0;
871
+ }
872
+ function graphBoundaries(nodes, moduleId, nameProp) {
873
+ const boundaries = new Map();
874
+ for (const node of nodes) {
875
+ if (node.parent || node.moduleId !== moduleId)
876
+ continue;
877
+ const name = savedStringProp(node.props[nameProp]).trim() || node.id;
878
+ if (boundaries.has(name))
879
+ misuse("runtime/duplicate-graph-boundary", `Duplicate graph boundary name ${name}`);
880
+ boundaries.set(name, node);
881
+ }
882
+ return boundaries;
883
+ }
884
+ function boundaryInspection(boundaries, portName) {
885
+ return Object.freeze(Object.fromEntries([...boundaries].map(([name, node]) => [
886
+ name,
887
+ Object.freeze({
888
+ nodeId: node.id,
889
+ type: node.definition.outputs?.[portName]?.kind === "data"
890
+ ? node.definition.outputs[portName].type
891
+ : "any",
892
+ }),
893
+ ])));
894
+ }
895
+ function isStructuralProp(node, name) {
896
+ if (node.moduleId === "cascade.core.Input")
897
+ return name === "inputIndex" || name === "inputName" || name === "dataType";
898
+ if (node.moduleId === "cascade.core.Output")
899
+ return name === "outputIndex" || name === "outputName" || name === "dataType";
900
+ return false;
901
+ }
902
+ function materializeConnection(connection, index, nodes) {
903
+ const endpoints = Array.isArray(connection)
904
+ ? {
905
+ source: {
906
+ nodeId: connection[0][0],
907
+ outputName: connection[0][2] ??
908
+ outputName(nodes.get(connection[0][0]), connection[0][1]),
909
+ },
910
+ target: {
911
+ nodeId: connection[1][0],
912
+ inputName: connection[1][2] ??
913
+ inputName(nodes.get(connection[1][0]), connection[1][1]),
914
+ },
915
+ }
916
+ : {
917
+ source: {
918
+ nodeId: connection.source.nodeId,
919
+ outputName: connection.source.outputName,
920
+ },
921
+ target: {
922
+ nodeId: connection.target.nodeId,
923
+ inputName: connection.target.inputName,
924
+ },
925
+ };
926
+ const source = nodes.get(endpoints.source.nodeId);
927
+ const target = nodes.get(endpoints.target.nodeId);
928
+ if (!source || !target)
929
+ misuse("runtime/invalid-connection", `Connection ${index} references an unknown node`);
930
+ const output = source.definition.outputs?.[endpoints.source.outputName];
931
+ let input = target.definition.inputs?.[endpoints.target.inputName];
932
+ let variadicIndex;
933
+ if (!input && /^input_\d+$/.test(endpoints.target.inputName)) {
934
+ const variadic = variadicInputName(target);
935
+ if (variadic !== undefined) {
936
+ variadicIndex = Number(endpoints.target.inputName.slice("input_".length));
937
+ endpoints.target.inputName = variadic;
938
+ input = target.definition.inputs?.[variadic];
939
+ }
940
+ }
941
+ if (!output || !input)
942
+ misuse("runtime/invalid-connection", `Connection ${index} references an unknown port`);
943
+ if (output.kind !== input.kind)
944
+ misuse("runtime/connection-kind", `Connection ${index} mixes ${output.kind} and ${input.kind}`);
945
+ if (output.kind === "data" &&
946
+ input.kind === "data" &&
947
+ !canConnectTypes(output.type, input.type))
948
+ misuse("runtime/connection-type", `Connection ${index} has incompatible types ${output.type} and ${input.type}`);
949
+ return Object.freeze({
950
+ source: Object.freeze(endpoints.source),
951
+ target: Object.freeze(endpoints.target),
952
+ kind: output.kind,
953
+ order: index,
954
+ ...(variadicIndex === undefined ? {} : { variadicIndex }),
955
+ });
956
+ }
957
+ function validateCardinality(nodes, connections) {
958
+ const counts = new Map();
959
+ for (const connection of connections) {
960
+ if (connection.kind !== "data")
961
+ continue;
962
+ const key = `${connection.target.nodeId}\0${connection.target.inputName}`;
963
+ const count = (counts.get(key) ?? 0) + 1;
964
+ counts.set(key, count);
965
+ const input = nodes.get(connection.target.nodeId)?.definition.inputs?.[connection.target.inputName];
966
+ if (count > 1 && input?.kind === "data" && !input.variadic)
967
+ misuse("runtime/input-cardinality", `${connection.target.nodeId}.${connection.target.inputName} accepts one connection`);
968
+ }
969
+ }
970
+ function validateHierarchy(nodes, byId) {
971
+ for (const node of nodes) {
972
+ if (!node.parent)
973
+ continue;
974
+ const parent = byId.get(node.parent);
975
+ if (!parent)
976
+ misuse("runtime/parent-not-found", `Node ${node.id} references missing parent ${node.parent}`);
977
+ if (parent.definition.container !== "subnet")
978
+ misuse("runtime/parent-not-container", `Node ${node.id} parent ${node.parent} is not a subnet container`);
979
+ }
980
+ for (const node of nodes) {
981
+ const visited = new Set([node.id]);
982
+ let parentId = node.parent;
983
+ while (parentId) {
984
+ if (visited.has(parentId))
985
+ misuse("runtime/parent-cycle", `Node hierarchy contains a cycle through ${parentId}`);
986
+ visited.add(parentId);
987
+ parentId = byId.get(parentId)?.parent;
988
+ }
989
+ }
990
+ }
991
+ function snapshotRegistration(registration) {
992
+ if (registration.kind === "legacy-dynamic")
993
+ return Object.freeze({ ...registration });
994
+ const definition = snapshot(registration.definition);
995
+ return Object.freeze({ ...registration, definition });
996
+ }
997
+ function isGated(node) {
998
+ return Object.values(node.definition.inputs ?? {}).some((input) => input.kind === "trigger");
999
+ }
1000
+ function outputName(node, index) {
1001
+ const name = Object.keys(node?.definition.outputs ?? {})[index];
1002
+ if (!name)
1003
+ misuse("runtime/invalid-connection", `Unknown output index ${index}`);
1004
+ return name;
1005
+ }
1006
+ /**
1007
+ * The declared name of a node's variadic input, if it has exactly one.
1008
+ *
1009
+ * `input_0`, `input_1`, ... is the wire spelling for the Nth connection into a
1010
+ * variadic input: it is what Studio's port list produces and what saved graphs
1011
+ * carry, and it is not the port's declared name. Translating it used to be
1012
+ * conditional on the target being `cascade.core.Switch` or `cascade.core.Merge`,
1013
+ * which made variadic wiring a property of two module ids rather than of a
1014
+ * definition, so `cascade.geo.Merge` and any project node declaring
1015
+ * `variadic: true` could not be wired at all. Marcus called that a bug and asked
1016
+ * for the general fix, 2026-09-04.
1017
+ *
1018
+ * Derived from the definition, so a node whose input is declared variadic
1019
+ * accepts `input_N` whoever wrote it. Two variadic inputs on one definition make
1020
+ * `input_N` genuinely ambiguous, so nothing is guessed and the connection is
1021
+ * refused as an unknown port; no definition declares two today, and the honest
1022
+ * failure is better than picking the first one.
1023
+ */
1024
+ function variadicInputName(node) {
1025
+ const names = Object.entries(node.definition.inputs ?? {})
1026
+ .filter(([, input]) => input.kind === "data" && input.variadic === true)
1027
+ .map(([name]) => name);
1028
+ return names.length === 1 ? names[0] : undefined;
1029
+ }
1030
+ function inputName(node, index) {
1031
+ const name = Object.keys(node?.definition.inputs ?? {})[index];
1032
+ if (!name)
1033
+ misuse("runtime/invalid-connection", `Unknown input index ${index}`);
1034
+ return name;
1035
+ }
1036
+ function validateTopology(nodes, connections) {
1037
+ topological(nodes, connections);
1038
+ }
1039
+ function topological(nodes, connections) {
1040
+ const indegree = new Map(nodes.map((node) => [node.id, 0]));
1041
+ const outgoing = new Map(nodes.map((node) => [node.id, []]));
1042
+ for (const dependency of graphDependencies(nodes, connections)) {
1043
+ outgoing.get(dependency.sourceId).push(dependency.targetId);
1044
+ indegree.set(dependency.targetId, indegree.get(dependency.targetId) + 1);
1045
+ }
1046
+ const queue = nodes.filter((node) => indegree.get(node.id) === 0);
1047
+ const result = [];
1048
+ while (queue.length) {
1049
+ const node = queue.shift();
1050
+ result.push(node);
1051
+ for (const id of outgoing.get(node.id)) {
1052
+ indegree.set(id, indegree.get(id) - 1);
1053
+ if (indegree.get(id) === 0)
1054
+ queue.push(nodes.find((candidate) => candidate.id === id));
1055
+ }
1056
+ }
1057
+ if (result.length !== nodes.length)
1058
+ misuse("runtime/cycle", "Graph contains a data or trigger cycle");
1059
+ return result;
1060
+ }
1061
+ function graphDependencies(nodes, connections) {
1062
+ const dependencies = new Map();
1063
+ for (const connection of connections) {
1064
+ const dependency = {
1065
+ sourceId: connection.source.nodeId,
1066
+ targetId: connection.target.nodeId,
1067
+ };
1068
+ dependencies.set(`${dependency.sourceId}\0${dependency.targetId}`, dependency);
1069
+ }
1070
+ for (const dependency of dataDependencies(nodes, connections))
1071
+ dependencies.set(`${dependency.sourceId}\0${dependency.targetId}`, dependency);
1072
+ return [...dependencies.values()];
1073
+ }
1074
+ function dataDependencies(nodes, connections) {
1075
+ const dependencies = new Map();
1076
+ for (const connection of connections) {
1077
+ if (connection.kind !== "data")
1078
+ continue;
1079
+ const dependency = {
1080
+ sourceId: connection.source.nodeId,
1081
+ targetId: connection.target.nodeId,
1082
+ };
1083
+ dependencies.set(`${dependency.sourceId}\0${dependency.targetId}`, dependency);
1084
+ }
1085
+ for (const node of nodes) {
1086
+ if (!node.parent)
1087
+ continue;
1088
+ if (node.moduleId === "cascade.core.Input") {
1089
+ const inputName = `input_${integerProp(node.props.inputIndex)}`;
1090
+ const source = connections.find((connection) => connection.kind === "data" &&
1091
+ connection.target.nodeId === node.parent &&
1092
+ connection.target.inputName === inputName);
1093
+ if (source) {
1094
+ const dependency = {
1095
+ sourceId: source.source.nodeId,
1096
+ targetId: node.id,
1097
+ };
1098
+ dependencies.set(`${dependency.sourceId}\0${dependency.targetId}`, dependency);
1099
+ }
1100
+ }
1101
+ else if (node.moduleId === "cascade.core.Output") {
1102
+ const dependency = { sourceId: node.id, targetId: node.parent };
1103
+ dependencies.set(`${dependency.sourceId}\0${dependency.targetId}`, dependency);
1104
+ }
1105
+ }
1106
+ return [...dependencies.values()];
1107
+ }
1108
+ function runtimeError(code, message) {
1109
+ return new CascadeRuntimeError(code, [{ phase: "runtime", code, message }]);
1110
+ }
1111
+ function misuse(code, message) {
1112
+ throw runtimeError(code, message);
1113
+ }
1114
+ function snapshot(value) {
1115
+ return cloneValue(value, new WeakSet());
1116
+ }
1117
+ function immutableMap(entries) {
1118
+ const source = new Map(entries);
1119
+ let view;
1120
+ view = Object.freeze({
1121
+ get size() { return source.size; },
1122
+ has(key) { return source.has(key); },
1123
+ get(key) { return source.get(key); },
1124
+ entries() { return source.entries(); },
1125
+ keys() { return source.keys(); },
1126
+ values() { return source.values(); },
1127
+ [Symbol.iterator]() { return source[Symbol.iterator](); },
1128
+ forEach(callback, thisArg) {
1129
+ source.forEach((value, key) => callback.call(thisArg, value, key, view));
1130
+ },
1131
+ });
1132
+ return view;
1133
+ }
1134
+ function cloneValue(value, ancestors) {
1135
+ if (typeof value === "number" && !Number.isFinite(value))
1136
+ misuse("runtime/non-serializable-value", "Runtime numbers must be finite");
1137
+ if (typeof value === "function" ||
1138
+ typeof value === "symbol" ||
1139
+ typeof value === "bigint")
1140
+ misuse("runtime/non-serializable-value", `Runtime values cannot contain ${typeof value}`);
1141
+ if (value === undefined || value === null || typeof value !== "object")
1142
+ return value;
1143
+ const prototype = Object.getPrototypeOf(value);
1144
+ if (!Array.isArray(value) &&
1145
+ prototype !== Object.prototype &&
1146
+ prototype !== null)
1147
+ return value;
1148
+ if (ancestors.has(value))
1149
+ misuse("runtime/non-serializable-value", "Runtime values cannot contain cycles");
1150
+ ancestors.add(value);
1151
+ const clone = Array.isArray(value)
1152
+ ? value.map((item) => cloneValue(item, ancestors))
1153
+ : Object.fromEntries(Object.entries(value).map(([key, item]) => [
1154
+ key,
1155
+ cloneValue(item, ancestors),
1156
+ ]));
1157
+ ancestors.delete(value);
1158
+ return Object.freeze(clone);
1159
+ }
1160
+ const TUPLE_LENGTHS = {
1161
+ vec2: 2,
1162
+ vec3: 3,
1163
+ vec4: 4,
1164
+ vec2i: 2,
1165
+ vec3i: 3,
1166
+ vec4i: 4,
1167
+ mat2: 4,
1168
+ mat3: 9,
1169
+ mat4: 16,
1170
+ color: 4,
1171
+ };
1172
+ function runtimeValue(value, type, allowUndefined, path) {
1173
+ if (value === undefined) {
1174
+ if (allowUndefined)
1175
+ return undefined;
1176
+ misuse("runtime/value-type", `${path} requires a ${type} value`);
1177
+ }
1178
+ const tupleLength = TUPLE_LENGTHS[type];
1179
+ const valid = (type === "float" && typeof value === "number" && Number.isFinite(value)) ||
1180
+ (type === "int" && typeof value === "number" && Number.isInteger(value)) ||
1181
+ (type === "bool" && typeof value === "boolean") ||
1182
+ (type === "string" && typeof value === "string") ||
1183
+ (tupleLength !== undefined &&
1184
+ Array.isArray(value) &&
1185
+ value.length === tupleLength &&
1186
+ value.every((item) => type.endsWith("i")
1187
+ ? typeof item === "number" && Number.isInteger(item)
1188
+ : typeof item === "number" && Number.isFinite(item))) ||
1189
+ (!tupleLength &&
1190
+ type !== "float" &&
1191
+ type !== "int" &&
1192
+ type !== "bool" &&
1193
+ type !== "string");
1194
+ if (!valid)
1195
+ misuse("runtime/value-type", `${path} is not a valid ${type}`);
1196
+ return snapshot(value);
1197
+ }
1198
+ //# sourceMappingURL=runtime.js.map