@webpieces/nx-webpieces-rules 0.4.871 → 0.4.873

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 (51) hide show
  1. package/README.md +36 -0
  2. package/package.json +7 -7
  3. package/src/executors/generate/executor.d.ts +3 -1
  4. package/src/executors/generate/executor.js +9 -1
  5. package/src/executors/generate/executor.js.map +1 -1
  6. package/src/index.d.ts +1 -0
  7. package/src/index.js +5 -1
  8. package/src/index.js.map +1 -1
  9. package/src/lib/dot-syntax.d.ts +9 -0
  10. package/src/lib/dot-syntax.js +17 -0
  11. package/src/lib/dot-syntax.js.map +1 -1
  12. package/src/lib/graph-color-modes.d.ts +136 -0
  13. package/src/lib/graph-color-modes.js +299 -0
  14. package/src/lib/graph-color-modes.js.map +1 -0
  15. package/src/lib/graph-filter.client.d.ts +11 -1
  16. package/src/lib/graph-filter.client.js +25 -4
  17. package/src/lib/graph-filter.client.js.map +1 -1
  18. package/src/lib/graph-impact.d.ts +119 -0
  19. package/src/lib/graph-impact.js +295 -0
  20. package/src/lib/graph-impact.js.map +1 -0
  21. package/src/lib/graph-legend.d.ts +34 -0
  22. package/src/lib/graph-legend.js +116 -0
  23. package/src/lib/graph-legend.js.map +1 -0
  24. package/src/lib/graph-names.d.ts +2 -2
  25. package/src/lib/graph-names.js +2 -2
  26. package/src/lib/graph-names.js.map +1 -1
  27. package/src/lib/graph-navigation.d.ts +10 -0
  28. package/src/lib/graph-navigation.js +47 -12
  29. package/src/lib/graph-navigation.js.map +1 -1
  30. package/src/lib/graph-node-menu.d.ts +4 -3
  31. package/src/lib/graph-node-menu.js +137 -20
  32. package/src/lib/graph-node-menu.js.map +1 -1
  33. package/src/lib/graph-page-shell.d.ts +34 -0
  34. package/src/lib/graph-page-shell.js +121 -0
  35. package/src/lib/graph-page-shell.js.map +1 -0
  36. package/src/lib/graph-page-styles.d.ts +20 -0
  37. package/src/lib/graph-page-styles.js +144 -0
  38. package/src/lib/graph-page-styles.js.map +1 -0
  39. package/src/lib/graph-render-model.d.ts +14 -2
  40. package/src/lib/graph-render-model.js +19 -3
  41. package/src/lib/graph-render-model.js.map +1 -1
  42. package/src/lib/graph-responsibilities.js +1 -1
  43. package/src/lib/graph-responsibilities.js.map +1 -1
  44. package/src/lib/graph-visualizer.client.d.ts +65 -0
  45. package/src/lib/graph-visualizer.client.js +311 -7
  46. package/src/lib/graph-visualizer.client.js.map +1 -1
  47. package/src/lib/graph-visualizer.d.ts +19 -31
  48. package/src/lib/graph-visualizer.js +50 -286
  49. package/src/lib/graph-visualizer.js.map +1 -1
  50. package/src/lib/runtime-html-page.js +2 -2
  51. package/src/lib/runtime-html-page.js.map +1 -1
package/README.md CHANGED
@@ -106,6 +106,42 @@ afterward does not establish that refresh succeeded. Browser-open failures print
106
106
  the HTML path for manual opening. Architecture/API validation and CI still run
107
107
  their existing checks independently of visualization.
108
108
 
109
+ ### Color modes and the page shell
110
+
111
+ `architecture/dependencies.html` opens with the graph filling the viewport and every control
112
+ in a collapsible dark drawer on the left: the color mode, a type-to-search **Lock** field
113
+ (`/` focuses it, `Esc` unlocks), a **Hide unconnected** toggle, the legend (with a pop-out
114
+ button) and the edge key. Help is the **?** popover, zoom floats bottom-right, and
115
+ **Responsibilities** opens a right-hand panel from the drawer footer.
116
+
117
+ The graph answers one question at a time. Pick a mode in the drawer, from **Mode ▸** in any
118
+ box's menu, or with keys `1` `2` `3`; the choice is remembered per viewer and deep-linkable as
119
+ `#runtime`, `#architecture` or `#impact`.
120
+
121
+ - **Runtime** — where the code can run. Base runtimes are equal vertical stripes in the fixed
122
+ order browser | node | react-native; angular and react are an inset inside the browser
123
+ stripe, express inside the node stripe. A box may depend on a box that carries every color it
124
+ has. A box with several stripes or an inset carries its name on a light plate between two
125
+ stripe bands, so the text never straddles two colors.
126
+ - **Architecture** — servers · clients · APIs. A solid fill by role (server, client, app, bundle,
127
+ api-lib, api-client, designed-lib, lib); framework color is not shown.
128
+ - **Impact** — what this branch touches. `architecture:generate` runs
129
+ `nx show projects --affected --base=<fork point> --json` (no `--head`, so uncommitted and
130
+ untracked work counts). Projects that own a changed file, by nx's own file ownership, are solid
131
+ amber ("touched — files changed on this branch"); the rest of the affected set is light amber
132
+ ("affected — tests and build re-run"); every transitive dependency of the affected set that is
133
+ not itself affected is light slate with a dashed border ("build input — compiled or restored from
134
+ cache, unchanged"); everything else is grey. The drawer shows the counts per shade. The answer is per branch, so
135
+ it is written to `architecture/.impact/dependencies.impact.js`, never into the committed html or
136
+ json. That directory holds its own `.gitignore` (`*`), so a consuming repo needs no gitignore
137
+ entry. When nx or git cannot answer, no sidecar is written and Impact is disabled with a
138
+ one-line reason; generation never fails over it. `architecture:visualize` leaves the sidecar
139
+ as it is.
140
+
141
+ The legend is generated per mode from the projects actually drawn, and the framework colors
142
+ are keyed off `KNOWN_FRAMEWORKS` (`@webpieces/rules-sdk`), the same list the framework-tag
143
+ validator checks.
144
+
109
145
  ### Compact graph filtering
110
146
 
111
147
  Architecture and runtime node menus offer **Filter Unconnected** alongside
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/nx-webpieces-rules",
3
- "version": "0.4.871",
3
+ "version": "0.4.873",
4
4
  "description": "Nx-specific webpieces validation rules, graph tooling, executors, and inference plugin.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -20,12 +20,12 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@webpieces/api-doc-model": "0.4.871",
24
- "@webpieces/core-util": "0.4.871",
25
- "@webpieces/eslint-rules": "0.4.871",
26
- "@webpieces/rules-config": "0.4.871",
23
+ "@webpieces/api-doc-model": "0.4.873",
24
+ "@webpieces/core-util": "0.4.873",
25
+ "@webpieces/eslint-rules": "0.4.873",
26
+ "@webpieces/rules-config": "0.4.873",
27
27
  "madge": "8.0.0",
28
- "@webpieces/rules-sdk": "0.4.871"
28
+ "@webpieces/rules-sdk": "0.4.873"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@nx/devkit": ">=18.0.0"
@@ -41,7 +41,7 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@webpieces/tooling-testkit": "0.4.871"
44
+ "@webpieces/tooling-testkit": "0.4.873"
45
45
  },
46
46
  "types": "./src/index.d.ts",
