@webpieces/nx-webpieces-rules 0.4.978 → 0.4.980

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 (32) hide show
  1. package/README.md +32 -21
  2. package/package.json +7 -7
  3. package/src/lib/graph-filter.client.d.ts +25 -11
  4. package/src/lib/graph-filter.client.js +65 -29
  5. package/src/lib/graph-filter.client.js.map +1 -1
  6. package/src/lib/graph-multi-select.client.d.ts +57 -0
  7. package/src/lib/graph-multi-select.client.js +222 -0
  8. package/src/lib/graph-multi-select.client.js.map +1 -0
  9. package/src/lib/graph-node-menu.d.ts +1 -1
  10. package/src/lib/graph-node-menu.js +1 -1
  11. package/src/lib/graph-node-menu.js.map +1 -1
  12. package/src/lib/graph-page-shell.d.ts +23 -2
  13. package/src/lib/graph-page-shell.js +63 -13
  14. package/src/lib/graph-page-shell.js.map +1 -1
  15. package/src/lib/graph-page-styles.d.ts +6 -0
  16. package/src/lib/graph-page-styles.js +38 -10
  17. package/src/lib/graph-page-styles.js.map +1 -1
  18. package/src/lib/graph-render-model.js +2 -2
  19. package/src/lib/graph-render-model.js.map +1 -1
  20. package/src/lib/graph-visualizer.client.d.ts +39 -19
  21. package/src/lib/graph-visualizer.client.js +96 -110
  22. package/src/lib/graph-visualizer.client.js.map +1 -1
  23. package/src/lib/graph-visualizer.d.ts +7 -11
  24. package/src/lib/graph-visualizer.js +14 -32
  25. package/src/lib/graph-visualizer.js.map +1 -1
  26. package/src/lib/runtime-html-page.d.ts +6 -1
  27. package/src/lib/runtime-html-page.js +13 -6
  28. package/src/lib/runtime-html-page.js.map +1 -1
  29. package/src/lib/runtime-visualizer.client.js +3 -1
  30. package/src/lib/runtime-visualizer.client.js.map +1 -1
  31. package/src/lib/runtime-visualizer.js +5 -3
  32. package/src/lib/runtime-visualizer.js.map +1 -1
