@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/operations.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { existsSync, mkdirSync, readFileSync, statSync, unlinkSync, writeFileSyn
|
|
|
11
11
|
import { dirname, isAbsolute, join } from 'node:path';
|
|
12
12
|
import { type ZodIssue, z } from 'zod';
|
|
13
13
|
import { writeFileAtomic } from './atomic-write.ts';
|
|
14
|
+
import { inlineComponentSpecs } from './component-spec-resolver.ts';
|
|
14
15
|
import { type LayoutOptions, computeLayout } from './layout.ts';
|
|
15
16
|
import { mergeFlowAndStyle, splitFlow } from './merge.ts';
|
|
16
17
|
import {
|
|
@@ -24,13 +25,14 @@ import {
|
|
|
24
25
|
import type { Registry } from './registry.ts';
|
|
25
26
|
import {
|
|
26
27
|
ColorTokenSchema,
|
|
28
|
+
ComponentSpecSchema,
|
|
27
29
|
EdgePinSchema,
|
|
28
30
|
type Flow,
|
|
29
31
|
FlowSchema,
|
|
32
|
+
NodeTypeSchema,
|
|
30
33
|
PlayActionSchema,
|
|
31
34
|
type ResolvedFlow,
|
|
32
35
|
ResolvedFlowSchema,
|
|
33
|
-
ShapeKindSchema,
|
|
34
36
|
SourceHandleIdSchema,
|
|
35
37
|
StateSourceSchema,
|
|
36
38
|
StatusActionSchema,
|
|
@@ -82,25 +84,16 @@ export type ReorderBody = z.infer<typeof ReorderBodySchema>;
|
|
|
82
84
|
// other key lands inside node.data. Final validity is enforced by re-parsing
|
|
83
85
|
// the whole demo through ResolvedFlowSchema after the merge — this body schema just
|
|
84
86
|
// rejects unknown top-level keys to catch typos.
|
|
85
|
-
const NodeTypeSchema = z.enum([
|
|
86
|
-
'playNode',
|
|
87
|
-
'stateNode',
|
|
88
|
-
'shapeNode',
|
|
89
|
-
'imageNode',
|
|
90
|
-
'iconNode',
|
|
91
|
-
'htmlNode',
|
|
92
|
-
]);
|
|
93
|
-
|
|
94
87
|
export const NodePatchBodySchema = z
|
|
95
88
|
.object({
|
|
96
89
|
// When supplied AND different from the node's current type, the merged
|
|
97
90
|
// node is reclassified in place: data keys not allowed on the new type's
|
|
98
91
|
// FlowDataSchema are stripped, visuals (which route to style.json) are
|
|
99
92
|
// preserved, and the post-merge ResolvedFlowSchema reparse enforces the
|
|
100
|
-
// new type's required fields (e.g.
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
93
|
+
// new type's required fields (e.g. type:'image' without a `path` in the
|
|
94
|
+
// same body surfaces as `badSchema`). The per-node folder under
|
|
95
|
+
// `nodes/<id>/` is keyed by id, so retype keeps scripts, detail.md, and
|
|
96
|
+
// view.html attached.
|
|
104
97
|
type: NodeTypeSchema.optional(),
|
|
105
98
|
position: PositionBodySchema.optional(),
|
|
106
99
|
name: z.string().optional(),
|
|
@@ -114,22 +107,22 @@ export const NodePatchBodySchema = z
|
|
|
114
107
|
cornerRadius: z.number().min(0).optional(),
|
|
115
108
|
width: z.number().positive().optional(),
|
|
116
109
|
height: z.number().positive().optional(),
|
|
117
|
-
//
|
|
110
|
+
// type:'html'-only: when true, the renderer measures content and React Flow
|
|
118
111
|
// sizes the wrapper around it. mergeNodeUpdates enforces the invariant
|
|
119
112
|
// that autoSize:true never coexists with persisted width/height.
|
|
120
113
|
autoSize: z.boolean().optional(),
|
|
121
|
-
|
|
122
|
-
//
|
|
123
|
-
//
|
|
114
|
+
// type:'icon'-only: stroke color token. Lands at data.color; the
|
|
115
|
+
// post-merge ResolvedFlowSchema reparse gates that this is only valid on
|
|
116
|
+
// type:'icon'.
|
|
124
117
|
color: ColorTokenSchema.optional(),
|
|
125
|
-
//
|
|
118
|
+
// type:'icon'-only: glyph stroke width. Lands at data.strokeWidth; the
|
|
126
119
|
// post-merge reparse gates the [0.5, 4] bound and arm validity.
|
|
127
120
|
strokeWidth: z.number().min(0.5).max(4).optional(),
|
|
128
|
-
//
|
|
121
|
+
// type:'icon'/type:'image'-only: accessible alt text. Lands at data.alt.
|
|
129
122
|
alt: z.string().optional(),
|
|
130
123
|
// kebab-case Lucide icon name. Lands at data.icon. The post-merge reparse
|
|
131
124
|
// enforces the schema's `.min(1)` non-empty rule for nodes that require
|
|
132
|
-
// icon (
|
|
125
|
+
// icon (type:'icon'), and gates which variants allow it. Explicit `null`
|
|
133
126
|
// clears the field (mergeNodeUpdates strips the key from disk) — mirrors
|
|
134
127
|
// the empty-string clear convention used for description / detail.
|
|
135
128
|
icon: z.string().min(1).nullable().optional(),
|
|
@@ -138,17 +131,24 @@ export const NodePatchBodySchema = z
|
|
|
138
131
|
// serialize signal — `mergeNodeUpdates` strips the key from disk.
|
|
139
132
|
description: z.string().optional(),
|
|
140
133
|
detail: z.string().optional(),
|
|
141
|
-
//
|
|
134
|
+
// type:'html'-only: inline HTML content. Externalized to
|
|
142
135
|
// `<project>/nodes/<id>/view.html` by patchNodeImpl; the file:// ref on
|
|
143
136
|
// the node persists. Empty string empties the file but keeps the ref.
|
|
144
137
|
html: z.string().optional(),
|
|
145
|
-
//
|
|
146
|
-
// behaviour onto a previously-created node without re-issuing it.
|
|
147
|
-
//
|
|
148
|
-
//
|
|
138
|
+
// Capability attach: lets the skill (or any consumer) wire executable
|
|
139
|
+
// behaviour onto a previously-created node without re-issuing it. All
|
|
140
|
+
// capabilities are valid on every node type — presence drives renderer
|
|
141
|
+
// chrome. Final validity is enforced by the post-merge
|
|
142
|
+
// ResolvedFlowSchema reparse.
|
|
149
143
|
playAction: PlayActionSchema.optional(),
|
|
150
144
|
statusAction: StatusActionSchema.optional(),
|
|
151
145
|
stateSource: StateSourceSchema.optional(),
|
|
146
|
+
// type:'component'-only: json-render spec describing the reactive UI.
|
|
147
|
+
// Externalized to `<project>/nodes/<id>/spec.json` by patchNodeImpl; the
|
|
148
|
+
// in-memory ResolvedFlow keeps `data.spec` populated for the post-merge
|
|
149
|
+
// reparse + SSE broadcast, but splitFlow strips it from flow.json so the
|
|
150
|
+
// sidecar is the source of truth on disk.
|
|
151
|
+
spec: ComponentSpecSchema.optional(),
|
|
152
152
|
})
|
|
153
153
|
.strict();
|
|
154
154
|
export type NodePatchBody = z.infer<typeof NodePatchBodySchema>;
|
|
@@ -170,7 +170,6 @@ const NODE_DATA_PATCH_KEYS = [
|
|
|
170
170
|
'width',
|
|
171
171
|
'height',
|
|
172
172
|
'autoSize',
|
|
173
|
-
'shape',
|
|
174
173
|
'color',
|
|
175
174
|
'strokeWidth',
|
|
176
175
|
'alt',
|
|
@@ -181,6 +180,7 @@ const NODE_DATA_PATCH_KEYS = [
|
|
|
181
180
|
'playAction',
|
|
182
181
|
'statusAction',
|
|
183
182
|
'stateSource',
|
|
183
|
+
'spec',
|
|
184
184
|
] as const satisfies ReadonlyArray<keyof NodePatchBody>;
|
|
185
185
|
|
|
186
186
|
const EXTERNALIZED_FIELD_NAMES = new Set<string>(EXTERNALIZED_NODE_FIELDS.map((e) => e.field));
|
|
@@ -190,35 +190,67 @@ const EXTERNALIZED_FIELD_NAMES = new Set<string>(EXTERNALIZED_NODE_FIELDS.map((e
|
|
|
190
190
|
// they route to style.json on write. Everything else gets stripped from
|
|
191
191
|
// `data` when a node changes type so the post-merge ResolvedFlowSchema reparse
|
|
192
192
|
// doesn't reject lingering fields from the previous variant. Missing required
|
|
193
|
-
// fields on the new type (e.g.
|
|
193
|
+
// fields on the new type (e.g. retype to type:'image' without a `path`)
|
|
194
194
|
// surface as the normal `badSchema` outcome from the reparse.
|
|
195
|
+
const GEOMETRIC_SEMANTIC_KEYS: ReadonlySet<string> = new Set([
|
|
196
|
+
'name',
|
|
197
|
+
'description',
|
|
198
|
+
'detail',
|
|
199
|
+
'icon',
|
|
200
|
+
'stateSource',
|
|
201
|
+
'handlerModule',
|
|
202
|
+
'playAction',
|
|
203
|
+
'statusAction',
|
|
204
|
+
]);
|
|
205
|
+
|
|
195
206
|
const SEMANTIC_KEYS_BY_TYPE: Record<z.infer<typeof NodeTypeSchema>, ReadonlySet<string>> = {
|
|
196
|
-
|
|
207
|
+
rectangle: GEOMETRIC_SEMANTIC_KEYS,
|
|
208
|
+
ellipse: GEOMETRIC_SEMANTIC_KEYS,
|
|
209
|
+
sticky: GEOMETRIC_SEMANTIC_KEYS,
|
|
210
|
+
text: GEOMETRIC_SEMANTIC_KEYS,
|
|
211
|
+
database: GEOMETRIC_SEMANTIC_KEYS,
|
|
212
|
+
server: GEOMETRIC_SEMANTIC_KEYS,
|
|
213
|
+
user: GEOMETRIC_SEMANTIC_KEYS,
|
|
214
|
+
queue: GEOMETRIC_SEMANTIC_KEYS,
|
|
215
|
+
cloud: GEOMETRIC_SEMANTIC_KEYS,
|
|
216
|
+
image: new Set([
|
|
197
217
|
'name',
|
|
198
|
-
'kind',
|
|
199
|
-
'stateSource',
|
|
200
|
-
'handlerModule',
|
|
201
|
-
'icon',
|
|
202
218
|
'description',
|
|
203
219
|
'detail',
|
|
220
|
+
'icon',
|
|
221
|
+
'stateSource',
|
|
222
|
+
'handlerModule',
|
|
204
223
|
'playAction',
|
|
205
224
|
'statusAction',
|
|
225
|
+
'path',
|
|
226
|
+
'alt',
|
|
206
227
|
]),
|
|
207
|
-
|
|
228
|
+
html: new Set([
|
|
208
229
|
'name',
|
|
209
|
-
'
|
|
230
|
+
'description',
|
|
231
|
+
'detail',
|
|
232
|
+
'icon',
|
|
210
233
|
'stateSource',
|
|
211
234
|
'handlerModule',
|
|
212
|
-
'
|
|
235
|
+
'playAction',
|
|
236
|
+
'statusAction',
|
|
237
|
+
'html',
|
|
238
|
+
]),
|
|
239
|
+
icon: new Set([
|
|
240
|
+
'name',
|
|
213
241
|
'description',
|
|
214
242
|
'detail',
|
|
243
|
+
'icon',
|
|
244
|
+
'stateSource',
|
|
245
|
+
'handlerModule',
|
|
215
246
|
'playAction',
|
|
216
247
|
'statusAction',
|
|
248
|
+
'alt',
|
|
217
249
|
]),
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
250
|
+
// Component nodes externalize `spec` to <project>/nodes/<id>/spec.json; the
|
|
251
|
+
// semantic-key set covers only the universal capability fields so retype
|
|
252
|
+
// never drags `spec` through `data`. (US-007 wires the sidecar writer.)
|
|
253
|
+
component: GEOMETRIC_SEMANTIC_KEYS,
|
|
222
254
|
};
|
|
223
255
|
|
|
224
256
|
// Visual data keys — routed to style.json on write by splitFlow. Kept here
|
|
@@ -288,7 +320,7 @@ export const mergeNodeUpdates = (node: Record<string, unknown>, updates: NodePat
|
|
|
288
320
|
// doesn't reject lingering fields. The per-node folder under
|
|
289
321
|
// `nodes/<id>/` is keyed by id (unchanged), so scripts and externalized
|
|
290
322
|
// files stay attached. Missing required fields on the new type (e.g.
|
|
291
|
-
//
|
|
323
|
+
// retype to type:'image' without a `path` in the same patch) surface as
|
|
292
324
|
// `badSchema` from the ResolvedFlowSchema reparse.
|
|
293
325
|
if (updates.type !== undefined && updates.type !== node.type) {
|
|
294
326
|
node.type = updates.type;
|
|
@@ -302,12 +334,13 @@ export const mergeNodeUpdates = (node: Record<string, unknown>, updates: NodePat
|
|
|
302
334
|
}
|
|
303
335
|
}
|
|
304
336
|
|
|
305
|
-
//
|
|
337
|
+
// type:'html' + type:'component' invariant enforcement:
|
|
306
338
|
// autoSize === true ⊻ (width and height set).
|
|
307
339
|
// autoSize: true is the dominant signal — it strips width/height even if
|
|
308
340
|
// the same patch tried to write them. Writing width/height implicitly
|
|
309
|
-
// flips autoSize to false.
|
|
310
|
-
|
|
341
|
+
// flips autoSize to false. Both node types default to autoSize:true in the
|
|
342
|
+
// renderer and share the same shrink-wrap-to-content mechanism.
|
|
343
|
+
if (node.type === 'html' || node.type === 'component') {
|
|
311
344
|
// The autoSize invariant requires `width`/`height` to be ABSENT from the
|
|
312
345
|
// serialized JSON when autoSize is true — not present with value
|
|
313
346
|
// `undefined` (which would serialize as a stray `"width": null` or get
|
|
@@ -389,7 +422,7 @@ export type GetFlowOutcome =
|
|
|
389
422
|
| { kind: 'fileNotFound'; path: string };
|
|
390
423
|
|
|
391
424
|
// Lightweight graph projection — flow + nodes + connectors with file-backed
|
|
392
|
-
// fields (`detail` on every node, `html` on
|
|
425
|
+
// fields (`detail` on every node, `html` on type:'html') stripped so the
|
|
393
426
|
// caller can navigate the topology without paying for inlined bodies.
|
|
394
427
|
export interface FlowGraphResponse {
|
|
395
428
|
id: string;
|
|
@@ -738,7 +771,16 @@ export async function mutateMergedFlow<E extends { kind: string }>(
|
|
|
738
771
|
const styleParse = StyleSchema.safeParse(read.rawStyle);
|
|
739
772
|
if (!styleParse.success) return { kind: 'badSchema', issues: styleParse.error.issues };
|
|
740
773
|
|
|
741
|
-
|
|
774
|
+
// Inline component spec sidecars (<project>/nodes/<id>/spec.json) before the
|
|
775
|
+
// mutator runs so the post-mutation ResolvedFlowSchema parse sees `data.spec`
|
|
776
|
+
// on every existing component node. splitFlow strips `spec` back out before
|
|
777
|
+
// we write flow.json, keeping the sidecar as the on-disk source of truth.
|
|
778
|
+
const projectRoot = dirname(flowPath);
|
|
779
|
+
const { flow: inlinedFlow } = inlineComponentSpecs(
|
|
780
|
+
mergeFlowAndStyle(flowParse.data, styleParse.data),
|
|
781
|
+
projectRoot,
|
|
782
|
+
);
|
|
783
|
+
const merged = inlinedFlow as unknown as {
|
|
742
784
|
version: number;
|
|
743
785
|
name: string;
|
|
744
786
|
resetAction?: unknown;
|
|
@@ -1141,6 +1183,9 @@ export async function createProjectImpl(
|
|
|
1141
1183
|
try {
|
|
1142
1184
|
mkdirSync(folderPath, { recursive: true });
|
|
1143
1185
|
writeFileSync(demoFullPath, `${JSON.stringify(scaffold, null, 2)}\n`);
|
|
1186
|
+
const tmpDir = join(folderPath, '.tmp');
|
|
1187
|
+
mkdirSync(tmpDir, { recursive: true });
|
|
1188
|
+
writeFileSync(join(tmpDir, '.gitignore'), '*\n!.gitignore\n');
|
|
1144
1189
|
} catch (err) {
|
|
1145
1190
|
return { kind: 'scaffoldFailed', message: err instanceof Error ? err.message : String(err) };
|
|
1146
1191
|
}
|
|
@@ -1378,7 +1423,7 @@ export async function addFlowBulkImpl(
|
|
|
1378
1423
|
// schema violation surfaces honestly instead of being silently papered over.
|
|
1379
1424
|
// After the flow.json write, `removeNodeDir` cascades the node's whole
|
|
1380
1425
|
// `<project>/nodes/<id>/` folder — covering detail.md, view.html, and any
|
|
1381
|
-
//
|
|
1426
|
+
// type:'image' upload that lived there.
|
|
1382
1427
|
export async function deleteNodeImpl(
|
|
1383
1428
|
deps: OperationsDeps,
|
|
1384
1429
|
flowId: string,
|
|
@@ -1510,6 +1555,22 @@ export async function patchNodeImpl(
|
|
|
1510
1555
|
}
|
|
1511
1556
|
node.data = data;
|
|
1512
1557
|
}
|
|
1558
|
+
// Component spec sidecar — write the pretty-printed JSON to
|
|
1559
|
+
// `<project>/nodes/<id>/spec.json` so the on-disk source of truth stays
|
|
1560
|
+
// in sync. mergeNodeUpdates already put data.spec on the merged tree for
|
|
1561
|
+
// the post-mutation ResolvedFlowSchema parse; splitFlow strips it from
|
|
1562
|
+
// flow.json so we don't double-store the spec.
|
|
1563
|
+
if (node.type === 'component' && updates.spec !== undefined) {
|
|
1564
|
+
const specAbs = nodeFileAbsPath(entry.repoPath, nodeId, 'spec.json');
|
|
1565
|
+
try {
|
|
1566
|
+
writeNodeFile(specAbs, `${JSON.stringify(updates.spec, null, 2)}\n`);
|
|
1567
|
+
} catch (err) {
|
|
1568
|
+
return {
|
|
1569
|
+
kind: 'writeFailed',
|
|
1570
|
+
message: err instanceof Error ? err.message : String(err),
|
|
1571
|
+
};
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1513
1574
|
return { kind: 'ok' };
|
|
1514
1575
|
});
|
|
1515
1576
|
}
|
|
@@ -1765,11 +1826,7 @@ export async function applyLayoutImpl(
|
|
|
1765
1826
|
|
|
1766
1827
|
const flow = flowParse.data;
|
|
1767
1828
|
const result = await computeLayout(
|
|
1768
|
-
flow.nodes.map((n) => ({
|
|
1769
|
-
id: n.id,
|
|
1770
|
-
type: n.type,
|
|
1771
|
-
data: n.type === 'shapeNode' ? { shape: (n.data as { shape?: string }).shape } : undefined,
|
|
1772
|
-
})),
|
|
1829
|
+
flow.nodes.map((n) => ({ id: n.id, type: n.type })),
|
|
1773
1830
|
flow.connectors.map((c) => ({ id: c.id, source: c.source, target: c.target })),
|
|
1774
1831
|
options,
|
|
1775
1832
|
);
|
package/src/runtime.ts
CHANGED
|
@@ -76,3 +76,40 @@ export function isPidAlive(pid: number): boolean {
|
|
|
76
76
|
return false;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
|
+
|
|
80
|
+
// `0.0.0.0` / `::` are wildcard bind addresses, not connectable destinations —
|
|
81
|
+
// probe loopback when the caller passes one. IPv6 unspecified `::` maps to
|
|
82
|
+
// `::1`; everything else is treated as IPv4 wildcard → `127.0.0.1`.
|
|
83
|
+
function probeHost(host: string): string {
|
|
84
|
+
if (host === '::' || host === '[::]') return '::1';
|
|
85
|
+
if (host === '0.0.0.0' || host === '') return '127.0.0.1';
|
|
86
|
+
return host;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Detects whether a TCP listener is responding on host:port. Uses a short
|
|
91
|
+
* connect probe (300 ms default) so we catch actively-listening servers
|
|
92
|
+
* without padding fast paths. Returns false on any connect error or timeout.
|
|
93
|
+
*/
|
|
94
|
+
export async function portInUse(host: string, port: number, timeoutMs = 300): Promise<boolean> {
|
|
95
|
+
const hostname = probeHost(host);
|
|
96
|
+
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
97
|
+
try {
|
|
98
|
+
const socket = await Promise.race([
|
|
99
|
+
Bun.connect({
|
|
100
|
+
hostname,
|
|
101
|
+
port,
|
|
102
|
+
socket: { data() {}, open() {}, close() {}, error() {} },
|
|
103
|
+
}),
|
|
104
|
+
new Promise<never>((_, reject) => {
|
|
105
|
+
timer = setTimeout(() => reject(new Error('timeout')), timeoutMs);
|
|
106
|
+
}),
|
|
107
|
+
]);
|
|
108
|
+
socket.end();
|
|
109
|
+
return true;
|
|
110
|
+
} catch {
|
|
111
|
+
return false;
|
|
112
|
+
} finally {
|
|
113
|
+
if (timer) clearTimeout(timer);
|
|
114
|
+
}
|
|
115
|
+
}
|
package/src/schema-catalog.ts
CHANGED
|
@@ -8,14 +8,20 @@
|
|
|
8
8
|
import type { ZodTypeAny } from 'zod';
|
|
9
9
|
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
10
10
|
import {
|
|
11
|
+
FlowCloudNodeSchema,
|
|
11
12
|
FlowConnectorSchema,
|
|
13
|
+
FlowDatabaseNodeSchema,
|
|
14
|
+
FlowEllipseNodeSchema,
|
|
12
15
|
FlowEnvelopeSchema,
|
|
13
16
|
FlowHtmlNodeSchema,
|
|
14
17
|
FlowIconNodeSchema,
|
|
15
18
|
FlowImageNodeSchema,
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
FlowQueueNodeSchema,
|
|
20
|
+
FlowRectangleNodeSchema,
|
|
21
|
+
FlowServerNodeSchema,
|
|
22
|
+
FlowStickyNodeSchema,
|
|
23
|
+
FlowTextNodeSchema,
|
|
24
|
+
FlowUserNodeSchema,
|
|
19
25
|
PlayActionSchema,
|
|
20
26
|
ResetActionSchema,
|
|
21
27
|
StatusActionSchema,
|
|
@@ -44,7 +50,7 @@ const CATEGORIES: SchemaCategory[] = [
|
|
|
44
50
|
{
|
|
45
51
|
name: 'node',
|
|
46
52
|
description:
|
|
47
|
-
'All
|
|
53
|
+
'All 12 flat node variants (rectangle, ellipse, sticky, text, database, server, user, queue, cloud, image, html, icon). Visual kind is the type; capabilities (playAction / statusAction / stateSource) are independent optional fields on every variant.',
|
|
48
54
|
},
|
|
49
55
|
{
|
|
50
56
|
name: 'connector',
|
|
@@ -64,15 +70,21 @@ const PAYLOADS: Record<string, SchemaPayload> = {
|
|
|
64
70
|
},
|
|
65
71
|
node: {
|
|
66
72
|
schemas: {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
+
rectangle: toJsonSchema(FlowRectangleNodeSchema),
|
|
74
|
+
ellipse: toJsonSchema(FlowEllipseNodeSchema),
|
|
75
|
+
sticky: toJsonSchema(FlowStickyNodeSchema),
|
|
76
|
+
text: toJsonSchema(FlowTextNodeSchema),
|
|
77
|
+
database: toJsonSchema(FlowDatabaseNodeSchema),
|
|
78
|
+
server: toJsonSchema(FlowServerNodeSchema),
|
|
79
|
+
user: toJsonSchema(FlowUserNodeSchema),
|
|
80
|
+
queue: toJsonSchema(FlowQueueNodeSchema),
|
|
81
|
+
cloud: toJsonSchema(FlowCloudNodeSchema),
|
|
82
|
+
image: toJsonSchema(FlowImageNodeSchema),
|
|
83
|
+
html: toJsonSchema(FlowHtmlNodeSchema),
|
|
84
|
+
icon: toJsonSchema(FlowIconNodeSchema),
|
|
73
85
|
},
|
|
74
86
|
notes: [
|
|
75
|
-
"
|
|
87
|
+
"type:'image' data.path must start with 'nodes/<id>/'.",
|
|
76
88
|
"scriptPath in playAction/statusAction is relative to nodes/<nodeId>/ and may not contain '..' or absolute paths.",
|
|
77
89
|
],
|
|
78
90
|
},
|