@stacksjs/defaults 0.75.361 → 0.75.362

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (208) hide show
  1. package/ai/skills/stacks-ai/SKILL.md +22 -0
  2. package/ai/skills/stacks-native/CATALOG.md +2 -2
  3. package/ai/skills/stacks-technical-diagrams/LICENSE +202 -22
  4. package/ai/skills/stacks-technical-diagrams/LICENSE-MIT +22 -0
  5. package/ai/skills/stacks-technical-diagrams/NOTICE +26 -0
  6. package/ai/skills/stacks-technical-diagrams/SKILL.md +106 -266
  7. package/ai/skills/stacks-technical-diagrams/THIRD_PARTY_NOTICES.md +11 -2
  8. package/ai/skills/stacks-technical-diagrams/UPSTREAM.json +12 -0
  9. package/ai/skills/stacks-technical-diagrams/agents/openai.yaml +2 -2
  10. package/ai/skills/stacks-technical-diagrams/assets/template.html +3655 -3798
  11. package/ai/skills/stacks-technical-diagrams/bin/archify.mjs +7290 -0
  12. package/ai/skills/stacks-technical-diagrams/bin/delivery-update.mjs +7 -0
  13. package/ai/skills/stacks-technical-diagrams/bin/diagrams +1 -0
  14. package/ai/skills/stacks-technical-diagrams/bin/finalize.mjs +1056 -0
  15. package/ai/skills/stacks-technical-diagrams/bin/open-artifact.mjs +72 -12
  16. package/ai/skills/stacks-technical-diagrams/bin/preview.mjs +348 -31
  17. package/ai/skills/stacks-technical-diagrams/bin/recover-output.mjs +41 -0
  18. package/ai/skills/stacks-technical-diagrams/bin/technical-diagrams.mjs +3 -2097
  19. package/ai/skills/stacks-technical-diagrams/bin/visual-check.mjs +2142 -157
  20. package/ai/skills/stacks-technical-diagrams/brand-marks/README.md +3 -3
  21. package/ai/skills/stacks-technical-diagrams/delta/architecture-delta.mjs +130 -33
  22. package/ai/skills/stacks-technical-diagrams/examples/agent-run.lifecycle.json +25 -30
  23. package/ai/skills/stacks-technical-diagrams/examples/agent-tool-call.workflow.json +0 -20
  24. package/ai/skills/stacks-technical-diagrams/examples/archify-dev-activity.timeline.json +176 -0
  25. package/ai/skills/stacks-technical-diagrams/examples/archify-repository.tree.json +197 -0
  26. package/ai/skills/stacks-technical-diagrams/examples/async-job-roundtrip.sequence.json +1 -6
  27. package/ai/skills/stacks-technical-diagrams/examples/brand-aware-delivery.architecture.json +3 -16
  28. package/ai/skills/stacks-technical-diagrams/examples/cache-miss-request.sequence.json +2 -7
  29. package/ai/skills/stacks-technical-diagrams/examples/checkout-platform.base.architecture.json +2 -1
  30. package/ai/skills/stacks-technical-diagrams/examples/checkout-platform.head.architecture.json +2 -1
  31. package/ai/skills/stacks-technical-diagrams/examples/checkout-request.waterfall.json +20 -0
  32. package/ai/skills/stacks-technical-diagrams/examples/class-payment-processors-rendered.html +14987 -0
  33. package/ai/skills/stacks-technical-diagrams/examples/class-payments-rendered.html +14787 -0
  34. package/ai/skills/stacks-technical-diagrams/examples/dataflow-product-analytics.html +14880 -0
  35. package/ai/skills/stacks-technical-diagrams/examples/delivery.explainer.json +155 -0
  36. package/ai/skills/stacks-technical-diagrams/examples/deployment-release.lifecycle.json +57 -34
  37. package/ai/skills/stacks-technical-diagrams/examples/erd-orders-rendered.html +15112 -0
  38. package/ai/skills/stacks-technical-diagrams/examples/event-stream.dataflow.json +1 -6
  39. package/ai/skills/stacks-technical-diagrams/examples/example-rebuild.waterfall.json +212 -0
  40. package/ai/skills/stacks-technical-diagrams/examples/holiday-planning.workflow.json +143 -0
  41. package/ai/skills/stacks-technical-diagrams/examples/incident-response.workflow.json +1 -6
  42. package/ai/skills/stacks-technical-diagrams/examples/lifecycle-agent-run.html +14892 -0
  43. package/ai/skills/stacks-technical-diagrams/examples/locales/de.json +463 -0
  44. package/ai/skills/stacks-technical-diagrams/examples/locales/fr.json +463 -0
  45. package/ai/skills/stacks-technical-diagrams/examples/locales/fr.partial.json +22 -0
  46. package/ai/skills/stacks-technical-diagrams/examples/locales/it.json +463 -0
  47. package/ai/skills/stacks-technical-diagrams/examples/locales/ja.json +463 -0
  48. package/ai/skills/stacks-technical-diagrams/examples/locales/pt.json +463 -0
  49. package/ai/skills/stacks-technical-diagrams/examples/locales/ru.json +463 -0
  50. package/ai/skills/stacks-technical-diagrams/examples/orders.erd.json +337 -0
  51. package/ai/skills/stacks-technical-diagrams/examples/payment-incident.timeline.json +24 -0
  52. package/ai/skills/stacks-technical-diagrams/examples/payment-platform.tree.json +22 -0
  53. package/ai/skills/stacks-technical-diagrams/examples/payment-processors.class.json +177 -0
  54. package/ai/skills/stacks-technical-diagrams/examples/payments.class.json +85 -0
  55. package/ai/skills/stacks-technical-diagrams/examples/product-analytics.dataflow.json +1 -6
  56. package/ai/skills/stacks-technical-diagrams/examples/production-deployment.architecture.json +3 -23
  57. package/ai/skills/stacks-technical-diagrams/examples/release-delivery.workflow.json +1 -6
  58. package/ai/skills/stacks-technical-diagrams/examples/sequence-cache-miss-request.html +14895 -0
  59. package/ai/skills/stacks-technical-diagrams/examples/subscription-billing-rendered.html +15369 -0
  60. package/ai/skills/stacks-technical-diagrams/examples/subscription-billing.erd.json +548 -0
  61. package/ai/skills/stacks-technical-diagrams/examples/timeline-archify-dev-activity-rendered.html +15002 -0
  62. package/ai/skills/stacks-technical-diagrams/examples/timeline-payment-incident-rendered.html +14772 -0
  63. package/ai/skills/stacks-technical-diagrams/examples/tree-archify-repository-rendered.html +15071 -0
  64. package/ai/skills/stacks-technical-diagrams/examples/tree-payment-platform-rendered.html +14810 -0
  65. package/ai/skills/stacks-technical-diagrams/examples/waterfall-checkout-request-rendered.html +14754 -0
  66. package/ai/skills/stacks-technical-diagrams/examples/waterfall-example-rebuild-rendered.html +14940 -0
  67. package/ai/skills/stacks-technical-diagrams/examples/web-app-rendered.html +14889 -0
  68. package/ai/skills/stacks-technical-diagrams/examples/web-app.architecture.json +1 -6
  69. package/ai/skills/stacks-technical-diagrams/examples/workflow-agent-tool-call-rendered.html +14907 -0
  70. package/ai/skills/stacks-technical-diagrams/explainer/browser-check.mjs +208 -0
  71. package/ai/skills/stacks-technical-diagrams/explainer/charts.mjs +70 -0
  72. package/ai/skills/stacks-technical-diagrams/explainer/check.mjs +168 -0
  73. package/ai/skills/stacks-technical-diagrams/explainer/diagrams.mjs +518 -0
  74. package/ai/skills/stacks-technical-diagrams/explainer/diff.mjs +124 -0
  75. package/ai/skills/stacks-technical-diagrams/explainer/inline.mjs +155 -0
  76. package/ai/skills/stacks-technical-diagrams/explainer/layout.mjs +35 -0
  77. package/ai/skills/stacks-technical-diagrams/explainer/page-bundle.mjs +16 -0
  78. package/ai/skills/stacks-technical-diagrams/explainer/parse.mjs +300 -0
  79. package/ai/skills/stacks-technical-diagrams/explainer/patch.mjs +117 -0
  80. package/ai/skills/stacks-technical-diagrams/explainer/prepare.mjs +131 -0
  81. package/ai/skills/stacks-technical-diagrams/explainer/reader.css +472 -0
  82. package/ai/skills/stacks-technical-diagrams/explainer/reader.js +667 -0
  83. package/ai/skills/stacks-technical-diagrams/explainer/render.mjs +224 -0
  84. package/ai/skills/stacks-technical-diagrams/explainer/revision.mjs +20 -0
  85. package/ai/skills/stacks-technical-diagrams/explainer/sources.mjs +318 -0
  86. package/ai/skills/stacks-technical-diagrams/explainer/validate.mjs +138 -0
  87. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/LICENSE +40 -0
  88. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/README.md +5 -0
  89. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/3DKXF73K.js +4 -0
  90. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/42UTT654.js +3 -0
  91. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/DKTLEL5E.js +1 -0
  92. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/EEIX5ZYH.js +2 -0
  93. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/EJ5KOXYD.js +25 -0
  94. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/GUJTEFZK.js +1 -0
  95. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/IZOKQ2GT.js +4 -0
  96. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/L4LY553T.js +3 -0
  97. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/MSKIJHB2.js +2 -0
  98. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/RVARI57O.js +1 -0
  99. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/UKSIWDDN.js +2 -0
  100. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/UPTXNAXN.js +2 -0
  101. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/XTDUIGUI.js +9 -0
  102. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/chunks/YAYTP6L5.js +9 -0
  103. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/core.js +1 -0
  104. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/pie.js +1 -0
  105. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/quadrantChart.js +1 -0
  106. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/radar.js +1 -0
  107. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/sankey.js +1 -0
  108. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/venn.js +1 -0
  109. package/ai/skills/stacks-technical-diagrams/explainer/vendor/pele/diagrams/xychart.js +1 -0
  110. package/ai/skills/stacks-technical-diagrams/explainer/writing.mjs +139 -0
  111. package/ai/skills/stacks-technical-diagrams/locales/README.md +40 -0
  112. package/ai/skills/stacks-technical-diagrams/locales/ar.json +488 -0
  113. package/ai/skills/stacks-technical-diagrams/locales/en.json +488 -0
  114. package/ai/skills/stacks-technical-diagrams/locales/es.json +488 -0
  115. package/ai/skills/stacks-technical-diagrams/locales/ko.json +488 -0
  116. package/ai/skills/stacks-technical-diagrams/locales/manifest.json +10 -0
  117. package/ai/skills/stacks-technical-diagrams/locales/zh-CN.json +488 -0
  118. package/ai/skills/stacks-technical-diagrams/locales/zh-TW.json +488 -0
  119. package/ai/skills/stacks-technical-diagrams/migrations/workflow-v2.mjs +1 -0
  120. package/ai/skills/stacks-technical-diagrams/recipes/scenarios.mjs +115 -51
  121. package/ai/skills/stacks-technical-diagrams/references/architecture-layout-repair.md +18 -0
  122. package/ai/skills/stacks-technical-diagrams/references/authoring-contract.md +453 -51
  123. package/ai/skills/stacks-technical-diagrams/references/authoring-defaults.md +57 -0
  124. package/ai/skills/stacks-technical-diagrams/references/brand-marks.md +57 -3
  125. package/ai/skills/stacks-technical-diagrams/references/delivery-contract.md +603 -58
  126. package/ai/skills/stacks-technical-diagrams/references/explainer-authoring.md +274 -0
  127. package/ai/skills/stacks-technical-diagrams/references/repository-authoring.md +126 -0
  128. package/ai/skills/stacks-technical-diagrams/references/update-awareness.md +13 -0
  129. package/ai/skills/stacks-technical-diagrams/references/viewer-runtime.md +18 -11
  130. package/ai/skills/stacks-technical-diagrams/renderers/architecture/grid.mjs +1 -1
  131. package/ai/skills/stacks-technical-diagrams/renderers/architecture/labels.mjs +2 -0
  132. package/ai/skills/stacks-technical-diagrams/renderers/architecture/render-architecture.mjs +550 -424
  133. package/ai/skills/stacks-technical-diagrams/renderers/architecture/routing.mjs +1469 -0
  134. package/ai/skills/stacks-technical-diagrams/renderers/class/README.md +129 -0
  135. package/ai/skills/stacks-technical-diagrams/renderers/class/render-class.mjs +1095 -0
  136. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/README.md +89 -17
  137. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/dataflow-compiler.mjs +1605 -0
  138. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/joint-label-routing.mjs +165 -0
  139. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/label-capacity.mjs +81 -0
  140. package/ai/skills/stacks-technical-diagrams/renderers/dataflow/render-dataflow.mjs +14 -474
  141. package/ai/skills/stacks-technical-diagrams/renderers/erd/README.md +246 -0
  142. package/ai/skills/stacks-technical-diagrams/renderers/erd/grid.mjs +150 -0
  143. package/ai/skills/stacks-technical-diagrams/renderers/erd/render-erd.mjs +1588 -0
  144. package/ai/skills/stacks-technical-diagrams/renderers/explainer/render-explainer.mjs +42 -0
  145. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/README.md +83 -94
  146. package/ai/skills/stacks-technical-diagrams/renderers/lifecycle/render-lifecycle.mjs +990 -417
  147. package/ai/skills/stacks-technical-diagrams/renderers/sequence/README.md +49 -17
  148. package/ai/skills/stacks-technical-diagrams/renderers/sequence/render-sequence.mjs +735 -94
  149. package/ai/skills/stacks-technical-diagrams/renderers/shared/atomic-output.mjs +1982 -0
  150. package/ai/skills/stacks-technical-diagrams/renderers/shared/automatic-labels.mjs +290 -0
  151. package/ai/skills/stacks-technical-diagrams/renderers/shared/brand-marks.mjs +169 -26
  152. package/ai/skills/stacks-technical-diagrams/renderers/shared/cli.mjs +266 -54
  153. package/ai/skills/stacks-technical-diagrams/renderers/shared/desktop-readability.mjs +106 -0
  154. package/ai/skills/stacks-technical-diagrams/renderers/shared/diagnostics.mjs +0 -0
  155. package/ai/skills/stacks-technical-diagrams/renderers/shared/generated-validators.mjs +268 -2
  156. package/ai/skills/stacks-technical-diagrams/renderers/shared/geometry.mjs +774 -124
  157. package/ai/skills/stacks-technical-diagrams/renderers/shared/i18n.mjs +289 -556
  158. package/ai/skills/stacks-technical-diagrams/renderers/shared/layout-report.mjs +18 -1
  159. package/ai/skills/stacks-technical-diagrams/renderers/shared/legend.mjs +16 -6
  160. package/ai/skills/stacks-technical-diagrams/renderers/shared/node-card-style.mjs +32 -0
  161. package/ai/skills/stacks-technical-diagrams/renderers/shared/output-path.mjs +329 -229
  162. package/ai/skills/stacks-technical-diagrams/renderers/shared/owned-temp-directory.mjs +82 -0
  163. package/ai/skills/stacks-technical-diagrams/renderers/shared/path-semantics.mjs +1017 -0
  164. package/ai/skills/stacks-technical-diagrams/renderers/shared/portable-path.mjs +408 -0
  165. package/ai/skills/stacks-technical-diagrams/renderers/shared/repository-evidence.mjs +235 -79
  166. package/ai/skills/stacks-technical-diagrams/renderers/shared/repository-location.mjs +17 -4
  167. package/ai/skills/stacks-technical-diagrams/renderers/shared/route-quality.mjs +670 -0
  168. package/ai/skills/stacks-technical-diagrams/renderers/shared/sidecar-path.mjs +38 -0
  169. package/ai/skills/stacks-technical-diagrams/renderers/shared/software-notices.mjs +6 -0
  170. package/ai/skills/stacks-technical-diagrams/renderers/shared/spatial-grid.mjs +66 -0
  171. package/ai/skills/stacks-technical-diagrams/renderers/shared/svg-path-analysis.mjs +135 -0
  172. package/ai/skills/stacks-technical-diagrams/renderers/shared/text-fit.mjs +59 -3
  173. package/ai/skills/stacks-technical-diagrams/renderers/shared/utils.mjs +63 -35
  174. package/ai/skills/stacks-technical-diagrams/renderers/shared/validator.mjs +2 -5
  175. package/ai/skills/stacks-technical-diagrams/renderers/timeline/README.md +76 -0
  176. package/ai/skills/stacks-technical-diagrams/renderers/timeline/render-timeline.mjs +576 -0
  177. package/ai/skills/stacks-technical-diagrams/renderers/tree/README.md +96 -0
  178. package/ai/skills/stacks-technical-diagrams/renderers/tree/render-tree.mjs +573 -0
  179. package/ai/skills/stacks-technical-diagrams/renderers/waterfall/README.md +73 -0
  180. package/ai/skills/stacks-technical-diagrams/renderers/waterfall/render-waterfall.mjs +413 -0
  181. package/ai/skills/stacks-technical-diagrams/renderers/workflow/README.md +137 -23
  182. package/ai/skills/stacks-technical-diagrams/renderers/workflow/render-workflow.mjs +18 -6
  183. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-compiler.mjs +2230 -710
  184. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-migration-geometry.mjs +17 -0
  185. package/ai/skills/stacks-technical-diagrams/renderers/workflow/workflow-placement-repair.mjs +94 -0
  186. package/ai/skills/stacks-technical-diagrams/schemas/README.md +87 -40
  187. package/ai/skills/stacks-technical-diagrams/schemas/architecture.schema.json +7 -33
  188. package/ai/skills/stacks-technical-diagrams/schemas/class.schema.json +345 -0
  189. package/ai/skills/stacks-technical-diagrams/schemas/common.schema.json +68 -2
  190. package/ai/skills/stacks-technical-diagrams/schemas/dataflow.schema.json +14 -3
  191. package/ai/skills/stacks-technical-diagrams/schemas/erd.schema.json +381 -0
  192. package/ai/skills/stacks-technical-diagrams/schemas/explainer.schema.json +927 -0
  193. package/ai/skills/stacks-technical-diagrams/schemas/lifecycle.schema.json +23 -110
  194. package/ai/skills/stacks-technical-diagrams/schemas/sequence.schema.json +15 -4
  195. package/ai/skills/stacks-technical-diagrams/schemas/timeline.schema.json +160 -0
  196. package/ai/skills/stacks-technical-diagrams/schemas/tree.schema.json +141 -0
  197. package/ai/skills/stacks-technical-diagrams/schemas/waterfall.schema.json +147 -0
  198. package/ai/skills/stacks-technical-diagrams/schemas/workflow.schema.json +14 -3
  199. package/ai/skills/stacks-technical-diagrams/scripts/check-render-output.mjs +876 -243
  200. package/ai/skills/stacks-technical-diagrams/scripts/check-update.mjs +1717 -0
  201. package/ai/skills/stacks-technical-diagrams/scripts/delivery-update-child.mjs +26 -0
  202. package/ai/skills/stacks-technical-diagrams/scripts/generate-brand-marks.mjs +141 -0
  203. package/ai/skills/stacks-technical-diagrams/scripts/generate-validators.mjs +105 -0
  204. package/ai/skills/stacks-technical-diagrams/scripts/render-examples.mjs +11 -1
  205. package/ai/skills/stacks-technical-diagrams/scripts/update-contract.mjs +187 -0
  206. package/ai/skills/stacks-technical-diagrams/skill-release.json +10 -0
  207. package/ide/vscode/package.json +1 -1
  208. package/package.json +2 -2