@@ -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 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"]}
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, GraphPickOption, 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') + readCompiledClient('graph-multi-select.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 Keep/Focus pulldown 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 pickOptions: readonly GraphPickOption[] = [],\n responsibilitiesHtml: string = '',\n ): string {\n const parts = new ShellParts(\n title,\n pickOptions,\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 boxes the drawer's \"Keep connected to\" and \"Focus\" pulldowns offer (#1385): one per drawn\n * project, its VALUE the node id the SVG is keyed on, its grey label the level. The shell sorts them\n * alphabetically and renders the same rows into both pulldowns.\n */\n pickOptions(graph: EnhancedGraph): GraphPickOption[] {\n const options: GraphPickOption[] = [];\n for (const project of Object.keys(graph)) {\n if (this.isHidden(graph[project])) continue;\n options.push(new GraphPickOption(this.names.getNodeId(project), `L${graph[project].level}`));\n }\n return options;\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 pickOptions = this.pickOptions(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 pickOptions,\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"]}
@@ -14,7 +14,12 @@ export declare class RuntimeHtmlPage {
14
14
  /** Goal vs Current is dimmed in the markup itself when its comparison is unavailable (#1373). */
15
15
  private unavailableModes;
16
16
  private designLinks;
17
- private lockControl;
17
+ /**
18
+ * Every node either topology can draw — the page's own graph and the Goal vs Current overlay, whose
19
+ * removed-only boxes exist only there — offered by the Keep connected to and Focus pulldowns with
20
+ * its role (#1385). The page hides the rows the graph on screen does not draw.
21
+ */
22
+ private pickOptions;
18
23
  private legend;
19
24
  /**
20
25
  * The products some drawn box carries, colored from the ONE palette the boxes were painted with —
@@ -44,7 +44,7 @@ class RuntimeHtmlPage {
44
44
  ...new Set(model.nodes.flatMap((node) => (node.tags === null ? [] : [node.tags.role]))),
45
45
  ].sort();
46
46
  const products = this.shownProducts(model);
47
- const parts = new graph_page_shell_1.ShellParts(title, this.lockControl(model), this.legend(model), this.edgeKey(), this.assets.html() + '<span id="wp-context-count"></span>', new saved_snapshot_1.SavedSnapshot().html(), this.responsibilities(graph, options), products, true, frameworks, roles, '<div class="wp-empty" id="wp-empty" hidden>No matching services. Open Filter and Clear all, or turn off Hide unconnected.</div>', this.unavailableModes(model));
47
+ const parts = new graph_page_shell_1.ShellParts(title, this.pickOptions(model), this.legend(model), this.edgeKey(), this.assets.html() + '<span id="wp-context-count"></span>', new saved_snapshot_1.SavedSnapshot().html(), this.responsibilities(graph, options), products, true, frameworks, roles, '<div class="wp-empty" id="wp-empty" hidden>No matching services. Open Filter and Clear all, or Clear all in Keep connected to.</div>', this.unavailableModes(model));
48
48
  const shared = this.sharedJs()
49
49
  .split(graph_visualizer_2.CLIENT_MODEL_PLACEHOLDER)
50
50
  .join(this.assets.json(model))
@@ -87,11 +87,18 @@ class RuntimeHtmlPage {
87
87
  }
88
88
  return links;
89
89
  }
90
- lockControl(model) {
91
- const entries = model.nodes
92
- .map((node) => `<option value="${(0, dot_syntax_1.htmlLabelText)(node.id)}">${(0, dot_syntax_1.htmlLabelText)(node.id)} · ${node.tags?.role ?? 'context'}</option>`)
93
- .join('');
94
- 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>`;
90
+ /**
91
+ * Every node either topology can draw — the page's own graph and the Goal vs Current overlay, whose
92
+ * removed-only boxes exist only there — offered by the Keep connected to and Focus pulldowns with
93
+ * its role (#1385). The page hides the rows the graph on screen does not draw.
94
+ */
95
+ pickOptions(model) {
96
+ const options = new Map();
97
+ for (const node of [...model.nodes, ...(model.comparison?.model?.nodes ?? [])]) {
98
+ if (!options.has(node.id))
99
+ options.set(node.id, new graph_page_shell_1.GraphPickOption(node.id, node.tags?.role ?? 'context'));
100
+ }
101
+ return [...options.values()];
95
102
  }
96
103
  legend(model) {
97
104
  const tag = (group, value, color) => `<div class="wp-legend-row" data-wp-legend-group="${group}" data-wp-legend-value="${(0, dot_syntax_1.htmlLabelText)(value)}"><svg width="20" height="14"><rect width="20" height="14" rx="2" fill="${color}"/></svg>${(0, dot_syntax_1.htmlLabelText)(value)}</div>`;
@@ -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":"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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 } from './runtime-viz-options';\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, GraphMode, ModeUnavailable, 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 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 this.unavailableModes(model),\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 /** Goal vs Current is dimmed in the markup itself when its comparison is unavailable (#1373). */\n private unavailableModes(model: GraphRenderModel): ModeUnavailable[] {\n const comparison = model.comparison;\n if (comparison === undefined || comparison === null || comparison.available) return [];\n return [new ModeUnavailable(GraphMode.GOAL, comparison.reason, comparison.menuNote)];\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"]}
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,+DAA0D;AAC1D,iEAA4D;AAC5D,+DAAoD;AACpD,6DAAyD;AACzD,yDAAuE;AACvE,qDAAiD;AACjD,yDAI4B;AAC5B,uDAAkD;AAClD,yDAAiF;AACjF,2DAAsD;AACtD,iDAA6C;AAE7C,2DAAgG;AAChG,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,IAAI,8BAAa,EAAE,CAAC,IAAI,EAAE,EAC1B,IAAI,CAAC,gBAAgB,CAAC,KAAK,EAAE,OAAO,CAAC,EACrC,QAAQ,EACR,IAAI,EACJ,UAAU,EACV,KAAK,EACL,sIAAsI,EACtI,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,CAC/B,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;IAED,iGAAiG;IACzF,gBAAgB,CAAC,KAAuB;QAC5C,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;QACpC,IAAI,UAAU,KAAK,SAAS,IAAI,UAAU,KAAK,IAAI,IAAI,UAAU,CAAC,SAAS;YAAE,OAAO,EAAE,CAAC;QACvF,OAAO,CAAC,IAAI,mCAAe,CAAC,6BAAS,CAAC,IAAI,EAAE,UAAU,CAAC,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzF,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;IAED;;;;OAIG;IACK,WAAW,CAAC,KAAuB;QACvC,MAAM,OAAO,GAAG,IAAI,GAAG,EAA2B,CAAC;QACnD,KAAK,MAAM,IAAI,IAAI,CAAC,GAAG,KAAK,CAAC,KAAK,EAAE,GAAG,CAAC,KAAK,CAAC,UAAU,EAAE,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YAC7E,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,kCAAe,CAAC,IAAI,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,SAAS,CAAC,CAAC,CAAC;QAChH,CAAC;QACD,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IACjC,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,MAAM,GAAG,IAAI,0BAAW,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;aACrC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CACX,MAAM,CAAC,aAAa,CAChB,wDAAwD,IAAA,0BAAa,EAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EACvF,IAAA,0BAAa,EAAC,KAAK,CAAC,OAAO,CAAC,EAC5B,yEAAyE,KAAK,CAAC,KAAK,YAAY,IAAA,0BAAa,EAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CACjI,CACJ;aACA,IAAI,CAAC,EAAE,CAAC,CAAC;QACd,OAAO,CACH,iEAAiE,UAAU,GAAG,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,CAAC;4EAClD,KAAK,wEAAwE;YAC7I,uEAAuE,MAAM,CAAC,UAAU,EAAE,kFAAkF,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,2IAA2I;YACtV,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;AA5LD,0CA4LC","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 } from './runtime-viz-options';\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, GraphPickOption, ShellParts } from './graph-page-shell';\nimport { GraphPageStyles } from './graph-page-styles';\nimport { GraphLegend } from './graph-legend';\nimport { ProductColor } from './graph-products';\nimport { FRAMEWORK_STYLES, GraphMode, ModeUnavailable, 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.pickOptions(model),\n this.legend(model),\n this.edgeKey(),\n this.assets.html() + '<span id=\"wp-context-count\"></span>',\n 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 Clear all in Keep connected to.</div>',\n this.unavailableModes(model),\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 /** Goal vs Current is dimmed in the markup itself when its comparison is unavailable (#1373). */\n private unavailableModes(model: GraphRenderModel): ModeUnavailable[] {\n const comparison = model.comparison;\n if (comparison === undefined || comparison === null || comparison.available) return [];\n return [new ModeUnavailable(GraphMode.GOAL, comparison.reason, comparison.menuNote)];\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 /**\n * Every node either topology can draw — the page's own graph and the Goal vs Current overlay, whose\n * removed-only boxes exist only there — offered by the Keep connected to and Focus pulldowns with\n * its role (#1385). The page hides the rows the graph on screen does not draw.\n */\n private pickOptions(model: GraphRenderModel): GraphPickOption[] {\n const options = new Map<string, GraphPickOption>();\n for (const node of [...model.nodes, ...(model.comparison?.model?.nodes ?? [])]) {\n if (!options.has(node.id)) options.set(node.id, new GraphPickOption(node.id, node.tags?.role ?? 'context'));\n }\n return [...options.values()];\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"]}
@@ -13,7 +13,9 @@
13
13
  * renderSVGElement is then SYNCHRONOUS — v2's returned a promise);
14
14
  * 2. upgrade every queue node into a TRUE horizontal cylinder;
15
15
  * 3. make every node clickable, opening the SHARED floating menu (graph-node-menu.ts — the same one
16
- * architecture/dependencies.html and every design.html use) with independent Lock/Unlock and chain filtering.
16
+ * architecture/dependencies.html and every design.html use) with Add to Focus and Keep connected,
17
+ * the same two multi-select picks the drawer's Focus and Keep connected to pulldowns hold — both
18
+ * inherited from WpFilterPage / GraphPage, never re-implemented here (#1385).
17
19
  *
18
20
  * Why (2) is post-processing rather than a shape:
19
21
  *
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-visualizer.client.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-visualizer.client.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Browser-side script for tmp/webpieces/runtime-architecture.html.\n *\n * runtime-visualizer.ts reads this file's COMPILED output with readFileSync, substitutes the DOT\n * placeholder (see viz-client-globals.d.ts) with the JSON-encoded DOT, and inlines the result into a <script> tag. It runs in a\n * browser, never in node — but it is ordinary TypeScript, compiled in place by tsc like the package's\n * bin entry points, and linted like every other file here. (It used to be a committed .js asset\n * exempted from `no-js-files`; see graph-visualizer.client.ts for why that exemption is gone.)\n *\n * Three jobs:\n * 1. render the DOT with @viz-js/viz v3 (instance() resolves the WASM renderer, and\n * renderSVGElement is then SYNCHRONOUS — v2's returned a promise);\n * 2. upgrade every queue node into a TRUE horizontal cylinder;\n * 3. make every node clickable, opening the SHARED floating menu (graph-node-menu.ts — the same one\n * architecture/dependencies.html and every design.html use) with independent Lock/Unlock and chain filtering.\n *\n * Why (2) is post-processing rather than a shape:\n *\n * Graphviz has exactly one `cylinder` and it is upright-only. `orientation=` is documented as rotating\n * POLYGON shapes, and `cylinder` is drawn with beziers, so it silently ignores the attribute — graphviz\n * issue #2244, open since 2022 and still reproducible on Graphviz 15, the version this page's renderer\n * carries. All 54 native shapes were compared at the real label size; none is a horizontal cylinder. So\n * the DOT emits `Mrecord` (which renders sensibly on its own for anyone running `dot` over the\n * committed .dot file) and this script redraws it in the browser, where the geometry is computed from\n * each node's actual bounding box and therefore fits any label width — which a fixed image asset never\n * can.\n */\n\n// The DOT placeholder and the `Viz` global are declared once in viz-client-globals.d.ts — both\n// scripts share a global scope, so they cannot each declare them. Do NOT spell the placeholder token\n// in a comment: the inliner is a blind split/join, so every literal occurrence gets the whole DOT.\n\n/** Cap width as a fraction of the node's half-height. Below ~0.6 it reads as a rounded box. */\nconst CAP_RATIO = 0.75;\n/** A node narrower than this is not a queue box worth reshaping. */\nconst MIN_WIDTH = 40;\n\n/** The union bounding box of a node's shape elements, in user space. Data-only → a class. */\nclass Bounds {\n constructor(\n readonly x0: number,\n readonly x1: number,\n readonly y0: number,\n readonly y1: number,\n ) {}\n\n width(): number {\n return this.x1 - this.x0;\n }\n\n halfHeight(): number {\n return (this.y1 - this.y0) / 2;\n }\n}\n\n/**\n * Redraws the nodes the DOT marked `wp_queue` as cylinders lying on their side.\n *\n * Selected by CLASS, not by id prefix: a queue-kind external system shares the `system__` id space with\n * databases, and a database is an UPRIGHT cylinder that must be left alone.\n */\nclass QueueCylinders {\n private static readonly SVG_NS = 'http://www.w3.org/2000/svg';\n\n applyTo(svg: SVGSVGElement): void {\n svg.querySelectorAll('g.wp_queue').forEach((node: Element): void => {\n this.reshape(node);\n });\n }\n\n /**\n * Replace one node's shape elements with a horizontal cylinder sized to its own bounding box.\n *\n * The bbox is measured from the shape elements BEFORE they are removed, never from getBBox() on the\n * whole group — the group includes the text, which is inset, so using it would shrink the cylinder\n * inside the very label it is meant to contain.\n */\n private reshape(node: Element): void {\n const shapes = node.querySelectorAll('polygon, path, polyline');\n if (shapes.length === 0) return;\n const box = this.boundsOf(shapes);\n if (box === null || box.width() < MIN_WIDTH) return;\n\n const first = shapes[0];\n const fill = first.getAttribute('fill') ?? 'none';\n const stroke = first.getAttribute('stroke') ?? 'black';\n // A dashed outline (Goal vs Current's removed queue, #1239) survives the redraw.\n const dash = first.getAttribute('stroke-dasharray');\n // Measured BEFORE the shapes go: the record separator is where the label text starts.\n const separator = this.separatorX(shapes);\n shapes.forEach((shape: Element): void => {\n shape.remove();\n });\n\n const ry = box.halfHeight();\n const rx = this.capRadius(box, ry, separator);\n const body = this.bodyPath(box, rx, ry);\n // Only the NEAR end cap is drawn: that single arc is what reads as \"tube\" rather than\n // \"stadium\", and a real cylinder hides the far one behind the body.\n const cap =\n 'M' +\n (box.x0 + rx) +\n ',' +\n box.y0 +\n ' A' +\n rx +\n ',' +\n ry +\n ' 0 0 1 ' +\n (box.x0 + rx) +\n ',' +\n box.y1;\n\n const anchor = node.firstChild;\n if (anchor === null || anchor.nextSibling === null) return;\n node.insertBefore(this.pathEl(fill, stroke, body, dash), anchor.nextSibling);\n node.insertBefore(this.pathEl('none', stroke, cap, dash), anchor.nextSibling.nextSibling);\n\n // The label is deliberately NOT nudged. QUEUE_LABEL_PREFIX already gives the node an empty\n // leading record field, so Graphviz has centred the text in the space to the RIGHT of where the\n // cap lands. Shifting it again double-counts that offset and pushes the longest line out\n // through the far end of the cylinder.\n }\n\n private bodyPath(box: Bounds, rx: number, ry: number): string {\n return (\n 'M' +\n (box.x0 + rx) +\n ',' +\n box.y0 +\n ' L' +\n (box.x1 - rx) +\n ',' +\n box.y0 +\n ' A' +\n rx +\n ',' +\n ry +\n ' 0 0 1 ' +\n (box.x1 - rx) +\n ',' +\n box.y1 +\n ' L' +\n (box.x0 + rx) +\n ',' +\n box.y1 +\n ' A' +\n rx +\n ',' +\n ry +\n ' 0 0 1 ' +\n (box.x0 + rx) +\n ',' +\n box.y0 +\n ' Z'\n );\n }\n\n /**\n * A box listing several queues is TALL, and a cap scaled off half-height alone would then reach\n * past the empty leading field and sit on top of the first characters of the label. Graphviz sizes\n * that field from the font, not from the label's height, so the separator is the only honest bound\n * on how far right the cap may go.\n */\n private capRadius(box: Bounds, ry: number, separator: number | null): number {\n const fromHeight = Math.max(6, ry * CAP_RATIO);\n if (separator === null) return fromHeight;\n return Math.max(6, Math.min(fromHeight, separator - box.x0));\n }\n\n /**\n * The x of the record's field separator — the VERTICAL polyline Graphviz draws between the empty\n * leading field and the label — or null if this node has none.\n */\n private separatorX(shapes: NodeListOf<Element>): number | null {\n for (const shape of Array.from(shapes)) {\n if (shape.tagName !== 'polyline') continue;\n const nums = (shape.getAttribute('points') ?? '').match(/-?[\\d.]+/g);\n if (nums === null || nums.length < 4) continue;\n const x = parseFloat(nums[0]);\n if (Math.abs(x - parseFloat(nums[2])) < 0.5) return x;\n }\n return null;\n }\n\n /** The union bounding box of some SVG shape elements, from their raw geometry attributes. */\n private boundsOf(shapes: NodeListOf<Element>): Bounds | null {\n const xs: number[] = [];\n const ys: number[] = [];\n for (const shape of Array.from(shapes)) {\n const pts = shape.getAttribute('points');\n const source = pts ?? shape.getAttribute('d') ?? '';\n const nums = source.match(/-?[\\d.]+/g);\n if (nums === null) continue;\n for (let n = 0; n + 1 < nums.length; n += 2) {\n xs.push(parseFloat(nums[n]));\n ys.push(parseFloat(nums[n + 1]));\n }\n }\n if (xs.length === 0) return null;\n return new Bounds(Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys));\n }\n\n private pathEl(fill: string, stroke: string, d: string, dash: string | null): SVGPathElement {\n const el = document.createElementNS(QueueCylinders.SVG_NS, 'path');\n el.setAttribute('fill', fill);\n el.setAttribute('stroke', stroke);\n if (dash !== null) el.setAttribute('stroke-dasharray', dash);\n el.setAttribute('d', d);\n return el;\n }\n}\n\n/** Shape adapter for the shared viewer controller; no separate runtime control state. */\nclass RuntimeImplementsPayload {\n constructor(\n readonly name: string,\n readonly node: SVGGElement,\n ) {}\n}\n\nclass RuntimeViewerBindingImpl implements RuntimeViewerBinding {\n prepare(svg: SVGSVGElement): void {\n new QueueCylinders().applyTo(svg);\n // Keep identity titles readable by shared controllers without native SVG tooltips.\n svg.querySelectorAll('g.node > title, g.edge > title').forEach((title) => {\n title.parentElement?.setAttribute('aria-label', title.textContent ?? '');\n const identity = document.createElement('title');\n identity.textContent = title.textContent;\n title.replaceWith(identity);\n });\n }\n implementsItem(name: string, node: SVGGElement): WpNodeMenuItem | null {\n if (\n !node.querySelector('text') ||\n !Array.from(node.querySelectorAll('text')).some((text) =>\n text.textContent?.startsWith('Implements ('),\n )\n )\n return null;\n return new WpNodeMenuItem('Implements', (): void => {\n document.dispatchEvent(\n new CustomEvent('wp-runtime-implements', {\n detail: new RuntimeImplementsPayload(name, node),\n }),\n );\n });\n }\n}\nwindow.__WP_RUNTIME__ = new RuntimeViewerBindingImpl();\n"]}
1
+ {"version":3,"file":"runtime-visualizer.client.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-visualizer.client.ts"],"names":[],"mappings":"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","sourcesContent":["/*\n * Browser-side script for tmp/webpieces/runtime-architecture.html.\n *\n * runtime-visualizer.ts reads this file's COMPILED output with readFileSync, substitutes the DOT\n * placeholder (see viz-client-globals.d.ts) with the JSON-encoded DOT, and inlines the result into a <script> tag. It runs in a\n * browser, never in node — but it is ordinary TypeScript, compiled in place by tsc like the package's\n * bin entry points, and linted like every other file here. (It used to be a committed .js asset\n * exempted from `no-js-files`; see graph-visualizer.client.ts for why that exemption is gone.)\n *\n * Three jobs:\n * 1. render the DOT with @viz-js/viz v3 (instance() resolves the WASM renderer, and\n * renderSVGElement is then SYNCHRONOUS — v2's returned a promise);\n * 2. upgrade every queue node into a TRUE horizontal cylinder;\n * 3. make every node clickable, opening the SHARED floating menu (graph-node-menu.ts — the same one\n * architecture/dependencies.html and every design.html use) with Add to Focus and Keep connected,\n * the same two multi-select picks the drawer's Focus and Keep connected to pulldowns hold — both\n * inherited from WpFilterPage / GraphPage, never re-implemented here (#1385).\n *\n * Why (2) is post-processing rather than a shape:\n *\n * Graphviz has exactly one `cylinder` and it is upright-only. `orientation=` is documented as rotating\n * POLYGON shapes, and `cylinder` is drawn with beziers, so it silently ignores the attribute — graphviz\n * issue #2244, open since 2022 and still reproducible on Graphviz 15, the version this page's renderer\n * carries. All 54 native shapes were compared at the real label size; none is a horizontal cylinder. So\n * the DOT emits `Mrecord` (which renders sensibly on its own for anyone running `dot` over the\n * committed .dot file) and this script redraws it in the browser, where the geometry is computed from\n * each node's actual bounding box and therefore fits any label width — which a fixed image asset never\n * can.\n */\n\n// The DOT placeholder and the `Viz` global are declared once in viz-client-globals.d.ts — both\n// scripts share a global scope, so they cannot each declare them. Do NOT spell the placeholder token\n// in a comment: the inliner is a blind split/join, so every literal occurrence gets the whole DOT.\n\n/** Cap width as a fraction of the node's half-height. Below ~0.6 it reads as a rounded box. */\nconst CAP_RATIO = 0.75;\n/** A node narrower than this is not a queue box worth reshaping. */\nconst MIN_WIDTH = 40;\n\n/** The union bounding box of a node's shape elements, in user space. Data-only → a class. */\nclass Bounds {\n constructor(\n readonly x0: number,\n readonly x1: number,\n readonly y0: number,\n readonly y1: number,\n ) {}\n\n width(): number {\n return this.x1 - this.x0;\n }\n\n halfHeight(): number {\n return (this.y1 - this.y0) / 2;\n }\n}\n\n/**\n * Redraws the nodes the DOT marked `wp_queue` as cylinders lying on their side.\n *\n * Selected by CLASS, not by id prefix: a queue-kind external system shares the `system__` id space with\n * databases, and a database is an UPRIGHT cylinder that must be left alone.\n */\nclass QueueCylinders {\n private static readonly SVG_NS = 'http://www.w3.org/2000/svg';\n\n applyTo(svg: SVGSVGElement): void {\n svg.querySelectorAll('g.wp_queue').forEach((node: Element): void => {\n this.reshape(node);\n });\n }\n\n /**\n * Replace one node's shape elements with a horizontal cylinder sized to its own bounding box.\n *\n * The bbox is measured from the shape elements BEFORE they are removed, never from getBBox() on the\n * whole group — the group includes the text, which is inset, so using it would shrink the cylinder\n * inside the very label it is meant to contain.\n */\n private reshape(node: Element): void {\n const shapes = node.querySelectorAll('polygon, path, polyline');\n if (shapes.length === 0) return;\n const box = this.boundsOf(shapes);\n if (box === null || box.width() < MIN_WIDTH) return;\n\n const first = shapes[0];\n const fill = first.getAttribute('fill') ?? 'none';\n const stroke = first.getAttribute('stroke') ?? 'black';\n // A dashed outline (Goal vs Current's removed queue, #1239) survives the redraw.\n const dash = first.getAttribute('stroke-dasharray');\n // Measured BEFORE the shapes go: the record separator is where the label text starts.\n const separator = this.separatorX(shapes);\n shapes.forEach((shape: Element): void => {\n shape.remove();\n });\n\n const ry = box.halfHeight();\n const rx = this.capRadius(box, ry, separator);\n const body = this.bodyPath(box, rx, ry);\n // Only the NEAR end cap is drawn: that single arc is what reads as \"tube\" rather than\n // \"stadium\", and a real cylinder hides the far one behind the body.\n const cap =\n 'M' +\n (box.x0 + rx) +\n ',' +\n box.y0 +\n ' A' +\n rx +\n ',' +\n ry +\n ' 0 0 1 ' +\n (box.x0 + rx) +\n ',' +\n box.y1;\n\n const anchor = node.firstChild;\n if (anchor === null || anchor.nextSibling === null) return;\n node.insertBefore(this.pathEl(fill, stroke, body, dash), anchor.nextSibling);\n node.insertBefore(this.pathEl('none', stroke, cap, dash), anchor.nextSibling.nextSibling);\n\n // The label is deliberately NOT nudged. QUEUE_LABEL_PREFIX already gives the node an empty\n // leading record field, so Graphviz has centred the text in the space to the RIGHT of where the\n // cap lands. Shifting it again double-counts that offset and pushes the longest line out\n // through the far end of the cylinder.\n }\n\n private bodyPath(box: Bounds, rx: number, ry: number): string {\n return (\n 'M' +\n (box.x0 + rx) +\n ',' +\n box.y0 +\n ' L' +\n (box.x1 - rx) +\n ',' +\n box.y0 +\n ' A' +\n rx +\n ',' +\n ry +\n ' 0 0 1 ' +\n (box.x1 - rx) +\n ',' +\n box.y1 +\n ' L' +\n (box.x0 + rx) +\n ',' +\n box.y1 +\n ' A' +\n rx +\n ',' +\n ry +\n ' 0 0 1 ' +\n (box.x0 + rx) +\n ',' +\n box.y0 +\n ' Z'\n );\n }\n\n /**\n * A box listing several queues is TALL, and a cap scaled off half-height alone would then reach\n * past the empty leading field and sit on top of the first characters of the label. Graphviz sizes\n * that field from the font, not from the label's height, so the separator is the only honest bound\n * on how far right the cap may go.\n */\n private capRadius(box: Bounds, ry: number, separator: number | null): number {\n const fromHeight = Math.max(6, ry * CAP_RATIO);\n if (separator === null) return fromHeight;\n return Math.max(6, Math.min(fromHeight, separator - box.x0));\n }\n\n /**\n * The x of the record's field separator — the VERTICAL polyline Graphviz draws between the empty\n * leading field and the label — or null if this node has none.\n */\n private separatorX(shapes: NodeListOf<Element>): number | null {\n for (const shape of Array.from(shapes)) {\n if (shape.tagName !== 'polyline') continue;\n const nums = (shape.getAttribute('points') ?? '').match(/-?[\\d.]+/g);\n if (nums === null || nums.length < 4) continue;\n const x = parseFloat(nums[0]);\n if (Math.abs(x - parseFloat(nums[2])) < 0.5) return x;\n }\n return null;\n }\n\n /** The union bounding box of some SVG shape elements, from their raw geometry attributes. */\n private boundsOf(shapes: NodeListOf<Element>): Bounds | null {\n const xs: number[] = [];\n const ys: number[] = [];\n for (const shape of Array.from(shapes)) {\n const pts = shape.getAttribute('points');\n const source = pts ?? shape.getAttribute('d') ?? '';\n const nums = source.match(/-?[\\d.]+/g);\n if (nums === null) continue;\n for (let n = 0; n + 1 < nums.length; n += 2) {\n xs.push(parseFloat(nums[n]));\n ys.push(parseFloat(nums[n + 1]));\n }\n }\n if (xs.length === 0) return null;\n return new Bounds(Math.min(...xs), Math.max(...xs), Math.min(...ys), Math.max(...ys));\n }\n\n private pathEl(fill: string, stroke: string, d: string, dash: string | null): SVGPathElement {\n const el = document.createElementNS(QueueCylinders.SVG_NS, 'path');\n el.setAttribute('fill', fill);\n el.setAttribute('stroke', stroke);\n if (dash !== null) el.setAttribute('stroke-dasharray', dash);\n el.setAttribute('d', d);\n return el;\n }\n}\n\n/** Shape adapter for the shared viewer controller; no separate runtime control state. */\nclass RuntimeImplementsPayload {\n constructor(\n readonly name: string,\n readonly node: SVGGElement,\n ) {}\n}\n\nclass RuntimeViewerBindingImpl implements RuntimeViewerBinding {\n prepare(svg: SVGSVGElement): void {\n new QueueCylinders().applyTo(svg);\n // Keep identity titles readable by shared controllers without native SVG tooltips.\n svg.querySelectorAll('g.node > title, g.edge > title').forEach((title) => {\n title.parentElement?.setAttribute('aria-label', title.textContent ?? '');\n const identity = document.createElement('title');\n identity.textContent = title.textContent;\n title.replaceWith(identity);\n });\n }\n implementsItem(name: string, node: SVGGElement): WpNodeMenuItem | null {\n if (\n !node.querySelector('text') ||\n !Array.from(node.querySelectorAll('text')).some((text) =>\n text.textContent?.startsWith('Implements ('),\n )\n )\n return null;\n return new WpNodeMenuItem('Implements', (): void => {\n document.dispatchEvent(\n new CustomEvent('wp-runtime-implements', {\n detail: new RuntimeImplementsPayload(name, node),\n }),\n );\n });\n }\n}\nwindow.__WP_RUNTIME__ = new RuntimeViewerBindingImpl();\n"]}
@@ -45,11 +45,13 @@ const runtime_layout_1 = require("./runtime-layout");
45
45
  *
46
46
  * EVERY node on the rendered page is clickable and opens the SHARED floating menu
47
47
  * (graph-node-menu.ts — one implementation, also used by architecture/dependencies.html
48
- * and every project's design.html). Lock/Unlock pins separate complete incoming/outgoing chains, with hover adding a temporary chain. There is no "View Design"
48
+ * and every project's design.html). Add to Focus / Remove from Focus pins each picked box's complete
49
+ * incoming/outgoing chains (the union), with hover adding a temporary chain. There is no "View Design"
49
50
  * item here — a node is a running service, queue, datastore or third-party system, not
50
51
  * an nx project, so no design.html exists to point at and the item is absent rather than
51
- * dead. Filter Unconnected follows separate incoming/outgoing chains and compacts the rendered
52
- * topology; Turn off Filter restores the full picture. Lock remains independent of Filter.
52
+ * dead. Keep connected keeps the union of each picked box's incoming/outgoing chains and compacts the
53
+ * rendered topology; Clear all in "Keep connected to" restores the full picture. Focus and Keep
54
+ * connected to are independent multi-select pulldowns shared with the architecture page (#1385).
53
55
  */
54
56
  const fs = tslib_1.__importStar(require("fs"));
55
57
  const path = tslib_1.__importStar(require("path"));