@webpieces/nx-webpieces-rules 0.4.946 → 0.4.948

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 (44) hide show
  1. package/README.md +20 -11
  2. package/package.json +7 -7
  3. package/src/executors/validate-no-file-import-cycles/executor.js +7 -4
  4. package/src/executors/validate-no-file-import-cycles/executor.js.map +1 -1
  5. package/src/index.d.ts +1 -1
  6. package/src/index.js +3 -1
  7. package/src/index.js.map +1 -1
  8. package/src/lib/graph-color-modes.d.ts +18 -12
  9. package/src/lib/graph-color-modes.js +18 -20
  10. package/src/lib/graph-color-modes.js.map +1 -1
  11. package/src/lib/graph-legend.d.ts +11 -3
  12. package/src/lib/graph-legend.js +25 -9
  13. package/src/lib/graph-legend.js.map +1 -1
  14. package/src/lib/graph-mode-state.client.d.ts +2 -1
  15. package/src/lib/graph-mode-state.client.js +3 -1
  16. package/src/lib/graph-mode-state.client.js.map +1 -1
  17. package/src/lib/graph-page-shell.js +2 -2
  18. package/src/lib/graph-page-shell.js.map +1 -1
  19. package/src/lib/graph-page-styles.js +4 -1
  20. package/src/lib/graph-page-styles.js.map +1 -1
  21. package/src/lib/graph-product-paint.client.d.ts +92 -0
  22. package/src/lib/graph-product-paint.client.js +196 -0
  23. package/src/lib/graph-product-paint.client.js.map +1 -0
  24. package/src/lib/graph-products.d.ts +54 -6
  25. package/src/lib/graph-products.js +62 -9
  26. package/src/lib/graph-products.js.map +1 -1
  27. package/src/lib/graph-render-model.d.ts +12 -12
  28. package/src/lib/graph-render-model.js +12 -14
  29. package/src/lib/graph-render-model.js.map +1 -1
  30. package/src/lib/graph-visualizer.client.d.ts +13 -1
  31. package/src/lib/graph-visualizer.client.js +27 -4
  32. package/src/lib/graph-visualizer.client.js.map +1 -1
  33. package/src/lib/graph-visualizer.d.ts +3 -3
  34. package/src/lib/graph-visualizer.js +13 -12
  35. package/src/lib/graph-visualizer.js.map +1 -1
  36. package/src/lib/runtime-goal-comparison.js +1 -1
  37. package/src/lib/runtime-goal-comparison.js.map +1 -1
  38. package/src/lib/runtime-html-page.js +3 -2
  39. package/src/lib/runtime-html-page.js.map +1 -1
  40. package/src/lib/runtime-layout.js +1 -1
  41. package/src/lib/runtime-layout.js.map +1 -1
  42. package/src/lib/runtime-viewer-adapter.d.ts +4 -1
  43. package/src/lib/runtime-viewer-adapter.js +6 -13
  44. package/src/lib/runtime-viewer-adapter.js.map +1 -1
@@ -87,7 +87,9 @@ function readCompiledClient(name) {
87
87
  'by tsc, so it only exists after a build — run the package build, or inject the text.');
88
88
  }
89
89
  const source = fs.readFileSync(file, 'utf-8');
90
- return name === 'graph-visualizer.client.js' ? readCompiledClient('graph-mode-state.client.js') + source : source;
90
+ if (name !== 'graph-visualizer.client.js')
91
+ return source;
92
+ return readCompiledClient('graph-mode-state.client.js') + readCompiledClient('graph-product-paint.client.js') + source;
91
93
  }