@@ -1,483 +1,23 @@
1
1
  import path from 'node:path';
2
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
- };
3
+ import { loadDiagramWithBrandMarks, writeDiagram } from '../shared/cli.mjs';
4
+ import { recordDiagnostic } from '../shared/diagnostics.mjs';
5
+ import { compileDataflow } from './dataflow-compiler.mjs';
43
6
 
44
7
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
45
- const { diagram: dataflow, template, outPath } = await loadDiagramWithBrandMarks({
8
+ const { diagram: dataflow, template, outPath, sourceEvidence } = await loadDiagramWithBrandMarks({
46
9
  rendererDir: __dirname,
47
10
  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'],
11
+ defaultExample: 'product-analytics.dataflow.json',
335
12
  });
336
13
 
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
- });
14
+ let svg;
15
+ try {
16
+ svg = compileDataflow({ dataflow, sourceEvidence,
17
+ qualityProfile: process.env.ARCHIFY_QUALITY_PROFILE || dataflow.meta?.quality_profile });
18
+ } catch (error) {
19
+ for (const diagnostic of error?.archifyDiagnostics || []) recordDiagnostic(diagnostic);
20
+ throw error;
448
21
  }
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
- });
22
+ writeDiagram({ outPath, template, diagramType: 'dataflow', meta: dataflow.meta,
23
+ svg, cards: dataflow.cards, sourceEvidence });