@tuongaz/seeflow 0.1.64 → 0.1.68

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 (314) hide show
  1. package/README.md +4 -0
  2. package/dist/web/assets/abap-DsBKuouk.js +1 -0
  3. package/dist/web/assets/actionscript-3-D_z4Izcz.js +1 -0
  4. package/dist/web/assets/ada-727ZlQH0.js +1 -0
  5. package/dist/web/assets/andromeeda-C3khCPGq.js +1 -0
  6. package/dist/web/assets/angular-html-4alyEGLm.js +1 -0
  7. package/dist/web/assets/angular-ts-BixEUTMq.js +1 -0
  8. package/dist/web/assets/apache-Dn00JSTd.js +1 -0
  9. package/dist/web/assets/apex-COJ4H7py.js +1 -0
  10. package/dist/web/assets/apl-BBq3IX1j.js +1 -0
  11. package/dist/web/assets/applescript-Bu5BbsvL.js +1 -0
  12. package/dist/web/assets/ara-7O62HKoU.js +1 -0
  13. package/dist/web/assets/asciidoc-BPT9niGB.js +1 -0
  14. package/dist/web/assets/asm-Dhn9LcZ4.js +1 -0
  15. package/dist/web/assets/astro-CqkE3fuf.js +1 -0
  16. package/dist/web/assets/aurora-x-D-2ljcwZ.js +1 -0
  17. package/dist/web/assets/awk-eg146-Ew.js +1 -0
  18. package/dist/web/assets/ayu-dark-Cv9koXgw.js +1 -0
  19. package/dist/web/assets/ballerina-Du268qiB.js +1 -0
  20. package/dist/web/assets/bat-fje9CFhw.js +1 -0
  21. package/dist/web/assets/beancount-BwXTMy5W.js +1 -0
  22. package/dist/web/assets/berry-3xVqZejG.js +1 -0
  23. package/dist/web/assets/bibtex-xW4inM5L.js +1 -0
  24. package/dist/web/assets/bicep-DHo0CJ0O.js +1 -0
  25. package/dist/web/assets/blade-a8OxSdnT.js +1 -0
  26. package/dist/web/assets/bsl-Dgyn0ogV.js +1 -0
  27. package/dist/web/assets/c-C3t2pwGQ.js +1 -0
  28. package/dist/web/assets/cadence-DNquZEk8.js +1 -0
  29. package/dist/web/assets/cairo--RitsXJZ.js +1 -0
  30. package/dist/web/assets/catppuccin-frappe-CD_QflpE.js +1 -0
  31. package/dist/web/assets/catppuccin-latte-DRW-0cLl.js +1 -0
  32. package/dist/web/assets/catppuccin-macchiato-C-_shW-Y.js +1 -0
  33. package/dist/web/assets/catppuccin-mocha-LGGdnPYs.js +1 -0
  34. package/dist/web/assets/chart-8DxAnLoD.js +73 -0
  35. package/dist/web/assets/clarity-BHOwM8T6.js +1 -0
  36. package/dist/web/assets/clojure-DxSadP1t.js +1 -0
  37. package/dist/web/assets/cmake-DbXoA79R.js +1 -0
  38. package/dist/web/assets/cobol-PTqiYgYu.js +1 -0
  39. package/dist/web/assets/code-block-cx7LPXvE.js +13 -0
  40. package/dist/web/assets/codeowners-Bp6g37R7.js +1 -0
  41. package/dist/web/assets/codeql-sacFqUAJ.js +1 -0
  42. package/dist/web/assets/coffee-dyiR41kL.js +1 -0
  43. package/dist/web/assets/common-lisp-C7gG9l05.js +1 -0
  44. package/dist/web/assets/coq-Dsg_Bt_b.js +1 -0
  45. package/dist/web/assets/cpp-BksuvNSY.js +1 -0
  46. package/dist/web/assets/crystal-DtDmRg-F.js +1 -0
  47. package/dist/web/assets/csharp-D9R-vmeu.js +1 -0
  48. package/dist/web/assets/css-BPhBrDlE.js +1 -0
  49. package/dist/web/assets/csv-B0qRVHPH.js +1 -0
  50. package/dist/web/assets/cue-DtFQj3wx.js +1 -0
  51. package/dist/web/assets/cypher-m2LEI-9-.js +1 -0
  52. package/dist/web/assets/d-BoXegm-a.js +1 -0
  53. package/dist/web/assets/dark-plus-C3mMm8J8.js +1 -0
  54. package/dist/web/assets/dart-B9wLZaAG.js +1 -0
  55. package/dist/web/assets/dax-ClGRhx96.js +1 -0
  56. package/dist/web/assets/desktop-DEIpsLCJ.js +1 -0
  57. package/dist/web/assets/diff-BgYniUM_.js +1 -0
  58. package/dist/web/assets/docker-COcR7UxN.js +1 -0
  59. package/dist/web/assets/dotenv-BjQB5zDj.js +1 -0
  60. package/dist/web/assets/dracula-BzJJZx-M.js +1 -0
  61. package/dist/web/assets/dracula-soft-BXkSAIEj.js +1 -0
  62. package/dist/web/assets/dream-maker-C-nORZOA.js +1 -0
  63. package/dist/web/assets/edge-D5gP-w-T.js +1 -0
  64. package/dist/web/assets/elixir-CLiX3zqd.js +1 -0
  65. package/dist/web/assets/elm-CmHSxxaM.js +1 -0
  66. package/dist/web/assets/emacs-lisp-BX77sIaO.js +1 -0
  67. package/dist/web/assets/erb-BYTLMnw6.js +1 -0
  68. package/dist/web/assets/erlang-B-DoSBHF.js +1 -0
  69. package/dist/web/assets/everforest-dark-BgDCqdQA.js +1 -0
  70. package/dist/web/assets/everforest-light-C8M2exoo.js +1 -0
  71. package/dist/web/assets/fennel-bCA53EVm.js +1 -0
  72. package/dist/web/assets/fish-w-ucz2PV.js +1 -0
  73. package/dist/web/assets/fluent-Dayu4EKP.js +1 -0
  74. package/dist/web/assets/fortran-fixed-form-TqA4NnZg.js +1 -0
  75. package/dist/web/assets/fortran-free-form-DKXYxT9g.js +1 -0
  76. package/dist/web/assets/fsharp-XplgxFYe.js +1 -0
  77. package/dist/web/assets/gdresource-BHYsBjWJ.js +1 -0
  78. package/dist/web/assets/gdscript-DfxzS6Rs.js +1 -0
  79. package/dist/web/assets/gdshader-SKMF96pI.js +1 -0
  80. package/dist/web/assets/genie-ajMbGru0.js +1 -0
  81. package/dist/web/assets/gherkin--30QC5Em.js +1 -0
  82. package/dist/web/assets/git-commit-i4q6IMui.js +1 -0
  83. package/dist/web/assets/git-rebase-B-v9cOL2.js +1 -0
  84. package/dist/web/assets/github-dark-DHJKELXO.js +1 -0
  85. package/dist/web/assets/github-dark-default-Cuk6v7N8.js +1 -0
  86. package/dist/web/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  87. package/dist/web/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  88. package/dist/web/assets/github-light-DAi9KRSo.js +1 -0
  89. package/dist/web/assets/github-light-default-D7oLnXFd.js +1 -0
  90. package/dist/web/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  91. package/dist/web/assets/gleam-B430Bg39.js +1 -0
  92. package/dist/web/assets/glimmer-js-D-cwc0-E.js +1 -0
  93. package/dist/web/assets/glimmer-ts-pgjy16dm.js +1 -0
  94. package/dist/web/assets/glsl-DBO2IWDn.js +1 -0
  95. package/dist/web/assets/gnuplot-CM8KxXT1.js +1 -0
  96. package/dist/web/assets/go-B1SYOhNW.js +1 -0
  97. package/dist/web/assets/graphql-cDcHW_If.js +1 -0
  98. package/dist/web/assets/groovy-DkBy-JyN.js +1 -0
  99. package/dist/web/assets/hack-D1yCygmZ.js +1 -0
  100. package/dist/web/assets/haml-B2EZWmdv.js +1 -0
  101. package/dist/web/assets/handlebars-BQGss363.js +1 -0
  102. package/dist/web/assets/haskell-BILxekzW.js +1 -0
  103. package/dist/web/assets/haxe-C5wWYbrZ.js +1 -0
  104. package/dist/web/assets/hcl-HzYwdGDm.js +1 -0
  105. package/dist/web/assets/hjson-T-Tgc4AT.js +1 -0
  106. package/dist/web/assets/hlsl-ifBTmRxC.js +1 -0
  107. package/dist/web/assets/houston-DnULxvSX.js +1 -0
  108. package/dist/web/assets/html-C2L_23MC.js +1 -0
  109. package/dist/web/assets/html-derivative-CSfWNPLT.js +1 -0
  110. package/dist/web/assets/http-FRrOvY1W.js +1 -0
  111. package/dist/web/assets/hxml-TIA70rKU.js +1 -0
  112. package/dist/web/assets/hy-BMj5Y0dO.js +1 -0
  113. package/dist/web/assets/imba-bv_oIlVt.js +1 -0
  114. package/dist/web/assets/index-CeLShda7.css +1 -0
  115. package/dist/web/assets/index-Dp4QwEl0.js +8608 -0
  116. package/dist/web/assets/{index.es-DZEdTXNJ.js → index.es-C-uXEdZB.js} +1 -1
  117. package/dist/web/assets/ini-BjABl1g7.js +1 -0
  118. package/dist/web/assets/java-xI-RfyKK.js +1 -0
  119. package/dist/web/assets/javascript-ySlJ1b_l.js +1 -0
  120. package/dist/web/assets/jinja-DGy0s7-h.js +1 -0
  121. package/dist/web/assets/jison-BqZprYcd.js +1 -0
  122. package/dist/web/assets/json-BQoSv7ci.js +1 -0
  123. package/dist/web/assets/json5-w8dY5SsB.js +1 -0
  124. package/dist/web/assets/jsonc-TU54ms6u.js +1 -0
  125. package/dist/web/assets/jsonl-DREVFZK8.js +1 -0
  126. package/dist/web/assets/jsonnet-BfivnA6A.js +1 -0
  127. package/dist/web/assets/{jspdf.es.min-DT1Li8zz.js → jspdf.es.min-BFQufOcQ.js} +3 -3
  128. package/dist/web/assets/jssm-P4WzXJd0.js +1 -0
  129. package/dist/web/assets/jsx-BAng5TT0.js +1 -0
  130. package/dist/web/assets/julia-BBuGR-5E.js +1 -0
  131. package/dist/web/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  132. package/dist/web/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  133. package/dist/web/assets/kanagawa-wave-DWedfzmr.js +1 -0
  134. package/dist/web/assets/kotlin-B5lbUyaz.js +1 -0
  135. package/dist/web/assets/kusto-mebxcVVE.js +1 -0
  136. package/dist/web/assets/laserwave-DUszq2jm.js +1 -0
  137. package/dist/web/assets/latex-C-cWTeAZ.js +1 -0
  138. package/dist/web/assets/lean-XBlWyCtg.js +1 -0
  139. package/dist/web/assets/less-BfCpw3nA.js +1 -0
  140. package/dist/web/assets/light-plus-B7mTdjB0.js +1 -0
  141. package/dist/web/assets/liquid-D3W5UaiH.js +1 -0
  142. package/dist/web/assets/log-Cc5clBb7.js +1 -0
  143. package/dist/web/assets/logo-IuBKFhSY.js +1 -0
  144. package/dist/web/assets/lua-CvWAzNxB.js +1 -0
  145. package/dist/web/assets/luau-Du5NY7AG.js +1 -0
  146. package/dist/web/assets/make-Bvotw-X0.js +1 -0
  147. package/dist/web/assets/markdown-DK_1WFMa.js +1 -0
  148. package/dist/web/assets/markdown-UIAJJxZW.js +1 -0
  149. package/dist/web/assets/marko-z0MBrx5-.js +1 -0
  150. package/dist/web/assets/material-theme-D5KoaKCx.js +1 -0
  151. package/dist/web/assets/material-theme-darker-BfHTSMKl.js +1 -0
  152. package/dist/web/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  153. package/dist/web/assets/material-theme-ocean-CyktbL80.js +1 -0
  154. package/dist/web/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  155. package/dist/web/assets/matlab-D9-PGadD.js +1 -0
  156. package/dist/web/assets/mdc-DB_EDNY_.js +1 -0
  157. package/dist/web/assets/mdx-sdHcTMYB.js +1 -0
  158. package/dist/web/assets/mermaid-Ci6OQyBP.js +1 -0
  159. package/dist/web/assets/min-dark-CafNBF8u.js +1 -0
  160. package/dist/web/assets/min-light-CTRr51gU.js +1 -0
  161. package/dist/web/assets/mipsasm-BC5c_5Pe.js +1 -0
  162. package/dist/web/assets/mojo-Tz6hzZYG.js +1 -0
  163. package/dist/web/assets/monokai-D4h5O-jR.js +1 -0
  164. package/dist/web/assets/move-DB_GagMm.js +1 -0
  165. package/dist/web/assets/narrat-DLbgOhZU.js +1 -0
  166. package/dist/web/assets/nextflow-B0XVJmRM.js +1 -0
  167. package/dist/web/assets/nginx-D_VnBJ67.js +1 -0
  168. package/dist/web/assets/night-owl-C39BiMTA.js +1 -0
  169. package/dist/web/assets/nim-ZlGxZxc3.js +1 -0
  170. package/dist/web/assets/nix-shcSOmrb.js +1 -0
  171. package/dist/web/assets/nord-Ddv68eIx.js +1 -0
  172. package/dist/web/assets/nushell-D4Tzg5kh.js +1 -0
  173. package/dist/web/assets/objective-c-Deuh7S70.js +1 -0
  174. package/dist/web/assets/objective-cpp-BUEGK8hf.js +1 -0
  175. package/dist/web/assets/ocaml-BNioltXt.js +1 -0
  176. package/dist/web/assets/one-dark-pro-GBQ2dnAY.js +1 -0
  177. package/dist/web/assets/one-light-PoHY5YXO.js +1 -0
  178. package/dist/web/assets/pascal-JqZropPD.js +1 -0
  179. package/dist/web/assets/perl-CHQXSrWU.js +1 -0
  180. package/dist/web/assets/php-B5ebYQev.js +1 -0
  181. package/dist/web/assets/plastic-3e1v2bzS.js +1 -0
  182. package/dist/web/assets/plsql-LKU2TuZ1.js +1 -0
  183. package/dist/web/assets/po-BFLt1xDp.js +1 -0
  184. package/dist/web/assets/poimandres-CS3Unz2-.js +1 -0
  185. package/dist/web/assets/polar-DKykz6zU.js +1 -0
  186. package/dist/web/assets/postcss-B3ZDOciz.js +1 -0
  187. package/dist/web/assets/powerquery-CSHBycmS.js +1 -0
  188. package/dist/web/assets/powershell-BIEUsx6d.js +1 -0
  189. package/dist/web/assets/prisma-B48N-Iqd.js +1 -0
  190. package/dist/web/assets/prolog-BY-TUvya.js +1 -0
  191. package/dist/web/assets/proto-zocC4JxJ.js +1 -0
  192. package/dist/web/assets/pug-CM9l7STV.js +1 -0
  193. package/dist/web/assets/puppet-Cza_XSSt.js +1 -0
  194. package/dist/web/assets/purescript-Bg-kzb6g.js +1 -0
  195. package/dist/web/assets/python-DhUJRlN_.js +1 -0
  196. package/dist/web/assets/qml-D8XfuvdV.js +1 -0
  197. package/dist/web/assets/qmldir-C8lEn-DE.js +1 -0
  198. package/dist/web/assets/qss-DhMKtDLN.js +1 -0
  199. package/dist/web/assets/r-CwjWoCRV.js +1 -0
  200. package/dist/web/assets/racket-CzouJOBO.js +1 -0
  201. package/dist/web/assets/raku-B1bQXN8T.js +1 -0
  202. package/dist/web/assets/razor-CNLDkMZG.js +1 -0
  203. package/dist/web/assets/red-bN70gL4F.js +1 -0
  204. package/dist/web/assets/reg-5LuOXUq_.js +1 -0
  205. package/dist/web/assets/regexp-DWJ3fJO_.js +1 -0
  206. package/dist/web/assets/rel-DJlmqQ1C.js +1 -0
  207. package/dist/web/assets/riscv-QhoSD0DR.js +1 -0
  208. package/dist/web/assets/rose-pine-CmCqftbK.js +1 -0
  209. package/dist/web/assets/rose-pine-dawn-Ds-gbosJ.js +1 -0
  210. package/dist/web/assets/rose-pine-moon-CjDtw9vr.js +1 -0
  211. package/dist/web/assets/rst-4NLicBqY.js +1 -0
  212. package/dist/web/assets/ruby-DeZ3UC14.js +1 -0
  213. package/dist/web/assets/rust-Be6lgOlo.js +1 -0
  214. package/dist/web/assets/sas-BmTFh92c.js +1 -0
  215. package/dist/web/assets/sass-BJ4Li9vH.js +1 -0
  216. package/dist/web/assets/scala-DQVVAn-B.js +1 -0
  217. package/dist/web/assets/scheme-BJGe-b2p.js +1 -0
  218. package/dist/web/assets/scss-C31hgJw-.js +1 -0
  219. package/dist/web/assets/sdbl-BLhTXw86.js +1 -0
  220. package/dist/web/assets/shaderlab-B7qAK45m.js +1 -0
  221. package/dist/web/assets/shellscript-atvbtKCR.js +1 -0
  222. package/dist/web/assets/shellsession-C_rIy8kc.js +1 -0
  223. package/dist/web/assets/slack-dark-BthQWCQV.js +1 -0
  224. package/dist/web/assets/slack-ochin-DqwNpetd.js +1 -0
  225. package/dist/web/assets/smalltalk-DkLiglaE.js +1 -0
  226. package/dist/web/assets/snazzy-light-Bw305WKR.js +1 -0
  227. package/dist/web/assets/solarized-dark-DXbdFlpD.js +1 -0
  228. package/dist/web/assets/solarized-light-L9t79GZl.js +1 -0
  229. package/dist/web/assets/solidity-C1w2a3ep.js +1 -0
  230. package/dist/web/assets/soy-C-lX7w71.js +1 -0
  231. package/dist/web/assets/sparql-bYkjHRlG.js +1 -0
  232. package/dist/web/assets/splunk-Cf8iN4DR.js +1 -0
  233. package/dist/web/assets/sql-COK4E0Yg.js +1 -0
  234. package/dist/web/assets/ssh-config-BknIz3MU.js +1 -0
  235. package/dist/web/assets/stata-DorPZHa4.js +1 -0
  236. package/dist/web/assets/stylus-BeQkCIfX.js +1 -0
  237. package/dist/web/assets/svelte-MSaWC3Je.js +1 -0
  238. package/dist/web/assets/swift-BSxZ-RaX.js +1 -0
  239. package/dist/web/assets/synthwave-84-CbfX1IO0.js +1 -0
  240. package/dist/web/assets/system-verilog-C7L56vO4.js +1 -0
  241. package/dist/web/assets/systemd-CUnW07Te.js +1 -0
  242. package/dist/web/assets/talonscript-C1XDQQGZ.js +1 -0
  243. package/dist/web/assets/tasl-CQjiPCtT.js +1 -0
  244. package/dist/web/assets/tcl-DQ1-QYvQ.js +1 -0
  245. package/dist/web/assets/templ-dwX3ZSMB.js +1 -0
  246. package/dist/web/assets/terraform-BbSNqyBO.js +1 -0
  247. package/dist/web/assets/tex-rYs2v40G.js +1 -0
  248. package/dist/web/assets/tokyo-night-DBQeEorK.js +1 -0
  249. package/dist/web/assets/toml-CB2ApiWb.js +1 -0
  250. package/dist/web/assets/ts-tags-CipyTH0X.js +1 -0
  251. package/dist/web/assets/tsv-B_m7g4N7.js +1 -0
  252. package/dist/web/assets/tsx-B6W0miNI.js +1 -0
  253. package/dist/web/assets/turtle-BMR_PYu6.js +1 -0
  254. package/dist/web/assets/twig-NC5TFiHP.js +1 -0
  255. package/dist/web/assets/typescript-Dj6nwHGl.js +1 -0
  256. package/dist/web/assets/typespec-BpWG_bgh.js +1 -0
  257. package/dist/web/assets/typst-BVUVsWT6.js +1 -0
  258. package/dist/web/assets/v-CAQ2eGtk.js +1 -0
  259. package/dist/web/assets/vala-BFOHcciG.js +1 -0
  260. package/dist/web/assets/vb-CdO5JTpU.js +1 -0
  261. package/dist/web/assets/verilog-CJaU5se_.js +1 -0
  262. package/dist/web/assets/vesper-BEBZ7ncR.js +1 -0
  263. package/dist/web/assets/vhdl-DYoNaHQp.js +1 -0
  264. package/dist/web/assets/viml-m4uW47V2.js +1 -0
  265. package/dist/web/assets/vitesse-black-Bkuqu6BP.js +1 -0
  266. package/dist/web/assets/vitesse-dark-D0r3Knsf.js +1 -0
  267. package/dist/web/assets/vitesse-light-CVO1_9PV.js +1 -0
  268. package/dist/web/assets/vue-BuYVFjOK.js +1 -0
  269. package/dist/web/assets/vue-html-xdeiXROB.js +1 -0
  270. package/dist/web/assets/vyper-nyqBNV6O.js +1 -0
  271. package/dist/web/assets/wasm-C6j12Q_x.js +1 -0
  272. package/dist/web/assets/wasm-CG6Dc4jp.js +1 -0
  273. package/dist/web/assets/wenyan-7A4Fjokl.js +1 -0
  274. package/dist/web/assets/wgsl-CB0Krxn9.js +1 -0
  275. package/dist/web/assets/wikitext-DCE3LsBG.js +1 -0
  276. package/dist/web/assets/wolfram-C3FkfJm5.js +1 -0
  277. package/dist/web/assets/xml-e3z08dGr.js +1 -0
  278. package/dist/web/assets/xsl-Dd0NUgwM.js +1 -0
  279. package/dist/web/assets/yaml-CVw76BM1.js +1 -0
  280. package/dist/web/assets/zenscript-HnGAYVZD.js +1 -0
  281. package/dist/web/assets/zig-BVz_zdnA.js +1 -0
  282. package/dist/web/index.html +2 -2
  283. package/examples/component-showcase/README.md +29 -0
  284. package/examples/component-showcase/flow.json +40 -0
  285. package/examples/component-showcase/nodes/chart/spec.json +66 -0
  286. package/examples/component-showcase/nodes/counter/spec.json +57 -0
  287. package/examples/component-showcase/nodes/fetcher/actions/refresh.ts +35 -0
  288. package/examples/component-showcase/nodes/fetcher/spec.json +68 -0
  289. package/examples/component-showcase/nodes/form/spec.json +87 -0
  290. package/examples/component-showcase/package.json +6 -0
  291. package/examples/component-showcase/style.json +28 -0
  292. package/examples/ecommerce-platform/flow.json +28 -30
  293. package/examples/order-pipeline/flow.json +16 -16
  294. package/package.json +2 -1
  295. package/src/api.ts +73 -23
  296. package/src/cli-e2e.ts +6 -7
  297. package/src/cli-manifest.ts +3 -5
  298. package/src/cli.ts +27 -0
  299. package/src/component-action-runner.ts +188 -0
  300. package/src/component-spec-resolver.ts +60 -0
  301. package/src/diagram.ts +8 -5
  302. package/src/layout.ts +23 -26
  303. package/src/mcp.ts +2 -2
  304. package/src/merge.ts +8 -3
  305. package/src/node-files.ts +1 -1
  306. package/src/operations.ts +109 -52
  307. package/src/runtime.ts +37 -0
  308. package/src/schema-catalog.ts +23 -11
  309. package/src/schema.ts +256 -262
  310. package/src/server.ts +1 -1
  311. package/src/status-runner.ts +2 -1
  312. package/src/watcher.ts +46 -10
  313. package/dist/web/assets/index-BAEA18IR.js +0 -7838
  314. package/dist/web/assets/index-CwfFCUzZ.css +0 -1
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Dispatches a component node's `script`-kind ComponentAction over HTTP:
3
+ * resolves `scriptPath` under `<cwd>/nodes/<nodeId>/` with the same realpath
4
+ * escape check used by `runPlay` (see proxy.ts), spawns the interpreter via the
5
+ * injectable `ProcessSpawner` seam, pipes the request payload to stdin as JSON,
6
+ * and parses stdout back as JSON (falling back to the raw string).
7
+ *
8
+ * `set`-kind actions are intentionally rejected with statusHint 400: those
9
+ * mutate canvas state locally and never round-trip through the API. The runner
10
+ * is the single seam the API route calls; HTTP status mapping lives in the
11
+ * route handler via `statusHint`.
12
+ */
13
+
14
+ import { realpathSync } from 'node:fs';
15
+ import { join, resolve, sep } from 'node:path';
16
+ import type { EventBus } from './events.ts';
17
+ import { type ProcessSpawner, type SpawnHandle, defaultProcessSpawner } from './process-spawner.ts';
18
+ import type { ComponentAction } from './schema.ts';
19
+ import { shortId } from './short-id.ts';
20
+
21
+ const DEFAULT_TIMEOUT_MS = 5_000;
22
+ const SIGKILL_GRACE_MS = 2_000;
23
+ const SCRIPT_PATH_ESCAPE = 'scriptPath escapes node root';
24
+ const SET_KIND_REJECTION = 'Only script actions are dispatched over HTTP';
25
+
26
+ export interface RunComponentActionOptions {
27
+ events: EventBus;
28
+ flowId: string;
29
+ nodeId: string;
30
+ /** Project root (`<repoPath>`). Script resolves under `<cwd>/nodes/<nodeId>/`. */
31
+ cwd: string;
32
+ actionName: string;
33
+ action: ComponentAction;
34
+ payload: unknown;
35
+ /** Injectable for tests; defaults to `defaultProcessSpawner`. */
36
+ spawner?: ProcessSpawner;
37
+ }
38
+
39
+ export interface ComponentActionResult {
40
+ ok: boolean;
41
+ body?: unknown;
42
+ error?: string;
43
+ /** Suggested HTTP status for the API handler. */
44
+ statusHint: number;
45
+ }
46
+
47
+ type Resolved = { ok: true; absPath: string } | { ok: false };
48
+
49
+ // Resolve `<cwd>/nodes/<nodeId>/<scriptPath>` and verify via realpath it stays
50
+ // inside the node folder. Mirrors proxy.ts:resolveScript — symlink-escape
51
+ // defense in line with `resolveProjectFile` in api.ts.
52
+ function resolveScript(cwd: string, nodeId: string, scriptPath: string): Resolved {
53
+ const nodeRoot = join(cwd, 'nodes', nodeId);
54
+ let realRoot: string;
55
+ try {
56
+ realRoot = realpathSync(nodeRoot);
57
+ } catch {
58
+ return { ok: false };
59
+ }
60
+ const target = resolve(nodeRoot, scriptPath);
61
+ let realTarget: string;
62
+ try {
63
+ realTarget = realpathSync(target);
64
+ } catch {
65
+ return { ok: false };
66
+ }
67
+ const rootWithSep = realRoot.endsWith(sep) ? realRoot : realRoot + sep;
68
+ if (realTarget !== realRoot && !realTarget.startsWith(rootWithSep)) {
69
+ return { ok: false };
70
+ }
71
+ return { ok: true, absPath: realTarget };
72
+ }
73
+
74
+ // Copy `process.env` into a string-only record, then layer the per-run extras.
75
+ // Bun.spawn's env contract is `Record<string, string>` so the undefineds that
76
+ // `process.env` advertises in its type must be filtered out first.
77
+ function buildChildEnv(extra: Record<string, string>): Record<string, string> {
78
+ const env: Record<string, string> = {};
79
+ for (const [k, v] of Object.entries(process.env)) {
80
+ if (typeof v === 'string') env[k] = v;
81
+ }
82
+ return { ...env, ...extra };
83
+ }
84
+
85
+ async function writeStdinPayload(handle: SpawnHandle, payload: unknown): Promise<void> {
86
+ if (!handle.stdin) return;
87
+ const writer = handle.stdin.getWriter();
88
+ try {
89
+ await writer.write(new TextEncoder().encode(JSON.stringify(payload)));
90
+ } finally {
91
+ await writer.close().catch(() => {
92
+ /* stdin already closed by child — not fatal */
93
+ });
94
+ }
95
+ }
96
+
97
+ async function killWithGrace(handle: SpawnHandle): Promise<void> {
98
+ handle.kill('SIGTERM');
99
+ let graceTimer: ReturnType<typeof setTimeout> | undefined;
100
+ const gracePromise = new Promise<'grace'>((res) => {
101
+ graceTimer = setTimeout(() => res('grace'), SIGKILL_GRACE_MS);
102
+ });
103
+ const winner = await Promise.race([handle.exited.then(() => 'exited' as const), gracePromise]);
104
+ if (graceTimer) clearTimeout(graceTimer);
105
+ if (winner === 'grace') {
106
+ handle.kill('SIGKILL');
107
+ await handle.exited;
108
+ }
109
+ }
110
+
111
+ export async function runComponentAction(
112
+ opts: RunComponentActionOptions,
113
+ ): Promise<ComponentActionResult> {
114
+ if (opts.action.kind !== 'script') {
115
+ return { ok: false, error: SET_KIND_REJECTION, statusHint: 400 };
116
+ }
117
+ const spawner = opts.spawner ?? defaultProcessSpawner;
118
+ const resolved = resolveScript(opts.cwd, opts.nodeId, opts.action.scriptPath);
119
+ if (!resolved.ok) {
120
+ return { ok: false, error: SCRIPT_PATH_ESCAPE, statusHint: 400 };
121
+ }
122
+
123
+ const env = buildChildEnv({
124
+ SEEFLOW_DEMO_ID: opts.flowId,
125
+ SEEFLOW_NODE_ID: opts.nodeId,
126
+ SEEFLOW_ACTION_NAME: opts.actionName,
127
+ SEEFLOW_RUN_ID: shortId(),
128
+ });
129
+
130
+ let handle: SpawnHandle;
131
+ try {
132
+ handle = spawner.spawn({
133
+ cmd: [opts.action.interpreter, ...(opts.action.args ?? []), resolved.absPath],
134
+ cwd: opts.cwd,
135
+ env,
136
+ stdin: 'pipe',
137
+ });
138
+ } catch (err) {
139
+ const message = err instanceof Error ? err.message : String(err);
140
+ return { ok: false, error: message, statusHint: 500 };
141
+ }
142
+
143
+ // Drain stdout AND stderr CONCURRENTLY with the process running so OS pipe
144
+ // buffers (~64 KB) don't fill up and deadlock the child.
145
+ const stdoutPromise = new Response(handle.stdout).text();
146
+ const stderrPromise = new Response(handle.stderr).text();
147
+
148
+ // Write stdin and close BEFORE awaiting exit (otherwise a child blocked on
149
+ // `read(stdin)` and a parent blocked on `exited` deadlock each other).
150
+ await writeStdinPayload(handle, opts.payload);
151
+
152
+ const timeoutMs = opts.action.timeoutMs ?? DEFAULT_TIMEOUT_MS;
153
+ let timer: ReturnType<typeof setTimeout> | undefined;
154
+ const timeoutPromise = new Promise<'timeout'>((res) => {
155
+ timer = setTimeout(() => res('timeout'), timeoutMs);
156
+ });
157
+ const exitPromise = handle.exited.then((code) => ({ code }) as const);
158
+
159
+ const race = await Promise.race([exitPromise, timeoutPromise]);
160
+ if (timer) clearTimeout(timer);
161
+
162
+ if (race === 'timeout') {
163
+ await killWithGrace(handle);
164
+ await Promise.allSettled([stdoutPromise, stderrPromise]);
165
+ return {
166
+ ok: false,
167
+ error: `action timed out after ${timeoutMs}ms`,
168
+ statusHint: 504,
169
+ };
170
+ }
171
+
172
+ const [stdout, stderr] = await Promise.all([stdoutPromise, stderrPromise]);
173
+ if (race.code !== 0) {
174
+ return {
175
+ ok: false,
176
+ error: stderr.trim() || `exit ${race.code}`,
177
+ statusHint: 500,
178
+ };
179
+ }
180
+
181
+ let body: unknown;
182
+ try {
183
+ body = JSON.parse(stdout);
184
+ } catch {
185
+ body = stdout;
186
+ }
187
+ return { ok: true, body, statusHint: 200 };
188
+ }
@@ -0,0 +1,60 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import type { ResolvedFlow } from './schema.ts';
4
+
5
+ export interface SpecInlineError {
6
+ /** Logical path into the merged flow shape, like 'nodes/<id>/data/spec'. */
7
+ path: string;
8
+ message: string;
9
+ }
10
+
11
+ export interface InlineComponentSpecsResult {
12
+ flow: ResolvedFlow;
13
+ errors: SpecInlineError[];
14
+ /** Project-root-relative paths the watcher should track for live reload. */
15
+ refs: string[];
16
+ }
17
+
18
+ /**
19
+ * For every `'component'` node in `flow`, read `nodes/<id>/spec.json`,
20
+ * JSON.parse, and attach the result as `data.spec`. Missing files surface
21
+ * as a SpecInlineError; malformed JSON surfaces likewise. Non-component
22
+ * nodes pass through untouched.
23
+ *
24
+ * Returns a NEW flow object (no mutation of the input) so the watcher's
25
+ * snapshot caching stays safe.
26
+ */
27
+ export function inlineComponentSpecs(
28
+ flow: ResolvedFlow,
29
+ projectRoot: string,
30
+ ): InlineComponentSpecsResult {
31
+ const errors: SpecInlineError[] = [];
32
+ const refs: string[] = [];
33
+
34
+ const nodes = flow.nodes.map((node) => {
35
+ if (node.type !== 'component') return node;
36
+ const relPath = `nodes/${node.id}/spec.json`;
37
+ const absPath = join(projectRoot, relPath);
38
+ if (!existsSync(absPath)) {
39
+ errors.push({
40
+ path: `nodes/${node.id}/data/spec`,
41
+ message: `Missing spec file: ${relPath}`,
42
+ });
43
+ return node;
44
+ }
45
+ let parsed: unknown;
46
+ try {
47
+ parsed = JSON.parse(readFileSync(absPath, 'utf8'));
48
+ } catch (err) {
49
+ errors.push({
50
+ path: `nodes/${node.id}/data/spec`,
51
+ message: `Invalid JSON in ${relPath}: ${err instanceof Error ? err.message : String(err)}`,
52
+ });
53
+ return node;
54
+ }
55
+ refs.push(relPath);
56
+ return { ...node, data: { ...node.data, spec: parsed } } as typeof node;
57
+ });
58
+
59
+ return { flow: { ...flow, nodes } as ResolvedFlow, errors, refs };
60
+ }
package/src/diagram.ts CHANGED
@@ -240,9 +240,10 @@ const normalizeConnectors = (
240
240
  // Single-flow-node graphs short-circuit so callers can pin standalone
241
241
  // nodes via `layout.positions`.
242
242
  const isFloatingAnnotation = (n: Record<string, unknown>): boolean => {
243
- if (n.type !== 'shapeNode') return false;
244
- const shape = (n.data as { shape?: string } | undefined)?.shape;
245
- return shape === 'sticky' || shape === 'text';
243
+ // Flat-types refactor: visual kind IS the type. The annotation shapes
244
+ // (sticky + text) live as top-level type tags rather than nested shape
245
+ // under a generic shape variant.
246
+ return n.type === 'sticky' || n.type === 'text';
246
247
  };
247
248
 
248
249
  const autoLayout = async (
@@ -354,12 +355,14 @@ export const validateDemo = (req: ValidateRequest): ValidateReport => {
354
355
 
355
356
  const playable = nodes.filter((n) => {
356
357
  const data = n.data as { playAction?: unknown } | undefined;
357
- return n.type === 'playNode' || (n.type === 'stateNode' && data?.playAction !== undefined);
358
+ // Flat-types refactor: a node is playable iff it carries a playAction
359
+ // capability, regardless of variant.
360
+ return data?.playAction !== undefined;
358
361
  });
359
362
  if (tier !== 'static' && playable.length === 0) {
360
363
  issues.push({
361
364
  kind: 'tier-mismatch',
362
- message: `Tier '${tier}' requires at least one playable node; found 0. Either add a playNode or set tier=static.`,
365
+ message: `Tier '${tier}' requires at least one playable node; found 0. Set data.playAction on a node or set tier=static.`,
363
366
  });
364
367
  }
365
368
 
package/src/layout.ts CHANGED
@@ -51,7 +51,7 @@ export interface LayoutResult {
51
51
  export interface LayoutNode {
52
52
  id: string;
53
53
  type: FlowNode['type'];
54
- data?: { width?: number; height?: number; shape?: string };
54
+ data?: { width?: number; height?: number };
55
55
  }
56
56
 
57
57
  export interface LayoutEdge {
@@ -60,42 +60,39 @@ export interface LayoutEdge {
60
60
  target: string;
61
61
  }
62
62
 
63
+ // Per-type default size used when a node has no explicit width/height. Mirrors
64
+ // the canvas's SHAPE_DEFAULT_SIZE so ELK's layout matches what the canvas will
65
+ // paint.
63
66
  const DEFAULT_DIMENSIONS: Record<FlowNode['type'], { width: number; height: number }> = {
64
- playNode: { width: 220, height: 100 },
65
- stateNode: { width: 220, height: 100 },
66
- shapeNode: { width: 160, height: 80 },
67
- iconNode: { width: 80, height: 80 },
68
- htmlNode: { width: 320, height: 200 },
69
- imageNode: { width: 200, height: 150 },
70
- };
71
-
72
- const SHAPE_OVERRIDES: Record<string, { width: number; height: number }> = {
67
+ rectangle: { width: 200, height: 120 },
68
+ ellipse: { width: 200, height: 120 },
69
+ sticky: { width: 180, height: 180 },
73
70
  text: { width: 160, height: 40 },
74
- sticky: { width: 160, height: 180 },
71
+ database: { width: 120, height: 140 },
72
+ server: { width: 140, height: 120 },
73
+ user: { width: 100, height: 140 },
74
+ queue: { width: 220, height: 80 },
75
+ cloud: { width: 180, height: 120 },
76
+ image: { width: 200, height: 150 },
77
+ html: { width: 320, height: 200 },
78
+ icon: { width: 80, height: 80 },
79
+ component: { width: 320, height: 240 },
75
80
  };
76
81
 
77
- const FLOATING_SHAPES = new Set(['sticky', 'text']);
82
+ // Sticky / text variants are floating annotations. They never participate in
83
+ // layered layout — they sit in a side column so the orthogonal flow stays
84
+ // clean.
85
+ const FLOATING_TYPES: ReadonlySet<FlowNode['type']> = new Set(['sticky', 'text']);
78
86
 
79
87
  const nodeDimensions = (node: LayoutNode): { width: number; height: number } => {
80
88
  const data = node.data ?? {};
81
89
  if (typeof data.width === 'number' && typeof data.height === 'number') {
82
90
  return { width: data.width, height: data.height };
83
91
  }
84
- if (node.type === 'shapeNode' && data.shape) {
85
- const override = SHAPE_OVERRIDES[data.shape];
86
- if (override) return override;
87
- }
88
92
  return DEFAULT_DIMENSIONS[node.type];
89
93
  };
90
94
 
91
- // Sticky / text shapes are floating annotations. They never participate in
92
- // layered layout — they sit in a side column so the orthogonal flow stays
93
- // clean.
94
- const isFloatingAnnotation = (node: LayoutNode): boolean => {
95
- if (node.type !== 'shapeNode') return false;
96
- const shape = node.data?.shape;
97
- return shape !== undefined && FLOATING_SHAPES.has(shape);
98
- };
95
+ const isFloatingAnnotation = (node: LayoutNode): boolean => FLOATING_TYPES.has(node.type);
99
96
 
100
97
  // Schema vocabulary: SourceHandle ∈ {r, b}, TargetHandle ∈ {t, l}. After
101
98
  // ELK lays out positions we pick handles geometrically — the layered LR
@@ -162,7 +159,7 @@ export const computeLayout = async (
162
159
  'elk.separateConnectedComponents': 'true',
163
160
  },
164
161
  children: laidOut.map((n) => {
165
- const d = dims.get(n.id) ?? DEFAULT_DIMENSIONS.playNode;
162
+ const d = dims.get(n.id) ?? DEFAULT_DIMENSIONS.rectangle;
166
163
  return { id: n.id, width: d.width, height: d.height };
167
164
  }),
168
165
  edges: connectors
@@ -193,7 +190,7 @@ export const computeLayout = async (
193
190
  const columnX = laidOut.length > 0 ? maxRight + 200 : 0;
194
191
  let cursorY = 0;
195
192
  for (const n of floatingNodes) {
196
- const d = dims.get(n.id) ?? DEFAULT_DIMENSIONS.shapeNode;
193
+ const d = dims.get(n.id) ?? DEFAULT_DIMENSIONS.rectangle;
197
194
  result.nodes[n.id] = { position: { x: columnX, y: cursorY } };
198
195
  cursorY += d.height + nodeSpacing;
199
196
  }
package/src/mcp.ts CHANGED
@@ -461,7 +461,7 @@ const buildTools = (ops: Operations): McpTool[] => [
461
461
  {
462
462
  name: 'seeflow_add_node',
463
463
  description:
464
- 'Append a new node to a flow (cascade-safe; id auto-generated when omitted). Text content fields (detail on every node; html on htmlNode) are auto-externalized to <project>/nodes/<id>/ and stored as file:// refs in flow.json; reads inline the resolved content transparently.',
464
+ "Append a new node to a flow (cascade-safe; id auto-generated when omitted). Text content fields (detail on every node; html on type:'html') are auto-externalized to <project>/nodes/<id>/ and stored as file:// refs in flow.json; reads inline the resolved content transparently.",
465
465
  inputSchema: inputSchemaFromZod(AddNodeInputSchema),
466
466
  handler: async (args) => {
467
467
  const parsed = AddNodeInputSchema.safeParse(args);
@@ -585,7 +585,7 @@ const buildTools = (ops: Operations): McpTool[] => [
585
585
  {
586
586
  name: 'seeflow_patch_node',
587
587
  description:
588
- 'Update fields on an existing node (position, name, description, detail, icon, colors, border, font, shape, dimensions, autoSize, plus iconNode-only color/strokeWidth/alt). Setting detail (every node) or html (htmlNode) writes the content to <project>/nodes/<id>/{detail.md|view.html}; the file:// ref on the node persists. Empty-string detail empties the file but keeps the ref.',
588
+ "Update fields on an existing node (position, name, description, detail, icon, colors, border, font, dimensions, autoSize, plus type:'icon'-only color/strokeWidth/alt and capabilities playAction/statusAction/stateSource). `type` can flip a node between any of the 12 visual variants (rectangle/ellipse/sticky/text/database/server/user/queue/cloud/image/html/icon); the post-merge schema reparse gates required fields on the new type (image.data.path, icon.data.icon). Setting detail (every node) or html (type:'html') writes the content to <project>/nodes/<id>/{detail.md|view.html}; the file:// ref on the node persists. Empty-string detail empties the file but keeps the ref.",
589
589
  inputSchema: inputSchemaFromZod(PatchNodeInputSchema),
590
590
  handler: async (args) => {
591
591
  const parsed = PatchNodeInputSchema.safeParse(args);
package/src/merge.ts CHANGED
@@ -39,10 +39,12 @@ export function mergeFlowAndStyle(flow: Flow, style: Style): ResolvedFlow {
39
39
  }
40
40
 
41
41
  // Fields that live in a node's `data` block on flow.json. Every other data
42
- // field is visual and routes to style.json.
42
+ // field is visual and routes to style.json. The flat-types refactor folds
43
+ // the visual kind into `node.type` itself (no more nested data.shape / data.kind)
44
+ // and makes every capability (playAction / statusAction / stateSource) valid
45
+ // on every type.
43
46
  const NODE_DATA_FLOW_KEYS = new Set([
44
47
  'name',
45
- 'kind',
46
48
  'stateSource',
47
49
  'handlerModule',
48
50
  'icon',
@@ -50,7 +52,6 @@ const NODE_DATA_FLOW_KEYS = new Set([
50
52
  'detail',
51
53
  'playAction',
52
54
  'statusAction',
53
- 'shape',
54
55
  'path',
55
56
  'alt',
56
57
  'html',
@@ -132,6 +133,10 @@ export function splitFlow(resolved: {
132
133
  const flowData: Record<string, unknown> = {};
133
134
  for (const [k, v] of Object.entries(data)) {
134
135
  if (v === undefined) continue;
136
+ // Component nodes externalize `spec` to <project>/nodes/<id>/spec.json
137
+ // (the sidecar). Drop it from flow.json so the strict on-disk schema
138
+ // doesn't reject it and the spec stays single-sourced on disk.
139
+ if (node.type === 'component' && k === 'spec') continue;
135
140
  if (NODE_DATA_FLOW_KEYS.has(k)) {
136
141
  flowData[k] = v;
137
142
  } else if (NODE_STYLE_KEYS.has(k)) {
package/src/node-files.ts CHANGED
@@ -14,7 +14,7 @@ export interface ExternalizedFieldSpec {
14
14
 
15
15
  export const EXTERNALIZED_NODE_FIELDS: readonly ExternalizedFieldSpec[] = [
16
16
  { field: 'detail', fileName: 'detail.md' },
17
- { field: 'html', fileName: 'view.html', nodeTypes: ['htmlNode'] },
17
+ { field: 'html', fileName: 'view.html', nodeTypes: ['html'] },
18
18
  ];
19
19
 
20
20
  export const externalizedFieldsForNodeType = (