47
47
  "bin": {
@@ -9,6 +9,7 @@
9
9
  */
10
10
  import type { ExecutorContext } from '@nx/devkit';
11
11
  import { GraphVisualizer } from '../../lib/graph-visualizer';
12
+ import { ImpactRefresh } from '../../lib/graph-impact';
12
13
  export interface GenerateExecutorOptions {
13
14
  graphPath?: string;
14
15
  }
@@ -22,7 +23,8 @@ export interface ExecutorResult {
22
23
  */
23
24
  export declare class ArchitectureGenerator {
24
25
  private readonly visualizer;
25
- constructor(visualizer: GraphVisualizer);
26
+ private readonly impact;
27
+ constructor(visualizer: GraphVisualizer, impact?: ImpactRefresh);
26
28
  generate(workspaceRoot: string, graphPath: string | undefined): Promise<void>;
27
29
  }
28
30
  export default function runExecutor(options: GenerateExecutorOptions, context: ExecutorContext): Promise<ExecutorResult>;
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ArchitectureGenerator = void 0;
4
4
  exports.default = runExecutor;
5
+ const tslib_1 = require("tslib");
5
6
  const approved_graph_1 = require("../../lib/runtime-wiring/approved-graph");
6
7
  const graph_loader_1 = require("../../lib/graph-loader");
7
8
  const rules_config_1 = require("@webpieces/rules-config");
@@ -13,6 +14,8 @@ const api_contract_files_1 = require("../../lib/api-contract-files");
13
14
  const graph_metadata_1 = require("../../lib/graph-metadata");
14
15
  const tag_truth_1 = require("../../lib/tag-truth");
15
16
  const graph_visualizer_1 = require("../../lib/graph-visualizer");
17
+ const graph_impact_1 = require("../../lib/graph-impact");
18
+ const path = tslib_1.__importStar(require("path"));
16
19
  const runtime_graph_1 = require("../../lib/runtime-graph");
17
20
  const runtime_participant_resolver_1 = require("../../lib/runtime-participant-resolver");
18
21
  const toError_1 = require("../../toError");
@@ -102,8 +105,10 @@ function printGraphSummary(graph) {
102
105
  */
103
106
  class ArchitectureGenerator {
104
107
  visualizer;
105
- constructor(visualizer) {
108
+ impact;
109
+ constructor(visualizer, impact = new graph_impact_1.ImpactRefresh()) {
106
110
  this.visualizer = visualizer;
111
+ this.impact = impact;
107
112
  }
108
113
  async generate(workspaceRoot, graphPath) {
109
114
  // Step 1: Build the full graph from nx, then transitively reduce it to the view
@@ -140,6 +145,9 @@ class ArchitectureGenerator {
140
145
  // dependencies.html regenerates in lock-step with dependencies.json.
141
146
  const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot);
142
147
  console.log(`✅ Wrote ${vizPaths.htmlPath}`);
148
+ // Step 4c: The page's Impact mode — nx affected for THIS branch, into a gitignored sidecar
149
+ // beside the html (never into the committed files). Optional: it reports, it never throws.
150
+ console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, enhancedGraph));
143
151
  // Step 5: Generate the runtime microservice graph from the same scan.
144
152
  // Projects tagged drawOnGraph:false are threaded through so the runtime
145
153
  // graph hides them too (they stay flagged in runtime-dependencies.json).
@@ -1 +1 @@
1
- {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\n/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus\n * one contract file per API under architecture/apis/ (stale ones are deleted).\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport {\n writeTemplate,\n Option,\n RuleFailError,\n renderRuleFailForHuman,\n} from '@webpieces/rules-config';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { ProjectCycleDetector } from '../../lib/graph-cycles';\nimport { saveGraph, DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ProjectInfo } from '../../lib/project-info';\nimport type { ApiContracts, ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport type { EnhancedGraph, GraphEntry } from '../../lib/graph-sorter';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { deriveRuntimeGraphReport, saveRuntimeGraph } from '../../lib/runtime-graph';\nimport { printAutoHiddenServers } from '../../lib/runtime-participant-resolver';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/**\n * Generate the runtime microservice graph alongside the compile-time graph, DERIVED from the same\n * dependencies.json (its per-project apiRelations) — one regenerate produces both committed files, and\n * validate derives from the SAME source so they can't diverge. rpc APIs become direct runtime edges;\n * pubsub APIs become edges the viz draws through a queue.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction generateRuntimeGraph(\n workspaceRoot: string,\n graph: EnhancedGraph,\n hiddenProjects: Set<string>,\n apiContracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(graph, hiddenProjects, apiContracts, externalSystems);\n saveRuntimeGraph(report.graph, workspaceRoot);\n const serviceCount = Object.keys(report.graph.services).length;\n const queueCount = Object.keys(report.graph.queues).length;\n console.log(\n `✅ Runtime graph saved (${serviceCount} services, ${report.graph.runtimeEdges.length} runtime edges, ` +\n `${queueCount} queues, ${report.graph.triggers.length} cron/external triggers)`,\n );\n // Every edge the derivation had to GUESS at. Loud on purpose: a fanned-out edge is committed and\n // then reasoned about as if it were derived, which is how a fictional call survives for months.\n printRuntimeWarnings(report.warnings);\n printAutoHiddenServers(report.autoHidden);\n // Printed here, FAILED by validate-runtime-architecture: generate must still write the graph,\n // or the validator would have nothing to compare against and the error would be unfixable.\n if (report.problems.length > 0) {\n console.error(\n `❌ ${report.problems.length} client call(s) name a service no module answers to:`,\n );\n for (const problem of report.problems) console.error(` • ${problem}`);\n console.error(' This FAILS architecture:validate-runtime-architecture.');\n }\n}\n\n/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printRuntimeWarnings(warnings: string[]): void {\n if (warnings.length === 0) return;\n console.warn(`⚠️ ${warnings.length} runtime edge(s) could not be targeted to ONE service:`);\n for (const warning of warnings) console.warn(` • ${warning}`);\n}\n\n/**\n * Scan for api relations and attach them, surfacing any contract we could NOT map back to source.\n * Unresolved contracts mean the graph we are about to WRITE is incomplete, so they must be loud —\n * a green run that quietly omits a service gets committed, rendered, and trusted.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction scanApiRelations(\n workspaceRoot: string,\n graph: EnhancedGraph,\n projectInfos: Map<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n): ScannedTables {\n console.log('📖 Assembling approved runtime-deps.json declarations (no source extraction)...');\n new ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos, graphPath);\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing approved API contract references.',\n undefined,\n undefined,\n [\n new Option(\n 'Migrate src/wiring.ts and review runtime-deps.json and API contract approvals before generation.',\n true,\n ),\n ],\n );\n return new ScannedTables(\n new ApiContractFiles().load(workspaceRoot, graphPath, saved.apiContractFiles),\n saved.externalSystems,\n );\n}\n\n/** The two committed tables one scan produces, kept together so callers cannot persist just one. */\nclass ScannedTables {\n constructor(\n public readonly apiContracts: ApiContracts,\n public readonly externalSystems: ExternalSystemDecls,\n ) {}\n}\n\n/** Projects tagged drawOnGraph:false — kept in the JSON, omitted from every rendered graph. */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction hiddenProjectsIn(graph: EnhancedGraph): Set<string> {\n const hidden = new Set<string>();\n for (const name of Object.keys(graph)) {\n if (graph[name].drawOnGraph === false) hidden.add(name);\n }\n return hidden;\n}\n\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printGraphSummary(graph: EnhancedGraph): void {\n const levels = new Set(Object.values(graph).map((entry: GraphEntry) => entry.level));\n console.log(`\\n📈 Graph Summary:`);\n console.log(` Projects: ${Object.keys(graph).length}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n}\n\n/**\n * The whole generation pipeline. Split out of runExecutor so that function stays a thin\n * try/catch-and-report shell — the error reporting is what a caller reads on failure, and it should\n * not be separated from the throw by fifty lines of steps.\n */\nexport class ArchitectureGenerator {\n constructor(private readonly visualizer: GraphVisualizer) {}\n\n async generate(workspaceRoot: string, graphPath: string | undefined): Promise<void> {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"📊 Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 1b: The graph is a BUILD graph — refuse a cyclic one, naming EVERY cycle. This runs\n // before the sort deliberately: the sort also refuses, but reports one cycle and an\n // undifferentiated list of everything tangled with it, so a repo with several cycles pays one\n // full regeneration per cycle to discover them.\n console.log('🔄 Checking the project graph is acyclic...');\n const cycles = new ProjectCycleDetector();\n cycles.assertAcyclic(reducedGraph, 'the nx project graph');\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('🔄 Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n // ...and assert the stratification it just produced actually holds: every dependency strictly\n // below its dependent. Safe to assert only because the graph was sorted a line ago — a stale\n // committed file is never checked this way.\n cycles.assertLevelsDescend(\n reducedGraph,\n cycles.levelsOf(enhancedGraph),\n 'the freshly sorted graph',\n );\n\n // Step 3: Enrich with AI metadata (framework, shortDescription, file\n // pointers). This VALIDATES (responsibilities.md required per project)\n // and throws before any write, so a failure never clobbers the file.\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(enhancedGraph, projectInfos, workspaceRoot);\n new TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write\n\n // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by\n // scanning source, so dependencies.json + the viz + the runtime graph all\n // read the same derived truth.\n const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);\n const apiContracts = scanned.apiContracts;\n\n saveApprovedGraph(workspaceRoot, graphPath ?? DEFAULT_GRAPH_PATH, enhancedGraph, scanned);\n\n // Step 4b: Write the committed, clickable HTML view next to the JSON so\n // dependencies.html regenerates in lock-step with dependencies.json.\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 5: Generate the runtime microservice graph from the same scan.\n // Projects tagged drawOnGraph:false are threaded through so the runtime\n // graph hides them too (they stay flagged in runtime-dependencies.json).\n generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n );\n\n printGraphSummary(enhancedGraph);\n }\n}\n\n// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together\nfunction saveApprovedGraph(\n workspaceRoot: string,\n effectiveGraphPath: string,\n enhancedGraph: EnhancedGraph,\n scanned: ScannedTables,\n): void {\n // Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of\n // APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites\n // an api file and never the dependency graph. The runtime validator reads these files back.\n const apiContracts = scanned.apiContracts;\n const contractFiles = new ApiContractFiles();\n const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);\n console.log(`✅ Wrote ${written.written.length} api contract file(s)`);\n for (const deleted of written.deleted)\n console.log(`🗑️ Deleted stale api contract file ${deleted}`);\n\n // Step 4a: Save the graph, INCLUDING the contract-file links and the external-system\n // declarations the runtime derivation reads back — generate derives from the in-memory graph,\n // validate from the files, so anything not written here would make the two disagree.\n console.log('💾 Saving graph to architecture/dependencies.json...');\n const contractRefs = contractFiles.refsFor(apiContracts);\n saveGraph(\n enhancedGraph,\n workspaceRoot,\n effectiveGraphPath,\n contractRefs,\n scanned.externalSystems,\n );\n console.log('✅ Graph saved successfully');\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n📊 Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n await new ArchitectureGenerator(new GraphVisualizer()).generate(workspaceRoot, graphPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError carries its cures as Option[]; `Error.message` is only the aiMessage, so\n // printing that alone silently drops them. renderRuleFailForHuman is the ONE renderer that\n // numbers them, and this catch is the top-level handler for the nx target.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Graph generation failed:', rendered);\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' +\n mdPath +\n ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n return { success: false };\n }\n}\n"]}
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\n/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus\n * one contract file per API under architecture/apis/ (stale ones are deleted).\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport {\n writeTemplate,\n Option,\n RuleFailError,\n renderRuleFailForHuman,\n} from '@webpieces/rules-config';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { ProjectCycleDetector } from '../../lib/graph-cycles';\nimport { saveGraph, DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ProjectInfo } from '../../lib/project-info';\nimport type { ApiContracts, ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport type { EnhancedGraph, GraphEntry } from '../../lib/graph-sorter';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { ImpactRefresh } from '../../lib/graph-impact';\nimport * as path from 'path';\nimport { deriveRuntimeGraphReport, saveRuntimeGraph } from '../../lib/runtime-graph';\nimport { printAutoHiddenServers } from '../../lib/runtime-participant-resolver';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/**\n * Generate the runtime microservice graph alongside the compile-time graph, DERIVED from the same\n * dependencies.json (its per-project apiRelations) — one regenerate produces both committed files, and\n * validate derives from the SAME source so they can't diverge. rpc APIs become direct runtime edges;\n * pubsub APIs become edges the viz draws through a queue.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction generateRuntimeGraph(\n workspaceRoot: string,\n graph: EnhancedGraph,\n hiddenProjects: Set<string>,\n apiContracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(graph, hiddenProjects, apiContracts, externalSystems);\n saveRuntimeGraph(report.graph, workspaceRoot);\n const serviceCount = Object.keys(report.graph.services).length;\n const queueCount = Object.keys(report.graph.queues).length;\n console.log(\n `✅ Runtime graph saved (${serviceCount} services, ${report.graph.runtimeEdges.length} runtime edges, ` +\n `${queueCount} queues, ${report.graph.triggers.length} cron/external triggers)`,\n );\n // Every edge the derivation had to GUESS at. Loud on purpose: a fanned-out edge is committed and\n // then reasoned about as if it were derived, which is how a fictional call survives for months.\n printRuntimeWarnings(report.warnings);\n printAutoHiddenServers(report.autoHidden);\n // Printed here, FAILED by validate-runtime-architecture: generate must still write the graph,\n // or the validator would have nothing to compare against and the error would be unfixable.\n if (report.problems.length > 0) {\n console.error(\n `❌ ${report.problems.length} client call(s) name a service no module answers to:`,\n );\n for (const problem of report.problems) console.error(` • ${problem}`);\n console.error(' This FAILS architecture:validate-runtime-architecture.');\n }\n}\n\n/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printRuntimeWarnings(warnings: string[]): void {\n if (warnings.length === 0) return;\n console.warn(`⚠️ ${warnings.length} runtime edge(s) could not be targeted to ONE service:`);\n for (const warning of warnings) console.warn(` • ${warning}`);\n}\n\n/**\n * Scan for api relations and attach them, surfacing any contract we could NOT map back to source.\n * Unresolved contracts mean the graph we are about to WRITE is incomplete, so they must be loud —\n * a green run that quietly omits a service gets committed, rendered, and trusted.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction scanApiRelations(\n workspaceRoot: string,\n graph: EnhancedGraph,\n projectInfos: Map<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n): ScannedTables {\n console.log('📖 Assembling approved runtime-deps.json declarations (no source extraction)...');\n new ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos, graphPath);\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing approved API contract references.',\n undefined,\n undefined,\n [\n new Option(\n 'Migrate src/wiring.ts and review runtime-deps.json and API contract approvals before generation.',\n true,\n ),\n ],\n );\n return new ScannedTables(\n new ApiContractFiles().load(workspaceRoot, graphPath, saved.apiContractFiles),\n saved.externalSystems,\n );\n}\n\n/** The two committed tables one scan produces, kept together so callers cannot persist just one. */\nclass ScannedTables {\n constructor(\n public readonly apiContracts: ApiContracts,\n public readonly externalSystems: ExternalSystemDecls,\n ) {}\n}\n\n/** Projects tagged drawOnGraph:false — kept in the JSON, omitted from every rendered graph. */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction hiddenProjectsIn(graph: EnhancedGraph): Set<string> {\n const hidden = new Set<string>();\n for (const name of Object.keys(graph)) {\n if (graph[name].drawOnGraph === false) hidden.add(name);\n }\n return hidden;\n}\n\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printGraphSummary(graph: EnhancedGraph): void {\n const levels = new Set(Object.values(graph).map((entry: GraphEntry) => entry.level));\n console.log(`\\n📈 Graph Summary:`);\n console.log(` Projects: ${Object.keys(graph).length}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n}\n\n/**\n * The whole generation pipeline. Split out of runExecutor so that function stays a thin\n * try/catch-and-report shell — the error reporting is what a caller reads on failure, and it should\n * not be separated from the throw by fifty lines of steps.\n */\nexport class ArchitectureGenerator {\n constructor(\n private readonly visualizer: GraphVisualizer,\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n ) {}\n\n async generate(workspaceRoot: string, graphPath: string | undefined): Promise<void> {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"📊 Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 1b: The graph is a BUILD graph — refuse a cyclic one, naming EVERY cycle. This runs\n // before the sort deliberately: the sort also refuses, but reports one cycle and an\n // undifferentiated list of everything tangled with it, so a repo with several cycles pays one\n // full regeneration per cycle to discover them.\n console.log('🔄 Checking the project graph is acyclic...');\n const cycles = new ProjectCycleDetector();\n cycles.assertAcyclic(reducedGraph, 'the nx project graph');\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('🔄 Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n // ...and assert the stratification it just produced actually holds: every dependency strictly\n // below its dependent. Safe to assert only because the graph was sorted a line ago — a stale\n // committed file is never checked this way.\n cycles.assertLevelsDescend(\n reducedGraph,\n cycles.levelsOf(enhancedGraph),\n 'the freshly sorted graph',\n );\n\n // Step 3: Enrich with AI metadata (framework, shortDescription, file\n // pointers). This VALIDATES (responsibilities.md required per project)\n // and throws before any write, so a failure never clobbers the file.\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(enhancedGraph, projectInfos, workspaceRoot);\n new TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write\n\n // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by\n // scanning source, so dependencies.json + the viz + the runtime graph all\n // read the same derived truth.\n const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);\n const apiContracts = scanned.apiContracts;\n\n saveApprovedGraph(workspaceRoot, graphPath ?? DEFAULT_GRAPH_PATH, enhancedGraph, scanned);\n\n // Step 4b: Write the committed, clickable HTML view next to the JSON so\n // dependencies.html regenerates in lock-step with dependencies.json.\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 4c: The page's Impact mode — nx affected for THIS branch, into a gitignored sidecar\n // beside the html (never into the committed files). Optional: it reports, it never throws.\n console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, enhancedGraph));\n\n // Step 5: Generate the runtime microservice graph from the same scan.\n // Projects tagged drawOnGraph:false are threaded through so the runtime\n // graph hides them too (they stay flagged in runtime-dependencies.json).\n generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n );\n\n printGraphSummary(enhancedGraph);\n }\n}\n\n// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together\nfunction saveApprovedGraph(\n workspaceRoot: string,\n effectiveGraphPath: string,\n enhancedGraph: EnhancedGraph,\n scanned: ScannedTables,\n): void {\n // Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of\n // APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites\n // an api file and never the dependency graph. The runtime validator reads these files back.\n const apiContracts = scanned.apiContracts;\n const contractFiles = new ApiContractFiles();\n const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);\n console.log(`✅ Wrote ${written.written.length} api contract file(s)`);\n for (const deleted of written.deleted)\n console.log(`🗑️ Deleted stale api contract file ${deleted}`);\n\n // Step 4a: Save the graph, INCLUDING the contract-file links and the external-system\n // declarations the runtime derivation reads back — generate derives from the in-memory graph,\n // validate from the files, so anything not written here would make the two disagree.\n console.log('💾 Saving graph to architecture/dependencies.json...');\n const contractRefs = contractFiles.refsFor(apiContracts);\n saveGraph(\n enhancedGraph,\n workspaceRoot,\n effectiveGraphPath,\n contractRefs,\n scanned.externalSystems,\n );\n console.log('✅ Graph saved successfully');\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n📊 Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n await new ArchitectureGenerator(new GraphVisualizer()).generate(workspaceRoot, graphPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError carries its cures as Option[]; `Error.message` is only the aiMessage, so\n // printing that alone silently drops them. renderRuleFailForHuman is the ONE renderer that\n // numbers them, and this catch is the top-level handler for the nx target.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Graph generation failed:', rendered);\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' +\n mdPath +\n ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n return { success: false };\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -27,3 +27,4 @@ export { API_DOC_MODES, ApiRulesForOpenApiConfig, ApiRulesForMcpConfig } from '.
27
27
  export type { ApiDocMode } from './configs/api-doc-rules-config';
28
28
  export { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig } from './configs/tag-truth-configs';
29
29
  export { GraphRenderModel, RenderNode, RenderEdge } from './lib/graph-render-model';
30
+ export { GraphMode, NodeModeDots, LegendFacts } from './lib/graph-color-modes';
package/src/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RenderEdge = exports.RenderNode = exports.GraphRenderModel = exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = exports.ApiRulesForMcpConfig = exports.ApiRulesForOpenApiConfig = exports.API_DOC_MODES = exports.NoRootUnionApiTypeConfig = exports.WiringFormatConfig = exports.ValidateEslintSyncConfig = exports.ValidateVersionsLockedConfig = exports.ValidatePackageJsonConfig = exports.ValidateNoArchitectureCyclesConfig = exports.ValidateArchitectureUnchangedConfig = exports.MissingDesignAnnotationConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = exports.STRUCTURAL_MODES = exports.createNodesV2 = void 0;
3
+ exports.LegendFacts = exports.NodeModeDots = exports.GraphMode = exports.RenderEdge = exports.RenderNode = exports.GraphRenderModel = exports.FrameworkFolderConfig = exports.FrameworkFolderEntry = exports.ApiLibPathConfig = exports.ApiLibDependenciesConfig = exports.ApiClientPackagesEntry = exports.ApiRulesForMcpConfig = exports.ApiRulesForOpenApiConfig = exports.API_DOC_MODES = exports.NoRootUnionApiTypeConfig = exports.WiringFormatConfig = exports.ValidateEslintSyncConfig = exports.ValidateVersionsLockedConfig = exports.ValidatePackageJsonConfig = exports.ValidateNoArchitectureCyclesConfig = exports.ValidateArchitectureUnchangedConfig = exports.MissingDesignAnnotationConfig = exports.DiGraphConfig = exports.NxWiringConfig = exports.RuntimeArchitectureConfig = exports.NoFileImportCyclesConfig = exports.STRUCTURAL_MODES = exports.createNodesV2 = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  tslib_1.__exportStar(require("./lib/graph-generator"), exports);
6
6
  tslib_1.__exportStar(require("./lib/graph-sorter"), exports);
@@ -49,4 +49,8 @@ var graph_render_model_1 = require("./lib/graph-render-model");
49
49
  Object.defineProperty(exports, "GraphRenderModel", { enumerable: true, get: function () { return graph_render_model_1.GraphRenderModel; } });
50
50
  Object.defineProperty(exports, "RenderNode", { enumerable: true, get: function () { return graph_render_model_1.RenderNode; } });
51
51
  Object.defineProperty(exports, "RenderEdge", { enumerable: true, get: function () { return graph_render_model_1.RenderEdge; } });
52
+ var graph_color_modes_1 = require("./lib/graph-color-modes");
53
+ Object.defineProperty(exports, "GraphMode", { enumerable: true, get: function () { return graph_color_modes_1.GraphMode; } });
54
+ Object.defineProperty(exports, "NodeModeDots", { enumerable: true, get: function () { return graph_color_modes_1.NodeModeDots; } });
55
+ Object.defineProperty(exports, "LegendFacts", { enumerable: true, get: function () { return graph_color_modes_1.LegendFacts; } });
52
56
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,gEAAsC;AACtC,6DAAmC;AACnC,iEAAuC;AACvC,kEAAwC;AACxC,6DAAmC;AACnC,iEAAuC;AACvC,+DAAqC;AACrC,mEAAyC;AACzC,6EAAmD;AACnD,6DAAmC;AACnC,iEAAuC;AACvC,6DAAmC;AACnC,2EAAiD;AACjD,uEAA6C;AAC7C,qCAAyC;AAChC,8FADA,sBAAa,OACA;AACtB,kBAAe,EAAE,IAAI,EAAE,+BAA+B,EAAE,aAAa,EAAb,sBAAa,EAAE,CAAC;AAExE,uDAAyU;AAAhU,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,8GAAA,cAAc,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,6HAAA,6BAA6B,OAAA;AAAE,mIAAA,mCAAmC,OAAA;AAAE,kIAAA,kCAAkC,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,4HAAA,4BAA4B,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAExS,uEAAoE;AAA3D,0HAAA,kBAAkB,OAAA;AAC3B,uEAA0E;AAAjE,gIAAA,wBAAwB,OAAA;AACjC,uEAA+G;AAAtG,qHAAA,aAAa,OAAA;AAAE,gIAAA,wBAAwB,OAAA;AAAE,4HAAA,oBAAoB,OAAA;AAEtE,iEAA8J;AAArJ,2HAAA,sBAAsB,OAAA;AAAE,6HAAA,wBAAwB,OAAA;AAAE,qHAAA,gBAAgB,OAAA;AAAE,yHAAA,oBAAoB,OAAA;AAAE,0HAAA,qBAAqB,OAAA;AAExH,+DAAoF;AAA3E,sHAAA,gBAAgB,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,gHAAA,UAAU,OAAA","sourcesContent":["export * from './lib/graph-generator';\nexport * from './lib/graph-sorter';\nexport * from './lib/graph-comparator';\nexport * from './lib/package-validator';\nexport * from './lib/graph-loader';\nexport * from './lib/graph-visualizer';\nexport * from './lib/graph-metadata';\nexport * from './lib/framework-resolver';\nexport * from './lib/runtime-participant-resolver';\nexport * from './lib/project-info';\nexport * from './lib/responsibilities';\nexport * from './lib/di-graph/dot';\nexport * from './lib/di-graph/design-visualizer';\nexport * from './lib/di-graph/design-finder';\nimport { createNodesV2 } from './plugin';\nexport { createNodesV2 };\nexport default { name: '@webpieces/nx-webpieces-rules', createNodesV2 };\n\nexport { STRUCTURAL_MODES, NoFileImportCyclesConfig, RuntimeArchitectureConfig, NxWiringConfig, DiGraphConfig, MissingDesignAnnotationConfig, ValidateArchitectureUnchangedConfig, ValidateNoArchitectureCyclesConfig, ValidatePackageJsonConfig, ValidateVersionsLockedConfig, ValidateEslintSyncConfig } from './configs/rule-configs';\nexport type { StructuralMode } from './configs/rule-configs';\nexport { WiringFormatConfig } from './configs/wiring-format-config';\nexport { NoRootUnionApiTypeConfig } from './configs/no-root-union-config';\nexport { API_DOC_MODES, ApiRulesForOpenApiConfig, ApiRulesForMcpConfig } from './configs/api-doc-rules-config';\nexport type { ApiDocMode } from './configs/api-doc-rules-config';\nexport { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig } from './configs/tag-truth-configs';\n\nexport { GraphRenderModel, RenderNode, RenderEdge } from './lib/graph-render-model';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/index.ts"],"names":[],"mappings":";;;;AAAA,gEAAsC;AACtC,6DAAmC;AACnC,iEAAuC;AACvC,kEAAwC;AACxC,6DAAmC;AACnC,iEAAuC;AACvC,+DAAqC;AACrC,mEAAyC;AACzC,6EAAmD;AACnD,6DAAmC;AACnC,iEAAuC;AACvC,6DAAmC;AACnC,2EAAiD;AACjD,uEAA6C;AAC7C,qCAAyC;AAChC,8FADA,sBAAa,OACA;AACtB,kBAAe,EAAE,IAAI,EAAE,+BAA+B,EAAE,aAAa,EAAb,sBAAa,EAAE,CAAC;AAExE,uDAAyU;AAAhU,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,8GAAA,cAAc,OAAA;AAAE,6GAAA,aAAa,OAAA;AAAE,6HAAA,6BAA6B,OAAA;AAAE,mIAAA,mCAAmC,OAAA;AAAE,kIAAA,kCAAkC,OAAA;AAAE,yHAAA,yBAAyB,OAAA;AAAE,4HAAA,4BAA4B,OAAA;AAAE,wHAAA,wBAAwB,OAAA;AAExS,uEAAoE;AAA3D,0HAAA,kBAAkB,OAAA;AAC3B,uEAA0E;AAAjE,gIAAA,wBAAwB,OAAA;AACjC,uEAA+G;AAAtG,qHAAA,aAAa,OAAA;AAAE,gIAAA,wBAAwB,OAAA;AAAE,4HAAA,oBAAoB,OAAA;AAEtE,iEAA8J;AAArJ,2HAAA,sBAAsB,OAAA;AAAE,6HAAA,wBAAwB,OAAA;AAAE,qHAAA,gBAAgB,OAAA;AAAE,yHAAA,oBAAoB,OAAA;AAAE,0HAAA,qBAAqB,OAAA;AAExH,+DAAoF;AAA3E,sHAAA,gBAAgB,OAAA;AAAE,gHAAA,UAAU,OAAA;AAAE,gHAAA,UAAU,OAAA;AACjD,6DAA+E;AAAtE,8GAAA,SAAS,OAAA;AAAE,iHAAA,YAAY,OAAA;AAAE,gHAAA,WAAW,OAAA","sourcesContent":["export * from './lib/graph-generator';\nexport * from './lib/graph-sorter';\nexport * from './lib/graph-comparator';\nexport * from './lib/package-validator';\nexport * from './lib/graph-loader';\nexport * from './lib/graph-visualizer';\nexport * from './lib/graph-metadata';\nexport * from './lib/framework-resolver';\nexport * from './lib/runtime-participant-resolver';\nexport * from './lib/project-info';\nexport * from './lib/responsibilities';\nexport * from './lib/di-graph/dot';\nexport * from './lib/di-graph/design-visualizer';\nexport * from './lib/di-graph/design-finder';\nimport { createNodesV2 } from './plugin';\nexport { createNodesV2 };\nexport default { name: '@webpieces/nx-webpieces-rules', createNodesV2 };\n\nexport { STRUCTURAL_MODES, NoFileImportCyclesConfig, RuntimeArchitectureConfig, NxWiringConfig, DiGraphConfig, MissingDesignAnnotationConfig, ValidateArchitectureUnchangedConfig, ValidateNoArchitectureCyclesConfig, ValidatePackageJsonConfig, ValidateVersionsLockedConfig, ValidateEslintSyncConfig } from './configs/rule-configs';\nexport type { StructuralMode } from './configs/rule-configs';\nexport { WiringFormatConfig } from './configs/wiring-format-config';\nexport { NoRootUnionApiTypeConfig } from './configs/no-root-union-config';\nexport { API_DOC_MODES, ApiRulesForOpenApiConfig, ApiRulesForMcpConfig } from './configs/api-doc-rules-config';\nexport type { ApiDocMode } from './configs/api-doc-rules-config';\nexport { ApiClientPackagesEntry, ApiLibDependenciesConfig, ApiLibPathConfig, FrameworkFolderEntry, FrameworkFolderConfig } from './configs/tag-truth-configs';\n\nexport { GraphRenderModel, RenderNode, RenderEdge } from './lib/graph-render-model';\nexport { GraphMode, NodeModeDots, LegendFacts } from './lib/graph-color-modes';\n"]}
@@ -33,6 +33,15 @@ export declare function dotValue(value: string): string;
33
33
  * horizontal-cylinder queue node uses a `" |"` prefix — that empty first field is deliberate.
34
34
  */
35
35
  export declare function recordValue(value: string): string;
36
+ /**
37
+ * Escape a runtime value for TEXT inside a Graphviz HTML-like label (`label=<...>`).
38
+ *
39
+ * An HTML-like label is not a quoted string, so {@link dotValue} is the WRONG escape there: a `"`
40
+ * is harmless but a `<`, `>` or `&` in a project name would open a tag, close the label early or
41
+ * start an entity, and Graphviz rejects the whole graph. Graphviz parses these labels as XML, so the
42
+ * cure is entity escaping — `&` FIRST, so an escape we emit is never escaped twice.
43
+ */
44
+ export declare function htmlLabelText(value: string): string;
36
45
  /** Thrown when the generator produces DOT that Graphviz could not parse. */
37
46
  export declare class InvalidDotError extends Error {
38
47
  constructor(message: string);
@@ -16,6 +16,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
16
16
  exports.InvalidDotError = void 0;
17
17
  exports.dotValue = dotValue;
18
18
  exports.recordValue = recordValue;
19
+ exports.htmlLabelText = htmlLabelText;
19
20
  exports.assertValidDot = assertValidDot;
20
21
  /** Chars a quoted string may legally sit directly after, ignoring whitespace. */
21
22
  const LEGAL_BEFORE_STRING = new Set(['=', '[', ',', ';', '{', '}', '>', '-']);
@@ -49,6 +50,22 @@ function dotValue(value) {
49
50
  function recordValue(value) {
50
51
  return dotValue(value).replace(/[|{}<>]/g, (char) => `\\${char}`);
51
52
  }
53
+ /**
54
+ * Escape a runtime value for TEXT inside a Graphviz HTML-like label (`label=<...>`).
55
+ *
56
+ * An HTML-like label is not a quoted string, so {@link dotValue} is the WRONG escape there: a `"`
57
+ * is harmless but a `<`, `>` or `&` in a project name would open a tag, close the label early or
58
+ * start an entity, and Graphviz rejects the whole graph. Graphviz parses these labels as XML, so the
59
+ * cure is entity escaping — `&` FIRST, so an escape we emit is never escaped twice.
60
+ */
61
+ // webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts
62
+ function htmlLabelText(value) {
63
+ return value
64
+ .replace(/&/g, '&amp;')
65
+ .replace(/</g, '&lt;')
66
+ .replace(/>/g, '&gt;')
67
+ .replace(/"/g, '&quot;');
68
+ }
52
69
  /** Thrown when the generator produces DOT that Graphviz could not parse. */
53
70
  class InvalidDotError extends Error {
54
71
  constructor(message) {
@@ -1 +1 @@
1
- {"version":3,"file":"dot-syntax.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/dot-syntax.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DOT syntax helpers\n *\n * Two small pieces that exist because a generated DOT that nobody parses is a DOT that WILL break:\n *\n * 1. `dotValue()` — the ONE place a runtime value (service name, api name, project name, title)\n * becomes safe to interpolate into a quoted DOT string. Inlining values at each call site is how\n * an unescaped `\"` shipped and took the whole diagram down: in DOT a bare `\"` TERMINATES the\n * string it appears in, so one bad node line makes the entire graph fail to parse.\n * 2. `assertValidDot()` — a structural check on the emitted DOT that turns exactly that class of\n * mistake into a thrown error at generation time, instead of a blank page with a Graphviz\n * \"syntax error in line N\" that only a human opening the HTML ever sees.\n */\n\n/** Chars a quoted string may legally sit directly after, ignoring whitespace. */\nconst LEGAL_BEFORE_STRING = new Set(['=', '[', ',', ';', '{', '}', '>', '-']);\n\n/** Chars a quoted string may legally be followed by, ignoring whitespace. */\nconst LEGAL_AFTER_STRING = new Set(['=', '[', ']', ',', ';', '{', '}', '-', '>']);\n\n/**\n * Escape a runtime value for use INSIDE a quoted DOT string.\n *\n * Only `\\` and `\"` matter: everything else (parens, spaces, `-`, `#`, unicode) is ordinary text once\n * it is inside quotes. Note this deliberately escapes `\\` FIRST, so a value containing a backslash\n * cannot smuggle an escape sequence in. Callers compose label lines with a literal `\\\\n` AFTER\n * escaping their values — the separator is ours, the value is theirs.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function dotValue(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Escape a runtime value for a `record`/`Mrecord` label, on TOP of {@link dotValue}.\n *\n * A record label is not ordinary label text: `|` splits fields, `{}` toggles the layout direction\n * and `<>` delimit port names. A queue name carrying any of them would not merely look wrong — it\n * would restructure the node, splitting one queue box into two or rotating it. Graphviz's record\n * grammar escapes those with a backslash, and since {@link dotValue} has already doubled real\n * backslashes, the `\\\\` emitted here survives into the record parser as a single one.\n *\n * Leading and trailing spaces are also dropped by the record parser, which is exactly why the\n * horizontal-cylinder queue node uses a `\" |\"` prefix — that empty first field is deliberate.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function recordValue(value: string): string {\n return dotValue(value).replace(/[|{}<>]/g, (char: string) => `\\\\${char}`);\n}\n\n/** Thrown when the generator produces DOT that Graphviz could not parse. */\nexport class InvalidDotError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidDotError';\n }\n}\n\n/**\n * Fail loudly on structurally broken DOT.\n *\n * This is not a full Graphviz parser — it is the check that catches the failure mode a string\n * builder actually has: a quote that ends a string early (or never ends it). It scans the quoted\n * strings honouring `\\\"` escapes and asserts each one is terminated and is bounded by DOT\n * punctuation rather than by bare text. An unescaped `\"` inside a label always violates that: the\n * string ends mid-label, and the remaining label text becomes stray tokens.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function assertValidDot(dot: string, source: string): void {\n // Comment text is not code: a `\"` or a word in it must never be read as a DOT token. Blanking\n // it (offsets preserved) keeps every reported line number the one Graphviz would report.\n const code = blankComments(dot);\n let index = 0;\n while (index < code.length) {\n if (code[index] !== '\"') {\n index++;\n continue;\n }\n const start = index;\n index++;\n while (index < code.length && code[index] !== '\"') {\n index += code[index] === '\\\\' ? 2 : 1;\n }\n if (index >= code.length) {\n throw new InvalidDotError(`${source}: unterminated string starting at ${describe(dot, start)}`);\n }\n const end = index;\n index++;\n checkNeighbor(code, dot, start, end, source);\n }\n}\n\n/** Replace `//`, `#` and `/* *\\/` comment bodies with spaces, preserving length and newlines. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction blankComments(dot: string): string {\n const out = dot.split('');\n let index = 0;\n let inString = false;\n while (index < out.length) {\n const two = dot.slice(index, index + 2);\n if (inString) {\n if (dot[index] === '\\\\') index++;\n else if (dot[index] === '\"') inString = false;\n index++;\n } else if (dot[index] === '\"') {\n inString = true;\n index++;\n } else if (two === '//' || dot[index] === '#') {\n while (index < out.length && out[index] !== '\\n') out[index++] = ' ';\n } else if (two === '/*') {\n while (index < out.length && dot.slice(index, index + 2) !== '*/') {\n if (out[index] !== '\\n') out[index] = ' ';\n index++;\n }\n if (index < out.length) {\n out[index] = ' ';\n out[index + 1] = ' ';\n index += 2;\n }\n } else {\n index++;\n }\n }\n return out.join('');\n}\n\n/** Verify the non-whitespace chars bracketing a quoted string are DOT punctuation, not stray text. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction checkNeighbor(code: string, dot: string, start: number, end: number, source: string): void {\n const before = nonSpaceChar(code, start - 1, -1);\n if (before !== undefined && !LEGAL_BEFORE_STRING.has(before)) {\n throw new InvalidDotError(\n `${source}: a quoted string starts right after '${before}' at ${describe(dot, start)} — ` +\n `an unescaped '\"' in an interpolated value almost certainly ended the previous string early. ` +\n `Interpolate values through dotValue().`,\n );\n }\n const after = nonSpaceChar(code, end + 1, 1);\n if (after !== undefined && !LEGAL_AFTER_STRING.has(after)) {\n throw new InvalidDotError(\n `${source}: a quoted string is followed by '${after}' at ${describe(dot, end)} — ` +\n `an unescaped '\"' in an interpolated value almost certainly ended this string early. ` +\n `Interpolate values through dotValue().`,\n );\n }\n}\n\n/** The first non-whitespace char walking `step` from `from`, or undefined at either end. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction nonSpaceChar(dot: string, from: number, step: number): string | undefined {\n for (let i = from; i >= 0 && i < dot.length; i += step) {\n if (!/\\s/.test(dot[i])) return dot[i];\n }\n return undefined;\n}\n\n/** `line N: <the line>` for the offset, so the error names the same line Graphviz would. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction describe(dot: string, offset: number): string {\n const lineNumber = dot.slice(0, offset).split('\\n').length;\n const line = dot.split('\\n')[lineNumber - 1];\n return `line ${lineNumber}: ${line.trim()}`;\n}\n"]}
1
+ {"version":3,"file":"dot-syntax.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/dot-syntax.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DOT syntax helpers\n *\n * Two small pieces that exist because a generated DOT that nobody parses is a DOT that WILL break:\n *\n * 1. `dotValue()` — the ONE place a runtime value (service name, api name, project name, title)\n * becomes safe to interpolate into a quoted DOT string. Inlining values at each call site is how\n * an unescaped `\"` shipped and took the whole diagram down: in DOT a bare `\"` TERMINATES the\n * string it appears in, so one bad node line makes the entire graph fail to parse.\n * 2. `assertValidDot()` — a structural check on the emitted DOT that turns exactly that class of\n * mistake into a thrown error at generation time, instead of a blank page with a Graphviz\n * \"syntax error in line N\" that only a human opening the HTML ever sees.\n */\n\n/** Chars a quoted string may legally sit directly after, ignoring whitespace. */\nconst LEGAL_BEFORE_STRING = new Set(['=', '[', ',', ';', '{', '}', '>', '-']);\n\n/** Chars a quoted string may legally be followed by, ignoring whitespace. */\nconst LEGAL_AFTER_STRING = new Set(['=', '[', ']', ',', ';', '{', '}', '-', '>']);\n\n/**\n * Escape a runtime value for use INSIDE a quoted DOT string.\n *\n * Only `\\` and `\"` matter: everything else (parens, spaces, `-`, `#`, unicode) is ordinary text once\n * it is inside quotes. Note this deliberately escapes `\\` FIRST, so a value containing a backslash\n * cannot smuggle an escape sequence in. Callers compose label lines with a literal `\\\\n` AFTER\n * escaping their values — the separator is ours, the value is theirs.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function dotValue(value: string): string {\n return value.replace(/\\\\/g, '\\\\\\\\').replace(/\"/g, '\\\\\"');\n}\n\n/**\n * Escape a runtime value for a `record`/`Mrecord` label, on TOP of {@link dotValue}.\n *\n * A record label is not ordinary label text: `|` splits fields, `{}` toggles the layout direction\n * and `<>` delimit port names. A queue name carrying any of them would not merely look wrong — it\n * would restructure the node, splitting one queue box into two or rotating it. Graphviz's record\n * grammar escapes those with a backslash, and since {@link dotValue} has already doubled real\n * backslashes, the `\\\\` emitted here survives into the record parser as a single one.\n *\n * Leading and trailing spaces are also dropped by the record parser, which is exactly why the\n * horizontal-cylinder queue node uses a `\" |\"` prefix — that empty first field is deliberate.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function recordValue(value: string): string {\n return dotValue(value).replace(/[|{}<>]/g, (char: string) => `\\\\${char}`);\n}\n\n/**\n * Escape a runtime value for TEXT inside a Graphviz HTML-like label (`label=<...>`).\n *\n * An HTML-like label is not a quoted string, so {@link dotValue} is the WRONG escape there: a `\"`\n * is harmless but a `<`, `>` or `&` in a project name would open a tag, close the label early or\n * start an entity, and Graphviz rejects the whole graph. Graphviz parses these labels as XML, so the\n * cure is entity escaping — `&` FIRST, so an escape we emit is never escaped twice.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function htmlLabelText(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n\n/** Thrown when the generator produces DOT that Graphviz could not parse. */\nexport class InvalidDotError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'InvalidDotError';\n }\n}\n\n/**\n * Fail loudly on structurally broken DOT.\n *\n * This is not a full Graphviz parser — it is the check that catches the failure mode a string\n * builder actually has: a quote that ends a string early (or never ends it). It scans the quoted\n * strings honouring `\\\"` escapes and asserts each one is terminated and is bounded by DOT\n * punctuation rather than by bare text. An unescaped `\"` inside a label always violates that: the\n * string ends mid-label, and the remaining label text becomes stray tokens.\n */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nexport function assertValidDot(dot: string, source: string): void {\n // Comment text is not code: a `\"` or a word in it must never be read as a DOT token. Blanking\n // it (offsets preserved) keeps every reported line number the one Graphviz would report.\n const code = blankComments(dot);\n let index = 0;\n while (index < code.length) {\n if (code[index] !== '\"') {\n index++;\n continue;\n }\n const start = index;\n index++;\n while (index < code.length && code[index] !== '\"') {\n index += code[index] === '\\\\' ? 2 : 1;\n }\n if (index >= code.length) {\n throw new InvalidDotError(`${source}: unterminated string starting at ${describe(dot, start)}`);\n }\n const end = index;\n index++;\n checkNeighbor(code, dot, start, end, source);\n }\n}\n\n/** Replace `//`, `#` and `/* *\\/` comment bodies with spaces, preserving length and newlines. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction blankComments(dot: string): string {\n const out = dot.split('');\n let index = 0;\n let inString = false;\n while (index < out.length) {\n const two = dot.slice(index, index + 2);\n if (inString) {\n if (dot[index] === '\\\\') index++;\n else if (dot[index] === '\"') inString = false;\n index++;\n } else if (dot[index] === '\"') {\n inString = true;\n index++;\n } else if (two === '//' || dot[index] === '#') {\n while (index < out.length && out[index] !== '\\n') out[index++] = ' ';\n } else if (two === '/*') {\n while (index < out.length && dot.slice(index, index + 2) !== '*/') {\n if (out[index] !== '\\n') out[index] = ' ';\n index++;\n }\n if (index < out.length) {\n out[index] = ' ';\n out[index + 1] = ' ';\n index += 2;\n }\n } else {\n index++;\n }\n }\n return out.join('');\n}\n\n/** Verify the non-whitespace chars bracketing a quoted string are DOT punctuation, not stray text. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction checkNeighbor(code: string, dot: string, start: number, end: number, source: string): void {\n const before = nonSpaceChar(code, start - 1, -1);\n if (before !== undefined && !LEGAL_BEFORE_STRING.has(before)) {\n throw new InvalidDotError(\n `${source}: a quoted string starts right after '${before}' at ${describe(dot, start)} — ` +\n `an unescaped '\"' in an interpolated value almost certainly ended the previous string early. ` +\n `Interpolate values through dotValue().`,\n );\n }\n const after = nonSpaceChar(code, end + 1, 1);\n if (after !== undefined && !LEGAL_AFTER_STRING.has(after)) {\n throw new InvalidDotError(\n `${source}: a quoted string is followed by '${after}' at ${describe(dot, end)} — ` +\n `an unescaped '\"' in an interpolated value almost certainly ended this string early. ` +\n `Interpolate values through dotValue().`,\n );\n }\n}\n\n/** The first non-whitespace char walking `step` from `from`, or undefined at either end. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction nonSpaceChar(dot: string, from: number, step: number): string | undefined {\n for (let i = from; i >= 0 && i < dot.length; i += step) {\n if (!/\\s/.test(dot[i])) return dot[i];\n }\n return undefined;\n}\n\n/** `line N: <the line>` for the offset, so the error names the same line Graphviz would. */\n// webpieces-disable no-function-outside-class -- DOT string helpers, matching the sibling builders in runtime-visualizer.ts\nfunction describe(dot: string, offset: number): string {\n const lineNumber = dot.slice(0, offset).split('\\n').length;\n const line = dot.split('\\n')[lineNumber - 1];\n return `line ${lineNumber}: ${line.trim()}`;\n}\n"]}
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Graph color modes (#1155)
3
+ *
4
+ * The architecture graph answers ONE question at a time instead of asking every box to encode two
5
+ * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch
6
+ * modes and re-render without regenerating anything:
7
+ *
8
+ * - RUNTIME "where the code can run". Base runtimes are equal vertical stripes in the fixed
9
+ * order browser | node | react-native; a specialization (angular/react inside
10
+ * browser, express inside node) is an inset inside its base stripe. The dependency
11
+ * rule becomes visual: a box may depend on a box carrying every color it has. The
12
+ * name and meta sit on a light label PLATE across the middle, so they read on every
13
+ * stripe combination while the stripes and insets stay visible above and below it.
14
+ * - ARCHITECTURE "servers · clients · APIs". A solid fill by ROLE; framework color is not shown.
15
+ * - IMPACT "what this branch touches". Projects owning a changed file are solid amber
16
+ * ("touched"); the rest of nx's affected set is light amber ("affected": their ci
17
+ * re-runs); every transitive dependency of the affected set that is not itself
18
+ * affected is light slate with a dashed border ("build input": compiled or restored
19
+ * from cache, unchanged); everything else is grey. Which box is which is only known
20
+ * per branch, so every box carries all four variants and the page picks one from the
21
+ * sidecar (graph-impact.ts).
22
+ */
23
+ import { KnownFramework } from '@webpieces/rules-sdk';
24
+ export declare enum GraphMode {
25
+ RUNTIME = "runtime",
26
+ ARCHITECTURE = "architecture",
27
+ IMPACT = "impact"
28
+ }
29
+ /** One selectable mode as the drawer and the node menu show it. */
30
+ export declare class ModeInfo {
31
+ readonly mode: GraphMode;
32
+ readonly name: string;
33
+ readonly subtitle: string;
34
+ constructor(mode: GraphMode, name: string, subtitle: string);
35
+ }
36
+ /** In key order: `1` selects the first, `2` the second, `3` the third. */
37
+ export declare const GRAPH_MODES: readonly ModeInfo[];
38
+ /** A framework value's color and the BASE runtime it is drawn inside (itself, for a base). */
39
+ export declare class FrameworkStyle {
40
+ readonly framework: KnownFramework;
41
+ readonly base: KnownFramework;
42
+ readonly color: string;
43
+ readonly description: string;
44
+ constructor(framework: KnownFramework, base: KnownFramework, color: string, description: string);
45
+ isBase(): boolean;
46
+ }
47
+ /**
48
+ * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a
49
+ * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how
50
+ * react-native shipped (#1155).
51
+ */
52
+ export declare const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle>;
53
+ /** Stripe order, left to right. Fixed, so the same set always reads the same way. */
54
+ export declare const BASE_ORDER: readonly KnownFramework[];
55
+ /** A box with no known framework tag. */
56
+ export declare const NO_RUNTIME_COLOR = "#e4e6ec";
57
+ /** A role's solid fill and the text color that stays readable on it. */
58
+ export declare class RoleStyle {
59
+ readonly role: string;
60
+ readonly fill: string;
61
+ readonly fontColor: string;
62
+ readonly description: string;
63
+ constructor(role: string, fill: string, fontColor: string, description: string);
64
+ }
65
+ /** Every role role-resolver knows, in legend order (runnable things first). */
66
+ export declare const ROLE_STYLES: readonly RoleStyle[];
67
+ /** The four impact shades (graph-impact.ts decides which box gets which). */
68
+ export declare const IMPACT_TOUCHED = "#f5a524";
69
+ export declare const IMPACT_AFFECTED = "#fde3b0";
70
+ export declare const IMPACT_BUILD_INPUT = "#e3e9f2";
71
+ export declare const IMPACT_BUILD_INPUT_BORDER = "#7c8aa3";
72
+ export declare const IMPACT_UNTOUCHED = "#eef0f4";
73
+ /** The light plate the name and meta sit on, across every stripe of a multi-color box. */
74
+ export declare const LABEL_PLATE = "#fbfbfd";
75
+ /** What a box's per-mode rendering is computed from. */
76
+ export declare class NodeFacts {
77
+ readonly nodeId: string;
78
+ readonly shortName: string;
79
+ readonly level: number;
80
+ readonly role: string;
81
+ readonly frameworks: string[];
82
+ constructor(nodeId: string, shortName: string, level: number, role: string, frameworks: string[]);
83
+ }
84
+ /** One box's DOT statement for every mode (and for each impact state). */
85
+ export declare class NodeModeDots {
86
+ readonly runtime: string;
87
+ readonly architecture: string;
88
+ readonly touched: string;
89
+ readonly affected: string;
90
+ readonly buildInput: string;
91
+ readonly untouched: string;
92
+ constructor(runtime: string, architecture: string, touched: string, affected: string, buildInput: string, untouched: string);
93
+ }
94
+ /** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */
95
+ export declare class LegendFacts {
96
+ frameworks: string[];
97
+ roles: string[];
98
+ multiRuntime: boolean;
99
+ noRuntime: boolean;
100
+ }
101
+ export declare class NodeModeStyler {
102
+ styleOf(framework: string): FrameworkStyle | undefined;
103
+ roleStyle(role: string): RoleStyle;
104
+ /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */
105
+ styledRoles(): string[];
106
+ /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */
107
+ bases(frameworks: string[]): KnownFramework[];
108
+ /** The specialization drawn inside `base`, if the set carries one. */
109
+ specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined;
110
+ dots(facts: NodeFacts): NodeModeDots;
111
+ /** Fold one drawn box into the legend facts. */
112
+ record(legend: LegendFacts, facts: NodeFacts): void;
113
+ private statement;
114
+ private plainLabel;
115
+ private runtimeMeta;
116
+ private envMeta;
117
+ private architectureAttrs;
118
+ /**
119
+ * Runtime mode. A box in ONE base runtime with no specialization is a plain fill — its dark text
120
+ * reads on any of the pastel base colors. Every other box (several runtimes, or a specialization)
121
+ * is an HTML-like table of three rows: a band of equal stripes, a light PLATE carrying the name and
122
+ * meta across all of them, and the same stripes again. The stripes and insets stay visible above
123
+ * and below the text, and the text never straddles two colors.
124
+ */
125
+ private runtime;
126
+ /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */
127
+ stripeAttrs(bases: KnownFramework[]): string;
128
+ /**
129
+ * Stripe band / plate / stripe band. Every stripe cell asks for the same minimum width (an
130
+ * estimate of the text's width split evenly), so the stripes come out equal and the plate spans
131
+ * exactly their total.
132
+ */
133
+ private plateTable;
134
+ /** One stripe of a band: the base color, with its specialization inset inside a margin of it. */
135
+ private stripeCell;
136
+ }