/** * Graph Visualizer * * Generates visual representations of the architecture graph: * - DOT format (for Graphviz) * - Interactive HTML (using viz.js) * * All behavior lives on the injectable GraphVisualizer class so webpieces DI + * @DocumentDesign can wire it — module-scope functions are a dead end the DI * graph can't reach. */ import type { EnhancedGraph } from './graph-sorter'; /** * One node's design page, handed to the browser so the node menu can offer "View Design" for the * boxes that HAVE one and omit the item entirely for the boxes that do not. * * The link is data on the PAGE now, not a `URL=` attribute in the DOT: a URL made viz.js wrap the * box in an `` and clicking navigated straight out, which the menu replaces. */ export declare class DesignLink { readonly nodeId: string; readonly href: string; constructor(nodeId: string, href: string); } export declare class VisualizationPaths { htmlPath: string; constructor(htmlPath: string); } /** * The token the client scripts carry where the DOT belongs. It must appear EXACTLY ONCE in a compiled * client — the inliner is a blind split/join, so a second occurrence (in a comment, say) would be * replaced by the entire DOT too, bloating every generated page. */ export declare const CLIENT_DOT_PLACEHOLDER: string; /** Same contract as CLIENT_DOT_PLACEHOLDER, for the node → design.html links the menu offers. */ export declare const CLIENT_DESIGN_LINKS_PLACEHOLDER: string; /** * Read a compiled browser client sitting beside this file. * * It is generated from the matching `.client.ts` by tsc, so it exists in `dist` and in the published * tarball but NOT in a source checkout. The error says that outright rather than surfacing a bare * ENOENT, because "the build has not run" and "the file is missing" look identical otherwise. */ export declare function readCompiledClient(name: string): string; export declare class GraphVisualizer { private readonly clientJs; private readonly names; private readonly responsibilities; private readonly bandLayout; private readonly cycles; /** The ONE floating-node-menu implementation, shared with the per-project design pages. */ private readonly nodeMenu; /** * How to obtain the browser client's text. Injected so HTML generation does not depend on BUILD * ORDER: the default reads the compiled sibling, which exists in the tarball and in dist but NOT in * a source checkout (the source there is .client.ts). A unit test running from source supplies the * text itself rather than requiring the package to have been built first. */ constructor(clientJs?: () => string); /** * A project tagged `drawOnGraph:false` is hidden from the rendered graph — * its node, its rank placement, its dropdown option, its responsibilities * card, and every edge touching it are all omitted. It stays in the JSON. */ private isHidden; /** * Fill color for an env set — the color of the first env in the set that has * a known color, else the default. */ private frameworkColor; /** * Role border styling — fill stays keyed on framework; the border shows a * project's ROLE at a glance. Server and client are the top-level runnable * nodes, so they get bold, colored borders to stand out: * server → thick GREEN border (a runnable server app) * app → thick BLUE border (a runnable non-HTTP app, e.g. a tooling app) * bundle → thick PURPLE border (an aggregator that bundles several apps) * client → thick RED border (a client app, e.g. angular) * designed-lib → bold border (a library with a generated @DocumentDesign design) * lib / other → plain thin border */ private roleBorderAttrs; /** * Edge styling by API-relation kind (why the edge exists): * implements → BLACK dashed (a controller serves this api-lib's contract) * uses → BLACK solid (a generated client calls it) — same as a * plain library import, since a plain dependency IS a use. * uses-implements → BLUE dashed, thicker (does both — implements some * contracts of the api-lib, uses others) * plain lib (none) → the default thin black solid arrow, unchanged. * `kind` is undefined for every non-api-lib dependency edge. */ private edgeAttrs; /** * The DOT attribute list for ONE dependency edge. An edge that IMPLEMENTS contracts is also * LABELED with them: the dashed line alone says "something here is implemented", which reads as * "the tool did not detect anything" to everyone who has not memorized the legend. Naming the * contracts makes `auth-store-api` visibly resolve to the server that serves it — the single * most important relationship in a microservice architecture, and the one the diagram was * silent about. */ private edgeDot; /** * The contract names for an edge label, sorted and capped so a shared api-lib serving a dozen * contracts cannot blow the edge label up into a wall of text. The truncation is stated in the * label ("+N more") rather than silent — the full list is in dependencies.json. */ private labelledApis; /** * Href for a node's design page: the project's committed design.html, made * relative to architecture/dependencies.html. Returns null when the project * has no generated DI design (no design.json → no design page exists). */ private designHtmlHref; /** * The design pages that EXIST, one entry per visible node that has one. A node absent from this * list gets no "View Design" item at all — the item is never rendered dead or greyed out. */ designLinks(graph: EnhancedGraph): DesignLink[]; /** * Generate Graphviz DOT format from the graph */ generateDot(graph: EnhancedGraph, title?: string): string; /** * Refuse to draw a graph that would render something false. * * Two conditions, both of which produced a confidently-wrong picture with no warning at all: * - two projects sharing a node id fuse into one box, which UNIONS their two rank sets and * collapses two whole dependency levels onto one row (see graph-names.ts); * - a cycle makes the level numbers every row is keyed on meaningless (see graph-cycles.ts). */ private assertDrawable; /** * The visible projects grouped into one band per dependency level, ordered HIGHEST LEVEL FIRST * so the emitted bands read top-to-bottom with L0 last (hidden projects are omitted, so no * stray rank=same name is emitted for an absent node). LevelBandLayout turns these into the * rank sets and the invisible chain that pins them — see graph-level-bands.ts for why the * chain is required at all. */ private levelBands; private dotNodes; private dotEdges; /** * Generate interactive HTML with embedded SVG using viz.js */ generateHTML(dot: string, links: DesignLink[], title?: string, lockControl?: string, responsibilitiesHtml?: string): string; /** * The lock control (a single-select dropdown, rendered below the legend). * Picking a module LOCKS the graph into that box's hover view — its full * ancestor + descendant chain stays lit while everything else stays dimmed — * and narrows the responsibilities list below the graph to just that chain. * The first option, "All", is the default and clears the lock. Hover still * works on top of a lock; leaving a box returns to the locked view. * * Options are ordered by level DESCENDING to match the responsibilities cards. */ lockControl(graph: EnhancedGraph): string; private styles; private componentStyles; private legend; private fillItems; private borderItems; private edgeItems; /** * The page script. The browser code lives in graph-visualizer.client.ts — real, linted TypeScript * that tsc compiles in place, so what this inlines is its COMPILED .js sitting beside this file. * * The substitution is a blind split/join, which is why the placeholder token must appear EXACTLY * ONCE in the client (never in one of its comments): every literal occurrence would otherwise be * replaced by the whole DOT. */ private script; /** * Write the committed architecture visualization to * architecture/dependencies.html, next to dependencies.json. * * This is a checked-in artifact, regenerated deterministically by * architecture:generate so every box keeps its menu, and the "View Design" * item keeps pointing at each project's committed design.html as designs * come and go. The DOT is embedded in the HTML (rendered client-side by * viz.js). Output is deterministic (sorted graph in → same bytes out) so git * only shows a diff when the architecture actually changed. */ writeVisualization(graph: EnhancedGraph, workspaceRoot: string, title?: string): VisualizationPaths; /** * Open the HTML visualization in the default browser */ openVisualization(htmlPath: string): boolean; }