@webpieces/nx-webpieces-rules 0.4.883 → 0.4.885
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.
- package/README.md +41 -8
- package/package.json +7 -7
- package/src/executors/visualize-runtime/executor.js +13 -1
- package/src/executors/visualize-runtime/executor.js.map +1 -1
- package/src/lib/dot-syntax.js +26 -0
- package/src/lib/dot-syntax.js.map +1 -1
- package/src/lib/graph-color-modes.d.ts +2 -1
- package/src/lib/graph-color-modes.js +5 -2
- package/src/lib/graph-color-modes.js.map +1 -1
- package/src/lib/graph-impact.js +1 -0
- package/src/lib/graph-impact.js.map +1 -1
- package/src/lib/graph-legend.d.ts +1 -1
- package/src/lib/graph-legend.js.map +1 -1
- package/src/lib/graph-mode-state.client.d.ts +32 -0
- package/src/lib/graph-mode-state.client.js +141 -0
- package/src/lib/graph-mode-state.client.js.map +1 -0
- package/src/lib/graph-page-shell.d.ts +5 -1
- package/src/lib/graph-page-shell.js +31 -12
- package/src/lib/graph-page-shell.js.map +1 -1
- package/src/lib/graph-render-model.d.ts +7 -5
- package/src/lib/graph-render-model.js +11 -8
- package/src/lib/graph-render-model.js.map +1 -1
- package/src/lib/graph-visualizer.client.d.ts +11 -46
- package/src/lib/graph-visualizer.client.js +103 -160
- package/src/lib/graph-visualizer.client.js.map +1 -1
- package/src/lib/graph-visualizer.js +2 -1
- package/src/lib/graph-visualizer.js.map +1 -1
- package/src/lib/runtime-details.d.ts +4 -1
- package/src/lib/runtime-details.js +102 -55
- package/src/lib/runtime-details.js.map +1 -1
- package/src/lib/runtime-html-page.d.ts +13 -40
- package/src/lib/runtime-html-page.js +96 -129
- package/src/lib/runtime-html-page.js.map +1 -1
- package/src/lib/runtime-viewer-adapter.d.ts +16 -0
- package/src/lib/runtime-viewer-adapter.js +58 -0
- package/src/lib/runtime-viewer-adapter.js.map +1 -0
- package/src/lib/runtime-visualizer.client.d.ts +9 -19
- package/src/lib/runtime-visualizer.client.js +75 -75
- package/src/lib/runtime-visualizer.client.js.map +1 -1
- package/src/lib/runtime-visualizer.d.ts +2 -16
- package/src/lib/runtime-visualizer.js +22 -44
- package/src/lib/runtime-visualizer.js.map +1 -1
- package/src/lib/runtime-viz-options.d.ts +19 -0
- package/src/lib/runtime-viz-options.js +22 -0
- package/src/lib/runtime-viz-options.js.map +1 -0
package/README.md
CHANGED
|
@@ -110,7 +110,7 @@ their existing checks independently of visualization.
|
|
|
110
110
|
|
|
111
111
|
`architecture/dependencies.html` opens with the graph filling the viewport and every control
|
|
112
112
|
in a collapsible charcoal drawer on the left (violet accent): the **Color by** pulldown, the
|
|
113
|
-
**Filter** button, a type-to-search **
|
|
113
|
+
**Filter** button, a type-to-search **Focus** field (`/` focuses it, `Esc` unlocks), a separate **Lock** switch, a **Hide
|
|
114
114
|
unconnected** toggle, the legend (with a pop-out button) and the edge key. Help is the **?**
|
|
115
115
|
popover, zoom floats bottom-right, and **Responsibilities** opens a right-hand panel from the
|
|
116
116
|
drawer footer.
|
|
@@ -191,7 +191,7 @@ incoming and outgoing chains, then reruns Graphviz. Architecture L-number rows k
|
|
|
191
191
|
their original identities; runtime chains include rendered queues, clocks, external
|
|
192
192
|
APIs and systems. The anchor indicator and all surviving node menus offer **Turn off
|
|
193
193
|
Filter**, restoring the full layout and independent Lock state. Responsibilities
|
|
194
|
-
follow the retained set intersected with architecture Lock.
|
|
194
|
+
follow the retained set intersected with architecture Lock. Both viewers’ Lock pins
|
|
195
195
|
its chain in the foreground; hover adds a temporary chain without dimming locked
|
|
196
196
|
nodes or edges, and leaving restores the Lock chain. The locked anchor keeps a violet outline,
|
|
197
197
|
and hover never changes responsibilities. A filtered-out Lock is suspended until
|
|
@@ -201,9 +201,42 @@ and visibility options remain untouched. See
|
|
|
201
201
|
[dependency graphs](../../../docs/architecture/dependency-graphs.md#filter-unconnected)
|
|
202
202
|
for traversal examples and browser verification.
|
|
203
203
|
|
|
204
|
-
The runtime
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
204
|
+
The runtime viewer uses the same drawer, staged Filter popover, legend pop-out, help,
|
|
205
|
+
saved-snapshot panel and floating navigation. **Color by** supports Runtime, Architecture,
|
|
206
|
+
Impact and Product, with `1`–`4` shortcuts, URL hashes and a separately remembered runtime
|
|
207
|
+
choice. Runtime/framework and responsibility metadata comes from saved `dependencies.json`;
|
|
208
|
+
viewing never regenerates it. Older runtime snapshots still open: missing framework/role facts
|
|
209
|
+
are explicitly unknown. Missing project facts disable Impact, leaving the runtime viewer usable.
|
|
210
|
+
|
|
211
|
+
Filters combine OR within Runtime/Role/Product groups and AND between groups and change scope.
|
|
212
|
+
Draft changes apply only with **Show N services**; closing cancels them. Clear all resets facets,
|
|
213
|
+
leaving Lock and Hide unconnected alone. Counts describe services, with contextual nodes reported
|
|
214
|
+
separately. Products preserve saved runtime closure across calls and queues. Attached queues,
|
|
215
|
+
clocks and external systems remain as context without resurrecting excluded services.
|
|
216
|
+
|
|
217
|
+
Impact refreshes the existing Nx/git comparisons into a local sidecar beside the runtime page in
|
|
218
|
+
`tmp/webpieces/.impact`. The service set is the intersection of runtime identities with Nx project
|
|
219
|
+
sets: changing a shared library can affect several services, but only a service owning a changed
|
|
220
|
+
file is **Changed**. Build input sets come from compile dependencies, never runtime-call traversal.
|
|
221
|
+
Main/detached HEAD compares the last commit; a fresh feature branch compares its fork point only;
|
|
222
|
+
a feature with commits offers both. Missing git/Nx data disables Impact-dependent controls.
|
|
223
|
+
Queues/vendors never claim source ownership or CI status: Runtime/Architecture/Impact retain
|
|
224
|
+
kind colors and shapes; Product uses inherited product colors (a gradient on contextual shapes).
|
|
225
|
+
No product is white, membership shared by all products is neutral. Runtime services use the
|
|
226
|
+
established nested/striped framework palette and Architecture uses declared roles.
|
|
227
|
+
|
|
228
|
+
Lock from Focus, the sidebar switch, or a box menu pins separate complete incoming/outgoing
|
|
229
|
+
chains; hover temporarily adds its own chain. Lock does not change layout. Hide unconnected
|
|
230
|
+
removes unrelated nodes and compacts the graph while preserving original runtime L-identities.
|
|
231
|
+
Switching Lock off retains the explicit Focus selection so it can be pinned again; menu Unlock
|
|
232
|
+
or clearing Focus clears that selection. A filtered-out lock is suspended and restored when visible.
|
|
233
|
+
|
|
234
|
+
**API details are click-only.** Every service shows plain **Implements (N)** summary text.
|
|
235
|
+
Click anywhere in the box for its menu, then **Implements** for full contracts/provenance and
|
|
236
|
+
conditions, including an explicit empty state. Auxiliary nodes have no Implements action.
|
|
237
|
+
**Uses** belongs exclusively on relationship lines: click or press Enter/Space on the edge
|
|
238
|
+
control for its targets, queue, owner, conditions and inference details. Hover/focus never opens
|
|
239
|
+
API panels. Close, Escape and outside-click dismiss them; explicit close returns focus to the
|
|
240
|
+
owner. Controls rebind after filtering or recoloring, and panels follow zoom/scroll.
|
|
241
|
+
|
|
242
|
+
Browser verification and screenshots: [runtime viewer evidence](../../../docs/architecture/runtime-viewer-parity.md).
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.885",
|
|
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.
|
|
24
|
-
"@webpieces/core-util": "0.4.
|
|
25
|
-
"@webpieces/eslint-rules": "0.4.
|
|
26
|
-
"@webpieces/rules-config": "0.4.
|
|
23
|
+
"@webpieces/api-doc-model": "0.4.885",
|
|
24
|
+
"@webpieces/core-util": "0.4.885",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.885",
|
|
26
|
+
"@webpieces/rules-config": "0.4.885",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.885"
|
|
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.
|
|
44
|
+
"@webpieces/tooling-testkit": "0.4.885"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -9,6 +9,11 @@
|
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
11
|
exports.default = runExecutor;
|
|
12
|
+
const tslib_1 = require("tslib");
|
|
13
|
+
const fs = tslib_1.__importStar(require("fs"));
|
|
14
|
+
const path = tslib_1.__importStar(require("path"));
|
|
15
|
+
const graph_loader_1 = require("../../lib/graph-loader");
|
|
16
|
+
const graph_impact_1 = require("../../lib/graph-impact");
|
|
12
17
|
const saved_snapshot_1 = require("../../lib/saved-snapshot");
|
|
13
18
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
14
19
|
const runtime_visualizer_1 = require("../../lib/runtime-visualizer");
|
|
@@ -24,10 +29,17 @@ async function runExecutor(_options, context) {
|
|
|
24
29
|
try {
|
|
25
30
|
const graph = snapshot.loadRuntime(workspaceRoot);
|
|
26
31
|
const config = (0, runtime_config_1.loadRuntimeConfig)(workspaceRoot);
|
|
27
|
-
const
|
|
32
|
+
const projects = fs.existsSync(path.join(workspaceRoot, graph_loader_1.DEFAULT_GRAPH_PATH)) ? snapshot.loadProjects(workspaceRoot, graph_loader_1.DEFAULT_GRAPH_PATH).projects : null;
|
|
33
|
+
const options = new runtime_visualizer_1.RuntimeVizOptions(config.showExternalNodes, projects, workspaceRoot);
|
|
28
34
|
const vizPaths = (0, runtime_visualizer_1.writeRuntimeVisualization)(graph, workspaceRoot, undefined, options);
|
|
29
35
|
console.log(`✅ Generated: ${vizPaths.dotPath}`);
|
|
30
36
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
37
|
+
if (projects !== null)
|
|
38
|
+
console.log(await new graph_impact_1.ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, graph_impact_1.VISUALIZE_IMPACT_KINDS));
|
|
39
|
+
else {
|
|
40
|
+
new graph_impact_1.ImpactSidecar().remove(path.dirname(vizPaths.htmlPath));
|
|
41
|
+
console.log('Impact and framework metadata unavailable: no saved project graph. Refresh explicitly with architecture:generate.');
|
|
42
|
+
}
|
|
31
43
|
console.log('\n🌐 Opening visualization in browser...');
|
|
32
44
|
if (new graph_visualizer_1.GraphVisualizer().openVisualization(vizPaths.htmlPath)) {
|
|
33
45
|
console.log('✅ Browser opened');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAsBH,8BAoCC;;AAxDD,+CAAyB;AACzB,mDAA6B;AAC7B,yDAA4D;AAC5D,yDAA8F;AAE9F,6DAAyD;AACzD,0DAAgF;AAChF,qEAA4F;AAC5F,6DAA6D;AAC7D,iEAA6D;AAC7D,2CAAwC;AAUzB,KAAK,UAAU,WAAW,CACrC,QAAiC,EACjC,OAAwB;IAExB,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IACnC,MAAM,QAAQ,GAAkB,IAAI,8BAAa,EAAE,CAAC;IAEpD,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IACzD,OAAO,CAAC,GAAG,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IAEhC,uIAAuI;IACvI,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,QAAQ,CAAC,WAAW,CAAC,aAAa,CAAC,CAAC;QAClD,MAAM,MAAM,GAAG,IAAA,kCAAiB,EAAC,aAAa,CAAC,CAAC;QAChD,MAAM,QAAQ,GAAG,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,iCAAkB,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC,aAAa,EAAE,iCAAkB,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;QACxJ,MAAM,OAAO,GAAG,IAAI,sCAAiB,CAAC,MAAM,CAAC,iBAAiB,EAAE,QAAQ,EAAE,aAAa,CAAC,CAAC;QACzF,MAAM,QAAQ,GAAG,IAAA,8CAAyB,EAAC,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QACrF,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;QAChD,OAAO,CAAC,GAAG,CAAC,gBAAgB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QACjD,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,CAAC,GAAG,CAAC,MAAM,IAAI,4BAAa,EAAE,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,EAAE,QAAQ,EAAE,qCAAsB,CAAC,CAAC,CAAC;aAC/I,CAAC;YAAC,IAAI,4BAAa,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC;YAAC,OAAO,CAAC,GAAG,CAAC,mHAAmH,CAAC,CAAC;QAAC,CAAC;QAEvM,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;QACxD,IAAI,IAAI,kCAAe,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;YAC7D,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAC,CAAC;QACpC,CAAC;aAAM,CAAC;YACJ,OAAO,CAAC,GAAG,CAAC,2CAA2C,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC;QAChF,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,MAAM,QAAQ,GAAG,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QAChG,OAAO,CAAC,KAAK,CAAC,4EAA4E,EAAE,QAAQ,CAAC,CAAC;QACtG,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Visualize Runtime Executor\n *\n * Renders the runtime microservice graph (committed\n * architecture/runtime-dependencies.json) to DOT + HTML and opens it.\n *\n * Usage: nx run architecture:visualize-runtime\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ImpactRefresh, ImpactSidecar, VISUALIZE_IMPACT_KINDS } from '../../lib/graph-impact';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { writeRuntimeVisualization, RuntimeVizOptions } from '../../lib/runtime-visualizer';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { toError } from '../../toError';\n\nexport interface VisualizeRuntimeOptions {\n // No options.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n _options: VisualizeRuntimeOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const snapshot: SavedSnapshot = new SavedSnapshot();\n\n console.log('\\n🎨 Runtime Microservice Visualization\\n');\n console.log(snapshot.message());\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx\n try {\n const graph = snapshot.loadRuntime(workspaceRoot);\n const config = loadRuntimeConfig(workspaceRoot);\n const projects = fs.existsSync(path.join(workspaceRoot, DEFAULT_GRAPH_PATH)) ? snapshot.loadProjects(workspaceRoot, DEFAULT_GRAPH_PATH).projects : null;\n const options = new RuntimeVizOptions(config.showExternalNodes, projects, workspaceRoot);\n const vizPaths = writeRuntimeVisualization(graph, workspaceRoot, undefined, options);\n console.log(`✅ Generated: ${vizPaths.dotPath}`);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n if (projects !== null) console.log(await new ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, VISUALIZE_IMPACT_KINDS));\n else { new ImpactSidecar().remove(path.dirname(vizPaths.htmlPath)); console.log('Impact and framework metadata unavailable: no saved project graph. Refresh explicitly with architecture:generate.'); }\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (new GraphVisualizer().openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Runtime visualization failed for architecture/runtime-dependencies.json:', rendered);\n return { success: false };\n }\n}\n"]}
|
package/src/lib/dot-syntax.js
CHANGED
|
@@ -90,6 +90,10 @@ function assertValidDot(dot, source) {
|
|
|
90
90
|
const code = blankComments(dot);
|
|
91
91
|
let index = 0;
|
|
92
92
|
while (index < code.length) {
|
|
93
|
+
if (code[index] === '<') {
|
|
94
|
+
index = new DotHtml().end(code, index, source);
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
93
97
|
if (code[index] !== '"') {
|
|
94
98
|
index++;
|
|
95
99
|
continue;
|
|
@@ -122,6 +126,9 @@ function blankComments(dot) {
|
|
|
122
126
|
inString = false;
|
|
123
127
|
index++;
|
|
124
128
|
}
|
|
129
|
+
else if (dot[index] === '<') {
|
|
130
|
+
index = new DotHtml().end(dot, index, 'DOT HTML label');
|
|
131
|
+
}
|
|
125
132
|
else if (dot[index] === '"') {
|
|
126
133
|
inString = true;
|
|
127
134
|
index++;
|
|
@@ -180,4 +187,23 @@ function describe(dot, offset) {
|
|
|
180
187
|
const line = dot.split('\n')[lineNumber - 1];
|
|
181
188
|
return `line ${lineNumber}: ${line.trim()}`;
|
|
182
189
|
}
|
|
190
|
+
/** Skip balanced HTML labels so entity # characters and attribute quotes are not DOT tokens. */
|
|
191
|
+
class DotHtml {
|
|
192
|
+
end(code, start, source) {
|
|
193
|
+
let depth = 0;
|
|
194
|
+
let quoted = false;
|
|
195
|
+
for (let index = start; index < code.length; index++) {
|
|
196
|
+
const character = code[index];
|
|
197
|
+
if (character === '"')
|
|
198
|
+
quoted = !quoted;
|
|
199
|
+
if (quoted)
|
|
200
|
+
continue;
|
|
201
|
+
if (character === '<')
|
|
202
|
+
depth++;
|
|
203
|
+
if (character === '>' && --depth === 0)
|
|
204
|
+
return index + 1;
|
|
205
|
+
}
|
|
206
|
+
throw new InvalidDotError(`${source}: unterminated HTML label at ${describe(code, start)}`);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
183
209
|
//# sourceMappingURL=dot-syntax.js.map
|
|
@@ -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/**\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, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\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, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"');\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] === '<') { index = new DotHtml().end(code, index, source); continue; }\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 index = new DotHtml().end(dot, index, 'DOT HTML label');\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\n/** Skip balanced HTML labels so entity # characters and attribute quotes are not DOT tokens. */\nclass DotHtml {\n end(code: string, start: number, source: string): number {\n let depth = 0;\n let quoted = false;\n for (let index = start; index < code.length; index++) {\n const character = code[index];\n if (character === '\"') quoted = !quoted;\n if (quoted) continue;\n if (character === '<') depth++;\n if (character === '>' && --depth === 0) return index + 1;\n }\n throw new InvalidDotError(`${source}: unterminated HTML label at ${describe(code, start)}`);\n }\n}\n"]}
|
|
@@ -108,9 +108,10 @@ export declare class NodeFacts {
|
|
|
108
108
|
readonly frameworks: string[];
|
|
109
109
|
/** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */
|
|
110
110
|
readonly products: string[];
|
|
111
|
+
readonly summary: string;
|
|
111
112
|
constructor(nodeId: string, shortName: string, level: number, role: string, frameworks: string[],
|
|
112
113
|
/** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */
|
|
113
|
-
products: string[]);
|
|
114
|
+
products: string[], summary?: string);
|
|
114
115
|
}
|
|
115
116
|
/** One box's DOT statement for every mode (and for each impact state). */
|
|
116
117
|
export declare class NodeModeDots {
|
|
@@ -165,15 +165,17 @@ class NodeFacts {
|
|
|
165
165
|
role;
|
|
166
166
|
frameworks;
|
|
167
167
|
products;
|
|
168
|
+
summary;
|
|
168
169
|
constructor(nodeId, shortName, level, role, frameworks,
|
|
169
170
|
/** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */
|
|
170
|
-
products) {
|
|
171
|
+
products, summary = '') {
|
|
171
172
|
this.nodeId = nodeId;
|
|
172
173
|
this.shortName = shortName;
|
|
173
174
|
this.level = level;
|
|
174
175
|
this.role = role;
|
|
175
176
|
this.frameworks = frameworks;
|
|
176
177
|
this.products = products;
|
|
178
|
+
this.summary = summary;
|
|
177
179
|
}
|
|
178
180
|
}
|
|
179
181
|
exports.NodeFacts = NodeFacts;
|
|
@@ -319,7 +321,8 @@ class NodeModeStyler {
|
|
|
319
321
|
*/
|
|
320
322
|
labelText(facts, fontColor) {
|
|
321
323
|
return (`<FONT COLOR="${fontColor}99">L${facts.level}</FONT>  <B>${(0, dot_syntax_1.htmlLabelText)(facts.shortName)}</B>` +
|
|
322
|
-
`<BR/>${(0, dot_syntax_1.htmlLabelText)(facts.role)}<BR/>${(0, dot_syntax_1.htmlLabelText)(this.frameworkLine(facts.frameworks))}`
|
|
324
|
+
`<BR/>${(0, dot_syntax_1.htmlLabelText)(facts.role)}<BR/>${(0, dot_syntax_1.htmlLabelText)(this.frameworkLine(facts.frameworks))}` +
|
|
325
|
+
(facts.summary === '' ? '' : `<BR/>${(0, dot_syntax_1.htmlLabelText)(facts.summary)}`));
|
|
323
326
|
}
|
|
324
327
|
/** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */
|
|
325
328
|
stripeAttrs(bases) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;;;AAEH,oDAAwE;AACxE,6CAAuD;AACvD,qDAA6H;AAE7H,IAAY,SAKX;AALD,WAAY,SAAS;IACjB,gCAAmB,CAAA;IACnB,0CAA6B,CAAA;IAC7B,8BAAiB,CAAA;IACjB,gCAAmB,CAAA;AACvB,CAAC,EALW,SAAS,yBAAT,SAAS,QAKpB;AAED,sEAAsE;AACtE,MAAa,QAAQ;IAEG;IACA;IACA;IAHpB,YACoB,IAAe,EACf,IAAY,EACZ,QAAgB;QAFhB,SAAI,GAAJ,IAAI,CAAW;QACf,SAAI,GAAJ,IAAI,CAAQ;QACZ,aAAQ,GAAR,QAAQ,CAAQ;IACjC,CAAC;CACP;AAND,4BAMC;AAED,0FAA0F;AAC7E,QAAA,WAAW,GAAwB;IAC5C,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,wBAAwB,CAAC;IACpE,IAAI,QAAQ,CAAC,SAAS,CAAC,YAAY,EAAE,cAAc,EAAE,0BAA0B,CAAC;IAChF,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,EAAE,cAAc,CAAC;IACxD,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,EAAE,8BAA8B,CAAC;CAC7E,CAAC;AAEF,4BAA4B;AACf,QAAA,SAAS,GAAG,SAAS,CAAC;AACnC,2BAA2B;AACd,QAAA,UAAU,GAAG,SAAS,CAAC;AAEpC;;;GAGG;AACH,MAAa,cAAc;IAEH;IACA;IACA;IACA;IACA;IALpB,YACoB,SAAyB,EACzB,IAAoB,EACpB,KAAa,EACb,SAAiB,EACjB,WAAmB;QAJnB,cAAS,GAAT,SAAS,CAAgB;QACzB,SAAI,GAAJ,IAAI,CAAgB;QACpB,UAAK,GAAL,KAAK,CAAQ;QACb,cAAS,GAAT,SAAS,CAAQ;QACjB,gBAAW,GAAX,WAAW,CAAQ;IACpC,CAAC;IAEJ,MAAM;QACF,OAAO,IAAI,CAAC,SAAS,KAAK,IAAI,CAAC,IAAI,CAAC;IACxC,CAAC;CACJ;AAZD,wCAYC;AAED;;;;GAIG;AACU,QAAA,gBAAgB,GAA2C;IACpE,OAAO,EAAE,IAAI,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAS,EAAE,sBAAsB,CAAC;IAC/F,IAAI,EAAE,IAAI,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAS,EAAE,mBAAmB,CAAC;IACnF,cAAc,EAAE,IAAI,cAAc,CAC9B,cAAc,EACd,cAAc,EACd,SAAS,EACT,iBAAS,EACT,gCAAgC,CACnC;IACD,OAAO,EAAE,IAAI,cAAc,CAAC,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAU,EAAE,gBAAgB,CAAC;IAC1F,KAAK,EAAE,IAAI,cAAc,CAAC,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,iBAAS,EAAE,gBAAgB,CAAC;IACrF,OAAO,EAAE,IAAI,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,kBAAU,EAAE,aAAa,CAAC;CACvF,CAAC;AAEF,qFAAqF;AACxE,QAAA,UAAU,GAA8B,CAAC,SAAS,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;AAEzF;;;GAGG;AACU,QAAA,eAAe,GAA8B;IACtD,SAAS;IACT,MAAM;IACN,cAAc;IACd,SAAS;IACT,OAAO;IACP,SAAS;CACZ,CAAC;AAEF,yCAAyC;AAC5B,QAAA,gBAAgB,GAAG,SAAS,CAAC;AAE1C,+FAA+F;AAClF,QAAA,aAAa,GAAG,YAAY,CAAC;AAE1C,gEAAgE;AAChE,MAAM,QAAQ,GAAG,CAAC,CAAC;AACnB,yDAAyD;AACzD,MAAM,SAAS,GAAG,CAAC,CAAC;AAEpB,wEAAwE;AACxE,MAAa,SAAS;IAEE;IACA;IACA;IACA;IAJpB,YACoB,IAAY,EACZ,IAAY,EACZ,SAAiB,EACjB,WAAmB;QAHnB,SAAI,GAAJ,IAAI,CAAQ;QACZ,SAAI,GAAJ,IAAI,CAAQ;QACZ,cAAS,GAAT,SAAS,CAAQ;QACjB,gBAAW,GAAX,WAAW,CAAQ;IACpC,CAAC;CACP;AAPD,8BAOC;AAED;;;GAGG;AACU,QAAA,WAAW,GAAyB;IAC7C,IAAI,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,kBAAU,EAAE,iBAAiB,CAAC;IACjE,IAAI,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,kBAAU,EAAE,8BAA8B,CAAC;IAC9E,IAAI,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,kBAAU,EAAE,uBAAuB,CAAC;IACpE,IAAI,SAAS,CAAC,QAAQ,EAAE,SAAS,EAAE,kBAAU,EAAE,sBAAsB,CAAC;IACtE,IAAI,SAAS,CAAC,SAAS,EAAE,SAAS,EAAE,iBAAS,EAAE,iBAAiB,CAAC;IACjE,IAAI,SAAS,CAAC,YAAY,EAAE,SAAS,EAAE,kBAAU,EAAE,+BAA+B,CAAC;IACnF,IAAI,SAAS,CAAC,cAAc,EAAE,SAAS,EAAE,kBAAU,EAAE,iCAAiC,CAAC;IACvF,IAAI,SAAS,CAAC,KAAK,EAAE,SAAS,EAAE,iBAAS,EAAE,eAAe,CAAC;CAC9D,CAAC;AAEF,6EAA6E;AAChE,QAAA,cAAc,GAAG,SAAS,CAAC;AAC3B,QAAA,eAAe,GAAG,SAAS,CAAC;AAC5B,QAAA,kBAAkB,GAAG,SAAS,CAAC;AAC/B,QAAA,yBAAyB,GAAG,SAAS,CAAC;AACtC,QAAA,gBAAgB,GAAG,SAAS,CAAC;AAC1C,MAAM,qBAAqB,GAAG,SAAS,CAAC;AAExC,kFAAkF;AAClF,MAAa,SAAS;IAEE;IACA;IACA;IACA;IACA;IAEA;IAPpB,YACoB,MAAc,EACd,SAAiB,EACjB,KAAa,EACb,IAAY,EACZ,UAAoB;IACpC,kGAAkG;IAClF,QAAkB;QANlB,WAAM,GAAN,MAAM,CAAQ;QACd,cAAS,GAAT,SAAS,CAAQ;QACjB,UAAK,GAAL,KAAK,CAAQ;QACb,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAU;QAEpB,aAAQ,GAAR,QAAQ,CAAU;IACnC,CAAC;CACP;AAVD,8BAUC;AAED,0EAA0E;AAC1E,MAAa,YAAY;IAED;IACA;IACA;IACA;IACA;IACA;IACA;IAPpB,YACoB,OAAe,EACf,YAAoB,EACpB,OAAe,EACf,QAAgB,EAChB,UAAkB,EAClB,SAAiB,EACjB,OAAe;QANf,YAAO,GAAP,OAAO,CAAQ;QACf,iBAAY,GAAZ,YAAY,CAAQ;QACpB,YAAO,GAAP,OAAO,CAAQ;QACf,aAAQ,GAAR,QAAQ,CAAQ;QAChB,eAAU,GAAV,UAAU,CAAQ;QAClB,cAAS,GAAT,SAAS,CAAQ;QACjB,YAAO,GAAP,OAAO,CAAQ;IAChC,CAAC;CACP;AAVD,oCAUC;AAED,0FAA0F;AAC1F,MAAa,YAAY;IAED;IACT;IACA;IAHX,YACoB,OAAe,EACxB,KAAa,EACb,IAAY;QAFH,YAAO,GAAP,OAAO,CAAQ;QACxB,UAAK,GAAL,KAAK,CAAQ;QACb,SAAI,GAAJ,IAAI,CAAQ;IACpB,CAAC;CACP;AAND,oCAMC;AAED,iGAAiG;AACjG,MAAa,WAAW;IACpB,UAAU,GAAa,EAAE,CAAC;IAC1B,KAAK,GAAa,EAAE,CAAC;IACrB,YAAY,GAAG,KAAK,CAAC;IACrB,SAAS,GAAG,KAAK,CAAC;IAClB,mGAAmG;IACnG,QAAQ,GAAmB,EAAE,CAAC;IAC9B,4DAA4D;IAC5D,WAAW,GAAG,CAAC,CAAC;IAChB,2BAA2B;IAC3B,SAAS,GAAG,CAAC,CAAC;IACd,qFAAqF;IACrF,YAAY,GAAG,KAAK,CAAC;IACrB,qFAAqF;IACrF,eAAe,GAAG,KAAK,CAAC;CAC3B;AAfD,kCAeC;AAED,MAAa,cAAc;IACvB,OAAO,CAAC,SAAiB;QACrB,IAAI,CAAE,4BAAsC,CAAC,QAAQ,CAAC,SAAS,CAAC;YAAE,OAAO,SAAS,CAAC;QACnF,OAAO,wBAAgB,CAAC,SAA2B,CAAC,CAAC;IACzD,CAAC;IAED,SAAS,CAAC,IAAY;QAClB,MAAM,KAAK,GAAG,mBAAW,CAAC,IAAI,CAAC,CAAC,SAAoB,EAAW,EAAE,CAAC,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;QAC3F,OAAO,KAAK,IAAK,mBAAW,CAAC,IAAI,CAAC,CAAC,CAAY,EAAW,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAe,CAAC;IACjG,CAAC;IAED,8FAA8F;IAC9F,WAAW;QACP,OAAO,mBAAW,CAAC,GAAG,CAAC,CAAC,KAAgB,EAAU,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACrE,CAAC;IAED,iGAAiG;IACjG,KAAK,CAAC,UAAoB;QACtB,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;QAC1C,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACjC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;YACtC,IAAI,KAAK,KAAK,SAAS;gBAAE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACrD,CAAC;QACD,OAAO,kBAAU,CAAC,MAAM,CAAC,CAAC,IAAoB,EAAW,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,CAAC;IAED,sEAAsE;IACtE,cAAc,CAAC,IAAoB,EAAE,UAAoB;QACrD,KAAK,MAAM,SAAS,IAAI,4BAAgB,EAAE,CAAC;YACvC,MAAM,KAAK,GAAG,wBAAgB,CAAC,SAAS,CAAC,CAAC;YAC1C,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,KAAK,CAAC,IAAI,KAAK,IAAI;gBAAE,SAAS;YACpD,IAAI,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAAE,OAAO,KAAK,CAAC;QACrD,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAED,IAAI,CAAC,KAAgB,EAAE,OAAuB;QAC1C,OAAO,IAAI,YAAY,CACnB,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EACnB,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,EAC5F,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,sBAAc,EAAE,iBAAS,EAAE,EAAE,CAAC,EAChD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,uBAAe,EAAE,iBAAS,EAAE,YAAY,sBAAc,eAAe,CAAC,EACxF,IAAI,CAAC,KAAK,CACN,KAAK,EACL,0BAAkB,EAClB,iBAAS,EACT,YAAY,iCAAyB,iBAAiB,EACtD,eAAe,CAClB,EACD,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,wBAAgB,EAAE,qBAAqB,EAAE,mBAAmB,CAAC,EAC/E,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,OAAO,CAAC,CAC/B,CAAC;IACN,CAAC;IAED,gDAAgD;IAChD,MAAM,CAAC,MAAmB,EAAE,KAAgB,EAAE,OAAuB;QACjE,IAAI,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;QAC5C,KAAK,MAAM,SAAS,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;YACvC,IAAI,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC;gBAC/E,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1C,CAAC;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC;QAC7C,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC;QACjD,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC;IACpD,CAAC;IAED,qGAAqG;IAC7F,cAAc,CAAC,MAAmB,EAAE,KAAgB,EAAE,OAAuB;QACjF,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5D,MAAM,CAAC,QAAQ,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,OAAe,EAAgB,EAAE,CAAC,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC1G,MAAM,CAAC,eAAe,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC;QACjD,CAAC;QACD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,MAAM,CAAC,SAAS,EAAE,CAAC;QACpD,IAAI,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,MAAM,CAAC,WAAW,EAAE,CAAC;aACzD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC;YAAE,MAAM,CAAC,YAAY,GAAG,IAAI,CAAC;QAC/D,KAAK,MAAM,KAAK,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;YAClC,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC;gBAAE,SAAS;YACtD,KAAK,CAAC,KAAK,EAAE,CAAC;YACd,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;gBAAE,KAAK,CAAC,IAAI,EAAE,CAAC;QAClD,CAAC;IACL,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,UAAoB;QAC9B,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,kBAAkB,CAAC;QACvD,MAAM,KAAK,GAAG,uBAAe,CAAC,MAAM,CAAC,CAAC,SAAyB,EAAW,EAAE,CACxE,UAAU,CAAC,QAAQ,CAAC,SAAS,CAAC,CACjC,CAAC;QACF,MAAM,OAAO,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,SAAiB,EAAW,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS,CAAC,CAAC;QACzG,OAAO,CAAC,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC;IAED;;;;OAIG;IACH,SAAS,CAAC,KAAgB,EAAE,SAAiB;QACzC,OAAO,CACH,gBAAgB,SAAS,QAAQ,KAAK,CAAC,KAAK,yBAAyB,IAAA,0BAAa,EAAC,KAAK,CAAC,SAAS,CAAC,MAAM;YACzG,QAAQ,IAAA,0BAAa,EAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,IAAA,0BAAa,EAAC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,EAAE,CACjG,CAAC;IACN,CAAC;IAED,oGAAoG;IACpG,WAAW,CAAC,KAAuB;QAC/B,OAAO,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAoB,EAAU,EAAE,CAAC,wBAAgB,CAAC,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC;IACnG,CAAC;IAED;;;OAGG;IACH,OAAO,CAAC,MAAgB;QACpB,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,8BAA8B,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC;QAC3E,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC7C,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,KAAa,EAAE,KAAa,EAAU,EAAE,CACjE,KAAK,KAAK,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,KAAK,IAAI,KAAK,EAAE,CAC5D,CAAC;QACF,OAAO,+BAA+B,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,qBAAa,GAAG,CAAC;IAC1F,CAAC;IAEO,SAAS,CAAC,KAAgB,EAAE,KAAa,EAAE,KAAa;QAC5D,OAAO,MAAM,IAAA,qBAAQ,EAAC,KAAK,CAAC,MAAM,CAAC,MAAM,KAAK,YAAY,KAAK,OAAO,CAAC;IAC3E,CAAC;IAED,8EAA8E;IACtE,KAAK,CAAC,KAAgB,EAAE,IAAY,EAAE,SAAiB,EAAE,KAAa,EAAE,KAAK,GAAG,QAAQ;QAC5F,OAAO,IAAI,CAAC,SAAS,CACjB,KAAK,EACL,UAAU,KAAK,iBAAiB,IAAI,iBAAiB,SAAS,IAAI,KAAK,EAAE,EACzE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,SAAS,CAAC,CACnC,CAAC;IACN,CAAC;IAED;;;OAGG;IACK,OAAO,CAAC,KAAgB,EAAE,OAAuB;QACrD,IAAI,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;YAC3B,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,kCAAiB,EAAE,iBAAS,EAAE,YAAY,oCAAmB,iBAAiB,EAAE,eAAe,CAAC,CAAC;QAC9H,IAAI,OAAO,CAAC,WAAW,CAAC,KAAK,CAAC,QAAQ,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,oCAAmB,EAAE,iBAAS,EAAE,EAAE,CAAC,CAAC;QACtG,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAe,EAAgB,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;QAC/F,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC5F,OAAO,IAAI,CAAC,SAAS,CACjB,KAAK,EACL,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAmB,EAAU,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,gBAAgB,iBAAS,GAAG,EACrG,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,iBAAS,CAAC,CACnC,CAAC;IACN,CAAC;IAED;;;;OAIG;IACK,OAAO,CAAC,KAAgB;QAC5B,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;QAC3C,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,wBAAgB,EAAE,iBAAS,EAAE,EAAE,CAAC,CAAC;QAClF,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAChB,OAAO,IAAI,CAAC,SAAS,CACjB,KAAK,EACL,GAAG,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,gBAAgB,iBAAS,GAAG,EACtD,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,iBAAS,CAAC,CACnC,CAAC;QACN,MAAM,IAAI,GAAG,wBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACxC,MAAM,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;QAC9D,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAClF,OAAO,IAAI,CAAC,SAAS,CACjB,KAAK,EACL,8BAA8B,IAAI,CAAC,KAAK,iBAAiB,KAAK,CAAC,SAAS,aAAa,EACrF,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAC9E,CAAC;IACN,CAAC;IAED;;;;OAIG;IACK,WAAW,CAAC,IAAY,EAAE,KAAqB,EAAE,IAAY;QACjE,OAAO,CACH,mEAAmE;YACnE,oBAAoB,IAAI,kBAAkB,QAAQ,IAAI;YACtD,iEAAiE,SAAS,cAAc,KAAK,CAAC,KAAK,oBAAoB;YACvH,WAAW,IAAI,sCAAsC,CACxD,CAAC;IACN,CAAC;CACJ;AAlMD,wCAkMC","sourcesContent":["/**\n * Graph color modes (#1155, restyled by #1158)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything. Every mode writes the SAME three lines on a box\n * (level + name, role, every framework tag); only the color changes:\n *\n * - RUNTIME \"where the code can run\". One runtime is a solid fill. One runtime plus a\n * specialization (angular/react inside browser, express inside node) is NESTED: an\n * outer frame in the base color around an inner rounded box in the specialization's\n * color, carrying the text. Several runtimes are full-height vertical stripes in the\n * fixed order browser | node | react-native, with the text written across them. The\n * dependency rule becomes visual: a box may depend on a box carrying every color it has.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what changed\" — on this branch, or in the last commit (graph-impact.ts).\n * Projects owning a changed file are solid amber (\"touched\"); the rest of nx's affected set is light amber (\"affected\": their ci\n * re-runs); every transitive dependency of the affected set that is not itself\n * affected is light slate with a dashed border (\"build input\": compiled or restored\n * from cache, unchanged); everything else is grey. Which box is which is only known\n * per checkout, so every box carries all four variants and the page picks one from the\n * sidecar (graph-impact.ts). The state is never written on the box: color + legend.\n * The words are the Filter's: changed · dependent · dependency (built, unchanged) ·\n * not in this build.\n * - PRODUCT \"which products it belongs to\" (#1179, graph-products.ts). One product is a solid\n * fill in that product's palette color; two or more (but not all) are the same\n * vertical stripes the multi-runtime box uses, one per product; a box shared by EVERY\n * product is one neutral fill (otherwise a shared foundation would be four-stripe\n * noise); a box in no product is white with a dashed border. The Filter HIDES other\n * products' boxes; this mode keeps every box and makes ownership visible.\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\nimport { PRODUCT_NONE_BORDER, PRODUCT_NONE_FILL, PRODUCT_SHARED_FILL, ProductColor, ProductPalette } from './graph-products';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n PRODUCT = 'product',\n}\n\n/** One selectable mode as the drawer's Color-by pulldown shows it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),\n new ModeInfo(GraphMode.PRODUCT, 'Product', 'which products it belongs to'),\n];\n\n/** Text on a light fill. */\nexport const DARK_TEXT = '#1a1c22';\n/** Text on a dark fill. */\nexport const LIGHT_TEXT = '#ffffff';\n\n/**\n * A framework value's color, the text color that reads on it, and the BASE runtime it is drawn\n * inside (itself, for a base).\n */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155). Every base is mid-light, so dark text reads on every stripe.\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#7d8cf2', DARK_TEXT, 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#6fcf8a', DARK_TEXT, 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#34b8a6',\n DARK_TEXT,\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#c8243f', LIGHT_TEXT, 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#7fd8ff', DARK_TEXT, 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#2e7d4f', LIGHT_TEXT, 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/**\n * The Filter popover's Runtime chips, and the order line 3 of a box lists framework tags in: the\n * bases first, then the specializations.\n */\nexport const FRAMEWORK_ORDER: readonly KnownFramework[] = [\n 'browser',\n 'node',\n 'react-native',\n 'angular',\n 'react',\n 'express',\n];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** The class Graphviz stamps on a multi-runtime box's `<g>`: its outline is its LAST shape. */\nexport const STRIPED_CLASS = 'wp-striped';\n\n/** Margin of base color around a specialization's inner box. */\nconst NEST_PAD = 5;\n/** Padding between the inner box's edge and its text. */\nconst INNER_PAD = 6;\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/**\n * Every role role-resolver knows, in legend order (runnable things first) — also the Filter popover's\n * Role chips. `bundle` is indigo so no box color equals the page's violet accent.\n */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', LIGHT_TEXT, 'runnable server'),\n new RoleStyle('client', '#e5533d', LIGHT_TEXT, 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', LIGHT_TEXT, 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#4b44c8', LIGHT_TEXT, 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', DARK_TEXT, 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', LIGHT_TEXT, 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', LIGHT_TEXT, 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', DARK_TEXT, 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_AFFECTED = '#fde3b0';\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\nconst IMPACT_UNTOUCHED_TEXT = '#6b7180';\n\n/** What a box's per-mode rendering — and the page's Filter — is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */\n public readonly products: string[],\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly affected: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n public readonly product: string,\n ) {}\n}\n\n/** How many drawn boxes one product holds, and how many of them no other product does. */\nexport class ProductCount {\n constructor(\n public readonly product: string,\n public total: number,\n public only: number,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */\n products: ProductCount[] = [];\n /** Boxes shared by every product (two or more declared). */\n sharedByAll = 0;\n /** Boxes in no product. */\n noProduct = 0;\n /** Some box belongs to two or more products, but not to every one: it is striped. */\n multiProduct = false;\n /** Some product is past the palette's eight and is painted with the neutral fill. */\n productOverflow = false;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts, palette: ProductPalette): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''),\n this.plain(facts, IMPACT_TOUCHED, DARK_TEXT, ''),\n this.plain(facts, IMPACT_AFFECTED, DARK_TEXT, `, color=\"${IMPACT_TOUCHED}\", penwidth=2`),\n this.plain(\n facts,\n IMPACT_BUILD_INPUT,\n DARK_TEXT,\n `, color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n 'filled,dashed',\n ),\n this.plain(facts, IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color=\"#b4b9c4\"'),\n this.product(facts, palette),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n this.recordProducts(legend, facts, palette);\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */\n private recordProducts(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n if (legend.products.length === 0 && palette.colors.length > 0) {\n legend.products = palette.names().map((product: string): ProductCount => new ProductCount(product, 0, 0));\n legend.productOverflow = palette.overflows();\n }\n if (facts.products.length === 0) legend.noProduct++;\n if (palette.sharedByAll(facts.products)) legend.sharedByAll++;\n else if (facts.products.length > 1) legend.multiProduct = true;\n for (const count of legend.products) {\n if (!facts.products.includes(count.product)) continue;\n count.total++;\n if (facts.products.length === 1) count.only++;\n }\n }\n\n /**\n * Line 3 of every box: every framework tag, bases before specializations, any tag outside the\n * shared vocabulary last (it still runs somewhere somebody declared).\n */\n frameworkLine(frameworks: string[]): string {\n if (frameworks.length === 0) return 'no framework tag';\n const known = FRAMEWORK_ORDER.filter((framework: KnownFramework): boolean =>\n frameworks.includes(framework),\n );\n const unknown = frameworks.filter((framework: string): boolean => this.styleOf(framework) === undefined);\n return [...known, ...unknown].join(' · ');\n }\n\n /**\n * The three lines, identical in every mode: `L3 name` (level dimmed, name bold), the role, and\n * the framework tags. The level is dimmed by giving its text color an alpha channel, which\n * Graphviz writes as `fill-opacity`, so it dims on any fill.\n */\n labelText(facts: NodeFacts, fontColor: string): string {\n return (\n `<FONT COLOR=\"${fontColor}99\">L${facts.level}</FONT>  <B>${htmlLabelText(facts.shortName)}</B>` +\n `<BR/>${htmlLabelText(facts.role)}<BR/>${htmlLabelText(this.frameworkLine(facts.frameworks))}`\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n return this.stripes(bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color));\n }\n\n /**\n * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product\n * box share. A single color is a plain fill.\n */\n stripes(colors: string[]): string {\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\", class=\"${STRIPED_CLASS}\"`;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, label=<${label}>];\\n`;\n }\n\n /** A solid fill, the three lines on it. `extra` carries border attributes. */\n private plain(facts: NodeFacts, fill: string, fontColor: string, extra: string, style = 'filled'): string {\n return this.statement(\n facts,\n `style=\"${style}\", fillcolor=\"${fill}\", fontcolor=\"${fontColor}\"${extra}`,\n this.labelText(facts, fontColor),\n );\n }\n\n /**\n * Product mode (#1179): one product solid in its color; several (not all) striped, one stripe per\n * product; every product the neutral shared fill; no product a white box with a dashed border.\n */\n private product(facts: NodeFacts, palette: ProductPalette): string {\n if (facts.products.length === 0)\n return this.plain(facts, PRODUCT_NONE_FILL, DARK_TEXT, `, color=\"${PRODUCT_NONE_BORDER}\", penwidth=1.5`, 'filled,dashed');\n if (palette.sharedByAll(facts.products)) return this.plain(facts, PRODUCT_SHARED_FILL, DARK_TEXT, '');\n const colors = facts.products.map((product: string): ProductColor => palette.colorOf(product));\n if (colors.length === 1) return this.plain(facts, colors[0].color, colors[0].fontColor, '');\n return this.statement(\n facts,\n `${this.stripes(colors.map((color: ProductColor): string => color.color))}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n }\n\n /**\n * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a\n * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a\n * specialization on such a box shows in line 3 only).\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n if (bases.length === 0) return this.plain(facts, NO_RUNTIME_COLOR, DARK_TEXT, '');\n if (bases.length > 1)\n return this.statement(\n facts,\n `${this.stripeAttrs(bases)}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n const base = FRAMEWORK_STYLES[bases[0]];\n const inner = this.specialization(bases[0], facts.frameworks);\n if (inner === undefined) return this.plain(facts, base.color, base.fontColor, '');\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${base.color}\", fontcolor=\"${inner.fontColor}\", margin=0`,\n this.nestedTable(base.color, inner, this.labelText(facts, inner.fontColor)),\n );\n }\n\n /**\n * An outer cell in the base color whose padding is the visible frame, holding an inner ROUNDED\n * table in the specialization's color that carries the text. The node's own polygon stays its\n * FIRST shape, so the hover glow and the lock outline land on the outer box only.\n */\n private nestedTable(base: string, inner: FrameworkStyle, text: string): string {\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR><TD BGCOLOR=\"${base}\" CELLPADDING=\"${NEST_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"${INNER_PAD}\" BGCOLOR=\"${inner.color}\" STYLE=\"rounded\">` +\n `<TR><TD>${text}</TD></TR></TABLE></TD></TR></TABLE>`\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-color-modes.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-color-modes.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Graph color modes (#1155, restyled by #1158)\n *\n * The architecture graph answers ONE question at a time instead of asking every box to encode two\n * (fill = framework AND border = role). Each box is emitted once per mode, so the page can switch\n * modes and re-render without regenerating anything. Every mode writes the SAME three lines on a box\n * (level + name, role, every framework tag); only the color changes:\n *\n * - RUNTIME \"where the code can run\". One runtime is a solid fill. One runtime plus a\n * specialization (angular/react inside browser, express inside node) is NESTED: an\n * outer frame in the base color around an inner rounded box in the specialization's\n * color, carrying the text. Several runtimes are full-height vertical stripes in the\n * fixed order browser | node | react-native, with the text written across them. The\n * dependency rule becomes visual: a box may depend on a box carrying every color it has.\n * - ARCHITECTURE \"servers · clients · APIs\". A solid fill by ROLE; framework color is not shown.\n * - IMPACT \"what changed\" — on this branch, or in the last commit (graph-impact.ts).\n * Projects owning a changed file are solid amber (\"touched\"); the rest of nx's affected set is light amber (\"affected\": their ci\n * re-runs); every transitive dependency of the affected set that is not itself\n * affected is light slate with a dashed border (\"build input\": compiled or restored\n * from cache, unchanged); everything else is grey. Which box is which is only known\n * per checkout, so every box carries all four variants and the page picks one from the\n * sidecar (graph-impact.ts). The state is never written on the box: color + legend.\n * The words are the Filter's: changed · dependent · dependency (built, unchanged) ·\n * not in this build.\n * - PRODUCT \"which products it belongs to\" (#1179, graph-products.ts). One product is a solid\n * fill in that product's palette color; two or more (but not all) are the same\n * vertical stripes the multi-runtime box uses, one per product; a box shared by EVERY\n * product is one neutral fill (otherwise a shared foundation would be four-stripe\n * noise); a box in no product is white with a dashed border. The Filter HIDES other\n * products' boxes; this mode keeps every box and makes ownership visible.\n */\n\nimport { KNOWN_FRAMEWORKS, KnownFramework } from '@webpieces/rules-sdk';\nimport { dotValue, htmlLabelText } from './dot-syntax';\nimport { PRODUCT_NONE_BORDER, PRODUCT_NONE_FILL, PRODUCT_SHARED_FILL, ProductColor, ProductPalette } from './graph-products';\n\nexport enum GraphMode {\n RUNTIME = 'runtime',\n ARCHITECTURE = 'architecture',\n IMPACT = 'impact',\n PRODUCT = 'product',\n}\n\n/** One selectable mode as the drawer's Color-by pulldown shows it. */\nexport class ModeInfo {\n constructor(\n public readonly mode: GraphMode,\n public readonly name: string,\n public readonly subtitle: string,\n ) {}\n}\n\n/** In key order: `1` selects the first, `2` the second, `3` the third, `4` the fourth. */\nexport const GRAPH_MODES: readonly ModeInfo[] = [\n new ModeInfo(GraphMode.RUNTIME, 'Runtime', 'where the code can run'),\n new ModeInfo(GraphMode.ARCHITECTURE, 'Architecture', 'servers · clients · APIs'),\n new ModeInfo(GraphMode.IMPACT, 'Impact', 'what changed'),\n new ModeInfo(GraphMode.PRODUCT, 'Product', 'which products it belongs to'),\n];\n\n/** Text on a light fill. */\nexport const DARK_TEXT = '#1a1c22';\n/** Text on a dark fill. */\nexport const LIGHT_TEXT = '#ffffff';\n\n/**\n * A framework value's color, the text color that reads on it, and the BASE runtime it is drawn\n * inside (itself, for a base).\n */\nexport class FrameworkStyle {\n constructor(\n public readonly framework: KnownFramework,\n public readonly base: KnownFramework,\n public readonly color: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n\n isBase(): boolean {\n return this.framework === this.base;\n }\n}\n\n/**\n * Keyed by the shared vocabulary, so a framework added to KNOWN_FRAMEWORKS without a color here is a\n * COMPILE error — not a box silently painted grey and missing from the legend, which is exactly how\n * react-native shipped (#1155). Every base is mid-light, so dark text reads on every stripe.\n */\nexport const FRAMEWORK_STYLES: Record<KnownFramework, FrameworkStyle> = {\n browser: new FrameworkStyle('browser', 'browser', '#7d8cf2', DARK_TEXT, 'browser base runtime'),\n node: new FrameworkStyle('node', 'node', '#6fcf8a', DARK_TEXT, 'node base runtime'),\n 'react-native': new FrameworkStyle(\n 'react-native',\n 'react-native',\n '#34b8a6',\n DARK_TEXT,\n 'its own runtime, not a browser',\n ),\n angular: new FrameworkStyle('angular', 'browser', '#c8243f', LIGHT_TEXT, 'inside browser'),\n react: new FrameworkStyle('react', 'browser', '#7fd8ff', DARK_TEXT, 'inside browser'),\n express: new FrameworkStyle('express', 'node', '#2e7d4f', LIGHT_TEXT, 'inside node'),\n};\n\n/** Stripe order, left to right. Fixed, so the same set always reads the same way. */\nexport const BASE_ORDER: readonly KnownFramework[] = ['browser', 'node', 'react-native'];\n\n/**\n * The Filter popover's Runtime chips, and the order line 3 of a box lists framework tags in: the\n * bases first, then the specializations.\n */\nexport const FRAMEWORK_ORDER: readonly KnownFramework[] = [\n 'browser',\n 'node',\n 'react-native',\n 'angular',\n 'react',\n 'express',\n];\n\n/** A box with no known framework tag. */\nexport const NO_RUNTIME_COLOR = '#e4e6ec';\n\n/** The class Graphviz stamps on a multi-runtime box's `<g>`: its outline is its LAST shape. */\nexport const STRIPED_CLASS = 'wp-striped';\n\n/** Margin of base color around a specialization's inner box. */\nconst NEST_PAD = 5;\n/** Padding between the inner box's edge and its text. */\nconst INNER_PAD = 6;\n\n/** A role's solid fill and the text color that stays readable on it. */\nexport class RoleStyle {\n constructor(\n public readonly role: string,\n public readonly fill: string,\n public readonly fontColor: string,\n public readonly description: string,\n ) {}\n}\n\n/**\n * Every role role-resolver knows, in legend order (runnable things first) — also the Filter popover's\n * Role chips. `bundle` is indigo so no box color equals the page's violet accent.\n */\nexport const ROLE_STYLES: readonly RoleStyle[] = [\n new RoleStyle('server', '#4caf50', LIGHT_TEXT, 'runnable server'),\n new RoleStyle('client', '#e5533d', LIGHT_TEXT, 'browser or mobile client app'),\n new RoleStyle('app', '#1e88e5', LIGHT_TEXT, 'runnable non-HTTP app'),\n new RoleStyle('bundle', '#4b44c8', LIGHT_TEXT, 'bundles several apps'),\n new RoleStyle('api-lib', '#f08a24', DARK_TEXT, 'contract + DTOs'),\n new RoleStyle('api-client', '#1a9aa6', LIGHT_TEXT, 'contract + SDK implementation'),\n new RoleStyle('designed-lib', '#7d8597', LIGHT_TEXT, 'library with a generated design'),\n new RoleStyle('lib', '#d7dae2', DARK_TEXT, 'plain library'),\n];\n\n/** The four impact shades (graph-impact.ts decides which box gets which). */\nexport const IMPACT_TOUCHED = '#f5a524';\nexport const IMPACT_AFFECTED = '#fde3b0';\nexport const IMPACT_BUILD_INPUT = '#e3e9f2';\nexport const IMPACT_BUILD_INPUT_BORDER = '#7c8aa3';\nexport const IMPACT_UNTOUCHED = '#eef0f4';\nconst IMPACT_UNTOUCHED_TEXT = '#6b7180';\n\n/** What a box's per-mode rendering — and the page's Filter — is computed from. */\nexport class NodeFacts {\n constructor(\n public readonly nodeId: string,\n public readonly shortName: string,\n public readonly level: number,\n public readonly role: string,\n public readonly frameworks: string[],\n /** The products the project belongs to (dependencies.json `products`), sorted; empty for none. */\n public readonly products: string[],\n public readonly summary: string = '',\n ) {}\n}\n\n/** One box's DOT statement for every mode (and for each impact state). */\nexport class NodeModeDots {\n constructor(\n public readonly runtime: string,\n public readonly architecture: string,\n public readonly touched: string,\n public readonly affected: string,\n public readonly buildInput: string,\n public readonly untouched: string,\n public readonly product: string,\n ) {}\n}\n\n/** How many drawn boxes one product holds, and how many of them no other product does. */\nexport class ProductCount {\n constructor(\n public readonly product: string,\n public total: number,\n public only: number,\n ) {}\n}\n\n/** Which colors and roles the drawn projects actually use, so the legend lists exactly those. */\nexport class LegendFacts {\n frameworks: string[] = [];\n roles: string[] = [];\n multiRuntime = false;\n noRuntime = false;\n /** One count per declared product, in palette (sorted-name) order; empty when none is declared. */\n products: ProductCount[] = [];\n /** Boxes shared by every product (two or more declared). */\n sharedByAll = 0;\n /** Boxes in no product. */\n noProduct = 0;\n /** Some box belongs to two or more products, but not to every one: it is striped. */\n multiProduct = false;\n /** Some product is past the palette's eight and is painted with the neutral fill. */\n productOverflow = false;\n}\n\nexport class NodeModeStyler {\n styleOf(framework: string): FrameworkStyle | undefined {\n if (!(KNOWN_FRAMEWORKS as readonly string[]).includes(framework)) return undefined;\n return FRAMEWORK_STYLES[framework as KnownFramework];\n }\n\n roleStyle(role: string): RoleStyle {\n const style = ROLE_STYLES.find((candidate: RoleStyle): boolean => candidate.role === role);\n return style ?? (ROLE_STYLES.find((s: RoleStyle): boolean => s.role === 'lib') as RoleStyle);\n }\n\n /** Every role the renderer styles; a spec asserts this covers role-resolver's KNOWN_ROLES. */\n styledRoles(): string[] {\n return ROLE_STYLES.map((style: RoleStyle): string => style.role);\n }\n\n /** The base runtimes of a framework set, in stripe order (a specialization implies its base). */\n bases(frameworks: string[]): KnownFramework[] {\n const present = new Set<KnownFramework>();\n for (const framework of frameworks) {\n const style = this.styleOf(framework);\n if (style !== undefined) present.add(style.base);\n }\n return BASE_ORDER.filter((base: KnownFramework): boolean => present.has(base));\n }\n\n /** The specialization drawn inside `base`, if the set carries one. */\n specialization(base: KnownFramework, frameworks: string[]): FrameworkStyle | undefined {\n for (const framework of KNOWN_FRAMEWORKS) {\n const style = FRAMEWORK_STYLES[framework];\n if (style.isBase() || style.base !== base) continue;\n if (frameworks.includes(framework)) return style;\n }\n return undefined;\n }\n\n dots(facts: NodeFacts, palette: ProductPalette): NodeModeDots {\n return new NodeModeDots(\n this.runtime(facts),\n this.plain(facts, this.roleStyle(facts.role).fill, this.roleStyle(facts.role).fontColor, ''),\n this.plain(facts, IMPACT_TOUCHED, DARK_TEXT, ''),\n this.plain(facts, IMPACT_AFFECTED, DARK_TEXT, `, color=\"${IMPACT_TOUCHED}\", penwidth=2`),\n this.plain(\n facts,\n IMPACT_BUILD_INPUT,\n DARK_TEXT,\n `, color=\"${IMPACT_BUILD_INPUT_BORDER}\", penwidth=1.5`,\n 'filled,dashed',\n ),\n this.plain(facts, IMPACT_UNTOUCHED, IMPACT_UNTOUCHED_TEXT, ', color=\"#b4b9c4\"'),\n this.product(facts, palette),\n );\n }\n\n /** Fold one drawn box into the legend facts. */\n record(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n this.recordProducts(legend, facts, palette);\n for (const framework of facts.frameworks) {\n if (this.styleOf(framework) !== undefined && !legend.frameworks.includes(framework))\n legend.frameworks.push(framework);\n }\n const role = this.roleStyle(facts.role).role;\n if (!legend.roles.includes(role)) legend.roles.push(role);\n const bases = this.bases(facts.frameworks);\n if (bases.length > 1) legend.multiRuntime = true;\n if (bases.length === 0) legend.noRuntime = true;\n }\n\n /** The Product section's counts: per product (total, and only-this-product), shared-by-all, none. */\n private recordProducts(legend: LegendFacts, facts: NodeFacts, palette: ProductPalette): void {\n if (legend.products.length === 0 && palette.colors.length > 0) {\n legend.products = palette.names().map((product: string): ProductCount => new ProductCount(product, 0, 0));\n legend.productOverflow = palette.overflows();\n }\n if (facts.products.length === 0) legend.noProduct++;\n if (palette.sharedByAll(facts.products)) legend.sharedByAll++;\n else if (facts.products.length > 1) legend.multiProduct = true;\n for (const count of legend.products) {\n if (!facts.products.includes(count.product)) continue;\n count.total++;\n if (facts.products.length === 1) count.only++;\n }\n }\n\n /**\n * Line 3 of every box: every framework tag, bases before specializations, any tag outside the\n * shared vocabulary last (it still runs somewhere somebody declared).\n */\n frameworkLine(frameworks: string[]): string {\n if (frameworks.length === 0) return 'no framework tag';\n const known = FRAMEWORK_ORDER.filter((framework: KnownFramework): boolean =>\n frameworks.includes(framework),\n );\n const unknown = frameworks.filter((framework: string): boolean => this.styleOf(framework) === undefined);\n return [...known, ...unknown].join(' · ');\n }\n\n /**\n * The three lines, identical in every mode: `L3 name` (level dimmed, name bold), the role, and\n * the framework tags. The level is dimmed by giving its text color an alpha channel, which\n * Graphviz writes as `fill-opacity`, so it dims on any fill.\n */\n labelText(facts: NodeFacts, fontColor: string): string {\n return (\n `<FONT COLOR=\"${fontColor}99\">L${facts.level}</FONT>  <B>${htmlLabelText(facts.shortName)}</B>` +\n `<BR/>${htmlLabelText(facts.role)}<BR/>${htmlLabelText(this.frameworkLine(facts.frameworks))}` +\n (facts.summary === '' ? '' : `<BR/>${htmlLabelText(facts.summary)}`)\n );\n }\n\n /** `filled` for one runtime; equal `striped` weights for several (the last takes the remainder). */\n stripeAttrs(bases: KnownFramework[]): string {\n return this.stripes(bases.map((base: KnownFramework): string => FRAMEWORK_STYLES[base].color));\n }\n\n /**\n * One equal vertical stripe per color — the renderer the multi-runtime box and the multi-product\n * box share. A single color is a plain fill.\n */\n stripes(colors: string[]): string {\n if (colors.length === 1) return `style=\"filled\", fillcolor=\"${colors[0]}\"`;\n const share = (1 / colors.length).toFixed(3);\n const weighted = colors.map((color: string, index: number): string =>\n index === colors.length - 1 ? color : `${color};${share}`,\n );\n return `style=\"striped\", fillcolor=\"${weighted.join(':')}\", class=\"${STRIPED_CLASS}\"`;\n }\n\n private statement(facts: NodeFacts, attrs: string, label: string): string {\n return ` \"${dotValue(facts.nodeId)}\" [${attrs}, label=<${label}>];\\n`;\n }\n\n /** A solid fill, the three lines on it. `extra` carries border attributes. */\n private plain(facts: NodeFacts, fill: string, fontColor: string, extra: string, style = 'filled'): string {\n return this.statement(\n facts,\n `style=\"${style}\", fillcolor=\"${fill}\", fontcolor=\"${fontColor}\"${extra}`,\n this.labelText(facts, fontColor),\n );\n }\n\n /**\n * Product mode (#1179): one product solid in its color; several (not all) striped, one stripe per\n * product; every product the neutral shared fill; no product a white box with a dashed border.\n */\n private product(facts: NodeFacts, palette: ProductPalette): string {\n if (facts.products.length === 0)\n return this.plain(facts, PRODUCT_NONE_FILL, DARK_TEXT, `, color=\"${PRODUCT_NONE_BORDER}\", penwidth=1.5`, 'filled,dashed');\n if (palette.sharedByAll(facts.products)) return this.plain(facts, PRODUCT_SHARED_FILL, DARK_TEXT, '');\n const colors = facts.products.map((product: string): ProductColor => palette.colorOf(product));\n if (colors.length === 1) return this.plain(facts, colors[0].color, colors[0].fontColor, '');\n return this.statement(\n facts,\n `${this.stripes(colors.map((color: ProductColor): string => color.color))}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n }\n\n /**\n * Runtime mode. No runtime, or one runtime with no specialization: a solid fill. One runtime with a\n * specialization: NESTED. Several runtimes: full-height stripes with the text across them (a\n * specialization on such a box shows in line 3 only).\n */\n private runtime(facts: NodeFacts): string {\n const bases = this.bases(facts.frameworks);\n if (bases.length === 0) return this.plain(facts, NO_RUNTIME_COLOR, DARK_TEXT, '');\n if (bases.length > 1)\n return this.statement(\n facts,\n `${this.stripeAttrs(bases)}, fontcolor=\"${DARK_TEXT}\"`,\n this.labelText(facts, DARK_TEXT),\n );\n const base = FRAMEWORK_STYLES[bases[0]];\n const inner = this.specialization(bases[0], facts.frameworks);\n if (inner === undefined) return this.plain(facts, base.color, base.fontColor, '');\n return this.statement(\n facts,\n `style=\"filled\", fillcolor=\"${base.color}\", fontcolor=\"${inner.fontColor}\", margin=0`,\n this.nestedTable(base.color, inner, this.labelText(facts, inner.fontColor)),\n );\n }\n\n /**\n * An outer cell in the base color whose padding is the visible frame, holding an inner ROUNDED\n * table in the specialization's color that carries the text. The node's own polygon stays its\n * FIRST shape, so the hover glow and the lock outline land on the outer box only.\n */\n private nestedTable(base: string, inner: FrameworkStyle, text: string): string {\n return (\n '<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">' +\n `<TR><TD BGCOLOR=\"${base}\" CELLPADDING=\"${NEST_PAD}\">` +\n `<TABLE BORDER=\"0\" CELLBORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"${INNER_PAD}\" BGCOLOR=\"${inner.color}\" STYLE=\"rounded\">` +\n `<TR><TD>${text}</TD></TR></TABLE></TD></TR></TABLE>`\n );\n }\n}\n"]}
|