92
94
  class GraphVisualizer {
93
95
  clientJs;
@@ -221,7 +223,7 @@ class GraphVisualizer {
221
223
  dot += '\n';
222
224
  dot += this.bandLayout.dot(bands);
223
225
  dot += '\n';
224
- dot += this.dotEdges(graph, model, palette);
226
+ dot += this.dotEdges(graph, model);
225
227
  const footerStart = dot.length;
226
228
  dot += '\n labelloc="t";\n';
227
229
  dot += ` label="${(0, dot_syntax_1.dotValue)(title)}\\n(from architecture/dependencies.json)";\n`;
@@ -285,7 +287,7 @@ class GraphVisualizer {
285
287
  // Identity is the project key; the LABEL is the pretty short name.
286
288
  const facts = new graph_color_modes_1.NodeFacts(this.names.getNodeId(project), this.names.getShortName(project), info.level, info.role ?? 'lib', info.framework ?? [], info.products ?? []);
287
289
  this.styler.record(model.legend, facts, palette);
288
- dot += model.styledNode(facts, this.styler.dots(facts, palette));
290
+ dot += model.styledNode(facts, this.styler.dots(facts));
289
291
  }
290
292
  return dot;
291
293
  }
@@ -293,7 +295,7 @@ class GraphVisualizer {
293
295
  // (implements/uses/uses-implements, from apiRelations); every other dependency
294
296
  // keeps the default plain arrow. In the Product color mode an edge crossing into another
295
297
  // product is ALSO dashed in that product's color (#1204), so product boundaries read at a glance.
296
- dotEdges(graph, model, palette) {
298
+ dotEdges(graph, model) {
297
299
  let dot = '';
298
300
  for (const project of Object.keys(graph)) {
299
301
  const info = graph[project];
@@ -305,21 +307,20 @@ class GraphVisualizer {
305
307
  // is dropped so no connection dangles into empty space.
306
308
  if (graph[dep] !== undefined && this.isHidden(graph[dep]))
307
309
  continue;
308
- dot += model.edge(nodeId, this.names.getNodeId(dep), this.edgeDot(nodeId, this.names.getNodeId(dep), info.apiRelations?.[dep]), this.crossProductAttrs(info.products ?? [], graph[dep]?.products ?? [], model, palette));
310
+ dot += model.edge(nodeId, this.names.getNodeId(dep), this.edgeDot(nodeId, this.names.getNodeId(dep), info.apiRelations?.[dep]), this.crossesInto(info.products ?? [], graph[dep]?.products ?? [], model));
309
311
  }
310
312
  }
311
313
  return dot;
312
314
  }
313
315
  /**
314
- * The Product color mode's extra attributes for one dependency: dashed, thicker and in the color
315
- * of the product it crosses into, or '' for an edge that stays inside its source's products.
316
+ * The products one dependency crosses into, counted for the legend; empty for an edge that stays
317
+ * inside its source's products. The page dashes it in the first ON one's color (#1319).
316
318
  */
317
- crossProductAttrs(from, to, model, palette) {
319
+ crossesInto(from, to, model) {
318
320
  const into = this.boundary.crossedInto(from, to);
319
- if (into === null)
320
- return '';
321
- model.legend.crossProductEdges++;
322
- return `style=dashed, color="${palette.colorOf(into).color}", penwidth=2`;
321
+ if (into.length > 0)
322
+ model.legend.crossProductEdges++;
323
+ return into;
323
324
  }
324
325
  /**
325
326
  * Generate the interactive page: the drawer shell around a viz.js-rendered graph.
@@ -1 +1 @@
1
- {"version":3,"file":"graph-visualizer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-visualizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { GraphNavigation, NavigationLayout } from './graph-navigation';\n/**\n * Graph Visualizer\n *\n * Generates visual representations of the architecture graph:\n * - DOT format (for Graphviz)\n * - Interactive HTML (using viz.js)\n *\n * All behavior lives on the injectable GraphVisualizer class so webpieces DI +\n * @DocumentDesign can wire it — module-scope functions are a dead end the DI\n * graph can't reach.\n */\n\nimport * as fs from 'fs';\nimport { SavedSnapshot } from './saved-snapshot';\nimport * as path from 'path';\nimport { execSync } from 'child_process';\nimport type { EnhancedGraph } from './graph-sorter';\nimport type { ApiRef, ApiRelation, ApiRelationKind } from './api-usage/api-relations';\nimport { GraphNames, NodeIdOwner } from './graph-names';\nimport { LevelBand, LevelBandLayout } from './graph-level-bands';\nimport { ProjectAdjacency, ProjectCycleDetector } from './graph-cycles';\nimport { ResponsibilitiesRenderer } from './graph-responsibilities';\nimport { GraphNodeMenu } from './graph-node-menu';\nimport { dotValue } from './dot-syntax';\nimport { GraphRenderModel, GraphFilterAssets } from './graph-render-model';\nimport { NodeFacts, NodeModeStyler } from './graph-color-modes';\nimport { ProductBoundary, ProductPalette } from './graph-products';\nimport { ProductDependencyMatrix } from './product-dependencies';\nimport { GraphLegend } from './graph-legend';\nimport { GraphPageShell, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { IMPACT_SIDECAR_SRC } from './graph-impact';\nimport { toError } from '../toError';\n\n/**\n * Directory (repo-relative) that the committed architecture HTML lives in.\n * Node click-through links are computed relative to this so they resolve when\n * the file is opened straight from the checkout.\n */\nconst ARCH_OUTPUT_DIR = 'architecture';\n\n/** Contracts named on one `implements` edge label before it truncates to \"+N more\". */\nconst MAX_EDGE_LABEL_APIS = 4;\n\n/**\n * One node's design page, handed to the browser so the node menu can offer \"View Design\" for the\n * boxes that HAVE one and omit the item entirely for the boxes that do not.\n *\n * The link is data on the PAGE now, not a `URL=` attribute in the DOT: a URL made viz.js wrap the\n * box in an `<a>` and clicking navigated straight out, which the menu replaces.\n */\nexport class DesignLink {\n constructor(\n public readonly nodeId: string,\n public readonly href: string,\n ) {}\n}\n\nexport class VisualizationPaths {\n htmlPath: string;\n\n constructor(htmlPath: string) {\n this.htmlPath = htmlPath;\n }\n}\n\n/**\n * The token the client scripts carry where the DOT belongs. It must appear EXACTLY ONCE in a compiled\n * client — the inliner is a blind split/join, so a second occurrence (in a comment, say) would be\n * replaced by the entire DOT too, bloating every generated page.\n */\nexport const CLIENT_MODEL_PLACEHOLDER = '__' + 'RENDER_MODEL' + '__';\n\n/** Same contract as CLIENT_MODEL_PLACEHOLDER, for the node → design.html links the menu offers. */\nexport const CLIENT_DESIGN_LINKS_PLACEHOLDER = '__' + 'DESIGN_LINKS' + '__';\n\n/**\n * Read a compiled browser client sitting beside this file.\n *\n * It is generated from the matching `.client.ts` by tsc, so it exists in `dist` and in the published\n * tarball but NOT in a source checkout. The error says that outright rather than surfacing a bare\n * ENOENT, because \"the build has not run\" and \"the file is missing\" look identical otherwise.\n */\n// webpieces-disable no-function-outside-class -- module-level resolver for the default injected into GraphVisualizer/RuntimeHtmlPage; making it a class would need a container in a module both of them load eagerly\nexport function readCompiledClient(name: string): string {\n const file = path.join(__dirname, name);\n if (!fs.existsSync(file)) {\n throw new Error(\n `${name} not found beside ${__dirname}. It is COMPILED from ${name.replace(/\\.js$/, '.ts')} ` +\n 'by tsc, so it only exists after a build — run the package build, or inject the text.',\n );\n }\n const source = fs.readFileSync(file, 'utf-8');\n return name === 'graph-visualizer.client.js' ? readCompiledClient('graph-mode-state.client.js') + source : source;\n}\n\nexport class GraphVisualizer {\n private readonly filterAssets = new GraphFilterAssets();\n private readonly snapshot = new SavedSnapshot();\n private readonly names = new GraphNames();\n private readonly responsibilities = new ResponsibilitiesRenderer();\n private readonly bandLayout = new LevelBandLayout();\n private readonly cycles = new ProjectCycleDetector();\n /** The ONE floating-node-menu implementation, shared with the per-project design pages. */\n private readonly nodeMenu = new GraphNodeMenu();\n private readonly styler = new NodeModeStyler();\n private readonly legend = new GraphLegend();\n private readonly shell = new GraphPageShell();\n private readonly pageStyles = new GraphPageStyles();\n private readonly navigation = new GraphNavigation(NavigationLayout.FLOATING);\n private readonly boundary = new ProductBoundary();\n\n /**\n * How to obtain the browser client's text. Injected so HTML generation does not depend on BUILD\n * ORDER: the default reads the compiled sibling, which exists in the tarball and in dist but NOT in\n * a source checkout (the source there is .client.ts). A unit test running from source supplies the\n * text itself rather than requiring the package to have been built first.\n */\n constructor(\n private readonly clientJs: () => string = (): string =>\n readCompiledClient('graph-visualizer.client.js'),\n private readonly filterJs: () => string = (): string =>\n readCompiledClient('graph-filter.client.js'),\n ) {}\n\n /**\n * A project tagged `drawOnGraph:false` is hidden from the rendered graph —\n * its node, its rank placement, its lock-search option, its responsibilities\n * card, and every edge touching it are all omitted. It stays in the JSON.\n */\n private isHidden(entry: EnhancedGraph[string]): boolean {\n return entry.drawOnGraph === false;\n }\n\n /**\n * Edge styling by API-relation kind (why the edge exists):\n * implements → BLACK dashed (a controller serves this api-lib's contract)\n * uses → BLACK solid (a generated client calls it) — same as a\n * plain library import, since a plain dependency IS a use.\n * uses-implements → BLUE dashed, thicker (does both — implements some\n * contracts of the api-lib, uses others)\n * plain lib (none) → the default thin black solid arrow, unchanged.\n * `kind` is undefined for every non-api-lib dependency edge.\n */\n private edgeAttrs(kind: ApiRelationKind | undefined): string {\n if (kind === 'implements') return 'style=dashed';\n if (kind === 'uses-implements') return 'style=dashed, color=\"#1976d2\", penwidth=2';\n return '';\n }\n\n /**\n * The DOT attribute list for ONE dependency edge. An edge that IMPLEMENTS contracts is also\n * LABELED with them: the dashed line alone says \"something here is implemented\", which reads as\n * \"the tool did not detect anything\" to everyone who has not memorized the legend. Naming the\n * contracts makes `auth-store-api` visibly resolve to the server that serves it — the single\n * most important relationship in a microservice architecture, and the one the diagram was\n * silent about.\n */\n private edgeDot(from: string, to: string, relation: ApiRelation | undefined): string {\n const attrs: string[] = [];\n const styling = this.edgeAttrs(relation?.kind);\n if (styling !== '') attrs.push(styling);\n const served = (relation?.implements ?? []).map((ref: ApiRef) => ref.api);\n if (served.length > 0)\n attrs.push(`label=\"implements: ${this.labelledApis(served)}\", fontsize=9`);\n const suffix = attrs.length === 0 ? '' : ` [${attrs.join(', ')}]`;\n return ` \"${dotValue(from)}\" -> \"${dotValue(to)}\"${suffix};\\n`;\n }\n\n /**\n * The contract names for an edge label, sorted and capped so a shared api-lib serving a dozen\n * contracts cannot blow the edge label up into a wall of text. The truncation is stated in the\n * label (\"+N more\") rather than silent — the full list is in dependencies.json.\n */\n private labelledApis(apis: string[]): string {\n const sorted = [...apis].sort();\n if (sorted.length <= MAX_EDGE_LABEL_APIS) return sorted.join(', ');\n const shown = sorted.slice(0, MAX_EDGE_LABEL_APIS).join(', ');\n return `${shown} +${sorted.length - MAX_EDGE_LABEL_APIS} more`;\n }\n\n /**\n * Href for a node's design page: the project's committed design.html, made\n * relative to architecture/dependencies.html. Returns null when the project\n * has no generated DI design (no design.json → no design page exists).\n */\n private designHtmlHref(designFile: string | undefined): string | null {\n if (!designFile) return null;\n const designHtml = designFile.replace(/design\\.json$/, 'design.html');\n return path.posix.relative(ARCH_OUTPUT_DIR, designHtml);\n }\n\n /**\n * The design pages that EXIST, one entry per visible node that has one. A node absent from this\n * list gets no \"View Design\" item at all — the item is never rendered dead or greyed out.\n */\n designLinks(graph: EnhancedGraph): DesignLink[] {\n const links: DesignLink[] = [];\n for (const project of Object.keys(graph)) {\n const info = graph[project];\n if (this.isHidden(info)) continue;\n const href = this.designHtmlHref(info.designFile);\n if (href === null) continue;\n links.push(new DesignLink(this.names.getNodeId(project), href));\n }\n return links;\n }\n\n /**\n * Generate Graphviz DOT format from the graph\n */\n generateDot(graph: EnhancedGraph, title: string = 'Monorepo Dependency Architecture'): string {\n return this.generateRenderModel(graph, title).fullDot;\n }\n\n generateRenderModel(\n graph: EnhancedGraph,\n title: string = 'Monorepo Dependency Architecture',\n ): GraphRenderModel {\n this.assertDrawable(graph);\n const model = new GraphRenderModel();\n let dot = 'digraph Architecture {\\n';\n dot += ' rankdir=TB;\\n';\n dot += ' node [shape=box, style=filled, fontname=\"Arial\"];\\n';\n dot += ' edge [fontname=\"Arial\"];\\n\\n';\n\n model.header = dot;\n const bands = this.levelBands(graph);\n model.bands = bands;\n\n const palette = ProductPalette.fromGraph(graph);\n dot += this.dotNodes(graph, model, palette);\n dot += '\\n';\n dot += this.bandLayout.dot(bands);\n dot += '\\n';\n dot += this.dotEdges(graph, model, palette);\n\n const footerStart = dot.length;\n dot += '\\n labelloc=\"t\";\\n';\n dot += ` label=\"${dotValue(title)}\\\\n(from architecture/dependencies.json)\";\\n`;\n dot += ' fontsize=20;\\n';\n dot += '}\\n';\n\n model.footer = dot.slice(footerStart);\n model.completeEndpoints();\n model.fullDot = dot;\n return model;\n }\n\n /**\n * Refuse to draw a graph that would render something false.\n *\n * Two conditions, both of which produced a confidently-wrong picture with no warning at all:\n * - two projects sharing a node id fuse into one box, which UNIONS their two rank sets and\n * collapses two whole dependency levels onto one row (see graph-names.ts);\n * - a cycle makes the level numbers every row is keyed on meaningless (see graph-cycles.ts).\n */\n private assertDrawable(graph: EnhancedGraph): void {\n const owners: NodeIdOwner[] = [];\n const adjacency: ProjectAdjacency = {};\n for (const project of Object.keys(graph)) {\n owners.push(new NodeIdOwner(project, graph[project].level));\n adjacency[project] = graph[project].dependsOn ?? [];\n }\n this.names.assertUniqueNodeIds(owners);\n this.cycles.assertAcyclic(adjacency, 'architecture/dependencies.json');\n }\n\n /**\n * The visible projects grouped into one band per dependency level, ordered HIGHEST LEVEL FIRST\n * so the emitted bands read top-to-bottom with L0 last (hidden projects are omitted, so no\n * stray rank=same name is emitted for an absent node). LevelBandLayout turns these into the\n * rank sets and the invisible chain that pins them — see graph-level-bands.ts for why the\n * chain is required at all.\n */\n private levelBands(graph: EnhancedGraph): LevelBand[] {\n const byLevel = new Map<number, string[]>();\n for (const project of Object.keys(graph)) {\n if (this.isHidden(graph[project])) continue;\n const level = graph[project].level;\n const nodeIds = byLevel.get(level);\n if (nodeIds === undefined) byLevel.set(level, [this.names.getNodeId(project)]);\n else nodeIds.push(this.names.getNodeId(project));\n }\n const levels = [...byLevel.keys()].sort((a: number, b: number): number => b - a);\n return levels.map(\n (level: number): LevelBand =>\n new LevelBand(level, [...(byLevel.get(level) as string[])].sort()),\n );\n }\n\n // Node lines: one statement per color mode (graph-color-modes.ts) — the label is the short\n // name plus a short meta line, because the fill now shows what the env list used to spell out.\n // No node carries a URL: EVERY box is clickable and opens the floating node menu instead, which\n // is where a design page is reached (see designLinks).\n private dotNodes(graph: EnhancedGraph, model: GraphRenderModel, palette: ProductPalette): string {\n let dot = '';\n for (const project of Object.keys(graph)) {\n const info = graph[project];\n if (this.isHidden(info)) continue;\n // Identity is the project key; the LABEL is the pretty short name.\n const facts = new NodeFacts(\n this.names.getNodeId(project),\n this.names.getShortName(project),\n info.level,\n info.role ?? 'lib',\n info.framework ?? [],\n info.products ?? [],\n );\n this.styler.record(model.legend, facts, palette);\n dot += model.styledNode(facts, this.styler.dots(facts, palette));\n }\n return dot;\n }\n\n // Edge lines (dependencies). An edge to an api-lib is styled by WHY it exists\n // (implements/uses/uses-implements, from apiRelations); every other dependency\n // keeps the default plain arrow. In the Product color mode an edge crossing into another\n // product is ALSO dashed in that product's color (#1204), so product boundaries read at a glance.\n private dotEdges(graph: EnhancedGraph, model: GraphRenderModel, palette: ProductPalette): string {\n let dot = '';\n for (const project of Object.keys(graph)) {\n const info = graph[project];\n if (this.isHidden(info)) continue;\n const nodeId = this.names.getNodeId(project);\n for (const dep of info.dependsOn || []) {\n // Both endpoints must be visible — an edge to/from a hidden box\n // is dropped so no connection dangles into empty space.\n if (graph[dep] !== undefined && this.isHidden(graph[dep])) continue;\n dot += model.edge(\n nodeId,\n this.names.getNodeId(dep),\n this.edgeDot(nodeId, this.names.getNodeId(dep), info.apiRelations?.[dep]),\n this.crossProductAttrs(info.products ?? [], graph[dep]?.products ?? [], model, palette),\n );\n }\n }\n return dot;\n }\n\n /**\n * The Product color mode's extra attributes for one dependency: dashed, thicker and in the color\n * of the product it crosses into, or '' for an edge that stays inside its source's products.\n */\n private crossProductAttrs(\n from: readonly string[],\n to: readonly string[],\n model: GraphRenderModel,\n palette: ProductPalette,\n ): string {\n const into = this.boundary.crossedInto(from, to);\n if (into === null) return '';\n model.legend.crossProductEdges++;\n return `style=dashed, color=\"${palette.colorOf(into).color}\", penwidth=2`;\n }\n\n /**\n * Generate the interactive page: the drawer shell around a viz.js-rendered graph.\n *\n * The Impact sidecar is a plain `<script src>` loaded BEFORE the page scripts, so the page knows\n * the branch's impact when it first draws. It is per-branch and gitignored (graph-impact.ts):\n * when it is absent the browser skips it and Impact is disabled with its reason.\n */\n generateHTML(\n model: GraphRenderModel,\n links: DesignLink[],\n title: string = 'Monorepo Dependency Architecture',\n lockControl: string = '',\n responsibilitiesHtml: string = '',\n ): string {\n const parts = new ShellParts(\n title,\n lockControl,\n this.legend.sections(model.legend),\n this.legend.edgeKey(),\n this.filterAssets.html(),\n this.snapshot.html(),\n responsibilitiesHtml,\n model.legend.palette.colors,\n );\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${title}</title>\n <script src=\"https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js\"></script>\n <script src=\"${IMPACT_SIDECAR_SRC}\"></script>\n <style>${this.styles()}</style>\n</head>\n<body>\n ${this.shell.body(parts)}\n <script>${this.nodeMenu.script()}</script>\n <script>${this.filterJs()}</script>\n <script>${this.navigation.script()}</script>\n <script>${this.script(model, links)}</script>\n</body>\n</html>`;\n }\n\n /** Shared menu/dim/filter/zoom rules first, then the page's own, so its `#graph` rules win. */\n private styles(): string {\n return (\n this.nodeMenu.styles() +\n this.nodeMenu.dimStyles('#graph') +\n this.filterAssets.styles() +\n this.navigation.styles() +\n this.pageStyles.css()\n );\n }\n\n /**\n * The drawer's type-to-search lock field, replacing a `<select>` that listed every project and\n * was unusable at hundreds of options. Picking a project LOCKS the graph into that box's hover\n * view — its full ancestor + descendant chain stays lit while everything else dims — and narrows\n * the responsibilities panel to that chain. Clearing the field, or Esc, unlocks.\n *\n * Each option's VALUE is the node id the SVG is keyed on; its label is the level and short name.\n * Options are ordered by level DESCENDING to match the responsibilities cards.\n */\n lockControl(graph: EnhancedGraph): string {\n const projects = Object.keys(graph);\n projects.sort((a: string, b: string): number => {\n const levelDiff = graph[b].level - graph[a].level;\n if (levelDiff !== 0) return levelDiff;\n return a.localeCompare(b);\n });\n let options = '';\n for (const project of projects) {\n if (this.isHidden(graph[project])) continue;\n const nodeId = this.escapeHtml(this.names.getNodeId(project));\n const shortName = this.escapeHtml(this.names.getShortName(project));\n options += `<option value=\"${nodeId}\">L${graph[project].level} · ${shortName}</option>`;\n }\n return (\n '<input type=\"search\" id=\"wp-lock\" list=\"wp-lock-options\" placeholder=\"Lock a project… ( / )\" ' +\n 'autocomplete=\"off\" spellcheck=\"false\">' +\n `<datalist id=\"wp-lock-options\">${options}</datalist>`\n );\n }\n\n private escapeHtml(text: string): string {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n }\n\n /**\n * The page script. The browser code lives in graph-visualizer.client.ts — real, linted TypeScript\n * that tsc compiles in place, so what this inlines is its COMPILED .js sitting beside this file.\n *\n * The substitution is a blind split/join, which is why the placeholder token must appear EXACTLY\n * ONCE in the client (never in one of its comments): every literal occurrence would otherwise be\n * replaced by the whole DOT.\n */\n private script(model: GraphRenderModel, links: DesignLink[]): string {\n return this.clientJs()\n .split(CLIENT_MODEL_PLACEHOLDER)\n .join(this.filterAssets.json(model))\n .split(CLIENT_DESIGN_LINKS_PLACEHOLDER)\n .join(this.filterAssets.json(links));\n }\n\n /**\n * Write the committed architecture visualization to\n * architecture/dependencies.html, next to dependencies.json.\n *\n * This is a checked-in artifact, regenerated deterministically by\n * architecture:generate so every box keeps its menu, and the \"View Design\"\n * item keeps pointing at each project's committed design.html as designs\n * come and go. The DOT is embedded in the HTML (rendered client-side by\n * viz.js). Output is deterministic (sorted graph in → same bytes out) so git\n * only shows a diff when the architecture actually changed.\n *\n * `productDependencies` is the `product-tags` rule's matrix, listed in the Product legend; null\n * when the rule is OFF, and the legend then lists no matrix.\n */\n writeVisualization(\n graph: EnhancedGraph,\n workspaceRoot: string,\n productDependencies: ProductDependencyMatrix | null,\n title: string = 'Monorepo Dependency Architecture',\n ): VisualizationPaths {\n const outputDir = path.join(workspaceRoot, ARCH_OUTPUT_DIR);\n\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n const lockControl = this.lockControl(graph);\n const responsibilities = this.responsibilities.generateSection(graph, workspaceRoot);\n const model = this.generateRenderModel(graph, title);\n model.legend.productDependencies = productDependencies === null ? null : [...productDependencies.entries];\n const html = this.generateHTML(\n model,\n this.designLinks(graph),\n title,\n lockControl,\n responsibilities,\n );\n const htmlPath = path.join(outputDir, 'dependencies.html');\n fs.writeFileSync(htmlPath, html, 'utf-8');\n\n return new VisualizationPaths(htmlPath);\n }\n\n /**\n * Open the HTML visualization in the default browser\n */\n openVisualization(htmlPath: string): boolean {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const platform = process.platform;\n let openCommand: string;\n\n if (platform === 'darwin') {\n openCommand = `open \"${htmlPath}\"`;\n } else if (platform === 'win32') {\n openCommand = `start \"\" \"${htmlPath}\"`;\n } else {\n openCommand = `xdg-open \"${htmlPath}\"`;\n }\n\n execSync(openCommand, { stdio: 'ignore' });\n return true;\n } catch (err: unknown) {\n const error = toError(err);\n console.warn(`⚠️ Could not open browser: ${error.message}`);\n return false;\n }\n }\n}\n"]}
1
+ {"version":3,"file":"graph-visualizer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-visualizer.ts"],"names":[],"mappings":";;;AAqFA,gDAWC;;AAhGD,yDAAuE;AACvE;;;;;;;;;;GAUG;AAEH,+CAAyB;AACzB,qDAAiD;AACjD,mDAA6B;AAC7B,iDAAyC;AAGzC,+CAAwD;AACxD,2DAAiE;AACjE,iDAAwE;AACxE,qEAAoE;AACpE,uDAAkD;AAClD,6CAAwC;AACxC,6DAA2E;AAC3E,2DAAgE;AAChE,qDAAmE;AAEnE,iDAA6C;AAC7C,yDAAgE;AAChE,2DAAsD;AACtD,iDAAoD;AACpD,wCAAqC;AAErC;;;;GAIG;AACH,MAAM,eAAe,GAAG,cAAc,CAAC;AAEvC,uFAAuF;AACvF,MAAM,mBAAmB,GAAG,CAAC,CAAC;AAE9B;;;;;;GAMG;AACH,MAAa,UAAU;IAEC;IACA;IAFpB,YACoB,MAAc,EACd,IAAY;QADZ,WAAM,GAAN,MAAM,CAAQ;QACd,SAAI,GAAJ,IAAI,CAAQ;IAC7B,CAAC;CACP;AALD,gCAKC;AAED,MAAa,kBAAkB;IAC3B,QAAQ,CAAS;IAEjB,YAAY,QAAgB;QACxB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC7B,CAAC;CACJ;AAND,gDAMC;AAED;;;;GAIG;AACU,QAAA,wBAAwB,GAAG,IAAI,GAAG,cAAc,GAAG,IAAI,CAAC;AAErE,mGAAmG;AACtF,QAAA,+BAA+B,GAAG,IAAI,GAAG,cAAc,GAAG,IAAI,CAAC;AAE5E;;;;;;GAMG;AACH,qNAAqN;AACrN,SAAgB,kBAAkB,CAAC,IAAY;IAC3C,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;IACxC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACX,GAAG,IAAI,qBAAqB,SAAS,yBAAyB,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG;YACzF,sFAAsF,CAC7F,CAAC;IACN,CAAC;IACD,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC9C,IAAI,IAAI,KAAK,4BAA4B;QAAE,OAAO,MAAM,CAAC;IACzD,OAAO,kBAAkB,CAAC,4BAA4B,CAAC,GAAG,kBAAkB,CAAC,+BAA+B,CAAC,GAAG,MAAM,CAAC;AAC3H,CAAC;AAED,MAAa,eAAe;IAuBH;IAEA;IAxBJ,YAAY,GAAG,IAAI,sCAAiB,EAAE,CAAC;IACvC,QAAQ,GAAG,IAAI,8BAAa,EAAE,CAAC;IAC/B,KAAK,GAAG,IAAI,wBAAU,EAAE,CAAC;IACzB,gBAAgB,GAAG,IAAI,iDAAwB,EAAE,CAAC;IAClD,UAAU,GAAG,IAAI,mCAAe,EAAE,CAAC;IACnC,MAAM,GAAG,IAAI,mCAAoB,EAAE,CAAC;IACrD,2FAA2F;IAC1E,QAAQ,GAAG,IAAI,+BAAa,EAAE,CAAC;IAC/B,MAAM,GAAG,IAAI,kCAAc,EAAE,CAAC;IAC9B,MAAM,GAAG,IAAI,0BAAW,EAAE,CAAC;IAC3B,KAAK,GAAG,IAAI,iCAAc,EAAE,CAAC;IAC7B,UAAU,GAAG,IAAI,mCAAe,EAAE,CAAC;IACnC,UAAU,GAAG,IAAI,kCAAe,CAAC,mCAAgB,CAAC,QAAQ,CAAC,CAAC;IAC5D,QAAQ,GAAG,IAAI,gCAAe,EAAE,CAAC;IAElD;;;;;OAKG;IACH,YACqB,WAAyB,GAAW,EAAE,CACnD,kBAAkB,CAAC,4BAA4B,CAAC,EACnC,WAAyB,GAAW,EAAE,CACnD,kBAAkB,CAAC,wBAAwB,CAAC;QAH/B,aAAQ,GAAR,QAAQ,CAC2B;QACnC,aAAQ,GAAR,QAAQ,CACuB;IACjD,CAAC;IAEJ;;;;OAIG;IACK,QAAQ,CAAC,KAA4B;QACzC,OAAO,KAAK,CAAC,WAAW,KAAK,KAAK,CAAC;IACvC,CAAC;IAED;;;;;;;;;OASG;IACK,SAAS,CAAC,IAAiC;QAC/C,IAAI,IAAI,KAAK,YAAY;YAAE,OAAO,cAAc,CAAC;QACjD,IAAI,IAAI,KAAK,iBAAiB;YAAE,OAAO,2CAA2C,CAAC;QACnF,OAAO,EAAE,CAAC;IACd,CAAC;IAED;;;;;;;OAOG;IACK,OAAO,CAAC,IAAY,EAAE,EAAU,EAAE,QAAiC;QACvE,MAAM,KAAK,GAAa,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC/C,IAAI,OAAO,KAAK,EAAE;YAAE,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACxC,MAAM,MAAM,GAAG,CAAC,QAAQ,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,GAAW,EAAE,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC1E,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC;YACjB,KAAK,CAAC,IAAI,CAAC,sBAAsB,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,eAAe,CAAC,CAAC;QAC/E,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAClE,OAAO,MAAM,IAAA,qBAAQ,EAAC,IAAI,CAAC,SAAS,IAAA,qBAAQ,EAAC,EAAE,CAAC,IAAI,MAAM,KAAK,CAAC;IACpE,CAAC;IAED;;;;OAIG;IACK,YAAY,CAAC,IAAc;QAC/B,MAAM,MAAM,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC;QAChC,IAAI,MAAM,CAAC,MAAM,IAAI,mBAAmB;YAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,mBAAmB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,OAAO,GAAG,KAAK,KAAK,MAAM,CAAC,MAAM,GAAG,mBAAmB,OAAO,CAAC;IACnE,CAAC;IAED;;;;OAIG;IACK,cAAc,CAAC,UAA8B;QACjD,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC;QAC7B,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,eAAe,EAAE,aAAa,CAAC,CAAC;QACtE,OAAO,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,EAAE,UAAU,CAAC,CAAC;IAC5D,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,KAAoB;QAC5B,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAClC,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;YAClD,IAAI,IAAI,KAAK,IAAI;gBAAE,SAAS;YAC5B,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC,CAAC;QACpE,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,WAAW,CAAC,KAAoB,EAAE,QAAgB,kCAAkC;QAChF,OAAO,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC;IAC1D,CAAC;IAED,mBAAmB,CACf,KAAoB,EACpB,QAAgB,kCAAkC;QAElD,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;QAC3B,MAAM,KAAK,GAAG,IAAI,qCAAgB,EAAE,CAAC;QACrC,IAAI,GAAG,GAAG,0BAA0B,CAAC;QACrC,GAAG,IAAI,iBAAiB,CAAC;QACzB,GAAG,IAAI,uDAAuD,CAAC;QAC/D,GAAG,IAAI,gCAAgC,CAAC;QAExC,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC;QACnB,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QACrC,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC;QAEpB,MAAM,OAAO,GAAG,+BAAc,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAChD,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC5C,GAAG,IAAI,IAAI,CAAC;QACZ,GAAG,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;QAClC,GAAG,IAAI,IAAI,CAAC;QACZ,GAAG,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QAEnC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC;QAC/B,GAAG,IAAI,qBAAqB,CAAC;QAC7B,GAAG,IAAI,YAAY,IAAA,qBAAQ,EAAC,KAAK,CAAC,8CAA8C,CAAC;QACjF,GAAG,IAAI,kBAAkB,CAAC;QAC1B,GAAG,IAAI,KAAK,CAAC;QAEb,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACtC,KAAK,CAAC,iBAAiB,EAAE,CAAC;QAC1B,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC;QACpB,OAAO,KAAK,CAAC;IACjB,CAAC;IAED;;;;;;;OAOG;IACK,cAAc,CAAC,KAAoB;QACvC,MAAM,MAAM,GAAkB,EAAE,CAAC;QACjC,MAAM,SAAS,GAAqB,EAAE,CAAC;QACvC,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,CAAC,IAAI,CAAC,IAAI,yBAAW,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;YAC5D,SAAS,CAAC,OAAO,CAAC,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,SAAS,IAAI,EAAE,CAAC;QACxD,CAAC;QACD,IAAI,CAAC,KAAK,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;QACvC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,SAAS,EAAE,gCAAgC,CAAC,CAAC;IAC3E,CAAC;IAED;;;;;;OAMG;IACK,UAAU,CAAC,KAAoB;QACnC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAoB,CAAC;QAC5C,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAAE,SAAS;YAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC;YACnC,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnC,IAAI,OAAO,KAAK,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;;gBAC1E,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;QACrD,CAAC;QACD,MAAM,MAAM,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACjF,OAAO,MAAM,CAAC,GAAG,CACb,CAAC,KAAa,EAAa,EAAE,CACzB,IAAI,6BAAS,CAAC,KAAK,EAAE,CAAC,GAAI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAc,CAAC,CAAC,IAAI,EAAE,CAAC,CACzE,CAAC;IACN,CAAC;IAED,2FAA2F;IAC3F,+FAA+F;IAC/F,gGAAgG;IAChG,uDAAuD;IAC/C,QAAQ,CAAC,KAAoB,EAAE,KAAuB,EAAE,OAAuB;QACnF,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAClC,mEAAmE;YACnE,MAAM,KAAK,GAAG,IAAI,6BAAS,CACvB,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAC7B,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,EAChC,IAAI,CAAC,KAAK,EACV,IAAI,CAAC,IAAI,IAAI,KAAK,EAClB,IAAI,CAAC,SAAS,IAAI,EAAE,EACpB,IAAI,CAAC,QAAQ,IAAI,EAAE,CACtB,CAAC;YACF,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;YACjD,GAAG,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAC5D,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED,8EAA8E;IAC9E,+EAA+E;IAC/E,yFAAyF;IACzF,kGAAkG;IAC1F,QAAQ,CAAC,KAAoB,EAAE,KAAuB;QAC1D,IAAI,GAAG,GAAG,EAAE,CAAC;QACb,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC;YAC5B,IAAI,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;gBAAE,SAAS;YAClC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC7C,KAAK,MAAM,GAAG,IAAI,IAAI,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;gBACrC,gEAAgE;gBAChE,wDAAwD;gBACxD,IAAI,KAAK,CAAC,GAAG,CAAC,KAAK,SAAS,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;oBAAE,SAAS;gBACpE,GAAG,IAAI,KAAK,CAAC,IAAI,CACb,MAAM,EACN,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EACzB,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,EAAE,IAAI,CAAC,YAAY,EAAE,CAAC,GAAG,CAAC,CAAC,EACzE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,KAAK,CAAC,GAAG,CAAC,EAAE,QAAQ,IAAI,EAAE,EAAE,KAAK,CAAC,CAC3E,CAAC;YACN,CAAC;QACL,CAAC;QACD,OAAO,GAAG,CAAC;IACf,CAAC;IAED;;;OAGG;IACK,WAAW,CAAC,IAAuB,EAAE,EAAqB,EAAE,KAAuB;QACvF,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QACjD,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,KAAK,CAAC,MAAM,CAAC,iBAAiB,EAAE,CAAC;QACtD,OAAO,IAAI,CAAC;IAChB,CAAC;IAED;;;;;;OAMG;IACH,YAAY,CACR,KAAuB,EACvB,KAAmB,EACnB,QAAgB,kCAAkC,EAClD,cAAsB,EAAE,EACxB,uBAA+B,EAAE;QAEjC,MAAM,KAAK,GAAG,IAAI,6BAAU,CACxB,KAAK,EACL,WAAW,EACX,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,EAClC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,EACrB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EACxB,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,EACpB,oBAAoB,EACpB,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAC9B,CAAC;QACF,OAAO;;;;;aAKF,KAAK;;mBAEC,iCAAkB;aACxB,IAAI,CAAC,MAAM,EAAE;;;MAGpB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;cACd,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;cACtB,IAAI,CAAC,QAAQ,EAAE;cACf,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;cACxB,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC;;QAE/B,CAAC;IACL,CAAC;IAED,+FAA+F;IACvF,MAAM;QACV,OAAO,CACH,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YACtB,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,QAAQ,CAAC;YACjC,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE;YAC1B,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;YACxB,IAAI,CAAC,UAAU,CAAC,GAAG,EAAE,CACxB,CAAC;IACN,CAAC;IAED;;;;;;;;OAQG;IACH,WAAW,CAAC,KAAoB;QAC5B,MAAM,QAAQ,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACpC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAS,EAAE,CAAS,EAAU,EAAE;YAC3C,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;YAClD,IAAI,SAAS,KAAK,CAAC;gBAAE,OAAO,SAAS,CAAC;YACtC,OAAO,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;QAC9B,CAAC,CAAC,CAAC;QACH,IAAI,OAAO,GAAG,EAAE,CAAC;QACjB,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAAE,SAAS;YAC5C,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC;YAC9D,MAAM,SAAS,GAAG,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC;YACpE,OAAO,IAAI,kBAAkB,MAAM,MAAM,KAAK,CAAC,OAAO,CAAC,CAAC,KAAK,MAAM,SAAS,WAAW,CAAC;QAC5F,CAAC;QACD,OAAO,CACH,gGAAgG;YAChG,wCAAwC;YACxC,kCAAkC,OAAO,aAAa,CACzD,CAAC;IACN,CAAC;IAEO,UAAU,CAAC,IAAY;QAC3B,OAAO,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;IAC3G,CAAC;IAED;;;;;;;OAOG;IACK,MAAM,CAAC,KAAuB,EAAE,KAAmB;QACvD,OAAO,IAAI,CAAC,QAAQ,EAAE;aACjB,KAAK,CAAC,gCAAwB,CAAC;aAC/B,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aACnC,KAAK,CAAC,uCAA+B,CAAC;aACtC,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,kBAAkB,CACd,KAAoB,EACpB,aAAqB,EACrB,mBAAmD,EACnD,QAAgB,kCAAkC;QAElD,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;QAE5D,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YAC5B,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,CAAC;QAC5C,MAAM,gBAAgB,GAAG,IAAI,CAAC,gBAAgB,CAAC,eAAe,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QACrF,MAAM,KAAK,GAAG,IAAI,CAAC,mBAAmB,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;QACrD,KAAK,CAAC,MAAM,CAAC,mBAAmB,GAAG,mBAAmB,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC1G,MAAM,IAAI,GAAG,IAAI,CAAC,YAAY,CAC1B,KAAK,EACL,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EACvB,KAAK,EACL,WAAW,EACX,gBAAgB,CACnB,CAAC;QACF,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;QAC3D,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,CAAC;QAE1C,OAAO,IAAI,kBAAkB,CAAC,QAAQ,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,iBAAiB,CAAC,QAAgB;QAC9B,8DAA8D;QAC9D,IAAI,CAAC;YACD,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC;YAClC,IAAI,WAAmB,CAAC;YAExB,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBACxB,WAAW,GAAG,SAAS,QAAQ,GAAG,CAAC;YACvC,CAAC;iBAAM,IAAI,QAAQ,KAAK,OAAO,EAAE,CAAC;gBAC9B,WAAW,GAAG,aAAa,QAAQ,GAAG,CAAC;YAC3C,CAAC;iBAAM,CAAC;gBACJ,WAAW,GAAG,aAAa,QAAQ,GAAG,CAAC;YAC3C,CAAC;YAED,IAAA,wBAAQ,EAAC,WAAW,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC3C,OAAO,IAAI,CAAC;QAChB,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,OAAO,CAAC,IAAI,CAAC,+BAA+B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC7D,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;CACJ;AA3aD,0CA2aC","sourcesContent":["import { GraphNavigation, NavigationLayout } from './graph-navigation';\n/**\n * Graph Visualizer\n *\n * Generates visual representations of the architecture graph:\n * - DOT format (for Graphviz)\n * - Interactive HTML (using viz.js)\n *\n * All behavior lives on the injectable GraphVisualizer class so webpieces DI +\n * @DocumentDesign can wire it — module-scope functions are a dead end the DI\n * graph can't reach.\n */\n\nimport * as fs from 'fs';\nimport { SavedSnapshot } from './saved-snapshot';\nimport * as path from 'path';\nimport { execSync } from 'child_process';\nimport type { EnhancedGraph } from './graph-sorter';\nimport type { ApiRef, ApiRelation, ApiRelationKind } from './api-usage/api-relations';\nimport { GraphNames, NodeIdOwner } from './graph-names';\nimport { LevelBand, LevelBandLayout } from './graph-level-bands';\nimport { ProjectAdjacency, ProjectCycleDetector } from './graph-cycles';\nimport { ResponsibilitiesRenderer } from './graph-responsibilities';\nimport { GraphNodeMenu } from './graph-node-menu';\nimport { dotValue } from './dot-syntax';\nimport { GraphRenderModel, GraphFilterAssets } from './graph-render-model';\nimport { NodeFacts, NodeModeStyler } from './graph-color-modes';\nimport { ProductBoundary, ProductPalette } from './graph-products';\nimport { ProductDependencyMatrix } from './product-dependencies';\nimport { GraphLegend } from './graph-legend';\nimport { GraphPageShell, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { IMPACT_SIDECAR_SRC } from './graph-impact';\nimport { toError } from '../toError';\n\n/**\n * Directory (repo-relative) that the committed architecture HTML lives in.\n * Node click-through links are computed relative to this so they resolve when\n * the file is opened straight from the checkout.\n */\nconst ARCH_OUTPUT_DIR = 'architecture';\n\n/** Contracts named on one `implements` edge label before it truncates to \"+N more\". */\nconst MAX_EDGE_LABEL_APIS = 4;\n\n/**\n * One node's design page, handed to the browser so the node menu can offer \"View Design\" for the\n * boxes that HAVE one and omit the item entirely for the boxes that do not.\n *\n * The link is data on the PAGE now, not a `URL=` attribute in the DOT: a URL made viz.js wrap the\n * box in an `<a>` and clicking navigated straight out, which the menu replaces.\n */\nexport class DesignLink {\n constructor(\n public readonly nodeId: string,\n public readonly href: string,\n ) {}\n}\n\nexport class VisualizationPaths {\n htmlPath: string;\n\n constructor(htmlPath: string) {\n this.htmlPath = htmlPath;\n }\n}\n\n/**\n * The token the client scripts carry where the DOT belongs. It must appear EXACTLY ONCE in a compiled\n * client — the inliner is a blind split/join, so a second occurrence (in a comment, say) would be\n * replaced by the entire DOT too, bloating every generated page.\n */\nexport const CLIENT_MODEL_PLACEHOLDER = '__' + 'RENDER_MODEL' + '__';\n\n/** Same contract as CLIENT_MODEL_PLACEHOLDER, for the node → design.html links the menu offers. */\nexport const CLIENT_DESIGN_LINKS_PLACEHOLDER = '__' + 'DESIGN_LINKS' + '__';\n\n/**\n * Read a compiled browser client sitting beside this file.\n *\n * It is generated from the matching `.client.ts` by tsc, so it exists in `dist` and in the published\n * tarball but NOT in a source checkout. The error says that outright rather than surfacing a bare\n * ENOENT, because \"the build has not run\" and \"the file is missing\" look identical otherwise.\n */\n// webpieces-disable no-function-outside-class -- module-level resolver for the default injected into GraphVisualizer/RuntimeHtmlPage; making it a class would need a container in a module both of them load eagerly\nexport function readCompiledClient(name: string): string {\n const file = path.join(__dirname, name);\n if (!fs.existsSync(file)) {\n throw new Error(\n `${name} not found beside ${__dirname}. It is COMPILED from ${name.replace(/\\.js$/, '.ts')} ` +\n 'by tsc, so it only exists after a build — run the package build, or inject the text.',\n );\n }\n const source = fs.readFileSync(file, 'utf-8');\n if (name !== 'graph-visualizer.client.js') return source;\n return readCompiledClient('graph-mode-state.client.js') + readCompiledClient('graph-product-paint.client.js') + source;\n}\n\nexport class GraphVisualizer {\n private readonly filterAssets = new GraphFilterAssets();\n private readonly snapshot = new SavedSnapshot();\n private readonly names = new GraphNames();\n private readonly responsibilities = new ResponsibilitiesRenderer();\n private readonly bandLayout = new LevelBandLayout();\n private readonly cycles = new ProjectCycleDetector();\n /** The ONE floating-node-menu implementation, shared with the per-project design pages. */\n private readonly nodeMenu = new GraphNodeMenu();\n private readonly styler = new NodeModeStyler();\n private readonly legend = new GraphLegend();\n private readonly shell = new GraphPageShell();\n private readonly pageStyles = new GraphPageStyles();\n private readonly navigation = new GraphNavigation(NavigationLayout.FLOATING);\n private readonly boundary = new ProductBoundary();\n\n /**\n * How to obtain the browser client's text. Injected so HTML generation does not depend on BUILD\n * ORDER: the default reads the compiled sibling, which exists in the tarball and in dist but NOT in\n * a source checkout (the source there is .client.ts). A unit test running from source supplies the\n * text itself rather than requiring the package to have been built first.\n */\n constructor(\n private readonly clientJs: () => string = (): string =>\n readCompiledClient('graph-visualizer.client.js'),\n private readonly filterJs: () => string = (): string =>\n readCompiledClient('graph-filter.client.js'),\n ) {}\n\n /**\n * A project tagged `drawOnGraph:false` is hidden from the rendered graph —\n * its node, its rank placement, its lock-search option, its responsibilities\n * card, and every edge touching it are all omitted. It stays in the JSON.\n */\n private isHidden(entry: EnhancedGraph[string]): boolean {\n return entry.drawOnGraph === false;\n }\n\n /**\n * Edge styling by API-relation kind (why the edge exists):\n * implements → BLACK dashed (a controller serves this api-lib's contract)\n * uses → BLACK solid (a generated client calls it) — same as a\n * plain library import, since a plain dependency IS a use.\n * uses-implements → BLUE dashed, thicker (does both — implements some\n * contracts of the api-lib, uses others)\n * plain lib (none) → the default thin black solid arrow, unchanged.\n * `kind` is undefined for every non-api-lib dependency edge.\n */\n private edgeAttrs(kind: ApiRelationKind | undefined): string {\n if (kind === 'implements') return 'style=dashed';\n if (kind === 'uses-implements') return 'style=dashed, color=\"#1976d2\", penwidth=2';\n return '';\n }\n\n /**\n * The DOT attribute list for ONE dependency edge. An edge that IMPLEMENTS contracts is also\n * LABELED with them: the dashed line alone says \"something here is implemented\", which reads as\n * \"the tool did not detect anything\" to everyone who has not memorized the legend. Naming the\n * contracts makes `auth-store-api` visibly resolve to the server that serves it — the single\n * most important relationship in a microservice architecture, and the one the diagram was\n * silent about.\n */\n private edgeDot(from: string, to: string, relation: ApiRelation | undefined): string {\n const attrs: string[] = [];\n const styling = this.edgeAttrs(relation?.kind);\n if (styling !== '') attrs.push(styling);\n const served = (relation?.implements ?? []).map((ref: ApiRef) => ref.api);\n if (served.length > 0)\n attrs.push(`label=\"implements: ${this.labelledApis(served)}\", fontsize=9`);\n const suffix = attrs.length === 0 ? '' : ` [${attrs.join(', ')}]`;\n return ` \"${dotValue(from)}\" -> \"${dotValue(to)}\"${suffix};\\n`;\n }\n\n /**\n * The contract names for an edge label, sorted and capped so a shared api-lib serving a dozen\n * contracts cannot blow the edge label up into a wall of text. The truncation is stated in the\n * label (\"+N more\") rather than silent — the full list is in dependencies.json.\n */\n private labelledApis(apis: string[]): string {\n const sorted = [...apis].sort();\n if (sorted.length <= MAX_EDGE_LABEL_APIS) return sorted.join(', ');\n const shown = sorted.slice(0, MAX_EDGE_LABEL_APIS).join(', ');\n return `${shown} +${sorted.length - MAX_EDGE_LABEL_APIS} more`;\n }\n\n /**\n * Href for a node's design page: the project's committed design.html, made\n * relative to architecture/dependencies.html. Returns null when the project\n * has no generated DI design (no design.json → no design page exists).\n */\n private designHtmlHref(designFile: string | undefined): string | null {\n if (!designFile) return null;\n const designHtml = designFile.replace(/design\\.json$/, 'design.html');\n return path.posix.relative(ARCH_OUTPUT_DIR, designHtml);\n }\n\n /**\n * The design pages that EXIST, one entry per visible node that has one. A node absent from this\n * list gets no \"View Design\" item at all — the item is never rendered dead or greyed out.\n */\n designLinks(graph: EnhancedGraph): DesignLink[] {\n const links: DesignLink[] = [];\n for (const project of Object.keys(graph)) {\n const info = graph[project];\n if (this.isHidden(info)) continue;\n const href = this.designHtmlHref(info.designFile);\n if (href === null) continue;\n links.push(new DesignLink(this.names.getNodeId(project), href));\n }\n return links;\n }\n\n /**\n * Generate Graphviz DOT format from the graph\n */\n generateDot(graph: EnhancedGraph, title: string = 'Monorepo Dependency Architecture'): string {\n return this.generateRenderModel(graph, title).fullDot;\n }\n\n generateRenderModel(\n graph: EnhancedGraph,\n title: string = 'Monorepo Dependency Architecture',\n ): GraphRenderModel {\n this.assertDrawable(graph);\n const model = new GraphRenderModel();\n let dot = 'digraph Architecture {\\n';\n dot += ' rankdir=TB;\\n';\n dot += ' node [shape=box, style=filled, fontname=\"Arial\"];\\n';\n dot += ' edge [fontname=\"Arial\"];\\n\\n';\n\n model.header = dot;\n const bands = this.levelBands(graph);\n model.bands = bands;\n\n const palette = ProductPalette.fromGraph(graph);\n dot += this.dotNodes(graph, model, palette);\n dot += '\\n';\n dot += this.bandLayout.dot(bands);\n dot += '\\n';\n dot += this.dotEdges(graph, model);\n\n const footerStart = dot.length;\n dot += '\\n labelloc=\"t\";\\n';\n dot += ` label=\"${dotValue(title)}\\\\n(from architecture/dependencies.json)\";\\n`;\n dot += ' fontsize=20;\\n';\n dot += '}\\n';\n\n model.footer = dot.slice(footerStart);\n model.completeEndpoints();\n model.fullDot = dot;\n return model;\n }\n\n /**\n * Refuse to draw a graph that would render something false.\n *\n * Two conditions, both of which produced a confidently-wrong picture with no warning at all:\n * - two projects sharing a node id fuse into one box, which UNIONS their two rank sets and\n * collapses two whole dependency levels onto one row (see graph-names.ts);\n * - a cycle makes the level numbers every row is keyed on meaningless (see graph-cycles.ts).\n */\n private assertDrawable(graph: EnhancedGraph): void {\n const owners: NodeIdOwner[] = [];\n const adjacency: ProjectAdjacency = {};\n for (const project of Object.keys(graph)) {\n owners.push(new NodeIdOwner(project, graph[project].level));\n adjacency[project] = graph[project].dependsOn ?? [];\n }\n this.names.assertUniqueNodeIds(owners);\n this.cycles.assertAcyclic(adjacency, 'architecture/dependencies.json');\n }\n\n /**\n * The visible projects grouped into one band per dependency level, ordered HIGHEST LEVEL FIRST\n * so the emitted bands read top-to-bottom with L0 last (hidden projects are omitted, so no\n * stray rank=same name is emitted for an absent node). LevelBandLayout turns these into the\n * rank sets and the invisible chain that pins them — see graph-level-bands.ts for why the\n * chain is required at all.\n */\n private levelBands(graph: EnhancedGraph): LevelBand[] {\n const byLevel = new Map<number, string[]>();\n for (const project of Object.keys(graph)) {\n if (this.isHidden(graph[project])) continue;\n const level = graph[project].level;\n const nodeIds = byLevel.get(level);\n if (nodeIds === undefined) byLevel.set(level, [this.names.getNodeId(project)]);\n else nodeIds.push(this.names.getNodeId(project));\n }\n const levels = [...byLevel.keys()].sort((a: number, b: number): number => b - a);\n return levels.map(\n (level: number): LevelBand =>\n new LevelBand(level, [...(byLevel.get(level) as string[])].sort()),\n );\n }\n\n // Node lines: one statement per color mode (graph-color-modes.ts) — the label is the short\n // name plus a short meta line, because the fill now shows what the env list used to spell out.\n // No node carries a URL: EVERY box is clickable and opens the floating node menu instead, which\n // is where a design page is reached (see designLinks).\n private dotNodes(graph: EnhancedGraph, model: GraphRenderModel, palette: ProductPalette): string {\n let dot = '';\n for (const project of Object.keys(graph)) {\n const info = graph[project];\n if (this.isHidden(info)) continue;\n // Identity is the project key; the LABEL is the pretty short name.\n const facts = new NodeFacts(\n this.names.getNodeId(project),\n this.names.getShortName(project),\n info.level,\n info.role ?? 'lib',\n info.framework ?? [],\n info.products ?? [],\n );\n this.styler.record(model.legend, facts, palette);\n dot += model.styledNode(facts, this.styler.dots(facts));\n }\n return dot;\n }\n\n // Edge lines (dependencies). An edge to an api-lib is styled by WHY it exists\n // (implements/uses/uses-implements, from apiRelations); every other dependency\n // keeps the default plain arrow. In the Product color mode an edge crossing into another\n // product is ALSO dashed in that product's color (#1204), so product boundaries read at a glance.\n private dotEdges(graph: EnhancedGraph, model: GraphRenderModel): string {\n let dot = '';\n for (const project of Object.keys(graph)) {\n const info = graph[project];\n if (this.isHidden(info)) continue;\n const nodeId = this.names.getNodeId(project);\n for (const dep of info.dependsOn || []) {\n // Both endpoints must be visible — an edge to/from a hidden box\n // is dropped so no connection dangles into empty space.\n if (graph[dep] !== undefined && this.isHidden(graph[dep])) continue;\n dot += model.edge(\n nodeId,\n this.names.getNodeId(dep),\n this.edgeDot(nodeId, this.names.getNodeId(dep), info.apiRelations?.[dep]),\n this.crossesInto(info.products ?? [], graph[dep]?.products ?? [], model),\n );\n }\n }\n return dot;\n }\n\n /**\n * The products one dependency crosses into, counted for the legend; empty for an edge that stays\n * inside its source's products. The page dashes it in the first ON one's color (#1319).\n */\n private crossesInto(from: readonly string[], to: readonly string[], model: GraphRenderModel): string[] {\n const into = this.boundary.crossedInto(from, to);\n if (into.length > 0) model.legend.crossProductEdges++;\n return into;\n }\n\n /**\n * Generate the interactive page: the drawer shell around a viz.js-rendered graph.\n *\n * The Impact sidecar is a plain `<script src>` loaded BEFORE the page scripts, so the page knows\n * the branch's impact when it first draws. It is per-branch and gitignored (graph-impact.ts):\n * when it is absent the browser skips it and Impact is disabled with its reason.\n */\n generateHTML(\n model: GraphRenderModel,\n links: DesignLink[],\n title: string = 'Monorepo Dependency Architecture',\n lockControl: string = '',\n responsibilitiesHtml: string = '',\n ): string {\n const parts = new ShellParts(\n title,\n lockControl,\n this.legend.sections(model.legend),\n this.legend.edgeKey(),\n this.filterAssets.html(),\n this.snapshot.html(),\n responsibilitiesHtml,\n model.legend.palette.colors,\n );\n return `<!DOCTYPE html>\n<html>\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <title>${title}</title>\n <script src=\"https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js\"></script>\n <script src=\"${IMPACT_SIDECAR_SRC}\"></script>\n <style>${this.styles()}</style>\n</head>\n<body>\n ${this.shell.body(parts)}\n <script>${this.nodeMenu.script()}</script>\n <script>${this.filterJs()}</script>\n <script>${this.navigation.script()}</script>\n <script>${this.script(model, links)}</script>\n</body>\n</html>`;\n }\n\n /** Shared menu/dim/filter/zoom rules first, then the page's own, so its `#graph` rules win. */\n private styles(): string {\n return (\n this.nodeMenu.styles() +\n this.nodeMenu.dimStyles('#graph') +\n this.filterAssets.styles() +\n this.navigation.styles() +\n this.pageStyles.css()\n );\n }\n\n /**\n * The drawer's type-to-search lock field, replacing a `<select>` that listed every project and\n * was unusable at hundreds of options. Picking a project LOCKS the graph into that box's hover\n * view — its full ancestor + descendant chain stays lit while everything else dims — and narrows\n * the responsibilities panel to that chain. Clearing the field, or Esc, unlocks.\n *\n * Each option's VALUE is the node id the SVG is keyed on; its label is the level and short name.\n * Options are ordered by level DESCENDING to match the responsibilities cards.\n */\n lockControl(graph: EnhancedGraph): string {\n const projects = Object.keys(graph);\n projects.sort((a: string, b: string): number => {\n const levelDiff = graph[b].level - graph[a].level;\n if (levelDiff !== 0) return levelDiff;\n return a.localeCompare(b);\n });\n let options = '';\n for (const project of projects) {\n if (this.isHidden(graph[project])) continue;\n const nodeId = this.escapeHtml(this.names.getNodeId(project));\n const shortName = this.escapeHtml(this.names.getShortName(project));\n options += `<option value=\"${nodeId}\">L${graph[project].level} · ${shortName}</option>`;\n }\n return (\n '<input type=\"search\" id=\"wp-lock\" list=\"wp-lock-options\" placeholder=\"Lock a project… ( / )\" ' +\n 'autocomplete=\"off\" spellcheck=\"false\">' +\n `<datalist id=\"wp-lock-options\">${options}</datalist>`\n );\n }\n\n private escapeHtml(text: string): string {\n return text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/\"/g, '&quot;');\n }\n\n /**\n * The page script. The browser code lives in graph-visualizer.client.ts — real, linted TypeScript\n * that tsc compiles in place, so what this inlines is its COMPILED .js sitting beside this file.\n *\n * The substitution is a blind split/join, which is why the placeholder token must appear EXACTLY\n * ONCE in the client (never in one of its comments): every literal occurrence would otherwise be\n * replaced by the whole DOT.\n */\n private script(model: GraphRenderModel, links: DesignLink[]): string {\n return this.clientJs()\n .split(CLIENT_MODEL_PLACEHOLDER)\n .join(this.filterAssets.json(model))\n .split(CLIENT_DESIGN_LINKS_PLACEHOLDER)\n .join(this.filterAssets.json(links));\n }\n\n /**\n * Write the committed architecture visualization to\n * architecture/dependencies.html, next to dependencies.json.\n *\n * This is a checked-in artifact, regenerated deterministically by\n * architecture:generate so every box keeps its menu, and the \"View Design\"\n * item keeps pointing at each project's committed design.html as designs\n * come and go. The DOT is embedded in the HTML (rendered client-side by\n * viz.js). Output is deterministic (sorted graph in → same bytes out) so git\n * only shows a diff when the architecture actually changed.\n *\n * `productDependencies` is the `product-tags` rule's matrix, listed in the Product legend; null\n * when the rule is OFF, and the legend then lists no matrix.\n */\n writeVisualization(\n graph: EnhancedGraph,\n workspaceRoot: string,\n productDependencies: ProductDependencyMatrix | null,\n title: string = 'Monorepo Dependency Architecture',\n ): VisualizationPaths {\n const outputDir = path.join(workspaceRoot, ARCH_OUTPUT_DIR);\n\n if (!fs.existsSync(outputDir)) {\n fs.mkdirSync(outputDir, { recursive: true });\n }\n\n const lockControl = this.lockControl(graph);\n const responsibilities = this.responsibilities.generateSection(graph, workspaceRoot);\n const model = this.generateRenderModel(graph, title);\n model.legend.productDependencies = productDependencies === null ? null : [...productDependencies.entries];\n const html = this.generateHTML(\n model,\n this.designLinks(graph),\n title,\n lockControl,\n responsibilities,\n );\n const htmlPath = path.join(outputDir, 'dependencies.html');\n fs.writeFileSync(htmlPath, html, 'utf-8');\n\n return new VisualizationPaths(htmlPath);\n }\n\n /**\n * Open the HTML visualization in the default browser\n */\n openVisualization(htmlPath: string): boolean {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const platform = process.platform;\n let openCommand: string;\n\n if (platform === 'darwin') {\n openCommand = `open \"${htmlPath}\"`;\n } else if (platform === 'win32') {\n openCommand = `start \"\" \"${htmlPath}\"`;\n } else {\n openCommand = `xdg-open \"${htmlPath}\"`;\n }\n\n execSync(openCommand, { stdio: 'ignore' });\n return true;\n } catch (err: unknown) {\n const error = toError(err);\n console.warn(`⚠️ Could not open browser: ${error.message}`);\n return false;\n }\n }\n}\n"]}
@@ -184,7 +184,7 @@ class RuntimeGoalComparison {
184
184
  const edge = model.edges[index];
185
185
  const changes = this.changes(ids.edge(edge, nodes), diff);
186
186
  const status = this.styler.status(changes);
187
- model.edges[index] = new graph_render_model_1.RenderEdge(edge.from, edge.to, this.styler.edge(edge.dot, status), edge.id, null);
187
+ model.edges[index] = new graph_render_model_1.RenderEdge(edge.from, edge.to, this.styler.edge(edge.dot, status), edge.id, []);
188
188
  details[edge.id] = [`${edge.from} → ${edge.to}`, ...changes.flatMap((change) => change.lines())];
189
189
  }
190
190
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-goal-comparison.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-goal-comparison.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal vs Current (#1239): turns the semantic diff of the two saved runtime files into the overlay the\n * runtime viewer's \"Goal vs Current\" color mode draws.\n *\n * The overlay is rendered by the SAME shared runtime renderer as both pages (generateRuntimeRenderModel\n * over RuntimeUnionGraph), so queue boxes, self-queue sub-rows, level bands and non-constraining\n * feedback edges behave exactly as on the normal pages. Only the statements are restyled afterwards:\n *\n * added in goal green\n * removed from goal red, dashed box / dotted arrow — still drawn, never dropped\n * changed amber; a moved level reads `L4 → L2`\n * unchanged subdued grey, kept for context\n *\n * Every drawn box and arrow maps back to the diff's FACT identities (never to display names or edge\n * ids), and its before/after lines travel in the page for the details panel.\n */\n\nimport { dotValue, htmlLabelText, assertValidDot } from './dot-syntax';\nimport { GraphRenderModel, RenderEdge, RenderNode, RuntimeComparisonView } from './graph-render-model';\nimport type { RuntimeGraph, RuntimeUnresolved } from './runtime-graph-model';\nimport { RuntimeGraphDiff, RuntimeDiffStatus, RuntimeFactChange, RuntimeGraphDiffResult } from './runtime-graph-diff';\nimport { RuntimeUnionGraph } from './runtime-union-graph';\nimport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\nimport { generateRuntimeRenderModel } from './runtime-visualizer';\nimport { RuntimeLayout } from './runtime-layout';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\nimport { RuntimeExternalTriggers } from './runtime-external-triggers';\nimport { DIFF_STYLES } from './runtime-goal-styles';\n\n/** Render-node / render-edge → the diff fact identities it draws. */\nexport class RuntimeComparisonIds {\n private readonly byNode = new Map<string, string[]>();\n private readonly triggerService = new Map<string, string>();\n\n constructor(private readonly graph: RuntimeGraph) {\n const safe = (raw: string): string => raw.replace(/[^A-Za-z0-9_]/g, '_');\n for (const name of Object.keys(graph.services)) this.put(name, `service:${name}`);\n for (const id of Object.keys(graph.externalSystems ?? {})) this.put(`system__${safe(id)}`, `external-system:${id}`);\n for (const name of Object.keys(graph.remoteServices ?? {})) this.put(new RemoteServiceNodeId().of(name), `remote-service:${name}`);\n for (const use of graph.unresolvedUses) {\n const owner = graph.apis[use.api]?.owner ?? use.api;\n if (graph.apis[use.api]?.externalSystem === undefined) this.put(`external__${owner}`, `external-library:${owner}`);\n }\n for (const trigger of graph.triggers) {\n const id = `trigger:${trigger.kind}:${trigger.api}.${trigger.method}@${trigger.service}`;\n this.triggerService.set(id, trigger.service);\n if (trigger.kind === 'cron') this.put(`cron__${safe(`${trigger.api}_${trigger.method}`)}`, id);\n }\n for (const group of new RuntimeExternalTriggers(graph.triggers).groups) {\n for (const trigger of group.triggers) this.put(group.from, `trigger:external:${trigger.api}.${trigger.method}@${trigger.service}`);\n }\n }\n\n node(node: RenderNode): string[] {\n if (node.queueKeys.length > 0) return this.queues(node);\n return this.byNode.get(node.id) ?? [];\n }\n\n edge(edge: RenderEdge, nodes: Map<string, RenderNode>): string[] {\n const from = nodes.get(edge.from);\n const to = nodes.get(edge.to);\n if (from !== undefined && from.queueKeys.length > 0) return this.queues(from).map((queue: string) => `deliver:${queue}->${edge.to}`);\n if (to !== undefined && to.queueKeys.length > 0) return this.queues(to).map((queue: string) => `enqueue:${edge.from}->${queue}`);\n if (this.graph.services[edge.from] === undefined)\n return (this.byNode.get(edge.from) ?? []).filter((id: string) => this.triggerService.get(id) === edge.to);\n if (this.graph.services[edge.to] !== undefined) return [`rpc:${edge.from}->${edge.to}`];\n return (this.byNode.get(edge.to) ?? []).flatMap((target: string) => this.towards(edge, target));\n }\n\n /** A service's arrow into a context node: an external system, a remote service or a vendor library. */\n private towards(edge: RenderEdge, target: string): string[] {\n if (target.startsWith('external-system:')) return [`external-use:${edge.from}->${target}`];\n if (target.startsWith('remote-service:'))\n return [`remote-call:${edge.from}->${target}#${edge.dot.includes('enqueue') ? 'pubsub' : 'rpc'}`];\n const owner = target.slice('external-library:'.length);\n return this.graph.unresolvedUses\n .filter((use: RuntimeUnresolved) => use.service === edge.from && (this.graph.apis[use.api]?.owner ?? use.api) === owner)\n .map((use: RuntimeUnresolved) => `unresolved-use:${use.service}->${use.api}`);\n }\n\n private queues(node: RenderNode): string[] {\n return node.queueKeys.map((key: string) => (key.startsWith('queue:') ? `contract-queue:${key.slice(6)}` : `queue:${key}`));\n }\n\n private put(renderId: string, factId: string): void {\n if (!this.byNode.has(renderId)) this.byNode.set(renderId, []);\n if (!this.byNode.get(renderId)!.includes(factId)) this.byNode.get(renderId)!.push(factId);\n }\n}\n\n/** Restyles one rendered statement for its diff status. */\nexport class RuntimeComparisonStyler {\n status(changes: RuntimeFactChange[]): RuntimeDiffStatus {\n const statuses = new Set(changes.map((change: RuntimeFactChange) => change.status));\n if (statuses.size === 0) return RuntimeDiffStatus.UNCHANGED;\n if (statuses.size === 1) return [...statuses][0];\n return RuntimeDiffStatus.CHANGED;\n }\n\n /** A service box: rebuilt (its HTML label carries the level badge and the status in words). */\n service(node: RenderNode, status: RuntimeDiffStatus, changes: RuntimeFactChange[]): string {\n const style = DIFF_STYLES[status];\n const change = changes[0];\n const level = this.levels(change);\n const changed = status === RuntimeDiffStatus.CHANGED && change !== undefined ? `: ${change.changedFields.join(', ')}` : '';\n const label =\n `<FONT COLOR=\"${style.text}99\">${htmlLabelText(level)}</FONT>&#160;&#160;<B>${htmlLabelText(node.id.split('/').pop() ?? node.id)}</B>` +\n `<BR/>${htmlLabelText(node.tags?.role ?? 'unknown')}<BR/>${htmlLabelText(`${style.word}${changed}`)}`;\n const boxStyle = status === RuntimeDiffStatus.REMOVED ? 'filled,rounded,dashed' : 'filled,rounded';\n return (\n ` \"${dotValue(node.id)}\" [style=\"${boxStyle}\", fillcolor=\"${style.fill}\", color=\"${style.border}\", ` +\n `penwidth=2, fontcolor=\"${style.text}\", label=<${label}>];\\n`\n );\n }\n\n /** A context box (queue, datastore, clock, vendor): kind shape kept, status colors laid over it. */\n context(node: RenderNode, status: RuntimeDiffStatus, changes: RuntimeFactChange[]): string {\n const style = DIFF_STYLES[status];\n let dot = node.dot;\n const moved = changes.find((change: RuntimeFactChange) => change.changedFields.includes('level'));\n if (node.queueKeys.length > 0 && moved !== undefined)\n dot = dot.replace(/(label=\" \\|)L\\d+ {2}/, `$1${this.levels(moved).replace(/>/g, '\\\\>')} `);\n const base = /style=\"([^\"]*)\"/.exec(dot)?.[1] ?? 'filled';\n const boxStyle = status === RuntimeDiffStatus.REMOVED && !base.includes('dashed') ? `${base},dashed` : base;\n return `${dot.slice(0, -2)} [style=\"${boxStyle}\", fillcolor=\"${style.fill}\", color=\"${style.border}\", penwidth=2, fontcolor=\"${style.text}\"];\\n`;\n }\n\n /** An arrow: its line style says rpc/event as always; color (and dots for a removal) says the diff. */\n edge(dot: string, status: RuntimeDiffStatus): string {\n const style = DIFF_STYLES[status];\n const removed = status === RuntimeDiffStatus.REMOVED ? ', style=dotted' : '';\n const width = status === RuntimeDiffStatus.UNCHANGED ? 1 : 2.2;\n return `${dot.slice(0, -2)} [color=\"${style.border}\", fontcolor=\"${style.border}\", penwidth=${width}${removed}];\\n`;\n }\n\n /** `L4`, or `L4 → L2` when the level moved. */\n levels(change: RuntimeFactChange | undefined): string {\n const before = change?.before?.['level'];\n const after = change?.after?.['level'];\n if (before !== undefined && after !== undefined && before !== after) return `${before} → ${after}`;\n return after ?? before ?? '';\n }\n}\n\n/** Builds the Goal vs Current view both runtime pages carry. */\nexport class RuntimeGoalComparison {\n private readonly styler = new RuntimeComparisonStyler();\n\n build(current: RuntimeGraph, goal: RuntimeGraph, options: RuntimeVizOptions): RuntimeComparisonView {\n const diff = new RuntimeGraphDiff().compare(current, goal);\n const union = new RuntimeUnionGraph().build(current, goal);\n const tags: Record<string, string> = {};\n for (const key of Object.keys(union.queues)) tags[key] = diff.get(`queue:${key}`)?.status ?? '';\n const model = generateRuntimeRenderModel(union, 'Goal vs Current', options.withPage(RuntimePageKind.COMPARISON, tags));\n const nodeDetails: Record<string, string[]> = {};\n const edgeDetails: Record<string, string[]> = {};\n const ids = new RuntimeComparisonIds(union);\n this.restyleNodes(model, ids, diff, nodeDetails);\n this.restyleEdges(model, ids, diff, edgeDetails);\n model.fullDot = new RuntimeLayout().assemble(model);\n assertValidDot(model.fullDot, 'runtime-architecture goal comparison');\n return new RuntimeComparisonView(\n true,\n '',\n 'Current → Goal',\n this.summary(diff),\n model,\n nodeDetails,\n edgeDetails,\n diff.differences().map((change: RuntimeFactChange) => this.line(change)),\n );\n }\n\n private restyleNodes(model: GraphRenderModel, ids: RuntimeComparisonIds, diff: RuntimeGraphDiffResult, details: Record<string, string[]>): void {\n for (let index = 0; index < model.nodes.length; index++) {\n const node = model.nodes[index];\n const changes = this.changes(ids.node(node), diff);\n const status = this.styler.status(changes);\n const dot = node.tags !== null ? this.styler.service(node, status, changes) : this.styler.context(node, status, changes);\n model.nodes[index] = new RenderNode(node.id, dot, null, node.tags, node.products, node.queueKeys);\n details[node.id] = changes.flatMap((change: RuntimeFactChange) => change.lines());\n }\n }\n\n private restyleEdges(model: GraphRenderModel, ids: RuntimeComparisonIds, diff: RuntimeGraphDiffResult, details: Record<string, string[]>): void {\n const nodes = new Map<string, RenderNode>();\n for (const node of model.nodes) nodes.set(node.id, node);\n for (let index = 0; index < model.edges.length; index++) {\n const edge = model.edges[index];\n const changes = this.changes(ids.edge(edge, nodes), diff);\n const status = this.styler.status(changes);\n model.edges[index] = new RenderEdge(edge.from, edge.to, this.styler.edge(edge.dot, status), edge.id, null);\n details[edge.id] = [`${edge.from} → ${edge.to}`, ...changes.flatMap((change: RuntimeFactChange) => change.lines())];\n }\n }\n\n private changes(factIds: string[], diff: RuntimeGraphDiffResult): RuntimeFactChange[] {\n return factIds\n .map((id: string) => diff.get(id))\n .filter((change: RuntimeFactChange | undefined): change is RuntimeFactChange => change !== undefined);\n }\n\n private summary(diff: RuntimeGraphDiffResult): string {\n if (diff.identical()) return 'No differences: the goal matches the current runtime graph.';\n const counts = diff.counts();\n const levels = diff.maxLevelBefore === diff.maxLevelAfter ? '' : ` · max level L${diff.maxLevelBefore} → L${diff.maxLevelAfter}`;\n return `${counts.added} added · ${counts.removed} removed · ${counts.changed} changed · ${counts.unchanged} unchanged${levels}`;\n }\n\n private line(change: RuntimeFactChange): string {\n const fields = change.status === RuntimeDiffStatus.CHANGED ? ` (${change.changedFields.join(', ')})` : '';\n return `${change.status}: ${change.label}${fields}`;\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-goal-comparison.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-goal-comparison.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Goal vs Current (#1239): turns the semantic diff of the two saved runtime files into the overlay the\n * runtime viewer's \"Goal vs Current\" color mode draws.\n *\n * The overlay is rendered by the SAME shared runtime renderer as both pages (generateRuntimeRenderModel\n * over RuntimeUnionGraph), so queue boxes, self-queue sub-rows, level bands and non-constraining\n * feedback edges behave exactly as on the normal pages. Only the statements are restyled afterwards:\n *\n * added in goal green\n * removed from goal red, dashed box / dotted arrow — still drawn, never dropped\n * changed amber; a moved level reads `L4 → L2`\n * unchanged subdued grey, kept for context\n *\n * Every drawn box and arrow maps back to the diff's FACT identities (never to display names or edge\n * ids), and its before/after lines travel in the page for the details panel.\n */\n\nimport { dotValue, htmlLabelText, assertValidDot } from './dot-syntax';\nimport { GraphRenderModel, RenderEdge, RenderNode, RuntimeComparisonView } from './graph-render-model';\nimport type { RuntimeGraph, RuntimeUnresolved } from './runtime-graph-model';\nimport { RuntimeGraphDiff, RuntimeDiffStatus, RuntimeFactChange, RuntimeGraphDiffResult } from './runtime-graph-diff';\nimport { RuntimeUnionGraph } from './runtime-union-graph';\nimport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\nimport { generateRuntimeRenderModel } from './runtime-visualizer';\nimport { RuntimeLayout } from './runtime-layout';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\nimport { RuntimeExternalTriggers } from './runtime-external-triggers';\nimport { DIFF_STYLES } from './runtime-goal-styles';\n\n/** Render-node / render-edge → the diff fact identities it draws. */\nexport class RuntimeComparisonIds {\n private readonly byNode = new Map<string, string[]>();\n private readonly triggerService = new Map<string, string>();\n\n constructor(private readonly graph: RuntimeGraph) {\n const safe = (raw: string): string => raw.replace(/[^A-Za-z0-9_]/g, '_');\n for (const name of Object.keys(graph.services)) this.put(name, `service:${name}`);\n for (const id of Object.keys(graph.externalSystems ?? {})) this.put(`system__${safe(id)}`, `external-system:${id}`);\n for (const name of Object.keys(graph.remoteServices ?? {})) this.put(new RemoteServiceNodeId().of(name), `remote-service:${name}`);\n for (const use of graph.unresolvedUses) {\n const owner = graph.apis[use.api]?.owner ?? use.api;\n if (graph.apis[use.api]?.externalSystem === undefined) this.put(`external__${owner}`, `external-library:${owner}`);\n }\n for (const trigger of graph.triggers) {\n const id = `trigger:${trigger.kind}:${trigger.api}.${trigger.method}@${trigger.service}`;\n this.triggerService.set(id, trigger.service);\n if (trigger.kind === 'cron') this.put(`cron__${safe(`${trigger.api}_${trigger.method}`)}`, id);\n }\n for (const group of new RuntimeExternalTriggers(graph.triggers).groups) {\n for (const trigger of group.triggers) this.put(group.from, `trigger:external:${trigger.api}.${trigger.method}@${trigger.service}`);\n }\n }\n\n node(node: RenderNode): string[] {\n if (node.queueKeys.length > 0) return this.queues(node);\n return this.byNode.get(node.id) ?? [];\n }\n\n edge(edge: RenderEdge, nodes: Map<string, RenderNode>): string[] {\n const from = nodes.get(edge.from);\n const to = nodes.get(edge.to);\n if (from !== undefined && from.queueKeys.length > 0) return this.queues(from).map((queue: string) => `deliver:${queue}->${edge.to}`);\n if (to !== undefined && to.queueKeys.length > 0) return this.queues(to).map((queue: string) => `enqueue:${edge.from}->${queue}`);\n if (this.graph.services[edge.from] === undefined)\n return (this.byNode.get(edge.from) ?? []).filter((id: string) => this.triggerService.get(id) === edge.to);\n if (this.graph.services[edge.to] !== undefined) return [`rpc:${edge.from}->${edge.to}`];\n return (this.byNode.get(edge.to) ?? []).flatMap((target: string) => this.towards(edge, target));\n }\n\n /** A service's arrow into a context node: an external system, a remote service or a vendor library. */\n private towards(edge: RenderEdge, target: string): string[] {\n if (target.startsWith('external-system:')) return [`external-use:${edge.from}->${target}`];\n if (target.startsWith('remote-service:'))\n return [`remote-call:${edge.from}->${target}#${edge.dot.includes('enqueue') ? 'pubsub' : 'rpc'}`];\n const owner = target.slice('external-library:'.length);\n return this.graph.unresolvedUses\n .filter((use: RuntimeUnresolved) => use.service === edge.from && (this.graph.apis[use.api]?.owner ?? use.api) === owner)\n .map((use: RuntimeUnresolved) => `unresolved-use:${use.service}->${use.api}`);\n }\n\n private queues(node: RenderNode): string[] {\n return node.queueKeys.map((key: string) => (key.startsWith('queue:') ? `contract-queue:${key.slice(6)}` : `queue:${key}`));\n }\n\n private put(renderId: string, factId: string): void {\n if (!this.byNode.has(renderId)) this.byNode.set(renderId, []);\n if (!this.byNode.get(renderId)!.includes(factId)) this.byNode.get(renderId)!.push(factId);\n }\n}\n\n/** Restyles one rendered statement for its diff status. */\nexport class RuntimeComparisonStyler {\n status(changes: RuntimeFactChange[]): RuntimeDiffStatus {\n const statuses = new Set(changes.map((change: RuntimeFactChange) => change.status));\n if (statuses.size === 0) return RuntimeDiffStatus.UNCHANGED;\n if (statuses.size === 1) return [...statuses][0];\n return RuntimeDiffStatus.CHANGED;\n }\n\n /** A service box: rebuilt (its HTML label carries the level badge and the status in words). */\n service(node: RenderNode, status: RuntimeDiffStatus, changes: RuntimeFactChange[]): string {\n const style = DIFF_STYLES[status];\n const change = changes[0];\n const level = this.levels(change);\n const changed = status === RuntimeDiffStatus.CHANGED && change !== undefined ? `: ${change.changedFields.join(', ')}` : '';\n const label =\n `<FONT COLOR=\"${style.text}99\">${htmlLabelText(level)}</FONT>&#160;&#160;<B>${htmlLabelText(node.id.split('/').pop() ?? node.id)}</B>` +\n `<BR/>${htmlLabelText(node.tags?.role ?? 'unknown')}<BR/>${htmlLabelText(`${style.word}${changed}`)}`;\n const boxStyle = status === RuntimeDiffStatus.REMOVED ? 'filled,rounded,dashed' : 'filled,rounded';\n return (\n ` \"${dotValue(node.id)}\" [style=\"${boxStyle}\", fillcolor=\"${style.fill}\", color=\"${style.border}\", ` +\n `penwidth=2, fontcolor=\"${style.text}\", label=<${label}>];\\n`\n );\n }\n\n /** A context box (queue, datastore, clock, vendor): kind shape kept, status colors laid over it. */\n context(node: RenderNode, status: RuntimeDiffStatus, changes: RuntimeFactChange[]): string {\n const style = DIFF_STYLES[status];\n let dot = node.dot;\n const moved = changes.find((change: RuntimeFactChange) => change.changedFields.includes('level'));\n if (node.queueKeys.length > 0 && moved !== undefined)\n dot = dot.replace(/(label=\" \\|)L\\d+ {2}/, `$1${this.levels(moved).replace(/>/g, '\\\\>')} `);\n const base = /style=\"([^\"]*)\"/.exec(dot)?.[1] ?? 'filled';\n const boxStyle = status === RuntimeDiffStatus.REMOVED && !base.includes('dashed') ? `${base},dashed` : base;\n return `${dot.slice(0, -2)} [style=\"${boxStyle}\", fillcolor=\"${style.fill}\", color=\"${style.border}\", penwidth=2, fontcolor=\"${style.text}\"];\\n`;\n }\n\n /** An arrow: its line style says rpc/event as always; color (and dots for a removal) says the diff. */\n edge(dot: string, status: RuntimeDiffStatus): string {\n const style = DIFF_STYLES[status];\n const removed = status === RuntimeDiffStatus.REMOVED ? ', style=dotted' : '';\n const width = status === RuntimeDiffStatus.UNCHANGED ? 1 : 2.2;\n return `${dot.slice(0, -2)} [color=\"${style.border}\", fontcolor=\"${style.border}\", penwidth=${width}${removed}];\\n`;\n }\n\n /** `L4`, or `L4 → L2` when the level moved. */\n levels(change: RuntimeFactChange | undefined): string {\n const before = change?.before?.['level'];\n const after = change?.after?.['level'];\n if (before !== undefined && after !== undefined && before !== after) return `${before} → ${after}`;\n return after ?? before ?? '';\n }\n}\n\n/** Builds the Goal vs Current view both runtime pages carry. */\nexport class RuntimeGoalComparison {\n private readonly styler = new RuntimeComparisonStyler();\n\n build(current: RuntimeGraph, goal: RuntimeGraph, options: RuntimeVizOptions): RuntimeComparisonView {\n const diff = new RuntimeGraphDiff().compare(current, goal);\n const union = new RuntimeUnionGraph().build(current, goal);\n const tags: Record<string, string> = {};\n for (const key of Object.keys(union.queues)) tags[key] = diff.get(`queue:${key}`)?.status ?? '';\n const model = generateRuntimeRenderModel(union, 'Goal vs Current', options.withPage(RuntimePageKind.COMPARISON, tags));\n const nodeDetails: Record<string, string[]> = {};\n const edgeDetails: Record<string, string[]> = {};\n const ids = new RuntimeComparisonIds(union);\n this.restyleNodes(model, ids, diff, nodeDetails);\n this.restyleEdges(model, ids, diff, edgeDetails);\n model.fullDot = new RuntimeLayout().assemble(model);\n assertValidDot(model.fullDot, 'runtime-architecture goal comparison');\n return new RuntimeComparisonView(\n true,\n '',\n 'Current → Goal',\n this.summary(diff),\n model,\n nodeDetails,\n edgeDetails,\n diff.differences().map((change: RuntimeFactChange) => this.line(change)),\n );\n }\n\n private restyleNodes(model: GraphRenderModel, ids: RuntimeComparisonIds, diff: RuntimeGraphDiffResult, details: Record<string, string[]>): void {\n for (let index = 0; index < model.nodes.length; index++) {\n const node = model.nodes[index];\n const changes = this.changes(ids.node(node), diff);\n const status = this.styler.status(changes);\n const dot = node.tags !== null ? this.styler.service(node, status, changes) : this.styler.context(node, status, changes);\n model.nodes[index] = new RenderNode(node.id, dot, null, node.tags, node.products, node.queueKeys);\n details[node.id] = changes.flatMap((change: RuntimeFactChange) => change.lines());\n }\n }\n\n private restyleEdges(model: GraphRenderModel, ids: RuntimeComparisonIds, diff: RuntimeGraphDiffResult, details: Record<string, string[]>): void {\n const nodes = new Map<string, RenderNode>();\n for (const node of model.nodes) nodes.set(node.id, node);\n for (let index = 0; index < model.edges.length; index++) {\n const edge = model.edges[index];\n const changes = this.changes(ids.edge(edge, nodes), diff);\n const status = this.styler.status(changes);\n model.edges[index] = new RenderEdge(edge.from, edge.to, this.styler.edge(edge.dot, status), edge.id, []);\n details[edge.id] = [`${edge.from} → ${edge.to}`, ...changes.flatMap((change: RuntimeFactChange) => change.lines())];\n }\n }\n\n private changes(factIds: string[], diff: RuntimeGraphDiffResult): RuntimeFactChange[] {\n return factIds\n .map((id: string) => diff.get(id))\n .filter((change: RuntimeFactChange | undefined): change is RuntimeFactChange => change !== undefined);\n }\n\n private summary(diff: RuntimeGraphDiffResult): string {\n if (diff.identical()) return 'No differences: the goal matches the current runtime graph.';\n const counts = diff.counts();\n const levels = diff.maxLevelBefore === diff.maxLevelAfter ? '' : ` · max level L${diff.maxLevelBefore} → L${diff.maxLevelAfter}`;\n return `${counts.added} added · ${counts.removed} removed · ${counts.changed} changed · ${counts.unchanged} unchanged${levels}`;\n }\n\n private line(change: RuntimeFactChange): string {\n const fields = change.status === RuntimeDiffStatus.CHANGED ? ` (${change.changedFields.join(', ')})` : '';\n return `${change.status}: ${change.label}${fields}`;\n }\n}\n"]}
@@ -98,12 +98,13 @@ class RuntimeHtmlPage {
98
98
  ]
99
99
  .map((role) => tag('role', role, graph_color_modes_1.ROLE_STYLES.find((style) => style.role === role)?.fill ?? '#e4e6ec'))
100
100
  .join('');
101
+ const legend = new graph_legend_1.GraphLegend();
101
102
  const products = this.shownProducts(model)
102
- .map((color) => tag('product', color.product, color.color))
103
+ .map((color) => legend.productToggle(`data-wp-legend-group="product" data-wp-legend-value="${(0, dot_syntax_1.htmlLabelText)(color.product)}"`, (0, dot_syntax_1.htmlLabelText)(color.product), `<svg width="20" height="14"><rect width="20" height="14" rx="2" fill="${color.color}"/></svg>${(0, dot_syntax_1.htmlLabelText)(color.product)}`))
103
104
  .join('');
104
105
  return (`<section data-wp-legend-mode="runtime" class="wp-legend-list">${frameworks}${tag('runtime', 'unknown', '#e4e6ec')}<p>Neutral: unknown runtime. Nested: specialization; stripes: multiple runtimes. Context retains kind colors.</p></section>
105
106
  <section data-wp-legend-mode="architecture" class="wp-legend-list" hidden>${roles}<p>Auxiliary nodes use kind colors, never project roles.</p></section>` +
106
- `<section data-wp-legend-mode="impact" class="wp-legend-list" hidden>${new graph_legend_1.GraphLegend().impactRows()}</section><section data-wp-legend-mode="product" class="wp-legend-list" hidden>${products}<p>Stripes: several products; neutral: all products; white/dashed: no product. Context inherits adjacent service memberships.</p></section>` +
107
+ `<section data-wp-legend-mode="impact" class="wp-legend-list" hidden>${legend.impactRows()}</section><section data-wp-legend-mode="product" class="wp-legend-list" hidden>${products}${legend.productNote()}<p>Queues, clocks, datastores and vendors inherit adjacent service memberships; several ON products are a gradient on them.</p></section>` +
107
108
  this.goalLegend(model) +
108
109
  '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>');
109
110
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-html-page.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-html-page.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,yDAAgD;AAChD,6DAA2E;AAC3E,uDAAmD;AAEnD,+DAA2E;AAC3E,2DAA4D;AAC5D,iEAA4D;AAC5D,+DAAoD;AACpD,6DAAyD;AACzD,yDAAuE;AACvE,qDAAiD;AACjD,yDAI4B;AAC5B,uDAAkD;AAClD,yDAAgE;AAChE,2DAAsD;AACtD,iDAA6C;AAE7C,2DAAoE;AACpE,iDAAoD;AACpD,qEAAoE;AACpE,6CAA6C;AAG7C,8FAA8F;AAC9F,MAAa,eAAe;IAMH;IAEA;IAEA;IATJ,MAAM,GAAG,IAAI,sCAAiB,EAAE,CAAC;IACjC,IAAI,GAAG,IAAI,+BAAa,EAAE,CAAC;IAC3B,UAAU,GAAG,IAAI,kCAAe,CAAC,mCAAgB,CAAC,QAAQ,CAAC,CAAC;IAE7E,YACqB,WAAyB,GAAW,EAAE,CACnD,IAAA,qCAAkB,EAAC,8BAA8B,CAAC,EACrC,WAAyB,GAAW,EAAE,CACnD,IAAA,qCAAkB,EAAC,wBAAwB,CAAC,EAC/B,WAAyB,GAAW,EAAE,CACnD,IAAA,qCAAkB,EAAC,4BAA4B,CAAC;QALnC,aAAQ,GAAR,QAAQ,CAC6B;QACrC,aAAQ,GAAR,QAAQ,CACuB;QAC/B,aAAQ,GAAR,QAAQ,CAC2B;IACrD,CAAC;IAEJ,MAAM,CACF,KAAuB,EACvB,KAAa,EACb,KAAmB,EACnB,UAA6B,IAAI,uCAAiB,EAAE;QAEpD,KAAK,CAAC,MAAM,GAAG,SAAS,CAAC;QACzB,MAAM,UAAU,GAAG;YACf,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,IAAI,EAAE,CAAC,CAAC;SACzE,CAAC,IAAI,EAAE,CAAC;QACT,MAAM,KAAK,GAAG;YACV,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAC1F,CAAC,IAAI,EAAE,CAAC;QACT,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QAC3C,MAAM,KAAK,GAAG,IAAI,6BAAU,CACxB,KAAK,EACL,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,EACvB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,EAClB,IAAI,CAAC,OAAO,EAAE,EACd,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,qCAAqC,EAC1D,OAAO,CAAC,IAAI,KAAK,qCAAe,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,yCAAqB,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,8BAAa,EAAE,CAAC,IAAI,EAAE,EACvG,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,EACrC,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,KAAK,EACL,iIAAiI,CACpI,CAAC;QACF,MAAM,MAAM,GAAG,IAAI,CAAC,QAAQ,EAAE;aACzB,KAAK,CAAC,2CAAwB,CAAC;aAC/B,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;aAC7B,KAAK,CAAC,kDAA+B,CAAC;aACtC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC;QAC9D,OAAO;SACN,IAAA,0BAAa,EAAC,KAAK,CAAC;;eAEd,iCAAkB;SACxB,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,GAAG,IAAI,mCAAe,EAAE,CAAC,GAAG,EAAE;;;;;;8CAM7F,OAAO,CAAC,QAAQ,KAAK,IAAI,KAAK,IAAI,iCAAc,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC;UAClG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,oBAAoB,IAAI,CAAC,QAAQ,EAAE,oBAAoB,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE;UACjG,IAAI,gCAAc,CAAC,KAAK,EAAE,OAAO,CAAC,iBAAiB,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE;UACpE,IAAI,yCAAkB,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,MAAM,EAAE;UACjD,IAAI,CAAC,QAAQ,EAAE,oBAAoB,MAAM,yBAAyB,CAAC;IACzE,CAAC;IAEO,WAAW,CAAC,KAAmB,EAAE,OAA0B;QAC/D,MAAM,KAAK,GAAiB,EAAE,CAAC;QAC/B,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;YACpD,IAAI,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,WAAW,KAAK,KAAK;gBAAE,SAAS;YACzD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC,EAAE,UAAU,EAAE,OAAO,CACxD,eAAe,EACf,aAAa,CAChB,CAAC;YACF,IACI,CAAC,MAAM;gBACP,CAAC,OAAO,CAAC,aAAa;gBACtB,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC;gBAExD,SAAS;YACb,KAAK,CAAC,IAAI,CAAC,IAAI,6BAAU,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;QACnF,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAEO,WAAW,CAAC,KAAuB;QACvC,MAAM,OAAO,GAAG,KAAK,CAAC,KAAK;aACtB,GAAG,CACA,CAAC,IAAI,EAAE,EAAE,CACL,kBAAkB,IAAA,0BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,KAAK,IAAA,0BAAa,EAAC,IAAI,CAAC,EAAE,CAAC,MAAM,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,SAAS,WAAW,CACvH;aACA,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,kKAAkK,OAAO,aAAa,CAAC;IAClM,CAAC;IAEO,MAAM,CAAC,KAAuB;QAClC,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,KAAa,EAAE,KAAa,EAAU,EAAE,CAChE,oDAAoD,KAAK,2BAA2B,IAAA,0BAAa,EAAC,KAAK,CAAC,2EAA2E,KAAK,YAAY,IAAA,0BAAa,EAAC,KAAK,CAAC,QAAQ,CAAC;QACrO,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,CAAC,oCAAgB,CAAC;aAC7C,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;aACpE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;aAC5D,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,MAAM,KAAK,GAAG;YACV,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;SAC1F;aACI,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CACV,GAAG,CACC,MAAM,EACN,IAAI,EACJ,+BAAW,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,EAAE,IAAI,IAAI,SAAS,CACtE,CACJ;aACA,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;aACrC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;aAC1D,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,CACH,iEAAiE,UAAU,GAAG,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;4EAClD,KAAK,wEAAwE;YAC7I,uEAAuE,IAAI,0BAAW,EAAE,CAAC,UAAU,EAAE,kFAAkF,QAAQ,6IAA6I;YAC5U,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC;YACtB,sHAAsH,CACzH,CAAC;IACN,CAAC;IAED;;;;OAIG;IACK,aAAa,CAAC,KAAuB;QACzC,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;QACpE,OAAO,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,KAAmB,EAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC;IAC1G,CAAC;IAED,mHAAmH;IAC3G,UAAU,CAAC,KAAuB;QACtC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,IAAI,UAAU,KAAK,IAAI,IAAI,CAAC,UAAU,CAAC,SAAS;YAC5C,OAAO,qGAAqG,IAAA,0BAAa,EAAC,UAAU,EAAE,MAAM,IAAI,iCAAiC,CAAC,gBAAgB,CAAC;QACvM,MAAM,IAAI,GAAG,CAAC,sCAAiB,CAAC,KAAK,EAAE,sCAAiB,CAAC,OAAO,EAAE,sCAAiB,CAAC,OAAO,EAAE,sCAAiB,CAAC,SAAS,CAAC;aACpH,GAAG,CAAC,CAAC,MAAyB,EAAE,EAAE;YAC/B,MAAM,KAAK,GAAG,iCAAW,CAAC,MAAM,CAAC,CAAC;YAClC,MAAM,IAAI,GAAG,MAAM,KAAK,sCAAiB,CAAC,OAAO,CAAC,CAAC,CAAC,yBAAyB,CAAC,CAAC,CAAC,EAAE,CAAC;YACnF,OAAO,4HAA4H,KAAK,CAAC,IAAI,aAAa,KAAK,CAAC,MAAM,qBAAqB,IAAI,WAAW,KAAK,CAAC,IAAI,QAAQ,CAAC;QACjO,CAAC,CAAC;aACD,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,MAAM,WAAW,GAAG,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,OAAO,IAAA,0BAAa,EAAC,IAAI,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QAC7G,OAAO,CACH,6FAA6F,IAAA,0BAAa,EAAC,UAAU,CAAC,SAAS,CAAC,eAAe,IAAA,0BAAa,EAAC,UAAU,CAAC,OAAO,CAAC,OAAO,IAAI,EAAE;YAC7L,gKAAgK;YAChK,CAAC,WAAW,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,kCAAkC,UAAU,CAAC,WAAW,CAAC,MAAM,kBAAkB,WAAW,iBAAiB,CAAC;YACzI,YAAY,CACf,CAAC;IACN,CAAC;IAEO,OAAO;QACX,OAAO,oMAAoM,CAAC;IAChN,CAAC;IAEO,gBAAgB,CAAC,KAAmB,EAAE,OAA0B;QACpE,MAAM,QAAQ,GAAkB,EAAE,CAAC;QACnC,KAAK,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC3D,QAAQ,CAAC,IAAI,CAAC,GAAG;gBACb,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,CAAC;gBAC3B,KAAK,EAAE,OAAO,CAAC,KAAK;gBACpB,SAAS,EAAE,OAAO,CAAC,SAAS;gBAC5B,WAAW,EAAE,OAAO,CAAC,WAAW;aACnC,CAAC;QACN,CAAC;QACD,OAAO,IAAI,iDAAwB,EAAE,CAAC,eAAe,CAAC,QAAQ,EAAE,OAAO,CAAC,aAAa,CAAC,CAAC;IAC3F,CAAC;CACJ;AA1KD,0CA0KC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { DesignLink } from './graph-visualizer';\nimport { GraphRenderModel, GraphFilterAssets } from './graph-render-model';\nimport { RuntimeDetails } from './runtime-details';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\nimport { RuntimeGoalProvenance } from './runtime-goal-file';\nimport { RuntimeGoalDetails } from './runtime-goal-details';\nimport { DIFF_STYLES } from './runtime-goal-styles';\nimport { RuntimeDiffStatus } from './runtime-graph-diff';\nimport { GraphNavigation, NavigationLayout } from './graph-navigation';\nimport { SavedSnapshot } from './saved-snapshot';\nimport {\n CLIENT_MODEL_PLACEHOLDER,\n CLIENT_DESIGN_LINKS_PLACEHOLDER,\n readCompiledClient,\n} from './graph-visualizer';\nimport { GraphNodeMenu } from './graph-node-menu';\nimport { GraphPageShell, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { GraphLegend } from './graph-legend';\nimport { ProductColor } from './graph-products';\nimport { FRAMEWORK_STYLES, ROLE_STYLES } from './graph-color-modes';\nimport { IMPACT_SIDECAR_SRC } from './graph-impact';\nimport { ResponsibilitiesRenderer } from './graph-responsibilities';\nimport { htmlLabelText } from './dot-syntax';\nimport type { EnhancedGraph } from './graph-sorter';\n\n/** Runtime facts and shape adapter inside the same shell/controller as the project viewer. */\nexport class RuntimeHtmlPage {\n private readonly assets = new GraphFilterAssets();\n private readonly menu = new GraphNodeMenu();\n private readonly navigation = new GraphNavigation(NavigationLayout.FLOATING);\n\n constructor(\n private readonly clientJs: () => string = (): string =>\n readCompiledClient('runtime-visualizer.client.js'),\n private readonly filterJs: () => string = (): string =>\n readCompiledClient('graph-filter.client.js'),\n private readonly sharedJs: () => string = (): string =>\n readCompiledClient('graph-visualizer.client.js'),\n ) {}\n\n render(\n model: GraphRenderModel,\n title: string,\n graph: RuntimeGraph,\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n ): string {\n model.viewer = 'runtime';\n const frameworks = [\n ...new Set(model.nodes.flatMap((node) => node.tags?.frameworks ?? [])),\n ].sort();\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ].sort();\n const products = this.shownProducts(model);\n const parts = new ShellParts(\n title,\n this.lockControl(model),\n this.legend(model),\n this.edgeKey(),\n this.assets.html() + '<span id=\"wp-context-count\"></span>',\n options.page === RuntimePageKind.GOAL ? new RuntimeGoalProvenance().html() : new SavedSnapshot().html(),\n this.responsibilities(graph, options),\n products,\n true,\n frameworks,\n roles,\n '<div class=\"wp-empty\" id=\"wp-empty\" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>',\n );\n const shared = this.sharedJs()\n .split(CLIENT_MODEL_PLACEHOLDER)\n .join(this.assets.json(model))\n .split(CLIENT_DESIGN_LINKS_PLACEHOLDER)\n .join(this.assets.json(this.designLinks(graph, options)));\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${htmlLabelText(title)}</title>\n<script src=\"https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js\"></script>\n<script src=\"${IMPACT_SIDECAR_SRC}\"></script>\n<style>${this.menu.styles()}${this.menu.dimStyles('#graph')}${this.assets.styles()}${this.navigation.styles()}${new GraphPageStyles().css()}\n.wp-graph-details, .wp-goal-details, .wp-impact-why { position:fixed; z-index:60; max-width:min(520px,calc(100vw - 24px)); max-height:70vh; overflow:auto; background:var(--wp-surface); border:1px solid var(--wp-line); border-radius:10px; padding:14px; box-shadow:0 8px 24px #0002; }\n.wp-graph-details button, .wp-goal-details button, .wp-impact-why button { cursor:pointer; } .wp-api-detail { cursor:pointer; text-decoration:underline; }\n.wp-api-detail:focus-visible { outline:3px solid var(--wp-accent); } #wp-context-count { font-size:11px; color:var(--wp-muted); }\n.wp-goal-edge { cursor:pointer; } .wp-goal-diff ul { margin:4px 0 0; padding-left:16px; max-height:220px; overflow:auto; }\n.wp-empty { position:absolute; top:70px; left:16px; padding:16px; background:var(--wp-surface); border-radius:10px; }\n</style></head><body data-wp-project-facts=\"${options.projects !== null}\">${new GraphPageShell().body(parts)}\n<script>${this.menu.script()}</script><script>${this.filterJs()}</script><script>${this.navigation.script()}</script>\n<script>${new RuntimeDetails(graph, options.showExternalNodes, model).script()}</script>\n<script>${new RuntimeGoalDetails(model.comparison).script()}</script>\n<script>${this.clientJs()}</script><script>${shared}</script></body></html>`;\n }\n\n private designLinks(graph: RuntimeGraph, options: RuntimeVizOptions): DesignLink[] {\n const links: DesignLink[] = [];\n for (const name of Object.keys(graph.services).sort()) {\n if (graph.services[name].drawOnGraph === false) continue;\n const design = options.projects?.[name]?.designFile?.replace(\n /design\\.json$/,\n 'design.html',\n );\n if (\n !design ||\n !options.workspaceRoot ||\n !fs.existsSync(path.join(options.workspaceRoot, design))\n )\n continue;\n links.push(new DesignLink(name, path.posix.relative('tmp/webpieces', design)));\n }\n return links;\n }\n\n private lockControl(model: GraphRenderModel): string {\n const entries = model.nodes\n .map(\n (node) =>\n `<option value=\"${htmlLabelText(node.id)}\">${htmlLabelText(node.id)} · ${node.tags?.role ?? 'context'}</option>`,\n )\n .join('');\n return `<input type=\"search\" id=\"wp-lock\" list=\"wp-lock-options\" placeholder=\"Lock a node… ( / )\" autocomplete=\"off\" spellcheck=\"false\"><datalist id=\"wp-lock-options\">${entries}</datalist>`;\n }\n\n private legend(model: GraphRenderModel): string {\n const tag = (group: string, value: string, color: string): string =>\n `<div class=\"wp-legend-row\" data-wp-legend-group=\"${group}\" data-wp-legend-value=\"${htmlLabelText(value)}\"><svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color}\"/></svg>${htmlLabelText(value)}</div>`;\n const frameworks = Object.values(FRAMEWORK_STYLES)\n .filter((style) => model.legend.frameworks.includes(style.framework))\n .map((style) => tag('runtime', style.framework, style.color))\n .join('');\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ]\n .map((role) =>\n tag(\n 'role',\n role,\n ROLE_STYLES.find((style) => style.role === role)?.fill ?? '#e4e6ec',\n ),\n )\n .join('');\n const products = this.shownProducts(model)\n .map((color) => tag('product', color.product, color.color))\n .join('');\n return (\n `<section data-wp-legend-mode=\"runtime\" class=\"wp-legend-list\">${frameworks}${tag('runtime', 'unknown', '#e4e6ec')}<p>Neutral: unknown runtime. Nested: specialization; stripes: multiple runtimes. Context retains kind colors.</p></section>\n<section data-wp-legend-mode=\"architecture\" class=\"wp-legend-list\" hidden>${roles}<p>Auxiliary nodes use kind colors, never project roles.</p></section>` +\n `<section data-wp-legend-mode=\"impact\" class=\"wp-legend-list\" hidden>${new GraphLegend().impactRows()}</section><section data-wp-legend-mode=\"product\" class=\"wp-legend-list\" hidden>${products}<p>Stripes: several products; neutral: all products; white/dashed: no product. Context inherits adjacent service memberships.</p></section>` +\n this.goalLegend(model) +\n '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>'\n );\n }\n\n /**\n * The products some drawn box carries, colored from the ONE palette the boxes were painted with —\n * built over every declared product (#1244), so a product keeps the dependency viewer's color even\n * though this page lists only the products it shows.\n */\n private shownProducts(model: GraphRenderModel): ProductColor[] {\n const shown = new Set(model.nodes.flatMap((node) => node.products));\n return model.legend.palette.colors.filter((color: ProductColor): boolean => shown.has(color.product));\n }\n\n /** Goal vs Current (#1239): the direction, the counts (or why it is unavailable), the key and every difference. */\n private goalLegend(model: GraphRenderModel): string {\n const comparison = model.comparison;\n if (comparison === null || !comparison.available)\n return `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list\" hidden><p>Goal vs Current unavailable: ${htmlLabelText(comparison?.reason ?? 'no comparison data on this page')}</p></section>`;\n const rows = [RuntimeDiffStatus.ADDED, RuntimeDiffStatus.REMOVED, RuntimeDiffStatus.CHANGED, RuntimeDiffStatus.UNCHANGED]\n .map((status: RuntimeDiffStatus) => {\n const style = DIFF_STYLES[status];\n const dash = status === RuntimeDiffStatus.REMOVED ? ' stroke-dasharray=\"3 2\"' : '';\n return `<div class=\"wp-legend-row wp-goal-row\"><svg width=\"20\" height=\"14\"><rect x=\"1\" y=\"1\" width=\"18\" height=\"12\" rx=\"2\" fill=\"${style.fill}\" stroke=\"${style.border}\" stroke-width=\"2\"${dash}/></svg>${style.word}</div>`;\n })\n .join('');\n const differences = comparison.differences.map((line: string) => `<li>${htmlLabelText(line)}</li>`).join('');\n return (\n `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list wp-goal-diff\" hidden><p><strong>${htmlLabelText(comparison.direction)}</strong> · ${htmlLabelText(comparison.summary)}</p>${rows}` +\n '<p>Arrows use the same colors; a removed arrow is dotted. A moved level reads L4 → L2. Click a box (Goal vs Current details) or an arrow for before/after.</p>' +\n (differences === '' ? '' : `<details><summary>Differences (${comparison.differences.length})</summary><ul>${differences}</ul></details>`) +\n '</section>'\n );\n }\n\n private edgeKey(): string {\n return '<div class=\"wp-legend-list\"><p>→ RPC · ⇢ queued/event · red: legacy cycle</p><p>Horizontal cylinder: queue<br>Upright cylinder: datastore<br>⏰: scheduler<br>Dashed box: external system</p></div>';\n }\n\n private responsibilities(graph: RuntimeGraph, options: RuntimeVizOptions): string {\n const projects: EnhancedGraph = {};\n for (const [name, service] of Object.entries(graph.services)) {\n projects[name] = {\n ...options.projects?.[name],\n level: service.level,\n dependsOn: service.dependsOn,\n drawOnGraph: service.drawOnGraph,\n };\n }\n return new ResponsibilitiesRenderer().generateSection(projects, options.workspaceRoot);\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-html-page.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-html-page.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { DesignLink } from './graph-visualizer';\nimport { GraphRenderModel, GraphFilterAssets } from './graph-render-model';\nimport { RuntimeDetails } from './runtime-details';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { RuntimeVizOptions, RuntimePageKind } from './runtime-viz-options';\nimport { RuntimeGoalProvenance } from './runtime-goal-file';\nimport { RuntimeGoalDetails } from './runtime-goal-details';\nimport { DIFF_STYLES } from './runtime-goal-styles';\nimport { RuntimeDiffStatus } from './runtime-graph-diff';\nimport { GraphNavigation, NavigationLayout } from './graph-navigation';\nimport { SavedSnapshot } from './saved-snapshot';\nimport {\n CLIENT_MODEL_PLACEHOLDER,\n CLIENT_DESIGN_LINKS_PLACEHOLDER,\n readCompiledClient,\n} from './graph-visualizer';\nimport { GraphNodeMenu } from './graph-node-menu';\nimport { GraphPageShell, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { GraphLegend } from './graph-legend';\nimport { ProductColor } from './graph-products';\nimport { FRAMEWORK_STYLES, ROLE_STYLES } from './graph-color-modes';\nimport { IMPACT_SIDECAR_SRC } from './graph-impact';\nimport { ResponsibilitiesRenderer } from './graph-responsibilities';\nimport { htmlLabelText } from './dot-syntax';\nimport type { EnhancedGraph } from './graph-sorter';\n\n/** Runtime facts and shape adapter inside the same shell/controller as the project viewer. */\nexport class RuntimeHtmlPage {\n private readonly assets = new GraphFilterAssets();\n private readonly menu = new GraphNodeMenu();\n private readonly navigation = new GraphNavigation(NavigationLayout.FLOATING);\n\n constructor(\n private readonly clientJs: () => string = (): string =>\n readCompiledClient('runtime-visualizer.client.js'),\n private readonly filterJs: () => string = (): string =>\n readCompiledClient('graph-filter.client.js'),\n private readonly sharedJs: () => string = (): string =>\n readCompiledClient('graph-visualizer.client.js'),\n ) {}\n\n render(\n model: GraphRenderModel,\n title: string,\n graph: RuntimeGraph,\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n ): string {\n model.viewer = 'runtime';\n const frameworks = [\n ...new Set(model.nodes.flatMap((node) => node.tags?.frameworks ?? [])),\n ].sort();\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ].sort();\n const products = this.shownProducts(model);\n const parts = new ShellParts(\n title,\n this.lockControl(model),\n this.legend(model),\n this.edgeKey(),\n this.assets.html() + '<span id=\"wp-context-count\"></span>',\n options.page === RuntimePageKind.GOAL ? new RuntimeGoalProvenance().html() : new SavedSnapshot().html(),\n this.responsibilities(graph, options),\n products,\n true,\n frameworks,\n roles,\n '<div class=\"wp-empty\" id=\"wp-empty\" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>',\n );\n const shared = this.sharedJs()\n .split(CLIENT_MODEL_PLACEHOLDER)\n .join(this.assets.json(model))\n .split(CLIENT_DESIGN_LINKS_PLACEHOLDER)\n .join(this.assets.json(this.designLinks(graph, options)));\n return `<!DOCTYPE html><html><head><meta charset=\"utf-8\"><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>${htmlLabelText(title)}</title>\n<script src=\"https://cdn.jsdelivr.net/npm/@viz-js/viz@3.28.0/dist/viz-global.js\"></script>\n<script src=\"${IMPACT_SIDECAR_SRC}\"></script>\n<style>${this.menu.styles()}${this.menu.dimStyles('#graph')}${this.assets.styles()}${this.navigation.styles()}${new GraphPageStyles().css()}\n.wp-graph-details, .wp-goal-details, .wp-impact-why { position:fixed; z-index:60; max-width:min(520px,calc(100vw - 24px)); max-height:70vh; overflow:auto; background:var(--wp-surface); border:1px solid var(--wp-line); border-radius:10px; padding:14px; box-shadow:0 8px 24px #0002; }\n.wp-graph-details button, .wp-goal-details button, .wp-impact-why button { cursor:pointer; } .wp-api-detail { cursor:pointer; text-decoration:underline; }\n.wp-api-detail:focus-visible { outline:3px solid var(--wp-accent); } #wp-context-count { font-size:11px; color:var(--wp-muted); }\n.wp-goal-edge { cursor:pointer; } .wp-goal-diff ul { margin:4px 0 0; padding-left:16px; max-height:220px; overflow:auto; }\n.wp-empty { position:absolute; top:70px; left:16px; padding:16px; background:var(--wp-surface); border-radius:10px; }\n</style></head><body data-wp-project-facts=\"${options.projects !== null}\">${new GraphPageShell().body(parts)}\n<script>${this.menu.script()}</script><script>${this.filterJs()}</script><script>${this.navigation.script()}</script>\n<script>${new RuntimeDetails(graph, options.showExternalNodes, model).script()}</script>\n<script>${new RuntimeGoalDetails(model.comparison).script()}</script>\n<script>${this.clientJs()}</script><script>${shared}</script></body></html>`;\n }\n\n private designLinks(graph: RuntimeGraph, options: RuntimeVizOptions): DesignLink[] {\n const links: DesignLink[] = [];\n for (const name of Object.keys(graph.services).sort()) {\n if (graph.services[name].drawOnGraph === false) continue;\n const design = options.projects?.[name]?.designFile?.replace(\n /design\\.json$/,\n 'design.html',\n );\n if (\n !design ||\n !options.workspaceRoot ||\n !fs.existsSync(path.join(options.workspaceRoot, design))\n )\n continue;\n links.push(new DesignLink(name, path.posix.relative('tmp/webpieces', design)));\n }\n return links;\n }\n\n private lockControl(model: GraphRenderModel): string {\n const entries = model.nodes\n .map(\n (node) =>\n `<option value=\"${htmlLabelText(node.id)}\">${htmlLabelText(node.id)} · ${node.tags?.role ?? 'context'}</option>`,\n )\n .join('');\n return `<input type=\"search\" id=\"wp-lock\" list=\"wp-lock-options\" placeholder=\"Lock a node… ( / )\" autocomplete=\"off\" spellcheck=\"false\"><datalist id=\"wp-lock-options\">${entries}</datalist>`;\n }\n\n private legend(model: GraphRenderModel): string {\n const tag = (group: string, value: string, color: string): string =>\n `<div class=\"wp-legend-row\" data-wp-legend-group=\"${group}\" data-wp-legend-value=\"${htmlLabelText(value)}\"><svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color}\"/></svg>${htmlLabelText(value)}</div>`;\n const frameworks = Object.values(FRAMEWORK_STYLES)\n .filter((style) => model.legend.frameworks.includes(style.framework))\n .map((style) => tag('runtime', style.framework, style.color))\n .join('');\n const roles = [\n ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),\n ]\n .map((role) =>\n tag(\n 'role',\n role,\n ROLE_STYLES.find((style) => style.role === role)?.fill ?? '#e4e6ec',\n ),\n )\n .join('');\n const legend = new GraphLegend();\n const products = this.shownProducts(model)\n .map((color) =>\n legend.productToggle(\n `data-wp-legend-group=\"product\" data-wp-legend-value=\"${htmlLabelText(color.product)}\"`,\n htmlLabelText(color.product),\n `<svg width=\"20\" height=\"14\"><rect width=\"20\" height=\"14\" rx=\"2\" fill=\"${color.color}\"/></svg>${htmlLabelText(color.product)}`,\n ),\n )\n .join('');\n return (\n `<section data-wp-legend-mode=\"runtime\" class=\"wp-legend-list\">${frameworks}${tag('runtime', 'unknown', '#e4e6ec')}<p>Neutral: unknown runtime. Nested: specialization; stripes: multiple runtimes. Context retains kind colors.</p></section>\n<section data-wp-legend-mode=\"architecture\" class=\"wp-legend-list\" hidden>${roles}<p>Auxiliary nodes use kind colors, never project roles.</p></section>` +\n `<section data-wp-legend-mode=\"impact\" class=\"wp-legend-list\" hidden>${legend.impactRows()}</section><section data-wp-legend-mode=\"product\" class=\"wp-legend-list\" hidden>${products}${legend.productNote()}<p>Queues, clocks, datastores and vendors inherit adjacent service memberships; several ON products are a gradient on them.</p></section>` +\n this.goalLegend(model) +\n '<p>Queues, clocks, datastores and vendors remain context in Impact; no source ownership or CI status is implied.</p>'\n );\n }\n\n /**\n * The products some drawn box carries, colored from the ONE palette the boxes were painted with —\n * built over every declared product (#1244), so a product keeps the dependency viewer's color even\n * though this page lists only the products it shows.\n */\n private shownProducts(model: GraphRenderModel): ProductColor[] {\n const shown = new Set(model.nodes.flatMap((node) => node.products));\n return model.legend.palette.colors.filter((color: ProductColor): boolean => shown.has(color.product));\n }\n\n /** Goal vs Current (#1239): the direction, the counts (or why it is unavailable), the key and every difference. */\n private goalLegend(model: GraphRenderModel): string {\n const comparison = model.comparison;\n if (comparison === null || !comparison.available)\n return `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list\" hidden><p>Goal vs Current unavailable: ${htmlLabelText(comparison?.reason ?? 'no comparison data on this page')}</p></section>`;\n const rows = [RuntimeDiffStatus.ADDED, RuntimeDiffStatus.REMOVED, RuntimeDiffStatus.CHANGED, RuntimeDiffStatus.UNCHANGED]\n .map((status: RuntimeDiffStatus) => {\n const style = DIFF_STYLES[status];\n const dash = status === RuntimeDiffStatus.REMOVED ? ' stroke-dasharray=\"3 2\"' : '';\n return `<div class=\"wp-legend-row wp-goal-row\"><svg width=\"20\" height=\"14\"><rect x=\"1\" y=\"1\" width=\"18\" height=\"12\" rx=\"2\" fill=\"${style.fill}\" stroke=\"${style.border}\" stroke-width=\"2\"${dash}/></svg>${style.word}</div>`;\n })\n .join('');\n const differences = comparison.differences.map((line: string) => `<li>${htmlLabelText(line)}</li>`).join('');\n return (\n `<section data-wp-legend-mode=\"goal\" class=\"wp-legend-list wp-goal-diff\" hidden><p><strong>${htmlLabelText(comparison.direction)}</strong> · ${htmlLabelText(comparison.summary)}</p>${rows}` +\n '<p>Arrows use the same colors; a removed arrow is dotted. A moved level reads L4 → L2. Click a box (Goal vs Current details) or an arrow for before/after.</p>' +\n (differences === '' ? '' : `<details><summary>Differences (${comparison.differences.length})</summary><ul>${differences}</ul></details>`) +\n '</section>'\n );\n }\n\n private edgeKey(): string {\n return '<div class=\"wp-legend-list\"><p>→ RPC · ⇢ queued/event · red: legacy cycle</p><p>Horizontal cylinder: queue<br>Upright cylinder: datastore<br>⏰: scheduler<br>Dashed box: external system</p></div>';\n }\n\n private responsibilities(graph: RuntimeGraph, options: RuntimeVizOptions): string {\n const projects: EnhancedGraph = {};\n for (const [name, service] of Object.entries(graph.services)) {\n projects[name] = {\n ...options.projects?.[name],\n level: service.level,\n dependsOn: service.dependsOn,\n drawOnGraph: service.drawOnGraph,\n };\n }\n return new ResponsibilitiesRenderer().generateSection(projects, options.workspaceRoot);\n }\n}\n"]}
@@ -15,7 +15,7 @@ class RuntimeLayout {
15
15
  const edge = model.edges[index];
16
16
  if (levels.get(edge.from) > levels.get(edge.to))
17
17
  continue;
18
- model.edges[index] = new graph_render_model_1.RenderEdge(edge.from, edge.to, this.unconstrained(edge.dot), edge.id, edge.productDot === null ? null : this.unconstrained(edge.productDot));
18
+ model.edges[index] = new graph_render_model_1.RenderEdge(edge.from, edge.to, this.unconstrained(edge.dot), edge.id, edge.crossesInto);
19
19
  }
20
20
  return this.assemble(model);
21
21
  }
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-layout.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-layout.ts"],"names":[],"mappings":";;;AAAA,6DAAoE;AACpE,2DAAsD;AAEtD,yFAAyF;AACzF,MAAa,aAAa;IACtB,MAAM,CAAC,KAAuB;QAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAE;gBAAE,SAAS;YAC5D,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,+BAAU,CAC/B,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAC5B,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CACxE,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,uGAAuG;IACvG,QAAQ,CAAC,KAAuB;QAC5B,OAAO,CACH,KAAK,CAAC,MAAM;YACZ,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,IAAI,mCAAe,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;YACtC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,KAAK,CAAC,MAAM,CACf,CAAC;IACN,CAAC;IAEO,aAAa,CAAC,GAAW;QAC7B,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC;IACvD,CAAC;CACJ;AAlCD,sCAkCC","sourcesContent":["import { GraphRenderModel, RenderEdge } from './graph-render-model';\nimport { LevelBandLayout } from './graph-level-bands';\n\n/** Saved levels own layout; real feedback arrows remain visible without moving ranks. */\nexport class RuntimeLayout {\n render(model: GraphRenderModel): string {\n const levels = new Map<string, number>();\n for (const band of model.bands) {\n for (const name of band.nodeNames) levels.set(name, band.level);\n }\n for (let index = 0; index < model.edges.length; index++) {\n const edge = model.edges[index];\n if (levels.get(edge.from)! > levels.get(edge.to)!) continue;\n model.edges[index] = new RenderEdge(\n edge.from,\n edge.to,\n this.unconstrained(edge.dot),\n edge.id,\n edge.productDot === null ? null : this.unconstrained(edge.productDot),\n );\n }\n return this.assemble(model);\n }\n\n /** The DOT for the model's current statements, without re-deciding constraints (restyled overlays). */\n assemble(model: GraphRenderModel): string {\n return (\n model.header +\n model.nodes.map((node) => node.dot).join('') +\n new LevelBandLayout().dot(model.bands) +\n model.edges.map((edge) => edge.dot).join('') +\n model.footer\n );\n }\n\n private unconstrained(dot: string): string {\n return `${dot.slice(0, -2)} [constraint=false];\\n`;\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-layout.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-layout.ts"],"names":[],"mappings":";;;AAAA,6DAAoE;AACpE,2DAAsD;AAEtD,yFAAyF;AACzF,MAAa,aAAa;IACtB,MAAM,CAAC,KAAuB;QAC1B,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;QACzC,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC7B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,SAAS;gBAAE,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACpE,CAAC;QACD,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;YACtD,MAAM,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;YAChC,IAAI,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAE,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAE;gBAAE,SAAS;YAC5D,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,IAAI,+BAAU,CAC/B,IAAI,CAAC,IAAI,EACT,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,GAAG,CAAC,EAC5B,IAAI,CAAC,EAAE,EACP,IAAI,CAAC,WAAW,CACnB,CAAC;QACN,CAAC;QACD,OAAO,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAChC,CAAC;IAED,uGAAuG;IACvG,QAAQ,CAAC,KAAuB;QAC5B,OAAO,CACH,KAAK,CAAC,MAAM;YACZ,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,IAAI,mCAAe,EAAE,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC;YACtC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC;YAC5C,KAAK,CAAC,MAAM,CACf,CAAC;IACN,CAAC;IAEO,aAAa,CAAC,GAAW;QAC7B,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,wBAAwB,CAAC;IACvD,CAAC;CACJ;AAlCD,sCAkCC","sourcesContent":["import { GraphRenderModel, RenderEdge } from './graph-render-model';\nimport { LevelBandLayout } from './graph-level-bands';\n\n/** Saved levels own layout; real feedback arrows remain visible without moving ranks. */\nexport class RuntimeLayout {\n render(model: GraphRenderModel): string {\n const levels = new Map<string, number>();\n for (const band of model.bands) {\n for (const name of band.nodeNames) levels.set(name, band.level);\n }\n for (let index = 0; index < model.edges.length; index++) {\n const edge = model.edges[index];\n if (levels.get(edge.from)! > levels.get(edge.to)!) continue;\n model.edges[index] = new RenderEdge(\n edge.from,\n edge.to,\n this.unconstrained(edge.dot),\n edge.id,\n edge.crossesInto,\n );\n }\n return this.assemble(model);\n }\n\n /** The DOT for the model's current statements, without re-deciding constraints (restyled overlays). */\n assemble(model: GraphRenderModel): string {\n return (\n model.header +\n model.nodes.map((node) => node.dot).join('') +\n new LevelBandLayout().dot(model.bands) +\n model.edges.map((edge) => edge.dot).join('') +\n model.footer\n );\n }\n\n private unconstrained(dot: string): string {\n return `${dot.slice(0, -2)} [constraint=false];\\n`;\n }\n}\n"]}
@@ -11,6 +11,9 @@ export declare class RuntimeViewerAdapter {
11
11
  constructor(graph: RuntimeGraph, projects: EnhancedGraph | null);
12
12
  service(model: GraphRenderModel, name: string, service: RuntimeService): string;
13
13
  levelBands(model: GraphRenderModel): LevelBand[];
14
- /** Context keeps its shape/kind palette; Impact never paints a queue as a source owner. */
14
+ /**
15
+ * Context keeps its shape/kind palette; Impact never paints a queue as a source owner. In Product
16
+ * mode the page recolors the kind statement's fill from the ON set (#1319, WpProductPainter).
17
+ */
15
18
  contextModes(model: GraphRenderModel): void;
16
19
  }
@@ -29,7 +29,7 @@ class RuntimeViewerAdapter {
29
29
  const role = service.role ?? project?.role ?? 'unknown';
30
30
  const facts = new graph_color_modes_1.NodeFacts(name, name.split('/').pop() ?? name, service.level, role, project?.framework ?? [], service.products ?? [], `Implements (${new Set(service.implements).size})${service.serviceName === undefined ? '' : ` · "${service.serviceName}"`}`);
31
31
  this.styler.record(model.legend, facts, this.palette);
32
- return model.styledNode(facts, this.styler.dots(facts, this.palette));
32
+ return model.styledNode(facts, this.styler.dots(facts));
33
33
  }
34
34
  levelBands(model) {
35
35
  const levels = new Map();
@@ -78,23 +78,16 @@ class RuntimeViewerAdapter {
78
78
  return band;
79
79
  });
80
80
  }
81
- /** Context keeps its shape/kind palette; Impact never paints a queue as a source owner. */
81
+ /**
82
+ * Context keeps its shape/kind palette; Impact never paints a queue as a source owner. In Product
83
+ * mode the page recolors the kind statement's fill from the ON set (#1319, WpProductPainter).
84
+ */
82
85
  contextModes(model) {
83
86
  for (let index = 0; index < model.nodes.length; index++) {
84
87
  const node = model.nodes[index];
85
88
  if (node.tags !== null)
86
89
  continue;
87
- const colors = node.products.map((product) => this.palette.colorOf(product).color);
88
- const fill = colors.length === 0
89
- ? graph_products_1.PRODUCT_NONE_FILL
90
- : this.palette.sharedByAll(node.products)
91
- ? graph_products_1.PRODUCT_SHARED_FILL
92
- : colors.join(':');
93
- // Graphviz cylinders cannot stripe; a gradient preserves recognizable kind geometry.
94
- const recolored = node.dot.replace(/fillcolor="[^"]*"/, `fillcolor="${fill}"`);
95
- const product = colors.length === 0
96
- ? recolored.replace(/style="[^"]*"/, 'style="filled,dashed"')
97
- : recolored;
90
+ const product = new graph_products_1.ProductTemplate(graph_products_1.ProductTemplateKind.CONTEXT, node.dot, '', '');
98
91
  const modes = new graph_color_modes_1.NodeModeDots(node.dot, node.dot, node.dot, node.dot, node.dot, node.dot, node.dot, node.dot, node.dot, product);
99
92
  model.nodes[index] = new graph_render_model_1.RenderNode(node.id, node.dot, modes, null, node.products, node.queueKeys);
100
93
  }