xonecode 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +49 -7
- package/THIRD_PARTY_NOTICES.md +128 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- package/apps/web/dist/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- package/apps/web/dist/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- package/apps/web/dist/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- package/apps/web/dist/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- package/apps/web/dist/assets/c-BIGW1oBm.js +1 -0
- package/apps/web/dist/assets/cpp-DddhVWhK.js +1 -0
- package/apps/web/dist/assets/csharp-DSvCPggb.js +1 -0
- package/apps/web/dist/assets/css-CLj8gQPS.js +1 -0
- package/apps/web/dist/assets/go-C27-OAKa.js +1 -0
- package/apps/web/dist/assets/html-CfGypltT.js +1 -0
- package/apps/web/dist/assets/index-BB3SamhR.css +1 -0
- package/apps/web/dist/assets/index-CFYAoHdw.js +477 -0
- package/apps/web/dist/assets/ini-BEwlwnbL.js +1 -0
- package/apps/web/dist/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- package/apps/web/dist/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- package/apps/web/dist/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- package/apps/web/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- package/apps/web/dist/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- package/apps/web/dist/assets/java-CylS5w8V.js +1 -0
- package/apps/web/dist/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
- package/apps/web/dist/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
- package/apps/web/dist/assets/kotlin-BdnUsdx6.js +1 -0
- package/apps/web/dist/assets/less-B1dDrJ26.js +1 -0
- package/apps/web/dist/assets/lua-BaeVxFsk.js +1 -0
- package/apps/web/dist/assets/markdown-Cvjx9yec.js +1 -0
- package/apps/web/dist/assets/mdx-Cmh6b_Ma.js +1 -0
- package/apps/web/dist/assets/php-D_2--9WK.js +1 -0
- package/apps/web/dist/assets/python-B6aJPvgy.js +1 -0
- package/apps/web/dist/assets/ruby-DSHSDbk1.js +1 -0
- package/apps/web/dist/assets/rust-B1yitclQ.js +1 -0
- package/apps/web/dist/assets/scss-D5BDwBP9.js +1 -0
- package/apps/web/dist/assets/sql-CRqJ_cUM.js +1 -0
- package/apps/web/dist/assets/swift-C2oV4EkX.js +1 -0
- package/apps/web/dist/assets/toml-vGWfd6FD.js +1 -0
- package/apps/web/dist/assets/xml-sdJ4AIDG.js +1 -0
- package/apps/web/dist/assets/yaml-Buea-lGh.js +1 -0
- package/apps/web/dist/iconos/xonecode.png +0 -0
- package/apps/web/dist/index.html +20 -0
- package/dist/agent/agentesEnDisco.js +539 -0
- package/dist/agent/agentesEnDisco.js.map +1 -0
- package/dist/agent/arbolDeProyecto.js +245 -0
- package/dist/agent/arbolDeProyecto.js.map +1 -0
- package/dist/agent/artefactosEnDisco.js +139 -0
- package/dist/agent/artefactosEnDisco.js.map +1 -0
- package/dist/agent/aumentador.js +175 -0
- package/dist/agent/aumentador.js.map +1 -0
- package/dist/agent/authEnDisco.js +76 -1
- package/dist/agent/authEnDisco.js.map +1 -1
- package/dist/agent/busquedaRegex.js +92 -0
- package/dist/agent/busquedaRegex.js.map +1 -0
- package/dist/agent/catalogoModelos.js +345 -0
- package/dist/agent/catalogoModelos.js.map +1 -0
- package/dist/agent/checkpointer.js +127 -0
- package/dist/agent/checkpointer.js.map +1 -0
- package/dist/agent/cloudstudioClient.js +194 -0
- package/dist/agent/cloudstudioClient.js.map +1 -0
- package/dist/agent/cloudstudioMcp.js +722 -0
- package/dist/agent/cloudstudioMcp.js.map +1 -0
- package/dist/agent/configEnDisco.js +309 -11
- package/dist/agent/configEnDisco.js.map +1 -1
- package/dist/agent/consumoExterno.js +99 -0
- package/dist/agent/consumoExterno.js.map +1 -0
- package/dist/agent/descarga.js +149 -0
- package/dist/agent/descarga.js.map +1 -0
- package/dist/agent/diagnosticoDeTools.js +51 -0
- package/dist/agent/diagnosticoDeTools.js.map +1 -0
- package/dist/agent/dispositivosEnMaquina.js +596 -0
- package/dist/agent/dispositivosEnMaquina.js.map +1 -0
- package/dist/agent/escrituraDeCodex.js +134 -0
- package/dist/agent/escrituraDeCodex.js.map +1 -0
- package/dist/agent/escrituraDeOpencode.js +80 -0
- package/dist/agent/escrituraDeOpencode.js.map +1 -0
- package/dist/agent/escrituraExterna.js +742 -0
- package/dist/agent/escrituraExterna.js.map +1 -0
- package/dist/agent/gemini.js +81 -0
- package/dist/agent/gemini.js.map +1 -0
- package/dist/agent/git.js +83 -0
- package/dist/agent/git.js.map +1 -0
- package/dist/agent/gitSync.js +441 -0
- package/dist/agent/gitSync.js.map +1 -0
- package/dist/agent/hotswap.js +448 -0
- package/dist/agent/hotswap.js.map +1 -0
- package/dist/agent/instalacionEnMaquina.js +252 -0
- package/dist/agent/instalacionEnMaquina.js.map +1 -0
- package/dist/agent/instantanea.js +7 -10
- package/dist/agent/instantanea.js.map +1 -1
- package/dist/agent/interrupts.js +11 -2
- package/dist/agent/interrupts.js.map +1 -1
- package/dist/agent/juezDeTarea.js +345 -0
- package/dist/agent/juezDeTarea.js.map +1 -0
- package/dist/agent/lanzamientoEnMaquina.js +377 -0
- package/dist/agent/lanzamientoEnMaquina.js.map +1 -0
- package/dist/agent/manifiesto.js +59 -0
- package/dist/agent/manifiesto.js.map +1 -0
- package/dist/agent/memoriaDeProyecto.js +60 -0
- package/dist/agent/memoriaDeProyecto.js.map +1 -0
- package/dist/agent/modelos.js +105 -17
- package/dist/agent/modelos.js.map +1 -1
- package/dist/agent/modelosDeMotor.js +204 -0
- package/dist/agent/modelosDeMotor.js.map +1 -0
- package/dist/agent/paqueteDelProyecto.js +181 -0
- package/dist/agent/paqueteDelProyecto.js.map +1 -0
- package/dist/agent/perfiles.js +92 -31
- package/dist/agent/perfiles.js.map +1 -1
- package/dist/agent/persona.js +40 -0
- package/dist/agent/persona.js.map +1 -0
- package/dist/agent/procesosEnMaquina.js +204 -0
- package/dist/agent/procesosEnMaquina.js.map +1 -0
- package/dist/agent/proyecto.js +311 -13
- package/dist/agent/proyecto.js.map +1 -1
- package/dist/agent/puente.js +63 -6
- package/dist/agent/puente.js.map +1 -1
- package/dist/agent/resumenDeContexto.js +30 -0
- package/dist/agent/resumenDeContexto.js.map +1 -0
- package/dist/agent/resumenDeTool.js +64 -18
- package/dist/agent/resumenDeTool.js.map +1 -1
- package/dist/agent/sesionGit.js +481 -0
- package/dist/agent/sesionGit.js.map +1 -0
- package/dist/agent/settingsEnDisco.js +189 -0
- package/dist/agent/settingsEnDisco.js.map +1 -0
- package/dist/agent/subagenteCodex.js +368 -0
- package/dist/agent/subagenteCodex.js.map +1 -0
- package/dist/agent/subagenteExterno.js +423 -0
- package/dist/agent/subagenteExterno.js.map +1 -0
- package/dist/agent/subagenteOpencode.js +389 -0
- package/dist/agent/subagenteOpencode.js.map +1 -0
- package/dist/agent/subida.js +202 -0
- package/dist/agent/subida.js.map +1 -0
- package/dist/agent/tareasEnDisco.js +466 -0
- package/dist/agent/tareasEnDisco.js.map +1 -0
- package/dist/agent/turnoReal.js +663 -50
- package/dist/agent/turnoReal.js.map +1 -1
- package/dist/agent/versionEnDisco.js +48 -0
- package/dist/agent/versionEnDisco.js.map +1 -0
- package/dist/agent/xoneAgent.js +290 -49
- package/dist/agent/xoneAgent.js.map +1 -1
- package/dist/agent/zip.js +52 -0
- package/dist/agent/zip.js.map +1 -0
- package/dist/cli/acuseDeModelo.js +30 -0
- package/dist/cli/acuseDeModelo.js.map +1 -0
- package/dist/cli/aprobar.js +55 -2
- package/dist/cli/aprobar.js.map +1 -1
- package/dist/cli/config.js +9 -13
- package/dist/cli/config.js.map +1 -1
- package/dist/cli/consola.js +750 -19
- package/dist/cli/consola.js.map +1 -1
- package/dist/cli/main.js +740 -81
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/markdown.js +188 -2
- package/dist/cli/markdown.js.map +1 -1
- package/dist/cli/run.js +32 -4
- package/dist/cli/run.js.map +1 -1
- package/dist/cli/stdio.js +46 -19
- package/dist/cli/stdio.js.map +1 -1
- package/dist/cli/tema.js +96 -8
- package/dist/cli/tema.js.map +1 -1
- package/dist/cli/tokens.js +29 -0
- package/dist/cli/tokens.js.map +1 -0
- package/dist/cli/tui/app.js +171 -0
- package/dist/cli/tui/app.js.map +1 -0
- package/dist/cli/tui/aprobarTui.js +80 -0
- package/dist/cli/tui/aprobarTui.js.map +1 -0
- package/dist/cli/tui/barra.js +37 -0
- package/dist/cli/tui/barra.js.map +1 -0
- package/dist/cli/tui/correrTui.js +478 -0
- package/dist/cli/tui/correrTui.js.map +1 -0
- package/dist/cli/tui/entrada.js +166 -0
- package/dist/cli/tui/entrada.js.map +1 -0
- package/dist/cli/tui/filas.js +24 -0
- package/dist/cli/tui/filas.js.map +1 -0
- package/dist/cli/tui/logo.js +49 -0
- package/dist/cli/tui/logo.js.map +1 -0
- package/dist/cli/tui/pielTui.js +14 -0
- package/dist/cli/tui/pielTui.js.map +1 -0
- package/dist/cli/tui/raton.js +126 -0
- package/dist/cli/tui/raton.js.map +1 -0
- package/dist/cli/tui/sidebar.js +86 -0
- package/dist/cli/tui/sidebar.js.map +1 -0
- package/dist/cli/tui/store.js +143 -0
- package/dist/cli/tui/store.js.map +1 -0
- package/dist/cli/tui/tarjeta.js +19 -0
- package/dist/cli/tui/tarjeta.js.map +1 -0
- package/dist/cli/tui/temaInk.js +114 -0
- package/dist/cli/tui/temaInk.js.map +1 -0
- package/dist/cli/tui/transcript.js +254 -0
- package/dist/cli/tui/transcript.js.map +1 -0
- package/dist/cli/wizardInicial.js +240 -0
- package/dist/cli/wizardInicial.js.map +1 -0
- package/dist/core/actos.js +113 -0
- package/dist/core/actos.js.map +1 -0
- package/dist/core/adjuntos.js +97 -0
- package/dist/core/adjuntos.js.map +1 -0
- package/dist/core/agentes.js +227 -0
- package/dist/core/agentes.js.map +1 -0
- package/dist/core/artefactos.js +149 -0
- package/dist/core/artefactos.js.map +1 -0
- package/dist/core/cloudstudio.js +7 -0
- package/dist/core/cloudstudio.js.map +1 -0
- package/dist/core/config.js +212 -5
- package/dist/core/config.js.map +1 -1
- package/dist/core/contextos.js +53 -1
- package/dist/core/contextos.js.map +1 -1
- package/dist/core/descargas.js +106 -0
- package/dist/core/descargas.js.map +1 -0
- package/dist/core/descriptoresDeApp.js +279 -0
- package/dist/core/descriptoresDeApp.js.map +1 -0
- package/dist/core/dispositivos.js +466 -0
- package/dist/core/dispositivos.js.map +1 -0
- package/dist/core/entrega.js +141 -0
- package/dist/core/entrega.js.map +1 -0
- package/dist/core/entrelazar.js +90 -0
- package/dist/core/entrelazar.js.map +1 -0
- package/dist/core/esqueleto.js +10 -0
- package/dist/core/esqueleto.js.map +1 -1
- package/dist/core/modelos.js +235 -3
- package/dist/core/modelos.js.map +1 -1
- package/dist/core/notify.js +27 -9
- package/dist/core/notify.js.map +1 -1
- package/dist/core/planDeSubida.js +127 -0
- package/dist/core/planDeSubida.js.map +1 -0
- package/dist/core/ports.js +189 -1
- package/dist/core/ports.js.map +1 -1
- package/dist/core/puedeLanzarse.js +239 -0
- package/dist/core/puedeLanzarse.js.map +1 -0
- package/dist/core/rutaVirtual.js +42 -0
- package/dist/core/rutaVirtual.js.map +1 -0
- package/dist/core/settings.js +246 -0
- package/dist/core/settings.js.map +1 -0
- package/dist/core/tareas.js +230 -0
- package/dist/core/tareas.js.map +1 -0
- package/dist/core/textos.js +36 -0
- package/dist/core/textos.js.map +1 -0
- package/dist/core/turno.js +56 -8
- package/dist/core/turno.js.map +1 -1
- package/dist/core/version.js +31 -0
- package/dist/core/version.js.map +1 -0
- package/dist/vendor/tokenTracking.js +22 -1
- package/dist/vendor/tokenTracking.js.map +1 -1
- package/dist/web/servidor/arranque.js +3964 -0
- package/dist/web/servidor/arranque.js.map +1 -0
- package/dist/web/servidor/consolaDeTarea.js +161 -0
- package/dist/web/servidor/consolaDeTarea.js.map +1 -0
- package/dist/web/servidor/consolaWeb.js +389 -0
- package/dist/web/servidor/consolaWeb.js.map +1 -0
- package/dist/web/servidor/corredorDeTareas.js +1119 -0
- package/dist/web/servidor/corredorDeTareas.js.map +1 -0
- package/dist/web/servidor/pielWeb.js +269 -0
- package/dist/web/servidor/pielWeb.js.map +1 -0
- package/dist/web/servidor/servidor.js +257 -0
- package/dist/web/servidor/servidor.js.map +1 -0
- package/dist/web/servidor/sesiones.js +440 -0
- package/dist/web/servidor/sesiones.js.map +1 -0
- package/dist/web/servidor/transporte.js +169 -0
- package/dist/web/servidor/transporte.js.map +1 -0
- package/dist/web/servidor/vestibulo.js +1276 -0
- package/dist/web/servidor/vestibulo.js.map +1 -0
- package/package.json +30 -8
- package/skills/archify/LICENSE +22 -0
- package/skills/archify/SKILL.md +109 -0
- package/skills/archify/THIRD_PARTY_NOTICES.md +56 -0
- package/skills/archify/assets/template.html +14787 -0
- package/skills/archify/bin/archify.mjs +1990 -0
- package/skills/archify/bin/open-artifact.mjs +86 -0
- package/skills/archify/bin/preview.mjs +648 -0
- package/skills/archify/bin/visual-check.mjs +829 -0
- package/skills/archify/brand-marks/README.md +31 -0
- package/skills/archify/brand-marks/catalog.json +131 -0
- package/skills/archify/delta/architecture-delta.mjs +1214 -0
- package/skills/archify/examples/agent-run.lifecycle.json +71 -0
- package/skills/archify/examples/agent-tool-call.workflow.json +94 -0
- package/skills/archify/examples/async-job-roundtrip.sequence.json +61 -0
- package/skills/archify/examples/brand-aware-delivery.architecture.json +47 -0
- package/skills/archify/examples/cache-miss-request.sequence.json +81 -0
- package/skills/archify/examples/checkout-platform.base.architecture.json +31 -0
- package/skills/archify/examples/checkout-platform.head.architecture.json +31 -0
- package/skills/archify/examples/dataflow-product-analytics.html +14898 -0
- package/skills/archify/examples/deployment-release.lifecycle.json +49 -0
- package/skills/archify/examples/event-stream.dataflow.json +57 -0
- package/skills/archify/examples/incident-response.workflow.json +64 -0
- package/skills/archify/examples/lifecycle-agent-run.html +14847 -0
- package/skills/archify/examples/product-analytics.dataflow.json +76 -0
- package/skills/archify/examples/production-deployment.architecture.json +71 -0
- package/skills/archify/examples/release-delivery.workflow.json +62 -0
- package/skills/archify/examples/sequence-cache-miss-request.html +14913 -0
- package/skills/archify/examples/web-app-rendered.html +14862 -0
- package/skills/archify/examples/web-app.architecture.json +46 -0
- package/skills/archify/examples/workflow-agent-tool-call-rendered.html +14904 -0
- package/skills/archify/migrations/workflow-v2.mjs +279 -0
- package/skills/archify/package.json +17 -0
- package/skills/archify/recipes/scenarios.mjs +391 -0
- package/skills/archify/references/authoring-contract.md +198 -0
- package/skills/archify/references/brand-marks.md +69 -0
- package/skills/archify/references/delivery-contract.md +116 -0
- package/skills/archify/references/viewer-runtime.md +45 -0
- package/skills/archify/renderers/architecture/grid.mjs +62 -0
- package/skills/archify/renderers/architecture/render-architecture.mjs +1078 -0
- package/skills/archify/renderers/dataflow/README.md +104 -0
- package/skills/archify/renderers/dataflow/render-dataflow.mjs +483 -0
- package/skills/archify/renderers/lifecycle/README.md +115 -0
- package/skills/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
- package/skills/archify/renderers/sequence/README.md +114 -0
- package/skills/archify/renderers/sequence/render-sequence.mjs +453 -0
- package/skills/archify/renderers/shared/brand-marks.mjs +563 -0
- package/skills/archify/renderers/shared/cli.mjs +218 -0
- package/skills/archify/renderers/shared/desktop-readability.mjs +26 -0
- package/skills/archify/renderers/shared/diagnostics.mjs +127 -0
- package/skills/archify/renderers/shared/engineering-profiles.mjs +157 -0
- package/skills/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
- package/skills/archify/renderers/shared/generated-validators.mjs +13 -0
- package/skills/archify/renderers/shared/geometry.mjs +1423 -0
- package/skills/archify/renderers/shared/i18n.mjs +594 -0
- package/skills/archify/renderers/shared/layout-report.mjs +40 -0
- package/skills/archify/renderers/shared/legend.mjs +217 -0
- package/skills/archify/renderers/shared/output-path.mjs +321 -0
- package/skills/archify/renderers/shared/repository-evidence.mjs +235 -0
- package/skills/archify/renderers/shared/text-fit.mjs +49 -0
- package/skills/archify/renderers/shared/utils.mjs +232 -0
- package/skills/archify/renderers/shared/validator.mjs +86 -0
- package/skills/archify/renderers/workflow/README.md +223 -0
- package/skills/archify/renderers/workflow/render-workflow.mjs +35 -0
- package/skills/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
- package/skills/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
- package/skills/archify/schemas/README.md +211 -0
- package/skills/archify/schemas/architecture.schema.json +176 -0
- package/skills/archify/schemas/common.schema.json +115 -0
- package/skills/archify/schemas/dataflow.schema.json +243 -0
- package/skills/archify/schemas/lifecycle.schema.json +266 -0
- package/skills/archify/schemas/sequence.schema.json +223 -0
- package/skills/archify/schemas/workflow.schema.json +428 -0
- package/skills/archify/scripts/check-render-output.mjs +835 -0
- package/skills/archify/scripts/check-update.mjs +1667 -0
- package/skills/archify/scripts/render-examples.mjs +26 -0
- package/skills/archify/scripts/update-contract.mjs +182 -0
- package/skills/archify/skill-release.json +10 -0
- package/skills/artifacts-builder/SKILL.md +120 -0
- package/skills/artifacts-builder/reference/diagramas.md +123 -0
- package/skills/artifacts-builder/reference/estilo.md +123 -0
- package/skills/artifacts-builder/reference/exportar-pdf.md +56 -0
- package/skills/artifacts-builder/reference/graficas.md +22 -0
- package/skills/artifacts-builder/scripts/bundle_artifact.py +109 -0
- package/skills/artifacts-builder/scripts/validate_artifact.py +345 -0
- package/skills/xone-hotswap/SKILL.md +185 -0
- package/skills/xone-hotswap/references/comandos.md +706 -0
- package/skills/xone-hotswap/references/conexion-y-despliegue.md +226 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# Data Flow Renderer
|
|
2
|
+
|
|
3
|
+
Render `diagram_type: "dataflow"` JSON files into the standard Archify HTML
|
|
4
|
+
template.
|
|
5
|
+
|
|
6
|
+
```bash
|
|
7
|
+
node archify/renderers/dataflow/render-dataflow.mjs input.dataflow.json output.html
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
The renderer validates input against `archify/schemas/dataflow.schema.json`
|
|
11
|
+
with the bundled standalone validator. No dependency installation is required.
|
|
12
|
+
|
|
13
|
+
If `output.html` is omitted, the renderer uses `meta.output` from the JSON file
|
|
14
|
+
or falls back to `dataflow.html` in the current working directory.
|
|
15
|
+
|
|
16
|
+
## Input
|
|
17
|
+
|
|
18
|
+
Data-flow JSON files must set:
|
|
19
|
+
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"schema_version": 1,
|
|
23
|
+
"diagram_type": "dataflow",
|
|
24
|
+
"meta": {
|
|
25
|
+
"title": "Product Analytics Data Flow",
|
|
26
|
+
"viewBox": [940, 720]
|
|
27
|
+
},
|
|
28
|
+
"stages": [],
|
|
29
|
+
"nodes": [],
|
|
30
|
+
"flows": [],
|
|
31
|
+
"cards": []
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
A complete worked example lives at
|
|
36
|
+
`archify/examples/product-analytics.dataflow.json`.
|
|
37
|
+
|
|
38
|
+
The schema lives at:
|
|
39
|
+
|
|
40
|
+
```text
|
|
41
|
+
archify/schemas/dataflow.schema.json
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Legend
|
|
45
|
+
|
|
46
|
+
The default visual legend derives kinds from `flows[].variant` (omitting
|
|
47
|
+
`variant` means `default`) and adds `database` only when a database node exists.
|
|
48
|
+
Supported `meta.legend.entries` keys, in stable order, are `emphasis`,
|
|
49
|
+
`security`, `dashed`, `database`, and `default`. Flow variants remain
|
|
50
|
+
visual-only because Archify has no compiled edge-kind facts in this slice. A
|
|
51
|
+
present `database` entry is different: it comes from exact
|
|
52
|
+
`nodes[].type: "database"` facts, so it publishes the normal Semantic Legend
|
|
53
|
+
count, accessible name, and keyboard interaction. Forcing `database` visible
|
|
54
|
+
without a database node keeps it visual-only.
|
|
55
|
+
|
|
56
|
+
## Layout budget
|
|
57
|
+
|
|
58
|
+
| Constant | Value |
|
|
59
|
+
|----------|-------|
|
|
60
|
+
| viewBox | default `[940, 720]`; schema minimum `[360, 360]` |
|
|
61
|
+
| Stages (2–5) | centers at x = 100 + stage×215; stage band 168 wide, header at y 46 |
|
|
62
|
+
| Row tops (`row` 0–4) | y = 128, 242, 356, 470, 584 (plus `yOffset`) |
|
|
63
|
+
| Default node | 112×58 |
|
|
64
|
+
| Node area | x within `[24, width − 24]`; y within `[104, height − 74]` |
|
|
65
|
+
| Node spacing | ≥10px between any two nodes (checked across stages and rows) |
|
|
66
|
+
| Flow length | ≥34px between endpoints |
|
|
67
|
+
| Legend row | y = height − 36 |
|
|
68
|
+
|
|
69
|
+
Route presets for flows: `straight`, `vertical-channel`, `bottom-channel`,
|
|
70
|
+
`top-channel`, explicit `via` points, or the default `auto` (midpoint elbow).
|
|
71
|
+
|
|
72
|
+
## Design Rules
|
|
73
|
+
|
|
74
|
+
- Use stages for data lifecycle boundaries: source, ingest, process, store,
|
|
75
|
+
consume.
|
|
76
|
+
- Place nodes by stage index and row index; do not hand-place raw SVG for the
|
|
77
|
+
common case.
|
|
78
|
+
- Use flow labels to name the data asset, not the transport primitive:
|
|
79
|
+
`clickstream`, `identity map`, `normalized facts`, `feature vectors`.
|
|
80
|
+
- Use `classification` for short sensitivity or governance context:
|
|
81
|
+
`PII touch`, `non-PII`, `approved only`, `batch`, `read-only`.
|
|
82
|
+
- Use `security` for PII, policy, consent, access-control, or restricted joins.
|
|
83
|
+
- Use `emphasis` for the primary data path and `dashed` for async or batch
|
|
84
|
+
derivations.
|
|
85
|
+
- Keep labels short enough to fit in narrow previews.
|
|
86
|
+
|
|
87
|
+
Schema violations exit non-zero with path-prefixed messages annotated with the
|
|
88
|
+
element's id or label. The renderer additionally fails when it can detect
|
|
89
|
+
layout problems, including missing stages, duplicate node IDs, nodes outside
|
|
90
|
+
the readable diagram area, node overlap, labels colliding with nodes or other
|
|
91
|
+
labels, labels wider than their node, unknown flow endpoints, missing flow
|
|
92
|
+
labels, unreadably short flows, flows crossing unrelated nodes (2px Clean Flow
|
|
93
|
+
clearance), or stages that exceed the viewBox. Stage frames remain intentional
|
|
94
|
+
pass-through containers. Text width
|
|
95
|
+
is estimated CJK-aware: fullwidth glyphs count as two units.
|
|
96
|
+
|
|
97
|
+
Set `meta.quality_profile` to `showcase` for polished delivery. Unrelated proper
|
|
98
|
+
X crossings then fail with `composition/proper-crossing`; default `standard`
|
|
99
|
+
keeps them as artifact-receipt warnings. Collinear stage corridors are outside
|
|
100
|
+
the proper-X rule, but a separate gate warns in `standard` and fails in
|
|
101
|
+
`showcase` when unrelated flows overlap for at least 8px. Shared semantic
|
|
102
|
+
endpoints, point touches, and shorter overlaps remain valid. Showcase also
|
|
103
|
+
rejects any route segment below 8px and any interior turn segment below 16px;
|
|
104
|
+
ordinary 8–15px endpoint stubs remain valid.
|
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
import { esc, renderDefinitions, renderSemanticSigil, textUnits } from '../shared/utils.mjs';
|
|
4
|
+
import { animateAttr, focusEdgeAttrs, focusNodeAttrs, focusNodeTitle, loadDiagramWithBrandMarks, writeDiagram, svgAccessibleText, svgRootAttrs } from '../shared/cli.mjs';
|
|
5
|
+
import { throwDiagnosticProblems } from '../shared/diagnostics.mjs';
|
|
6
|
+
import { resolveLegend, renderLegend as renderResolvedLegend } from '../shared/legend.mjs';
|
|
7
|
+
import { availableNodeTextWidth, fittedNodeFontSize, minimumNodeTextWidth } from '../shared/text-fit.mjs';
|
|
8
|
+
import { brandLabelFitWidth, brandMetadataFor, brandTopRailProblem, renderBrandMark } from '../shared/brand-marks.mjs';
|
|
9
|
+
import { translateMessage as i18nText } from '../shared/i18n.mjs';
|
|
10
|
+
import {
|
|
11
|
+
asArray,
|
|
12
|
+
isFinitePoint,
|
|
13
|
+
rectsOverlap,
|
|
14
|
+
cleanEndpointSideProblems,
|
|
15
|
+
cleanFlowProblems,
|
|
16
|
+
cleanCrossingProblems,
|
|
17
|
+
cleanAmbiguousCorridorProblems,
|
|
18
|
+
cleanBorderRunProblems,
|
|
19
|
+
cleanRouteRhythmProblems,
|
|
20
|
+
cleanLabelRouteClearanceProblems,
|
|
21
|
+
suggestLabelObstacleFix,
|
|
22
|
+
suggestLabelPairFix,
|
|
23
|
+
anchor,
|
|
24
|
+
automaticPortSpread,
|
|
25
|
+
defaultFromSide,
|
|
26
|
+
defaultToSide,
|
|
27
|
+
chosenSide,
|
|
28
|
+
polylinePath,
|
|
29
|
+
routePointsValue,
|
|
30
|
+
labelPoint,
|
|
31
|
+
componentFill,
|
|
32
|
+
componentText,
|
|
33
|
+
arrowClassMap,
|
|
34
|
+
variantAccent
|
|
35
|
+
} from '../shared/geometry.mjs';
|
|
36
|
+
|
|
37
|
+
const nodeTextFit = {
|
|
38
|
+
sublabelPreferred: 7,
|
|
39
|
+
sublabelMinimum: 6,
|
|
40
|
+
tagPreferred: 7,
|
|
41
|
+
tagMinimum: 6,
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
45
|
+
const { diagram: dataflow, template, outPath } = await loadDiagramWithBrandMarks({
|
|
46
|
+
rendererDir: __dirname,
|
|
47
|
+
diagramType: 'dataflow',
|
|
48
|
+
defaultExample: 'product-analytics.dataflow.json'
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const viewBox = dataflow.meta?.viewBox || [940, 720];
|
|
52
|
+
const layout = {
|
|
53
|
+
stageY: 46,
|
|
54
|
+
stageH: 36,
|
|
55
|
+
stageBottomPad: 74,
|
|
56
|
+
leftX: 100,
|
|
57
|
+
colGap: 215,
|
|
58
|
+
stageW: 168,
|
|
59
|
+
nodeW: 112,
|
|
60
|
+
nodeH: 58,
|
|
61
|
+
rowYs: [128, 242, 356, 470, 584],
|
|
62
|
+
labelH: 16
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function flowLabelSize(flow) {
|
|
66
|
+
const longestLine = Math.max(textUnits(flow.label), textUnits(flow.classification || ''));
|
|
67
|
+
return {
|
|
68
|
+
width: Math.round(Math.max(34, longestLine * 4.9 + 12) * 10) / 10,
|
|
69
|
+
height: flow.classification ? 27 : layout.labelH,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function stageX(index) {
|
|
74
|
+
return layout.leftX + index * layout.colGap;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function stageFrame(stage, index) {
|
|
78
|
+
return {
|
|
79
|
+
id: index,
|
|
80
|
+
label: stage.label,
|
|
81
|
+
kind: 'stage',
|
|
82
|
+
x: stageX(index) - layout.stageW / 2,
|
|
83
|
+
y: layout.stageY,
|
|
84
|
+
width: layout.stageW,
|
|
85
|
+
height: viewBox[1] - layout.stageY - layout.stageBottomPad,
|
|
86
|
+
radius: 10,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const compositionFrames = asArray(dataflow.stages).map(stageFrame);
|
|
91
|
+
|
|
92
|
+
function measureNode(node) {
|
|
93
|
+
const width = node.width || layout.nodeW;
|
|
94
|
+
const height = node.height || layout.nodeH;
|
|
95
|
+
const cx = stageX(node.stage);
|
|
96
|
+
const y = layout.rowYs[node.row] + (node.yOffset || 0);
|
|
97
|
+
return {
|
|
98
|
+
...node,
|
|
99
|
+
width,
|
|
100
|
+
height,
|
|
101
|
+
cx,
|
|
102
|
+
cy: y + height / 2,
|
|
103
|
+
x: cx - width / 2,
|
|
104
|
+
y
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const nodes = new Map(asArray(dataflow.nodes).map((node) => [node.id, measureNode(node)]));
|
|
109
|
+
const nodeSteps = new Map();
|
|
110
|
+
for (const [index, flow] of asArray(dataflow.flows).entries()) {
|
|
111
|
+
if (!nodeSteps.has(flow.from)) nodeSteps.set(flow.from, index);
|
|
112
|
+
if (!nodeSteps.has(flow.to)) nodeSteps.set(flow.to, index + 1);
|
|
113
|
+
}
|
|
114
|
+
for (const [index, node] of asArray(dataflow.nodes).entries()) {
|
|
115
|
+
if (!nodeSteps.has(node.id)) nodeSteps.set(node.id, index);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function validateDataflow() {
|
|
119
|
+
const problems = [];
|
|
120
|
+
if (nodes.size !== asArray(dataflow.nodes).length) problems.push('Node ids must be unique.');
|
|
121
|
+
|
|
122
|
+
const stageCount = asArray(dataflow.stages).length;
|
|
123
|
+
for (const node of nodes.values()) {
|
|
124
|
+
if (typeof node.stage !== 'number' || node.stage < 0 || node.stage >= stageCount) {
|
|
125
|
+
problems.push(`Node "${node.id}" uses invalid stage ${node.stage} — valid stages are 0..${stageCount - 1}.`);
|
|
126
|
+
}
|
|
127
|
+
if (typeof node.row !== 'number' || node.row < 0 || node.row >= layout.rowYs.length) {
|
|
128
|
+
problems.push(`Node "${node.id}" uses invalid row ${node.row} — valid rows are 0..${layout.rowYs.length - 1}.`);
|
|
129
|
+
}
|
|
130
|
+
if (!isFinitePoint(node.x, node.y, node.cx, node.cy)) {
|
|
131
|
+
problems.push(`Node "${node.id}" produced non-finite coordinates — check stage, row, width, height, and yOffset are numbers.`);
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
if (node.x < 24 || node.x + node.width > viewBox[0] - 24) {
|
|
135
|
+
problems.push(`Node "${node.id}" exceeds the horizontal bounds of the viewBox — reduce node.width or increase meta.viewBox[0].`);
|
|
136
|
+
}
|
|
137
|
+
if (node.y < layout.stageY + layout.stageH + 22 || node.y + node.height > viewBox[1] - layout.stageBottomPad) {
|
|
138
|
+
problems.push(`Node "${node.id}" exceeds the readable diagram area — keep y between ${layout.stageY + layout.stageH + 22} and ${viewBox[1] - layout.stageBottomPad} (adjust row/yOffset or increase meta.viewBox[1]).`);
|
|
139
|
+
}
|
|
140
|
+
const estLabelW = textUnits(node.label) * 6.2;
|
|
141
|
+
if (estLabelW > node.width + 6) {
|
|
142
|
+
problems.push(`Label "${node.label}" (~${Math.round(estLabelW)}px) is wider than node "${node.id}" (${node.width}px) — shorten the label or increase node.width.`);
|
|
143
|
+
}
|
|
144
|
+
const brandRailProblem = brandTopRailProblem(node, node.width, 8);
|
|
145
|
+
if (brandRailProblem) problems.push(brandRailProblem);
|
|
146
|
+
// sublabel and tag render as single unwrapped <text> elements; shrink-to-fit
|
|
147
|
+
// handles the ordinary case, this rejects what it cannot rescue.
|
|
148
|
+
const availableTextW = availableNodeTextWidth(node.width);
|
|
149
|
+
for (const [field, value, minimum] of [
|
|
150
|
+
['Sublabel', node.sublabel, nodeTextFit.sublabelMinimum],
|
|
151
|
+
['Tag', node.tag, nodeTextFit.tagMinimum],
|
|
152
|
+
]) {
|
|
153
|
+
if (!value) continue;
|
|
154
|
+
const minimumW = minimumNodeTextWidth(value, minimum);
|
|
155
|
+
if (minimumW > availableTextW) {
|
|
156
|
+
problems.push(`${field} "${value}" needs ~${Math.ceil(minimumW)}px at the ${minimum}px legible minimum, but node "${node.id}" provides ${availableTextW}px — shorten the ${field.toLowerCase()} or increase node.width.`);
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const nodeList = asArray(dataflow.nodes);
|
|
162
|
+
for (let i = 0; i < nodeList.length; i += 1) {
|
|
163
|
+
for (let j = i + 1; j < nodeList.length; j += 1) {
|
|
164
|
+
const a = nodes.get(nodeList[i].id);
|
|
165
|
+
const b = nodes.get(nodeList[j].id);
|
|
166
|
+
if (rectsOverlap(a, b, 10)) {
|
|
167
|
+
problems.push(`Nodes "${a.id}" and "${b.id}" are less than 10px apart — move one to another stage/row or adjust yOffset.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
for (const flow of asArray(dataflow.flows)) {
|
|
173
|
+
if (!nodes.has(flow.from)) problems.push(`Flow "${flow.label || flow.from}" references unknown source "${flow.from}".`);
|
|
174
|
+
if (!nodes.has(flow.to)) problems.push(`Flow "${flow.label || flow.to}" references unknown target "${flow.to}".`);
|
|
175
|
+
if (!flow.label) problems.push(`Flow "${flow.from}" -> "${flow.to}" must include a short data label.`);
|
|
176
|
+
if (nodes.has(flow.from) && nodes.has(flow.to)) {
|
|
177
|
+
const routed = pathFor(flow);
|
|
178
|
+
const [start, end] = [routed.points[0], routed.points[routed.points.length - 1]];
|
|
179
|
+
const distance = Math.hypot(end[0] - start[0], end[1] - start[1]);
|
|
180
|
+
if (distance < 34) problems.push(`Flow "${flow.label}" is too short (${Math.round(distance)}px; minimum 34px) — route it through a channel or spread its nodes.`);
|
|
181
|
+
if (Array.isArray(flow.via)) {
|
|
182
|
+
for (let segmentIndex = 0; segmentIndex < routed.points.length - 1; segmentIndex += 1) {
|
|
183
|
+
const segmentStart = routed.points[segmentIndex];
|
|
184
|
+
const segmentEnd = routed.points[segmentIndex + 1];
|
|
185
|
+
const isDiagonal = Math.abs(segmentStart[0] - segmentEnd[0]) > 0.01
|
|
186
|
+
&& Math.abs(segmentStart[1] - segmentEnd[1]) > 0.01;
|
|
187
|
+
if (!isDiagonal) continue;
|
|
188
|
+
const viaIndex = Math.min(segmentIndex, flow.via.length - 1);
|
|
189
|
+
problems.push(`Flow "${flow.label}" has a diagonal segment from (${segmentStart.join(', ')}) to (${segmentEnd.join(', ')}) — align via[${viaIndex}] with its adjacent point by sharing the same x or y coordinate.`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
problems.push(...cleanEndpointSideProblems({
|
|
196
|
+
relations: dataflow.flows,
|
|
197
|
+
endpointIds: new Set(nodes.keys()),
|
|
198
|
+
pathFor,
|
|
199
|
+
diagramType: 'dataflow',
|
|
200
|
+
relationCollection: 'flows',
|
|
201
|
+
fromSideFor: (flow) => flowSides(flow).fromSide,
|
|
202
|
+
toSideFor: (flow) => flowSides(flow).toSide,
|
|
203
|
+
routeHint: 'keep automatic routing, or choose fromSide/toSide and via points whose first and final segments cross node borders perpendicularly',
|
|
204
|
+
}));
|
|
205
|
+
problems.push(...cleanFlowProblems({
|
|
206
|
+
relations: dataflow.flows,
|
|
207
|
+
obstacles: nodes.values(),
|
|
208
|
+
pathFor,
|
|
209
|
+
diagramType: 'dataflow',
|
|
210
|
+
relationCollection: 'flows',
|
|
211
|
+
obstacleKind: 'node',
|
|
212
|
+
routeHint: 'adjust fromSide/toSide, set route/via or channelX/channelY, or move the node to another stage/row'
|
|
213
|
+
}));
|
|
214
|
+
problems.push(...cleanCrossingProblems({
|
|
215
|
+
relations: dataflow.flows,
|
|
216
|
+
endpointIds: new Set(nodes.keys()),
|
|
217
|
+
pathFor,
|
|
218
|
+
diagramType: 'dataflow',
|
|
219
|
+
relationCollection: 'flows',
|
|
220
|
+
profile: dataflow.meta?.quality_profile,
|
|
221
|
+
routeHint: 'adjust route/via or channelX/channelY so the flows use separate stage corridors'
|
|
222
|
+
}));
|
|
223
|
+
problems.push(...cleanAmbiguousCorridorProblems({
|
|
224
|
+
relations: dataflow.flows,
|
|
225
|
+
endpointIds: new Set(nodes.keys()),
|
|
226
|
+
pathFor,
|
|
227
|
+
diagramType: 'dataflow',
|
|
228
|
+
relationCollection: 'flows',
|
|
229
|
+
profile: dataflow.meta?.quality_profile,
|
|
230
|
+
routeHint: 'adjust route/via or channelX/channelY so unrelated flows do not visually merge'
|
|
231
|
+
}));
|
|
232
|
+
problems.push(...cleanBorderRunProblems({
|
|
233
|
+
relations: dataflow.flows,
|
|
234
|
+
endpointIds: new Set(nodes.keys()),
|
|
235
|
+
frames: compositionFrames,
|
|
236
|
+
pathFor,
|
|
237
|
+
diagramType: 'dataflow',
|
|
238
|
+
relationCollection: 'flows',
|
|
239
|
+
profile: dataflow.meta?.quality_profile,
|
|
240
|
+
routeHint: 'adjust route/via or channelX/channelY so the flow crosses the stage perpendicularly instead of following its border'
|
|
241
|
+
}));
|
|
242
|
+
problems.push(...cleanRouteRhythmProblems({
|
|
243
|
+
relations: dataflow.flows,
|
|
244
|
+
endpointIds: new Set(nodes.keys()),
|
|
245
|
+
pathFor,
|
|
246
|
+
diagramType: 'dataflow',
|
|
247
|
+
relationCollection: 'flows',
|
|
248
|
+
profile: dataflow.meta?.quality_profile,
|
|
249
|
+
routeHint: 'adjust route/via or channelX/channelY so each turn uses a clear inter-stage corridor'
|
|
250
|
+
}));
|
|
251
|
+
|
|
252
|
+
const labelRects = [];
|
|
253
|
+
for (const [flowIndex, flow] of asArray(dataflow.flows).entries()) {
|
|
254
|
+
if (!flow.label || !nodes.has(flow.from) || !nodes.has(flow.to)) continue;
|
|
255
|
+
const [lx, ly] = labelPoint(flow, pathFor(flow).points);
|
|
256
|
+
const { width, height } = flowLabelSize(flow);
|
|
257
|
+
labelRects.push({ relation: flow, relationIndex: flowIndex, label: flow.label, x: lx - width / 2, y: ly - 11, width, height, lx, ly });
|
|
258
|
+
}
|
|
259
|
+
for (const rect of labelRects) {
|
|
260
|
+
for (const node of nodes.values()) {
|
|
261
|
+
if (rectsOverlap(rect, node, -2)) {
|
|
262
|
+
problems.push(`Label "${rect.label}" overlaps node "${node.id}" — adjust labelDx/labelDy/labelSegment or set labelAt.\n${suggestLabelObstacleFix(rect, rect.lx, rect.ly, node, 'node')}`);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
for (let i = 0; i < labelRects.length; i += 1) {
|
|
267
|
+
for (let j = i + 1; j < labelRects.length; j += 1) {
|
|
268
|
+
if (rectsOverlap(labelRects[i], labelRects[j], -2)) {
|
|
269
|
+
problems.push(`Labels "${labelRects[i].label}" and "${labelRects[j].label}" overlap — adjust labelDx/labelDy.\n${suggestLabelPairFix(labelRects[i], labelRects[j])}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
problems.push(...cleanLabelRouteClearanceProblems({
|
|
274
|
+
relations: dataflow.flows,
|
|
275
|
+
labels: labelRects,
|
|
276
|
+
endpointIds: new Set(nodes.keys()),
|
|
277
|
+
pathFor,
|
|
278
|
+
diagramType: 'dataflow',
|
|
279
|
+
relationCollection: 'flows',
|
|
280
|
+
profile: dataflow.meta?.quality_profile,
|
|
281
|
+
routeHint: 'adjust labelAt, labelDx, labelDy, or labelSegment; otherwise adjust the other flow route/via/channelX/channelY'
|
|
282
|
+
}));
|
|
283
|
+
|
|
284
|
+
const lastStageX = stageX(asArray(dataflow.stages).length - 1);
|
|
285
|
+
if (lastStageX + layout.stageW / 2 > viewBox[0] - 24) {
|
|
286
|
+
problems.push(`Stages exceed viewBox width — set meta.viewBox[0] to at least ${Math.ceil(lastStageX + layout.stageW / 2 + 24)}.`);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (problems.length) {
|
|
290
|
+
throwDiagnosticProblems('Data-flow layout validation failed', problems, {
|
|
291
|
+
subject: { diagramType: 'dataflow' },
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function routeVia(flow, from, to, start, end) {
|
|
297
|
+
if (flow.via) return flow.via;
|
|
298
|
+
switch (flow.route || 'auto') {
|
|
299
|
+
case 'straight':
|
|
300
|
+
return [];
|
|
301
|
+
case 'vertical-channel': {
|
|
302
|
+
const x = flow.channelX ?? start[0] + (end[0] > start[0] ? 44 : -44);
|
|
303
|
+
return [[x, start[1]], [x, end[1]]];
|
|
304
|
+
}
|
|
305
|
+
case 'bottom-channel': {
|
|
306
|
+
const y = flow.channelY ?? Math.max(from.y + from.height, to.y + to.height) + 26;
|
|
307
|
+
return [[start[0], y], [end[0], y]];
|
|
308
|
+
}
|
|
309
|
+
case 'top-channel': {
|
|
310
|
+
const y = flow.channelY ?? Math.min(from.y, to.y) - 24;
|
|
311
|
+
return [[start[0], y], [end[0], y]];
|
|
312
|
+
}
|
|
313
|
+
case 'auto':
|
|
314
|
+
default: {
|
|
315
|
+
if (Math.abs(start[1] - end[1]) < 4) return [];
|
|
316
|
+
const midX = start[0] + (end[0] - start[0]) / 2;
|
|
317
|
+
return [[midX, start[1]], [midX, end[1]]];
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const pathCache = new Map();
|
|
323
|
+
|
|
324
|
+
function flowSides(flow) {
|
|
325
|
+
const from = nodes.get(flow.from);
|
|
326
|
+
const to = nodes.get(flow.to);
|
|
327
|
+
return {
|
|
328
|
+
fromSide: chosenSide(flow.fromSide, defaultFromSide(from, to)),
|
|
329
|
+
toSide: chosenSide(flow.toSide, defaultToSide(from, to)),
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const automaticPorts = automaticPortSpread(dataflow.flows, nodes, {
|
|
334
|
+
sideFor: (flow, endpoint) => flowSides(flow)[endpoint === 'source' ? 'fromSide' : 'toSide'],
|
|
335
|
+
});
|
|
336
|
+
|
|
337
|
+
function pathFor(flow) {
|
|
338
|
+
if (pathCache.has(flow)) return pathCache.get(flow);
|
|
339
|
+
const from = nodes.get(flow.from);
|
|
340
|
+
const to = nodes.get(flow.to);
|
|
341
|
+
const ports = automaticPorts.get(flow);
|
|
342
|
+
const { fromSide, toSide } = flowSides(flow);
|
|
343
|
+
const start = ports?.from || anchor(from, fromSide);
|
|
344
|
+
const end = ports?.to || anchor(to, toSide);
|
|
345
|
+
// Drop consecutive duplicate points so a purely vertical (or horizontal)
|
|
346
|
+
// auto-route never emits a zero-length final segment — SVG derives
|
|
347
|
+
// marker-end orientation from the last segment, and a degenerate segment
|
|
348
|
+
// leaves the arrowhead angle undefined (see #169).
|
|
349
|
+
const rawPoints = [start, ...routeVia(flow, from, to, start, end), end];
|
|
350
|
+
const points = [];
|
|
351
|
+
for (const p of rawPoints) {
|
|
352
|
+
const prev = points.at(-1);
|
|
353
|
+
if (!prev || Math.abs(p[0] - prev[0]) > 0.0001 || Math.abs(p[1] - prev[1]) > 0.0001) {
|
|
354
|
+
points.push(p);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// Guard against an all-degenerate route (e.g. start === end): keep both
|
|
358
|
+
// endpoints so the path is still well-formed even if the marker is hidden.
|
|
359
|
+
if (points.length < 2) points.push(end);
|
|
360
|
+
const routed = { d: polylinePath(points), points };
|
|
361
|
+
pathCache.set(flow, routed);
|
|
362
|
+
return routed;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function renderStage(stage, index) {
|
|
366
|
+
const frame = compositionFrames[index];
|
|
367
|
+
const cx = stageX(index);
|
|
368
|
+
return ` <rect data-graph-role="structural-frame" data-composition-frame-kind="stage" data-composition-frame-id="${index}" x="${frame.x}" y="${frame.y}" width="${frame.width}" height="${frame.height}" rx="${frame.radius}" class="c-lane" stroke-width="1"/>
|
|
369
|
+
<text x="${cx}" y="${layout.stageY + 22}" class="t-dim" font-size="9" font-weight="600" text-anchor="middle">${String(index + 1).padStart(2, '0')} / ${esc(stage.label)}</text>`;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
function renderNode(node) {
|
|
373
|
+
const fill = componentFill[node.type] || 'c-external';
|
|
374
|
+
const accent = componentText[node.type] || 't-muted';
|
|
375
|
+
const hasSub = node.sublabel != null && node.sublabel !== '';
|
|
376
|
+
const sub = hasSub
|
|
377
|
+
? `\n <text data-detail="context" x="${node.cx}" y="${node.y + 37}" class="t-muted" font-size="${fittedNodeFontSize(node.sublabel, node.width, nodeTextFit.sublabelPreferred, nodeTextFit.sublabelMinimum)}" text-anchor="middle">${esc(node.sublabel)}</text>`
|
|
378
|
+
: '';
|
|
379
|
+
const tag = node.tag
|
|
380
|
+
? `\n <text data-detail="fine" x="${node.cx}" y="${node.y + node.height - 11}" class="${accent}" font-size="${fittedNodeFontSize(node.tag, node.width, nodeTextFit.tagPreferred, nodeTextFit.tagMinimum)}" text-anchor="middle">${esc(node.tag)}</text>`
|
|
381
|
+
: '';
|
|
382
|
+
const stage = asArray(dataflow.stages)[node.stage];
|
|
383
|
+
const context = stage
|
|
384
|
+
? `${String(node.stage + 1).padStart(2, '0')} / ${stage.label}`
|
|
385
|
+
: i18nText(dataflow.meta.locale, 'node.context.dataflow');
|
|
386
|
+
const brand = renderBrandMark(node, { x: node.x + node.width - 22, y: node.y + 6 });
|
|
387
|
+
const labelFontSize = fittedNodeFontSize(node.label, brandLabelFitWidth(node, node.width), 10, 8);
|
|
388
|
+
const passport = { kind: node.type, sublabel: node.sublabel, tag: node.tag, context, ...brandMetadataFor(node) };
|
|
389
|
+
return ` <g ${focusNodeAttrs(node.id, node.label, passport, dataflow.meta.locale)}>
|
|
390
|
+
${focusNodeTitle(node.label, passport)}
|
|
391
|
+
<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="6" class="c-mask"/>
|
|
392
|
+
<rect x="${node.x}" y="${node.y}" width="${node.width}" height="${node.height}" rx="6" class="${fill}"${animateAttr(dataflow.meta, 'node', nodeSteps.get(node.id))} stroke-width="1.5"/>
|
|
393
|
+
${renderSemanticSigil(node.type, { x: node.x + 6, y: node.y + 6 })}${brand ? `\n ${brand}` : ''}
|
|
394
|
+
<text data-node-label=""${hasSub ? ' data-detail-anchor=""' : ''} x="${node.cx}" y="${node.y + 21}" class="t-primary" font-size="${labelFontSize}" font-weight="600" text-anchor="middle">${esc(node.label)}</text>${sub}${tag}
|
|
395
|
+
</g>`;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function renderFlowPath(flow, index) {
|
|
399
|
+
const [cls, marker] = arrowClassMap[flow.variant || 'default'] || arrowClassMap.default;
|
|
400
|
+
const routed = pathFor(flow);
|
|
401
|
+
const strokeWidth = flow.width || (flow.variant === 'emphasis' ? 1.8 : 1.4);
|
|
402
|
+
return ` <path ${focusEdgeAttrs(flow.from, flow.to, flow.label, index, flow.id)} data-composition-points="${routePointsValue(routed.points)}" d="${routed.d}" class="${cls}"${animateAttr(dataflow.meta, 'edge', index)} stroke-width="${strokeWidth}" marker-end="url(#${marker})"/>`;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
function renderFlowLabel(flow, index) {
|
|
406
|
+
const routed = pathFor(flow);
|
|
407
|
+
const [lx, ly] = labelPoint(flow, routed.points);
|
|
408
|
+
const { width: labelW, height: labelH } = flowLabelSize(flow);
|
|
409
|
+
const classification = flow.classification
|
|
410
|
+
? `\n <text data-detail="fine" x="${lx}" y="${ly + 11}" class="t-dim" font-size="7" text-anchor="middle">${esc(flow.classification)}</text>`
|
|
411
|
+
: '';
|
|
412
|
+
return ` <g data-detail="context" ${focusEdgeAttrs(flow.from, flow.to, flow.label, index, flow.id)}>
|
|
413
|
+
<rect x="${lx - labelW / 2}" y="${ly - 11}" width="${labelW}" height="${labelH}" rx="4" class="c-mask"/>
|
|
414
|
+
<text x="${lx}" y="${ly}" class="${variantAccent(flow.variant)}" font-size="8" text-anchor="middle">${esc(flow.label)}</text>${classification}
|
|
415
|
+
</g>`;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const LEGEND_CATALOG = [
|
|
419
|
+
{ kind: 'emphasis', className: 'a-emphasis', marker: 'arrowhead-emphasis', strokeWidth: 1.8, swatchWidth: 34, swatchGap: 9, interactive: false },
|
|
420
|
+
{ kind: 'security', className: 'a-security', marker: 'arrowhead-security', swatchWidth: 34, swatchGap: 9, interactive: false },
|
|
421
|
+
{ kind: 'dashed', className: 'a-dashed', marker: 'arrowhead-dashed', swatchWidth: 34, swatchGap: 9, interactive: false },
|
|
422
|
+
{ kind: 'database' },
|
|
423
|
+
{ kind: 'default', className: 'a-default', marker: 'arrowhead', swatchWidth: 34, swatchGap: 9, interactive: false },
|
|
424
|
+
].map((entry) => ({
|
|
425
|
+
...entry,
|
|
426
|
+
label: i18nText(dataflow.meta.locale, `legend.dataflow.${entry.kind}`),
|
|
427
|
+
}));
|
|
428
|
+
|
|
429
|
+
function renderLegend() {
|
|
430
|
+
const presentKinds = new Set(asArray(dataflow.flows).map((flow) => flow.variant || 'default'));
|
|
431
|
+
if ([...nodes.values()].some((node) => node.type === 'database')) presentKinds.add('database');
|
|
432
|
+
const entries = resolveLegend(dataflow.meta?.legend, LEGEND_CATALOG, presentKinds);
|
|
433
|
+
return renderResolvedLegend({
|
|
434
|
+
entries,
|
|
435
|
+
locale: dataflow.meta.locale,
|
|
436
|
+
layout: {
|
|
437
|
+
x: 40,
|
|
438
|
+
baselineY: viewBox[1] - 36,
|
|
439
|
+
width: viewBox[0] - 80,
|
|
440
|
+
minTitleY: viewBox[1] - 66,
|
|
441
|
+
unfit: dataflow.meta?.legend === undefined ? 'hide' : 'error',
|
|
442
|
+
diagramType: 'dataflow',
|
|
443
|
+
},
|
|
444
|
+
renderSwatch: (entry) => entry.kind === 'database'
|
|
445
|
+
? `<rect x="${entry.x}" y="${entry.baseline - 8}" width="14" height="9" rx="2" class="c-database" stroke-width="1"/>`
|
|
446
|
+
: `<path d="M ${entry.x} ${entry.baseline - 3} L ${entry.x + 34} ${entry.baseline - 3}" class="${entry.className}" stroke-width="${entry.strokeWidth || 1.4}" marker-end="url(#${entry.marker})"/>`,
|
|
447
|
+
});
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
function renderSvg() {
|
|
451
|
+
return ` <svg viewBox="0 0 ${viewBox[0]} ${viewBox[1]}" ${svgRootAttrs(dataflow.meta)}>
|
|
452
|
+
${svgAccessibleText(dataflow.meta, 'dataflow')}
|
|
453
|
+
${renderDefinitions()}
|
|
454
|
+
|
|
455
|
+
<!-- Background Grid -->
|
|
456
|
+
<rect width="100%" height="100%" fill="url(#grid)" />
|
|
457
|
+
|
|
458
|
+
<!-- Data Stages -->
|
|
459
|
+
${dataflow.stages.map(renderStage).join('\n\n')}
|
|
460
|
+
|
|
461
|
+
<!-- Flow paths -->
|
|
462
|
+
${asArray(dataflow.flows).map(renderFlowPath).join('\n')}
|
|
463
|
+
|
|
464
|
+
<!-- Nodes -->
|
|
465
|
+
${[...nodes.values()].map(renderNode).join('\n\n')}
|
|
466
|
+
|
|
467
|
+
<!-- Flow labels -->
|
|
468
|
+
${asArray(dataflow.flows).map(renderFlowLabel).join('\n')}
|
|
469
|
+
|
|
470
|
+
<!-- Legend -->
|
|
471
|
+
${renderLegend()}
|
|
472
|
+
</svg>`;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
validateDataflow();
|
|
476
|
+
writeDiagram({
|
|
477
|
+
outPath,
|
|
478
|
+
template,
|
|
479
|
+
diagramType: 'dataflow',
|
|
480
|
+
meta: dataflow.meta,
|
|
481
|
+
svg: renderSvg(),
|
|
482
|
+
cards: dataflow.cards,
|
|
483
|
+
});
|