@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.
- package/README.md +4 -0
- package/dist/web/assets/abap-DsBKuouk.js +1 -0
- package/dist/web/assets/actionscript-3-D_z4Izcz.js +1 -0
- package/dist/web/assets/ada-727ZlQH0.js +1 -0
- package/dist/web/assets/andromeeda-C3khCPGq.js +1 -0
- package/dist/web/assets/angular-html-4alyEGLm.js +1 -0
- package/dist/web/assets/angular-ts-BixEUTMq.js +1 -0
- package/dist/web/assets/apache-Dn00JSTd.js +1 -0
- package/dist/web/assets/apex-COJ4H7py.js +1 -0
- package/dist/web/assets/apl-BBq3IX1j.js +1 -0
- package/dist/web/assets/applescript-Bu5BbsvL.js +1 -0
- package/dist/web/assets/ara-7O62HKoU.js +1 -0
- package/dist/web/assets/asciidoc-BPT9niGB.js +1 -0
- package/dist/web/assets/asm-Dhn9LcZ4.js +1 -0
- package/dist/web/assets/astro-CqkE3fuf.js +1 -0
- package/dist/web/assets/aurora-x-D-2ljcwZ.js +1 -0
- package/dist/web/assets/awk-eg146-Ew.js +1 -0
- package/dist/web/assets/ayu-dark-Cv9koXgw.js +1 -0
- package/dist/web/assets/ballerina-Du268qiB.js +1 -0
- package/dist/web/assets/bat-fje9CFhw.js +1 -0
- package/dist/web/assets/beancount-BwXTMy5W.js +1 -0
- package/dist/web/assets/berry-3xVqZejG.js +1 -0
- package/dist/web/assets/bibtex-xW4inM5L.js +1 -0
- package/dist/web/assets/bicep-DHo0CJ0O.js +1 -0
- package/dist/web/assets/blade-a8OxSdnT.js +1 -0
- package/dist/web/assets/bsl-Dgyn0ogV.js +1 -0
- package/dist/web/assets/c-C3t2pwGQ.js +1 -0
- package/dist/web/assets/cadence-DNquZEk8.js +1 -0
- package/dist/web/assets/cairo--RitsXJZ.js +1 -0
- package/dist/web/assets/catppuccin-frappe-CD_QflpE.js +1 -0
- package/dist/web/assets/catppuccin-latte-DRW-0cLl.js +1 -0
- package/dist/web/assets/catppuccin-macchiato-C-_shW-Y.js +1 -0
- package/dist/web/assets/catppuccin-mocha-LGGdnPYs.js +1 -0
- package/dist/web/assets/chart-8DxAnLoD.js +73 -0
- package/dist/web/assets/clarity-BHOwM8T6.js +1 -0
- package/dist/web/assets/clojure-DxSadP1t.js +1 -0
- package/dist/web/assets/cmake-DbXoA79R.js +1 -0
- package/dist/web/assets/cobol-PTqiYgYu.js +1 -0
- package/dist/web/assets/code-block-cx7LPXvE.js +13 -0
- package/dist/web/assets/codeowners-Bp6g37R7.js +1 -0
- package/dist/web/assets/codeql-sacFqUAJ.js +1 -0
- package/dist/web/assets/coffee-dyiR41kL.js +1 -0
- package/dist/web/assets/common-lisp-C7gG9l05.js +1 -0
- package/dist/web/assets/coq-Dsg_Bt_b.js +1 -0
- package/dist/web/assets/cpp-BksuvNSY.js +1 -0
- package/dist/web/assets/crystal-DtDmRg-F.js +1 -0
- package/dist/web/assets/csharp-D9R-vmeu.js +1 -0
- package/dist/web/assets/css-BPhBrDlE.js +1 -0
- package/dist/web/assets/csv-B0qRVHPH.js +1 -0
- package/dist/web/assets/cue-DtFQj3wx.js +1 -0
- package/dist/web/assets/cypher-m2LEI-9-.js +1 -0
- package/dist/web/assets/d-BoXegm-a.js +1 -0
- package/dist/web/assets/dark-plus-C3mMm8J8.js +1 -0
- package/dist/web/assets/dart-B9wLZaAG.js +1 -0
- package/dist/web/assets/dax-ClGRhx96.js +1 -0
- package/dist/web/assets/desktop-DEIpsLCJ.js +1 -0
- package/dist/web/assets/diff-BgYniUM_.js +1 -0
- package/dist/web/assets/docker-COcR7UxN.js +1 -0
- package/dist/web/assets/dotenv-BjQB5zDj.js +1 -0
- package/dist/web/assets/dracula-BzJJZx-M.js +1 -0
- package/dist/web/assets/dracula-soft-BXkSAIEj.js +1 -0
- package/dist/web/assets/dream-maker-C-nORZOA.js +1 -0
- package/dist/web/assets/edge-D5gP-w-T.js +1 -0
- package/dist/web/assets/elixir-CLiX3zqd.js +1 -0
- package/dist/web/assets/elm-CmHSxxaM.js +1 -0
- package/dist/web/assets/emacs-lisp-BX77sIaO.js +1 -0
- package/dist/web/assets/erb-BYTLMnw6.js +1 -0
- package/dist/web/assets/erlang-B-DoSBHF.js +1 -0
- package/dist/web/assets/everforest-dark-BgDCqdQA.js +1 -0
- package/dist/web/assets/everforest-light-C8M2exoo.js +1 -0
- package/dist/web/assets/fennel-bCA53EVm.js +1 -0
- package/dist/web/assets/fish-w-ucz2PV.js +1 -0
- package/dist/web/assets/fluent-Dayu4EKP.js +1 -0
- package/dist/web/assets/fortran-fixed-form-TqA4NnZg.js +1 -0
- package/dist/web/assets/fortran-free-form-DKXYxT9g.js +1 -0
- package/dist/web/assets/fsharp-XplgxFYe.js +1 -0
- package/dist/web/assets/gdresource-BHYsBjWJ.js +1 -0
- package/dist/web/assets/gdscript-DfxzS6Rs.js +1 -0
- package/dist/web/assets/gdshader-SKMF96pI.js +1 -0
- package/dist/web/assets/genie-ajMbGru0.js +1 -0
- package/dist/web/assets/gherkin--30QC5Em.js +1 -0
- package/dist/web/assets/git-commit-i4q6IMui.js +1 -0
- package/dist/web/assets/git-rebase-B-v9cOL2.js +1 -0
- package/dist/web/assets/github-dark-DHJKELXO.js +1 -0
- package/dist/web/assets/github-dark-default-Cuk6v7N8.js +1 -0
- package/dist/web/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- package/dist/web/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- package/dist/web/assets/github-light-DAi9KRSo.js +1 -0
- package/dist/web/assets/github-light-default-D7oLnXFd.js +1 -0
- package/dist/web/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- package/dist/web/assets/gleam-B430Bg39.js +1 -0
- package/dist/web/assets/glimmer-js-D-cwc0-E.js +1 -0
- package/dist/web/assets/glimmer-ts-pgjy16dm.js +1 -0
- package/dist/web/assets/glsl-DBO2IWDn.js +1 -0
- package/dist/web/assets/gnuplot-CM8KxXT1.js +1 -0
- package/dist/web/assets/go-B1SYOhNW.js +1 -0
- package/dist/web/assets/graphql-cDcHW_If.js +1 -0
- package/dist/web/assets/groovy-DkBy-JyN.js +1 -0
- package/dist/web/assets/hack-D1yCygmZ.js +1 -0
- package/dist/web/assets/haml-B2EZWmdv.js +1 -0
- package/dist/web/assets/handlebars-BQGss363.js +1 -0
- package/dist/web/assets/haskell-BILxekzW.js +1 -0
- package/dist/web/assets/haxe-C5wWYbrZ.js +1 -0
- package/dist/web/assets/hcl-HzYwdGDm.js +1 -0
- package/dist/web/assets/hjson-T-Tgc4AT.js +1 -0
- package/dist/web/assets/hlsl-ifBTmRxC.js +1 -0
- package/dist/web/assets/houston-DnULxvSX.js +1 -0
- package/dist/web/assets/html-C2L_23MC.js +1 -0
- package/dist/web/assets/html-derivative-CSfWNPLT.js +1 -0
- package/dist/web/assets/http-FRrOvY1W.js +1 -0
- package/dist/web/assets/hxml-TIA70rKU.js +1 -0
- package/dist/web/assets/hy-BMj5Y0dO.js +1 -0
- package/dist/web/assets/imba-bv_oIlVt.js +1 -0
- package/dist/web/assets/index-CeLShda7.css +1 -0
- package/dist/web/assets/index-Dp4QwEl0.js +8608 -0
- package/dist/web/assets/{index.es-DZEdTXNJ.js → index.es-C-uXEdZB.js} +1 -1
- package/dist/web/assets/ini-BjABl1g7.js +1 -0
- package/dist/web/assets/java-xI-RfyKK.js +1 -0
- package/dist/web/assets/javascript-ySlJ1b_l.js +1 -0
- package/dist/web/assets/jinja-DGy0s7-h.js +1 -0
- package/dist/web/assets/jison-BqZprYcd.js +1 -0
- package/dist/web/assets/json-BQoSv7ci.js +1 -0
- package/dist/web/assets/json5-w8dY5SsB.js +1 -0
- package/dist/web/assets/jsonc-TU54ms6u.js +1 -0
- package/dist/web/assets/jsonl-DREVFZK8.js +1 -0
- package/dist/web/assets/jsonnet-BfivnA6A.js +1 -0
- package/dist/web/assets/{jspdf.es.min-DT1Li8zz.js → jspdf.es.min-BFQufOcQ.js} +3 -3
- package/dist/web/assets/jssm-P4WzXJd0.js +1 -0
- package/dist/web/assets/jsx-BAng5TT0.js +1 -0
- package/dist/web/assets/julia-BBuGR-5E.js +1 -0
- package/dist/web/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- package/dist/web/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- package/dist/web/assets/kanagawa-wave-DWedfzmr.js +1 -0
- package/dist/web/assets/kotlin-B5lbUyaz.js +1 -0
- package/dist/web/assets/kusto-mebxcVVE.js +1 -0
- package/dist/web/assets/laserwave-DUszq2jm.js +1 -0
- package/dist/web/assets/latex-C-cWTeAZ.js +1 -0
- package/dist/web/assets/lean-XBlWyCtg.js +1 -0
- package/dist/web/assets/less-BfCpw3nA.js +1 -0
- package/dist/web/assets/light-plus-B7mTdjB0.js +1 -0
- package/dist/web/assets/liquid-D3W5UaiH.js +1 -0
- package/dist/web/assets/log-Cc5clBb7.js +1 -0
- package/dist/web/assets/logo-IuBKFhSY.js +1 -0
- package/dist/web/assets/lua-CvWAzNxB.js +1 -0
- package/dist/web/assets/luau-Du5NY7AG.js +1 -0
- package/dist/web/assets/make-Bvotw-X0.js +1 -0
- package/dist/web/assets/markdown-DK_1WFMa.js +1 -0
- package/dist/web/assets/markdown-UIAJJxZW.js +1 -0
- package/dist/web/assets/marko-z0MBrx5-.js +1 -0
- package/dist/web/assets/material-theme-D5KoaKCx.js +1 -0
- package/dist/web/assets/material-theme-darker-BfHTSMKl.js +1 -0
- package/dist/web/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- package/dist/web/assets/material-theme-ocean-CyktbL80.js +1 -0
- package/dist/web/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- package/dist/web/assets/matlab-D9-PGadD.js +1 -0
- package/dist/web/assets/mdc-DB_EDNY_.js +1 -0
- package/dist/web/assets/mdx-sdHcTMYB.js +1 -0
- package/dist/web/assets/mermaid-Ci6OQyBP.js +1 -0
- package/dist/web/assets/min-dark-CafNBF8u.js +1 -0
- package/dist/web/assets/min-light-CTRr51gU.js +1 -0
- package/dist/web/assets/mipsasm-BC5c_5Pe.js +1 -0
- package/dist/web/assets/mojo-Tz6hzZYG.js +1 -0
- package/dist/web/assets/monokai-D4h5O-jR.js +1 -0
- package/dist/web/assets/move-DB_GagMm.js +1 -0
- package/dist/web/assets/narrat-DLbgOhZU.js +1 -0
- package/dist/web/assets/nextflow-B0XVJmRM.js +1 -0
- package/dist/web/assets/nginx-D_VnBJ67.js +1 -0
- package/dist/web/assets/night-owl-C39BiMTA.js +1 -0
- package/dist/web/assets/nim-ZlGxZxc3.js +1 -0
- package/dist/web/assets/nix-shcSOmrb.js +1 -0
- package/dist/web/assets/nord-Ddv68eIx.js +1 -0
- package/dist/web/assets/nushell-D4Tzg5kh.js +1 -0
- package/dist/web/assets/objective-c-Deuh7S70.js +1 -0
- package/dist/web/assets/objective-cpp-BUEGK8hf.js +1 -0
- package/dist/web/assets/ocaml-BNioltXt.js +1 -0
- package/dist/web/assets/one-dark-pro-GBQ2dnAY.js +1 -0
- package/dist/web/assets/one-light-PoHY5YXO.js +1 -0
- package/dist/web/assets/pascal-JqZropPD.js +1 -0
- package/dist/web/assets/perl-CHQXSrWU.js +1 -0
- package/dist/web/assets/php-B5ebYQev.js +1 -0
- package/dist/web/assets/plastic-3e1v2bzS.js +1 -0
- package/dist/web/assets/plsql-LKU2TuZ1.js +1 -0
- package/dist/web/assets/po-BFLt1xDp.js +1 -0
- package/dist/web/assets/poimandres-CS3Unz2-.js +1 -0
- package/dist/web/assets/polar-DKykz6zU.js +1 -0
- package/dist/web/assets/postcss-B3ZDOciz.js +1 -0
- package/dist/web/assets/powerquery-CSHBycmS.js +1 -0
- package/dist/web/assets/powershell-BIEUsx6d.js +1 -0
- package/dist/web/assets/prisma-B48N-Iqd.js +1 -0
- package/dist/web/assets/prolog-BY-TUvya.js +1 -0
- package/dist/web/assets/proto-zocC4JxJ.js +1 -0
- package/dist/web/assets/pug-CM9l7STV.js +1 -0
- package/dist/web/assets/puppet-Cza_XSSt.js +1 -0
- package/dist/web/assets/purescript-Bg-kzb6g.js +1 -0
- package/dist/web/assets/python-DhUJRlN_.js +1 -0
- package/dist/web/assets/qml-D8XfuvdV.js +1 -0
- package/dist/web/assets/qmldir-C8lEn-DE.js +1 -0
- package/dist/web/assets/qss-DhMKtDLN.js +1 -0
- package/dist/web/assets/r-CwjWoCRV.js +1 -0
- package/dist/web/assets/racket-CzouJOBO.js +1 -0
- package/dist/web/assets/raku-B1bQXN8T.js +1 -0
- package/dist/web/assets/razor-CNLDkMZG.js +1 -0
- package/dist/web/assets/red-bN70gL4F.js +1 -0
- package/dist/web/assets/reg-5LuOXUq_.js +1 -0
- package/dist/web/assets/regexp-DWJ3fJO_.js +1 -0
- package/dist/web/assets/rel-DJlmqQ1C.js +1 -0
- package/dist/web/assets/riscv-QhoSD0DR.js +1 -0
- package/dist/web/assets/rose-pine-CmCqftbK.js +1 -0
- package/dist/web/assets/rose-pine-dawn-Ds-gbosJ.js +1 -0
- package/dist/web/assets/rose-pine-moon-CjDtw9vr.js +1 -0
- package/dist/web/assets/rst-4NLicBqY.js +1 -0
- package/dist/web/assets/ruby-DeZ3UC14.js +1 -0
- package/dist/web/assets/rust-Be6lgOlo.js +1 -0
- package/dist/web/assets/sas-BmTFh92c.js +1 -0
- package/dist/web/assets/sass-BJ4Li9vH.js +1 -0
- package/dist/web/assets/scala-DQVVAn-B.js +1 -0
- package/dist/web/assets/scheme-BJGe-b2p.js +1 -0
- package/dist/web/assets/scss-C31hgJw-.js +1 -0
- package/dist/web/assets/sdbl-BLhTXw86.js +1 -0
- package/dist/web/assets/shaderlab-B7qAK45m.js +1 -0
- package/dist/web/assets/shellscript-atvbtKCR.js +1 -0
- package/dist/web/assets/shellsession-C_rIy8kc.js +1 -0
- package/dist/web/assets/slack-dark-BthQWCQV.js +1 -0
- package/dist/web/assets/slack-ochin-DqwNpetd.js +1 -0
- package/dist/web/assets/smalltalk-DkLiglaE.js +1 -0
- package/dist/web/assets/snazzy-light-Bw305WKR.js +1 -0
- package/dist/web/assets/solarized-dark-DXbdFlpD.js +1 -0
- package/dist/web/assets/solarized-light-L9t79GZl.js +1 -0
- package/dist/web/assets/solidity-C1w2a3ep.js +1 -0
- package/dist/web/assets/soy-C-lX7w71.js +1 -0
- package/dist/web/assets/sparql-bYkjHRlG.js +1 -0
- package/dist/web/assets/splunk-Cf8iN4DR.js +1 -0
- package/dist/web/assets/sql-COK4E0Yg.js +1 -0
- package/dist/web/assets/ssh-config-BknIz3MU.js +1 -0
- package/dist/web/assets/stata-DorPZHa4.js +1 -0
- package/dist/web/assets/stylus-BeQkCIfX.js +1 -0
- package/dist/web/assets/svelte-MSaWC3Je.js +1 -0
- package/dist/web/assets/swift-BSxZ-RaX.js +1 -0
- package/dist/web/assets/synthwave-84-CbfX1IO0.js +1 -0
- package/dist/web/assets/system-verilog-C7L56vO4.js +1 -0
- package/dist/web/assets/systemd-CUnW07Te.js +1 -0
- package/dist/web/assets/talonscript-C1XDQQGZ.js +1 -0
- package/dist/web/assets/tasl-CQjiPCtT.js +1 -0
- package/dist/web/assets/tcl-DQ1-QYvQ.js +1 -0
- package/dist/web/assets/templ-dwX3ZSMB.js +1 -0
- package/dist/web/assets/terraform-BbSNqyBO.js +1 -0
- package/dist/web/assets/tex-rYs2v40G.js +1 -0
- package/dist/web/assets/tokyo-night-DBQeEorK.js +1 -0
- package/dist/web/assets/toml-CB2ApiWb.js +1 -0
- package/dist/web/assets/ts-tags-CipyTH0X.js +1 -0
- package/dist/web/assets/tsv-B_m7g4N7.js +1 -0
- package/dist/web/assets/tsx-B6W0miNI.js +1 -0
- package/dist/web/assets/turtle-BMR_PYu6.js +1 -0
- package/dist/web/assets/twig-NC5TFiHP.js +1 -0
- package/dist/web/assets/typescript-Dj6nwHGl.js +1 -0
- package/dist/web/assets/typespec-BpWG_bgh.js +1 -0
- package/dist/web/assets/typst-BVUVsWT6.js +1 -0
- package/dist/web/assets/v-CAQ2eGtk.js +1 -0
- package/dist/web/assets/vala-BFOHcciG.js +1 -0
- package/dist/web/assets/vb-CdO5JTpU.js +1 -0
- package/dist/web/assets/verilog-CJaU5se_.js +1 -0
- package/dist/web/assets/vesper-BEBZ7ncR.js +1 -0
- package/dist/web/assets/vhdl-DYoNaHQp.js +1 -0
- package/dist/web/assets/viml-m4uW47V2.js +1 -0
- package/dist/web/assets/vitesse-black-Bkuqu6BP.js +1 -0
- package/dist/web/assets/vitesse-dark-D0r3Knsf.js +1 -0
- package/dist/web/assets/vitesse-light-CVO1_9PV.js +1 -0
- package/dist/web/assets/vue-BuYVFjOK.js +1 -0
- package/dist/web/assets/vue-html-xdeiXROB.js +1 -0
- package/dist/web/assets/vyper-nyqBNV6O.js +1 -0
- package/dist/web/assets/wasm-C6j12Q_x.js +1 -0
- package/dist/web/assets/wasm-CG6Dc4jp.js +1 -0
- package/dist/web/assets/wenyan-7A4Fjokl.js +1 -0
- package/dist/web/assets/wgsl-CB0Krxn9.js +1 -0
- package/dist/web/assets/wikitext-DCE3LsBG.js +1 -0
- package/dist/web/assets/wolfram-C3FkfJm5.js +1 -0
- package/dist/web/assets/xml-e3z08dGr.js +1 -0
- package/dist/web/assets/xsl-Dd0NUgwM.js +1 -0
- package/dist/web/assets/yaml-CVw76BM1.js +1 -0
- package/dist/web/assets/zenscript-HnGAYVZD.js +1 -0
- package/dist/web/assets/zig-BVz_zdnA.js +1 -0
- package/dist/web/index.html +2 -2
- package/examples/component-showcase/README.md +29 -0
- package/examples/component-showcase/flow.json +40 -0
- package/examples/component-showcase/nodes/chart/spec.json +66 -0
- package/examples/component-showcase/nodes/counter/spec.json +57 -0
- package/examples/component-showcase/nodes/fetcher/actions/refresh.ts +35 -0
- package/examples/component-showcase/nodes/fetcher/spec.json +68 -0
- package/examples/component-showcase/nodes/form/spec.json +87 -0
- package/examples/component-showcase/package.json +6 -0
- package/examples/component-showcase/style.json +28 -0
- package/examples/ecommerce-platform/flow.json +28 -30
- package/examples/order-pipeline/flow.json +16 -16
- package/package.json +2 -1
- package/src/api.ts +73 -23
- package/src/cli-e2e.ts +6 -7
- package/src/cli-manifest.ts +3 -5
- package/src/cli.ts +27 -0
- package/src/component-action-runner.ts +188 -0
- package/src/component-spec-resolver.ts +60 -0
- package/src/diagram.ts +8 -5
- package/src/layout.ts +23 -26
- package/src/mcp.ts +2 -2
- package/src/merge.ts +8 -3
- package/src/node-files.ts +1 -1
- package/src/operations.ts +109 -52
- package/src/runtime.ts +37 -0
- package/src/schema-catalog.ts +23 -11
- package/src/schema.ts +256 -262
- package/src/server.ts +1 -1
- package/src/status-runner.ts +2 -1
- package/src/watcher.ts +46 -10
- package/dist/web/assets/index-BAEA18IR.js +0 -7838
- package/dist/web/assets/index-CwfFCUzZ.css +0 -1
package/src/schema.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { COMPONENT_NAMES, componentCatalog } from '@seeflow/canvas/catalog';
|
|
1
2
|
import { z } from 'zod';
|
|
2
3
|
|
|
3
4
|
const PositionSchema = z.object({
|
|
@@ -5,11 +6,8 @@ const PositionSchema = z.object({
|
|
|
5
6
|
y: z.number(),
|
|
6
7
|
});
|
|
7
8
|
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
// US-008 cut resetAction the same way, no schema in this file uses HttpAction
|
|
11
|
-
// anymore. `HttpMethodSchema` is still used for the optional `method` field on
|
|
12
|
-
// connectors (documentation metadata).
|
|
9
|
+
// `HttpMethodSchema` is documentation metadata on connectors. No node schema
|
|
10
|
+
// uses it — PlayAction/StatusAction are script-based.
|
|
13
11
|
const HttpMethodSchema = z.enum(['GET', 'POST', 'PUT', 'PATCH', 'DELETE']);
|
|
14
12
|
|
|
15
13
|
// Curated palette tokens. Stored on disk as readable names; the frontend maps
|
|
@@ -26,7 +24,8 @@ export const ColorTokenSchema = z.enum([
|
|
|
26
24
|
]);
|
|
27
25
|
|
|
28
26
|
// Visual fields shared by every node type (functional + decorative). All
|
|
29
|
-
// optional —
|
|
27
|
+
// optional — every visual must work without per-field opinions. Live on
|
|
28
|
+
// resolved nodes; the disk-side flow.json strips them into style.json.
|
|
30
29
|
const NodeVisualBaseShape = {
|
|
31
30
|
width: z.number().positive().optional(),
|
|
32
31
|
height: z.number().positive().optional(),
|
|
@@ -39,19 +38,20 @@ const NodeVisualBaseShape = {
|
|
|
39
38
|
cornerRadius: z.number().min(0).optional(),
|
|
40
39
|
};
|
|
41
40
|
|
|
42
|
-
//
|
|
43
|
-
//
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
// spread NodeVisualBaseShape.
|
|
48
|
-
const NodeDescriptionBaseShape = {
|
|
41
|
+
// Semantic-data fields shared by every node type. `name` is optional —
|
|
42
|
+
// every visual works without a label. `icon` is decorative-by-default; the
|
|
43
|
+
// `type:'icon'` variant overrides it to required.
|
|
44
|
+
const NodeSemanticBaseShape = {
|
|
45
|
+
name: z.string().optional(),
|
|
49
46
|
description: z.string().optional(),
|
|
50
47
|
detail: z.string().optional(),
|
|
48
|
+
// Decorative header glyph. Lucide icon name (kebab-case) resolved by the
|
|
49
|
+
// canvas <Icon> primitive; falls back to a placeholder when unknown.
|
|
50
|
+
icon: z.string().optional(),
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
53
|
+
// Relative-path safety refine (textual). Mirrors the same rule used for
|
|
54
|
+
// image-node and html-node script paths. Realpath verification is layered on
|
|
55
55
|
// top by the proxy/status-runner before any spawn (symlink-escape defense).
|
|
56
56
|
const isCleanRelativePath = (s: string): boolean => {
|
|
57
57
|
if (s.length === 0) return false;
|
|
@@ -68,7 +68,7 @@ const isCleanRelativePath = (s: string): boolean => {
|
|
|
68
68
|
// interpreter; `input` (optional) gets JSON-serialized and written to the
|
|
69
69
|
// child's stdin then closed; `timeoutMs` caps execution (default applied at
|
|
70
70
|
// the spawn layer, not here).
|
|
71
|
-
const ScriptActionSchema = z.object({
|
|
71
|
+
export const ScriptActionSchema = z.object({
|
|
72
72
|
kind: z.literal('script'),
|
|
73
73
|
interpreter: z.string().min(1),
|
|
74
74
|
args: z.array(z.string()).optional(),
|
|
@@ -81,11 +81,7 @@ const ScriptActionSchema = z.object({
|
|
|
81
81
|
|
|
82
82
|
export const PlayActionSchema = ScriptActionSchema;
|
|
83
83
|
|
|
84
|
-
//
|
|
85
|
-
// script (interpreter + args + scriptPath + optional input/timeoutMs) but
|
|
86
|
-
// invoked from the /reset endpoint. The studio kills every live play and
|
|
87
|
-
// status script for the demo before running this script, so the running app
|
|
88
|
-
// sees a clean baseline when wiping its state.
|
|
84
|
+
// resetAction is a one-shot script with the same shape as a play script.
|
|
89
85
|
export const ResetActionSchema = ScriptActionSchema;
|
|
90
86
|
|
|
91
87
|
// Long-running status script. Same spawn shape as ScriptAction (interpreter +
|
|
@@ -117,54 +113,23 @@ export const StateSourceSchema = z.discriminatedUnion('kind', [
|
|
|
117
113
|
z.object({ kind: z.literal('event') }),
|
|
118
114
|
]);
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
// Decorative header glyph. Lucide icon name (kebab-case) resolved by the
|
|
127
|
-
// canvas <Icon> primitive; falls back to a placeholder when unknown.
|
|
128
|
-
icon: z.string().optional(),
|
|
129
|
-
...NodeVisualBaseShape,
|
|
130
|
-
...NodeDescriptionBaseShape,
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
const PlayNodeDataSchema = NodeDataBaseSchema.extend({
|
|
134
|
-
playAction: PlayActionSchema,
|
|
135
|
-
statusAction: StatusActionSchema.optional(),
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
const StateNodeDataSchema = NodeDataBaseSchema.extend({
|
|
116
|
+
// Capabilities — any subset of these makes a node Playable / Stateful. All
|
|
117
|
+
// optional, valid on every node type. A node is Playable iff `playAction` is
|
|
118
|
+
// set; Stateful iff `statusAction` is set; Both iff both. `stateSource` is
|
|
119
|
+
// informational metadata that pairs with statusAction. `handlerModule` is
|
|
120
|
+
// reserved for a future skills runtime and is schema-only at v1.
|
|
121
|
+
const NodeCapabilitiesShape = {
|
|
139
122
|
playAction: PlayActionSchema.optional(),
|
|
140
123
|
statusAction: StatusActionSchema.optional(),
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const NodeBaseShape = {
|
|
144
|
-
id: z.string().min(1),
|
|
145
|
-
position: PositionSchema,
|
|
124
|
+
stateSource: StateSourceSchema.optional(),
|
|
125
|
+
handlerModule: z.string().optional(),
|
|
146
126
|
};
|
|
147
127
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
const StateNodeSchema = z.object({
|
|
155
|
-
...NodeBaseShape,
|
|
156
|
-
type: z.literal('stateNode'),
|
|
157
|
-
data: StateNodeDataSchema,
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Decorative annotation node — rectangle / ellipse / sticky. No semantic
|
|
161
|
-
// payload (no kind/stateSource/playAction); reuses NodeVisualBaseShape so
|
|
162
|
-
// users can theme it the same way as functional nodes.
|
|
163
|
-
// US-009 added `database` as the first illustrative shape (cylinder rendered
|
|
164
|
-
// via inline SVG inside shape-node.tsx). Illustrative shapes share the same
|
|
165
|
-
// shapeNode wrapper and color/border fields but own their own visuals via a
|
|
166
|
-
// per-shape component under `apps/web/src/components/nodes/shapes/`.
|
|
167
|
-
export const ShapeKindSchema = z.enum([
|
|
128
|
+
// 12 flat node types. The first 9 are geometric/illustrative and share
|
|
129
|
+
// GeometricNodeData. `image`, `html`, `icon` carry per-type fields.
|
|
130
|
+
// The renderer picks the SVG / chrome by `type`; the schema treats them
|
|
131
|
+
// (apart from the per-type fields below) as identical.
|
|
132
|
+
export const GEOMETRIC_NODE_TYPES = [
|
|
168
133
|
'rectangle',
|
|
169
134
|
'ellipse',
|
|
170
135
|
'sticky',
|
|
@@ -174,148 +139,170 @@ export const ShapeKindSchema = z.enum([
|
|
|
174
139
|
'user',
|
|
175
140
|
'queue',
|
|
176
141
|
'cloud',
|
|
142
|
+
] as const;
|
|
143
|
+
|
|
144
|
+
export const NodeTypeSchema = z.enum([
|
|
145
|
+
...GEOMETRIC_NODE_TYPES,
|
|
146
|
+
'image',
|
|
147
|
+
'html',
|
|
148
|
+
'icon',
|
|
149
|
+
'component',
|
|
177
150
|
]);
|
|
178
151
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
152
|
+
// --- Component node spec/action schemas --------------------------------------
|
|
153
|
+
// The 'component' node renders a json-render-driven reactive UI on the canvas.
|
|
154
|
+
// `spec` is the source of truth for layout + interactivity; on disk it lives at
|
|
155
|
+
// `<project>/nodes/<id>/spec.json` (the resolver inlines it into data.spec for
|
|
156
|
+
// ResolvedFlowSchema). Element types and props are catalog-validated by a
|
|
157
|
+
// superRefine wired in a later story.
|
|
158
|
+
|
|
159
|
+
export const ComponentSpecElementSchema = z.object({
|
|
160
|
+
type: z.string().min(1),
|
|
161
|
+
props: z.record(z.string(), z.unknown()).optional(),
|
|
162
|
+
children: z.array(z.string()).optional(),
|
|
163
|
+
watch: z.record(z.string(), z.unknown()).optional(),
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Declarative state mutation. `path` is a JSON Pointer (starts with '/');
|
|
167
|
+
// `value` may itself carry { $param } / { $state } refs resolved by the
|
|
168
|
+
// runtime at dispatch time.
|
|
169
|
+
const SetActionSchema = z.object({
|
|
170
|
+
kind: z.literal('set'),
|
|
171
|
+
path: z
|
|
172
|
+
.string()
|
|
173
|
+
.min(1)
|
|
174
|
+
.startsWith('/', { message: 'path must be a JSON Pointer (start with /)' }),
|
|
175
|
+
value: z.unknown(),
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// Script-kind component actions reuse the existing ScriptActionSchema shape
|
|
179
|
+
// (interpreter, scriptPath, timeoutMs, ...). The action runner roots scriptPath
|
|
180
|
+
// under `<projectRoot>/nodes/<nodeId>/`.
|
|
181
|
+
export const ComponentActionSchema = z.discriminatedUnion('kind', [
|
|
182
|
+
SetActionSchema,
|
|
183
|
+
ScriptActionSchema,
|
|
184
|
+
]);
|
|
185
|
+
|
|
186
|
+
export const ComponentSpecSchema = z.object({
|
|
187
|
+
root: z.string().min(1),
|
|
188
|
+
elements: z.record(z.string(), ComponentSpecElementSchema),
|
|
189
|
+
state: z.record(z.string(), z.unknown()).optional(),
|
|
190
|
+
actions: z.record(z.string(), ComponentActionSchema).optional(),
|
|
184
191
|
});
|
|
185
192
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
193
|
+
export type ComponentSpec = z.infer<typeof ComponentSpecSchema>;
|
|
194
|
+
export type ComponentAction = z.infer<typeof ComponentActionSchema>;
|
|
195
|
+
export type ComponentSpecElement = z.infer<typeof ComponentSpecElementSchema>;
|
|
196
|
+
|
|
197
|
+
// ---- Resolved (in-memory) per-type data -------------------------------------
|
|
198
|
+
|
|
199
|
+
const ResolvedGeometricNodeData = z.object({
|
|
200
|
+
...NodeSemanticBaseShape,
|
|
201
|
+
...NodeVisualBaseShape,
|
|
202
|
+
...NodeCapabilitiesShape,
|
|
190
203
|
});
|
|
191
204
|
|
|
192
|
-
//
|
|
193
|
-
//
|
|
194
|
-
//
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
205
|
+
// Image node — references a file under the project root by relative path.
|
|
206
|
+
// `path` is constrained to live under `nodes/<id>/` (post-validate refine on
|
|
207
|
+
// ResolvedFlowSchema below) so the delete_node cascade owns cleanup.
|
|
208
|
+
const ResolvedImageNodeData = z.object({
|
|
209
|
+
...NodeSemanticBaseShape,
|
|
210
|
+
...NodeVisualBaseShape,
|
|
211
|
+
...NodeCapabilitiesShape,
|
|
198
212
|
path: z.string().min(1).refine(isCleanRelativePath, {
|
|
199
213
|
message: 'path must be a relative path under the project root (no absolute / traversal)',
|
|
200
214
|
}),
|
|
201
215
|
alt: z.string().optional(),
|
|
202
|
-
...NodeVisualBaseShape,
|
|
203
|
-
...NodeDescriptionBaseShape,
|
|
204
216
|
borderWidth: z.number().min(1).max(8).optional(),
|
|
205
217
|
});
|
|
206
218
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
// inline via `data.html`. The studio externalizes the content to
|
|
216
|
-
// `<project>/nodes/<id>/view.html` and stores a `file://` ref in flow.json;
|
|
217
|
-
// the resolver inlines the content back on read so consumers see
|
|
218
|
-
// the resolved HTML string. The renderer sanitizes before injection
|
|
219
|
-
// (US-013/US-014). Spreads NodeVisualBaseShape so authors can theme the
|
|
220
|
-
// wrapper (border / background / radius / font) with the same fields
|
|
221
|
-
// available on every other visual node.
|
|
222
|
-
export const HtmlNodeDataSchema = z.object({
|
|
219
|
+
// Html node — escape-hatch for content the curated visuals don't cover.
|
|
220
|
+
// `html` is externalized to `<project>/nodes/<id>/view.html` on write; the
|
|
221
|
+
// file-ref resolver inlines it back on read. The renderer sanitizes before
|
|
222
|
+
// injection. `autoSize:true` lets the renderer size around the content.
|
|
223
|
+
const ResolvedHtmlNodeData = z.object({
|
|
224
|
+
...NodeSemanticBaseShape,
|
|
225
|
+
...NodeVisualBaseShape,
|
|
226
|
+
...NodeCapabilitiesShape,
|
|
223
227
|
html: z.string().optional(),
|
|
224
|
-
name: z.string().optional(),
|
|
225
|
-
// Decorative caption glyph. Lucide icon name (kebab-case) resolved by the
|
|
226
|
-
// canvas <Icon> primitive; rendered inline with the caption when set.
|
|
227
|
-
icon: z.string().optional(),
|
|
228
|
-
// When true (or absent), the renderer measures the HTML content and React
|
|
229
|
-
// Flow sizes the wrapper around it (capped at 800×600 by the renderer's
|
|
230
|
-
// measuring container styles). The studio adapter (`mergeNodeUpdates`)
|
|
231
|
-
// enforces the invariant that `autoSize === true` and persisted
|
|
232
|
-
// `width`/`height` never coexist: writing width/height flips autoSize to
|
|
233
|
-
// false; writing autoSize: true strips width/height.
|
|
234
228
|
autoSize: z.boolean().optional(),
|
|
235
|
-
...NodeVisualBaseShape,
|
|
236
|
-
...NodeDescriptionBaseShape,
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
const HtmlNodeSchema = z.object({
|
|
240
|
-
...NodeBaseShape,
|
|
241
|
-
type: z.literal('htmlNode'),
|
|
242
|
-
data: HtmlNodeDataSchema,
|
|
243
229
|
});
|
|
244
230
|
|
|
245
|
-
//
|
|
246
|
-
// (
|
|
247
|
-
|
|
248
|
-
|
|
231
|
+
// Icon node — renders a Lucide glyph as its main visual. `icon` is required
|
|
232
|
+
// here (overrides the optional decorative `icon` from NodeSemanticBaseShape).
|
|
233
|
+
const ResolvedIconNodeData = z.object({
|
|
234
|
+
...NodeSemanticBaseShape,
|
|
235
|
+
...NodeVisualBaseShape,
|
|
236
|
+
...NodeCapabilitiesShape,
|
|
249
237
|
icon: z.string().min(1),
|
|
250
238
|
color: ColorTokenSchema.optional(),
|
|
251
239
|
strokeWidth: z.number().min(0.5).max(4).optional(),
|
|
252
|
-
width: z.number().positive().optional(),
|
|
253
|
-
height: z.number().positive().optional(),
|
|
254
240
|
alt: z.string().optional(),
|
|
255
|
-
// US-002: optional visible caption rendered below the icon. Distinct from
|
|
256
|
-
// `alt` (screen-reader text). Absent / empty → no caption rendered and the
|
|
257
|
-
// node's bounding box is byte-identical to the unlabeled layout.
|
|
258
|
-
name: z.string().optional(),
|
|
259
|
-
...NodeDescriptionBaseShape,
|
|
260
241
|
});
|
|
261
242
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
243
|
+
// Component node — `spec` is the json-render tree. On disk the spec lives in
|
|
244
|
+
// `<project>/nodes/<id>/spec.json`; the resolver inlines it into data.spec
|
|
245
|
+
// before ResolvedFlowSchema validates the merged shape. The on-disk
|
|
246
|
+
// FlowComponentNodeData below has no `spec` field.
|
|
247
|
+
const ResolvedComponentNodeData = z.object({
|
|
248
|
+
...NodeSemanticBaseShape,
|
|
249
|
+
...NodeVisualBaseShape,
|
|
250
|
+
...NodeCapabilitiesShape,
|
|
251
|
+
spec: ComponentSpecSchema,
|
|
252
|
+
autoSize: z.boolean().optional(),
|
|
266
253
|
});
|
|
267
254
|
|
|
255
|
+
const NodeBaseShape = {
|
|
256
|
+
id: z.string().min(1),
|
|
257
|
+
position: PositionSchema,
|
|
258
|
+
};
|
|
259
|
+
|
|
260
|
+
const makeResolvedGeometricSchema = (type: (typeof GEOMETRIC_NODE_TYPES)[number]) =>
|
|
261
|
+
z.object({
|
|
262
|
+
...NodeBaseShape,
|
|
263
|
+
type: z.literal(type),
|
|
264
|
+
data: ResolvedGeometricNodeData,
|
|
265
|
+
});
|
|
266
|
+
|
|
268
267
|
const NodeSchema = z.discriminatedUnion('type', [
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
268
|
+
makeResolvedGeometricSchema('rectangle'),
|
|
269
|
+
makeResolvedGeometricSchema('ellipse'),
|
|
270
|
+
makeResolvedGeometricSchema('sticky'),
|
|
271
|
+
makeResolvedGeometricSchema('text'),
|
|
272
|
+
makeResolvedGeometricSchema('database'),
|
|
273
|
+
makeResolvedGeometricSchema('server'),
|
|
274
|
+
makeResolvedGeometricSchema('user'),
|
|
275
|
+
makeResolvedGeometricSchema('queue'),
|
|
276
|
+
makeResolvedGeometricSchema('cloud'),
|
|
277
|
+
z.object({ ...NodeBaseShape, type: z.literal('image'), data: ResolvedImageNodeData }),
|
|
278
|
+
z.object({ ...NodeBaseShape, type: z.literal('html'), data: ResolvedHtmlNodeData }),
|
|
279
|
+
z.object({ ...NodeBaseShape, type: z.literal('icon'), data: ResolvedIconNodeData }),
|
|
280
|
+
z.object({
|
|
281
|
+
...NodeBaseShape,
|
|
282
|
+
type: z.literal('component'),
|
|
283
|
+
data: ResolvedComponentNodeData,
|
|
284
|
+
}),
|
|
275
285
|
]);
|
|
276
286
|
|
|
277
|
-
// Connector
|
|
278
|
-
// Edge from each connector at render time (id/source/target are reused;
|
|
279
|
-
// `label` becomes the edge label; visual style comes from optional
|
|
280
|
-
// `style`/`color` fields). v1 has no separate `edges[]` array — connectors
|
|
281
|
-
// are the sole source of truth for inter-node connections. Optional
|
|
282
|
-
// metadata fields (`method`/`url`/`eventName`/`queueName`) may be present
|
|
283
|
-
// for documentation purposes; the renderer does not branch on them.
|
|
287
|
+
// Connector — unchanged by the flat-types refactor.
|
|
284
288
|
const ConnectorStyleSchema = z.enum(['solid', 'dashed', 'dotted']);
|
|
285
289
|
const ConnectorDirectionSchema = z.enum(['forward', 'backward', 'both', 'none']);
|
|
286
|
-
// Path geometry — orthogonal to `style` (which means the dash pattern). Absent
|
|
287
|
-
// → renders as today's smooth bezier curve. 'step' renders as a smoothstep
|
|
288
|
-
// (right-angle / zigzag) path. (US-017)
|
|
289
290
|
const ConnectorPathSchema = z.enum(['curve', 'step']);
|
|
290
291
|
|
|
291
|
-
// Visual fields shared by every connector. All optional — existing
|
|
292
|
-
// demo files predate them and must continue to parse. `direction` defaults
|
|
293
|
-
// to 'forward' when absent (the historical behavior).
|
|
294
292
|
const ConnectorVisualBaseShape = {
|
|
295
293
|
style: ConnectorStyleSchema.optional(),
|
|
296
294
|
color: ColorTokenSchema.optional(),
|
|
297
295
|
direction: ConnectorDirectionSchema.optional(),
|
|
298
296
|
borderSize: z.number().positive().optional(),
|
|
299
297
|
path: ConnectorPathSchema.optional(),
|
|
300
|
-
// US-018: per-connector label font size in CSS pixels. Absent → fall back to
|
|
301
|
-
// the editable-edge default (11px). Mirrors NodeVisualBaseShape.fontSize.
|
|
302
298
|
fontSize: z.number().positive().optional(),
|
|
303
299
|
};
|
|
304
300
|
|
|
305
|
-
// Handle ids — every node type
|
|
306
|
-
//
|
|
307
|
-
// `sourceHandle` MUST be a source-side id and `targetHandle` MUST be a
|
|
308
|
-
// target-side id; sending the wrong role leaves a stranded endpoint at render
|
|
309
|
-
// time, so the schema rejects it (US-022).
|
|
301
|
+
// Handle ids — every node type uses the same four-handle layout:
|
|
302
|
+
// target-only on top + left, source-only on right + bottom.
|
|
310
303
|
export const SourceHandleIdSchema = z.enum(['r', 'b']);
|
|
311
304
|
export const TargetHandleIdSchema = z.enum(['t', 'l']);
|
|
312
305
|
|
|
313
|
-
// US-006: pinned endpoint position. `side` names one of the four perimeter
|
|
314
|
-
// sides of the connected node; `t` is the parameterized position along that
|
|
315
|
-
// side, [0, 1], measured from the top-left corner of the side (top/bottom →
|
|
316
|
-
// left-to-right; left/right → top-to-bottom). Pins are persisted so they
|
|
317
|
-
// survive node moves and resizes without drifting toward the other endpoint's
|
|
318
|
-
// center the way floating endpoints do.
|
|
319
306
|
const EdgePinSideSchema = z.enum(['top', 'right', 'bottom', 'left']);
|
|
320
307
|
export const EdgePinSchema = z.object({
|
|
321
308
|
side: EdgePinSideSchema,
|
|
@@ -326,21 +313,10 @@ const ConnectorBaseShape = {
|
|
|
326
313
|
id: z.string().min(1),
|
|
327
314
|
source: z.string().min(1),
|
|
328
315
|
target: z.string().min(1),
|
|
329
|
-
// Optional — connectors authored before the four-handle layout omit them and
|
|
330
|
-
// React Flow falls back to the first matching handle.
|
|
331
316
|
sourceHandle: SourceHandleIdSchema.optional(),
|
|
332
317
|
targetHandle: TargetHandleIdSchema.optional(),
|
|
333
|
-
// US-021: tracks whether each endpoint's handle was auto-picked by the
|
|
334
|
-
// facing-handle picker (true) or pinned by an explicit user handle drop
|
|
335
|
-
// (false / absent). Auto-picked endpoints get re-routed when nodes move so
|
|
336
|
-
// the connector keeps facing the other end; user-pinned ones never do.
|
|
337
318
|
sourceHandleAutoPicked: z.boolean().optional(),
|
|
338
319
|
targetHandleAutoPicked: z.boolean().optional(),
|
|
339
|
-
// US-006: optional explicit perimeter positions for each endpoint. When
|
|
340
|
-
// set, the endpoint is computed from `(side, t)` against the connected
|
|
341
|
-
// node's current bbox at render time — the position parameterizes with the
|
|
342
|
-
// node so the pin survives moves and resizes. Absent → floating /
|
|
343
|
-
// handle-based endpoint behavior (back-compat).
|
|
344
320
|
sourcePin: EdgePinSchema.optional(),
|
|
345
321
|
targetPin: EdgePinSchema.optional(),
|
|
346
322
|
label: z.string().optional(),
|
|
@@ -363,9 +339,8 @@ export const ResolvedFlowSchema = z
|
|
|
363
339
|
nodes: z.array(NodeSchema),
|
|
364
340
|
connectors: z.array(ConnectorSchema),
|
|
365
341
|
// Optional one-shot script the studio runs when the user clicks Restart.
|
|
366
|
-
//
|
|
367
|
-
//
|
|
368
|
-
// script (US-008), so the script sees no stragglers.
|
|
342
|
+
// The studio kills every live play + status script for the flow BEFORE
|
|
343
|
+
// invoking this script, so the script sees no stragglers.
|
|
369
344
|
resetAction: ResetActionSchema.optional(),
|
|
370
345
|
})
|
|
371
346
|
.superRefine((resolved, ctx) => {
|
|
@@ -386,31 +361,54 @@ export const ResolvedFlowSchema = z
|
|
|
386
361
|
});
|
|
387
362
|
}
|
|
388
363
|
});
|
|
389
|
-
//
|
|
364
|
+
// type:'image' upload paths must live under the node's own
|
|
390
365
|
// `nodes/<id>/` folder so delete_node's removeNodeDir cascade is the
|
|
391
366
|
// single source of cleanup.
|
|
392
367
|
resolved.nodes.forEach((node, idx) => {
|
|
393
|
-
if (node.type !== '
|
|
368
|
+
if (node.type !== 'image') return;
|
|
394
369
|
const path = (node.data as { path?: string }).path;
|
|
395
370
|
const expected = `nodes/${node.id}/`;
|
|
396
371
|
if (typeof path === 'string' && !path.startsWith(expected)) {
|
|
397
372
|
ctx.addIssue({
|
|
398
373
|
code: z.ZodIssueCode.custom,
|
|
399
374
|
path: ['nodes', idx, 'data', 'path'],
|
|
400
|
-
message: `
|
|
375
|
+
message: `image node path must start with "${expected}"`,
|
|
401
376
|
});
|
|
402
377
|
}
|
|
403
378
|
});
|
|
379
|
+
// type:'component' spec.elements entries are catalog-validated here so
|
|
380
|
+
// unknown component names and prop shape mismatches surface at flow-read
|
|
381
|
+
// time with paths pointing into the offending element.
|
|
382
|
+
resolved.nodes.forEach((node, idx) => {
|
|
383
|
+
if (node.type !== 'component') return;
|
|
384
|
+
const elements = node.data.spec.elements;
|
|
385
|
+
for (const [elId, entry] of Object.entries(elements)) {
|
|
386
|
+
const def = componentCatalog.components[entry.type];
|
|
387
|
+
if (!def) {
|
|
388
|
+
ctx.addIssue({
|
|
389
|
+
code: z.ZodIssueCode.custom,
|
|
390
|
+
path: ['nodes', idx, 'data', 'spec', 'elements', elId, 'type'],
|
|
391
|
+
message: `Unknown component type "${entry.type}". Valid names: ${COMPONENT_NAMES.join(', ')}`,
|
|
392
|
+
});
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
const propsResult = def.props.safeParse(entry.props ?? {});
|
|
396
|
+
if (!propsResult.success) {
|
|
397
|
+
for (const issue of propsResult.error.issues) {
|
|
398
|
+
ctx.addIssue({
|
|
399
|
+
code: z.ZodIssueCode.custom,
|
|
400
|
+
path: ['nodes', idx, 'data', 'spec', 'elements', elId, 'props', ...issue.path],
|
|
401
|
+
message: issue.message,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
});
|
|
404
407
|
});
|
|
405
408
|
|
|
406
409
|
export type ResolvedFlow = z.infer<typeof ResolvedFlowSchema>;
|
|
407
410
|
export type ResolvedFlowNode = z.infer<typeof NodeSchema>;
|
|
408
|
-
export type
|
|
409
|
-
export type ImageNode = z.infer<typeof ImageNodeSchema>;
|
|
410
|
-
export type IconNode = z.infer<typeof IconNodeSchema>;
|
|
411
|
-
export type HtmlNode = z.infer<typeof HtmlNodeSchema>;
|
|
412
|
-
export type HtmlNodeData = z.infer<typeof HtmlNodeDataSchema>;
|
|
413
|
-
export type ShapeKind = z.infer<typeof ShapeKindSchema>;
|
|
411
|
+
export type NodeType = z.infer<typeof NodeTypeSchema>;
|
|
414
412
|
export type ColorToken = z.infer<typeof ColorTokenSchema>;
|
|
415
413
|
export type Connector = z.infer<typeof ConnectorSchema>;
|
|
416
414
|
export type ConnectorStyle = z.infer<typeof ConnectorStyleSchema>;
|
|
@@ -429,63 +427,50 @@ export type StateSource = z.infer<typeof StateSourceSchema>;
|
|
|
429
427
|
// What lives on disk in <project>/flow.json after the split.
|
|
430
428
|
// =============================================================================
|
|
431
429
|
|
|
432
|
-
const
|
|
433
|
-
name: z.string().min(1),
|
|
434
|
-
stateSource: StateSourceSchema,
|
|
435
|
-
handlerModule: z.string().optional(),
|
|
436
|
-
icon: z.string().optional(),
|
|
437
|
-
...NodeDescriptionBaseShape,
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
const FlowPlayNodeDataSchema = z
|
|
441
|
-
.object({
|
|
442
|
-
...FlowNodeDataBaseShape,
|
|
443
|
-
playAction: PlayActionSchema,
|
|
444
|
-
statusAction: StatusActionSchema.optional(),
|
|
445
|
-
})
|
|
446
|
-
.strict();
|
|
447
|
-
|
|
448
|
-
const FlowStateNodeDataSchema = z
|
|
449
|
-
.object({
|
|
450
|
-
...FlowNodeDataBaseShape,
|
|
451
|
-
playAction: PlayActionSchema.optional(),
|
|
452
|
-
statusAction: StatusActionSchema.optional(),
|
|
453
|
-
})
|
|
454
|
-
.strict();
|
|
455
|
-
|
|
456
|
-
const FlowShapeNodeDataSchema = z
|
|
430
|
+
const FlowGeometricNodeData = z
|
|
457
431
|
.object({
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
...NodeDescriptionBaseShape,
|
|
432
|
+
...NodeSemanticBaseShape,
|
|
433
|
+
...NodeCapabilitiesShape,
|
|
461
434
|
})
|
|
462
435
|
.strict();
|
|
463
436
|
|
|
464
|
-
const
|
|
437
|
+
const FlowImageNodeData = z
|
|
465
438
|
.object({
|
|
439
|
+
...NodeSemanticBaseShape,
|
|
440
|
+
...NodeCapabilitiesShape,
|
|
466
441
|
path: z.string().min(1).refine(isCleanRelativePath, {
|
|
467
442
|
message: 'path must be a relative path under the project root (no absolute / traversal)',
|
|
468
443
|
}),
|
|
469
444
|
alt: z.string().optional(),
|
|
470
|
-
...NodeDescriptionBaseShape,
|
|
471
445
|
})
|
|
472
446
|
.strict();
|
|
473
447
|
|
|
474
|
-
const
|
|
448
|
+
const FlowHtmlNodeData = z
|
|
449
|
+
.object({
|
|
450
|
+
...NodeSemanticBaseShape,
|
|
451
|
+
...NodeCapabilitiesShape,
|
|
452
|
+
html: z.string().optional(),
|
|
453
|
+
})
|
|
454
|
+
.strict();
|
|
455
|
+
|
|
456
|
+
const FlowIconNodeData = z
|
|
475
457
|
.object({
|
|
458
|
+
...NodeSemanticBaseShape,
|
|
459
|
+
...NodeCapabilitiesShape,
|
|
476
460
|
icon: z.string().min(1),
|
|
477
461
|
alt: z.string().optional(),
|
|
478
|
-
name: z.string().optional(),
|
|
479
|
-
...NodeDescriptionBaseShape,
|
|
480
462
|
})
|
|
481
463
|
.strict();
|
|
482
464
|
|
|
483
|
-
|
|
465
|
+
// Component node, on-disk shape. `spec` is intentionally absent — the sidecar
|
|
466
|
+
// `<project>/nodes/<id>/spec.json` is the source of truth. `.strict()` rejects
|
|
467
|
+
// any stray spec field that slips through (the resolver layer is responsible
|
|
468
|
+
// for inlining + the writer for stripping it back out).
|
|
469
|
+
const FlowComponentNodeData = z
|
|
484
470
|
.object({
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
...NodeDescriptionBaseShape,
|
|
471
|
+
...NodeSemanticBaseShape,
|
|
472
|
+
...NodeCapabilitiesShape,
|
|
473
|
+
autoSize: z.boolean().optional(),
|
|
489
474
|
})
|
|
490
475
|
.strict();
|
|
491
476
|
|
|
@@ -493,61 +478,71 @@ const FlowNodeBaseShape = {
|
|
|
493
478
|
id: z.string().min(1),
|
|
494
479
|
};
|
|
495
480
|
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
481
|
+
const makeFlowGeometricSchema = (type: (typeof GEOMETRIC_NODE_TYPES)[number]) =>
|
|
482
|
+
z
|
|
483
|
+
.object({
|
|
484
|
+
...FlowNodeBaseShape,
|
|
485
|
+
type: z.literal(type),
|
|
486
|
+
data: FlowGeometricNodeData,
|
|
487
|
+
})
|
|
488
|
+
.strict();
|
|
489
|
+
|
|
490
|
+
export const FlowRectangleNodeSchema = makeFlowGeometricSchema('rectangle');
|
|
491
|
+
export const FlowEllipseNodeSchema = makeFlowGeometricSchema('ellipse');
|
|
492
|
+
export const FlowStickyNodeSchema = makeFlowGeometricSchema('sticky');
|
|
493
|
+
export const FlowTextNodeSchema = makeFlowGeometricSchema('text');
|
|
494
|
+
export const FlowDatabaseNodeSchema = makeFlowGeometricSchema('database');
|
|
495
|
+
export const FlowServerNodeSchema = makeFlowGeometricSchema('server');
|
|
496
|
+
export const FlowUserNodeSchema = makeFlowGeometricSchema('user');
|
|
497
|
+
export const FlowQueueNodeSchema = makeFlowGeometricSchema('queue');
|
|
498
|
+
export const FlowCloudNodeSchema = makeFlowGeometricSchema('cloud');
|
|
511
499
|
|
|
512
|
-
export const
|
|
500
|
+
export const FlowImageNodeSchema = z
|
|
513
501
|
.object({
|
|
514
502
|
...FlowNodeBaseShape,
|
|
515
|
-
type: z.literal('
|
|
516
|
-
data:
|
|
503
|
+
type: z.literal('image'),
|
|
504
|
+
data: FlowImageNodeData,
|
|
517
505
|
})
|
|
518
506
|
.strict();
|
|
519
507
|
|
|
520
|
-
export const
|
|
508
|
+
export const FlowHtmlNodeSchema = z
|
|
521
509
|
.object({
|
|
522
510
|
...FlowNodeBaseShape,
|
|
523
|
-
type: z.literal('
|
|
524
|
-
data:
|
|
511
|
+
type: z.literal('html'),
|
|
512
|
+
data: FlowHtmlNodeData,
|
|
525
513
|
})
|
|
526
514
|
.strict();
|
|
527
515
|
|
|
528
516
|
export const FlowIconNodeSchema = z
|
|
529
517
|
.object({
|
|
530
518
|
...FlowNodeBaseShape,
|
|
531
|
-
type: z.literal('
|
|
532
|
-
data:
|
|
519
|
+
type: z.literal('icon'),
|
|
520
|
+
data: FlowIconNodeData,
|
|
533
521
|
})
|
|
534
522
|
.strict();
|
|
535
523
|
|
|
536
|
-
export const
|
|
524
|
+
export const FlowComponentNodeSchema = z
|
|
537
525
|
.object({
|
|
538
526
|
...FlowNodeBaseShape,
|
|
539
|
-
type: z.literal('
|
|
540
|
-
data:
|
|
527
|
+
type: z.literal('component'),
|
|
528
|
+
data: FlowComponentNodeData,
|
|
541
529
|
})
|
|
542
530
|
.strict();
|
|
543
531
|
|
|
544
532
|
const FlowNodeSchema = z.discriminatedUnion('type', [
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
533
|
+
FlowRectangleNodeSchema,
|
|
534
|
+
FlowEllipseNodeSchema,
|
|
535
|
+
FlowStickyNodeSchema,
|
|
536
|
+
FlowTextNodeSchema,
|
|
537
|
+
FlowDatabaseNodeSchema,
|
|
538
|
+
FlowServerNodeSchema,
|
|
539
|
+
FlowUserNodeSchema,
|
|
540
|
+
FlowQueueNodeSchema,
|
|
541
|
+
FlowCloudNodeSchema,
|
|
548
542
|
FlowImageNodeSchema,
|
|
549
|
-
FlowIconNodeSchema,
|
|
550
543
|
FlowHtmlNodeSchema,
|
|
544
|
+
FlowIconNodeSchema,
|
|
545
|
+
FlowComponentNodeSchema,
|
|
551
546
|
]);
|
|
552
547
|
|
|
553
548
|
const FlowConnectorBaseShape = {
|
|
@@ -602,11 +597,10 @@ export type FlowNode = z.infer<typeof FlowNodeSchema>;
|
|
|
602
597
|
export type FlowConnector = z.infer<typeof FlowConnectorSchema>;
|
|
603
598
|
|
|
604
599
|
// Envelope-only flow shape for the `seeflow schema flow` surface. The full
|
|
605
|
-
// FlowSchema validates the whole graph; this companion
|
|
606
|
-
//
|
|
607
|
-
//
|
|
608
|
-
//
|
|
609
|
-
// detailed shapes. Not used for validation — only the catalog reads it.
|
|
600
|
+
// FlowSchema validates the whole graph; this companion describes the top-level
|
|
601
|
+
// shape without inlining every node variant or the connector shape, so the
|
|
602
|
+
// runtime-introspectable JSON Schema stays compact. Authors drill into
|
|
603
|
+
// `seeflow schema node` / `seeflow schema connector` for the detailed shapes.
|
|
610
604
|
export const FlowEnvelopeSchema = z
|
|
611
605
|
.object({
|
|
612
606
|
version: z.literal(2),
|
|
@@ -635,12 +629,12 @@ const NodeStyleSchema = z
|
|
|
635
629
|
fontSize: z.number().positive().optional(),
|
|
636
630
|
textColor: ColorTokenSchema.optional(),
|
|
637
631
|
cornerRadius: z.number().min(0).optional(),
|
|
638
|
-
//
|
|
632
|
+
// type:'image'-specific
|
|
639
633
|
borderWidth: z.number().min(1).max(8).optional(),
|
|
640
|
-
//
|
|
634
|
+
// type:'icon'-specific
|
|
641
635
|
color: ColorTokenSchema.optional(),
|
|
642
636
|
strokeWidth: z.number().min(0.5).max(4).optional(),
|
|
643
|
-
//
|
|
637
|
+
// type:'html'-specific
|
|
644
638
|
autoSize: z.boolean().optional(),
|
|
645
639
|
})
|
|
646
640
|
.strict();
|