@webpieces/nx-webpieces-rules 0.4.853 → 0.4.855
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 +14 -0
- package/executors.json +5 -0
- package/package.json +7 -7
- package/src/executors/generate/executor.js +31 -55
- package/src/executors/generate/executor.js.map +1 -1
- package/src/executors/runtime-wiring-check/executor.d.ts +10 -0
- package/src/executors/runtime-wiring-check/executor.js +42 -0
- package/src/executors/runtime-wiring-check/executor.js.map +1 -0
- package/src/executors/runtime-wiring-check/schema.json +14 -0
- package/src/executors/validate-api-lib-tag/executor.js +5 -3
- package/src/executors/validate-api-lib-tag/executor.js.map +1 -1
- package/src/executors/validate-api-relations/executor.js +11 -8
- package/src/executors/validate-api-relations/executor.js.map +1 -1
- package/src/executors/validate-architecture-unchanged/executor.d.ts +1 -1
- package/src/executors/validate-architecture-unchanged/executor.js +22 -24
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/index.d.ts +1 -0
- package/src/index.js +5 -1
- package/src/index.js.map +1 -1
- package/src/lib/api-usage/api-contract-approval.d.ts +7 -0
- package/src/lib/api-usage/api-contract-approval.js +50 -0
- package/src/lib/api-usage/api-contract-approval.js.map +1 -0
- package/src/lib/api-usage/api-contract-evidence.d.ts +6 -0
- package/src/lib/api-usage/api-contract-evidence.js +41 -0
- package/src/lib/api-usage/api-contract-evidence.js.map +1 -0
- package/src/lib/api-usage/api-relations-validator.d.ts +13 -2
- package/src/lib/api-usage/api-relations-validator.js +23 -0
- package/src/lib/api-usage/api-relations-validator.js.map +1 -1
- package/src/lib/api-usage/api-relations.d.ts +3 -0
- package/src/lib/api-usage/api-relations.js.map +1 -1
- package/src/lib/api-usage/api-scanner.d.ts +40 -0
- package/src/lib/api-usage/api-scanner.js +7 -1
- package/src/lib/api-usage/api-scanner.js.map +1 -1
- package/src/lib/graph-comparator.js +11 -1
- package/src/lib/graph-comparator.js.map +1 -1
- package/src/lib/graph-filter.client.d.ts +40 -0
- package/src/lib/graph-filter.client.js +190 -0
- package/src/lib/graph-filter.client.js.map +1 -0
- package/src/lib/graph-level-bands.d.ts +0 -18
- package/src/lib/graph-level-bands.js +4 -3
- package/src/lib/graph-level-bands.js.map +1 -1
- package/src/lib/graph-loader.js +12 -6
- package/src/lib/graph-loader.js.map +1 -1
- package/src/lib/graph-navigation.d.ts +5 -0
- package/src/lib/graph-navigation.js +102 -0
- package/src/lib/graph-navigation.js.map +1 -0
- package/src/lib/graph-node-menu.d.ts +1 -0
- package/src/lib/graph-node-menu.js +29 -0
- package/src/lib/graph-node-menu.js.map +1 -1
- package/src/lib/graph-render-model.d.ts +32 -0
- package/src/lib/graph-render-model.js +76 -0
- package/src/lib/graph-render-model.js.map +1 -0
- package/src/lib/graph-sorter.d.ts +2 -0
- package/src/lib/graph-sorter.js.map +1 -1
- package/src/lib/graph-visualizer.client.d.ts +14 -73
- package/src/lib/graph-visualizer.client.js +71 -243
- package/src/lib/graph-visualizer.client.js.map +1 -1
- package/src/lib/graph-visualizer.d.ts +8 -15
- package/src/lib/graph-visualizer.js +48 -26
- package/src/lib/graph-visualizer.js.map +1 -1
- package/src/lib/runtime-details.d.ts +13 -0
- package/src/lib/runtime-details.js +191 -0
- package/src/lib/runtime-details.js.map +1 -0
- package/src/lib/runtime-graph-model.d.ts +4 -1
- package/src/lib/runtime-graph-model.js.map +1 -1
- package/src/lib/runtime-graph.js +10 -4
- package/src/lib/runtime-graph.js.map +1 -1
- package/src/lib/runtime-html-page.d.ts +7 -3
- package/src/lib/runtime-html-page.js +19 -6
- package/src/lib/runtime-html-page.js.map +1 -1
- package/src/lib/runtime-visualizer.client.d.ts +8 -19
- package/src/lib/runtime-visualizer.client.js +28 -41
- package/src/lib/runtime-visualizer.client.js.map +1 -1
- package/src/lib/runtime-visualizer.d.ts +3 -45
- package/src/lib/runtime-visualizer.js +77 -79
- package/src/lib/runtime-visualizer.js.map +1 -1
- package/src/lib/runtime-viz-theme.js +4 -3
- package/src/lib/runtime-viz-theme.js.map +1 -1
- package/src/lib/runtime-wiring/DeclarationCompileAssertions.d.ts +4 -0
- package/src/lib/runtime-wiring/DeclarationCompileAssertions.js +27 -0
- package/src/lib/runtime-wiring/DeclarationCompileAssertions.js.map +1 -0
- package/src/lib/runtime-wiring/approved-graph.d.ts +8 -0
- package/src/lib/runtime-wiring/approved-graph.js +115 -0
- package/src/lib/runtime-wiring/approved-graph.js.map +1 -0
- package/src/lib/runtime-wiring/assembler.d.ts +24 -0
- package/src/lib/runtime-wiring/assembler.js +116 -0
- package/src/lib/runtime-wiring/assembler.js.map +1 -0
- package/src/lib/runtime-wiring/codec.d.ts +14 -0
- package/src/lib/runtime-wiring/codec.js +162 -0
- package/src/lib/runtime-wiring/codec.js.map +1 -0
- package/src/lib/runtime-wiring/declaration.d.ts +68 -0
- package/src/lib/runtime-wiring/declaration.js +88 -0
- package/src/lib/runtime-wiring/declaration.js.map +1 -0
- package/src/lib/runtime-wiring/source-extractor.d.ts +28 -0
- package/src/lib/runtime-wiring/source-extractor.js +253 -0
- package/src/lib/runtime-wiring/source-extractor.js.map +1 -0
- package/src/lib/runtime-wiring/source-values.d.ts +21 -0
- package/src/lib/runtime-wiring/source-values.js +204 -0
- package/src/lib/runtime-wiring/source-values.js.map +1 -0
- package/src/lib/runtime-wiring/verification.d.ts +9 -0
- package/src/lib/runtime-wiring/verification.js +56 -0
- package/src/lib/runtime-wiring/verification.js.map +1 -0
- package/src/plugin.d.ts +0 -21
- package/src/plugin.js +12 -7
- package/src/plugin.js.map +1 -1
- package/src/runtime-wiring-targets.d.ts +7 -0
- package/src/runtime-wiring-targets.js +58 -0
- package/src/runtime-wiring-targets.js.map +1 -0
- package/src/validation-targets.js +10 -2
- package/src/validation-targets.js.map +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-navigation.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-navigation.ts"],"names":[],"mappings":"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","sourcesContent":["/** Shared local-file navigation. Reset restores readable scale; Fit is an explicit overview. */\nexport class GraphNavigation {\n styles(): string {\n return `\n #graph { overflow: auto; max-height: 75vh; text-align: left; touch-action: pan-x pan-y; }\n #graph svg { max-width: none; max-height: none; display: block; }\n .wp-navigation { display: flex; gap: 8px; align-items: center; margin: 10px 0; }\n .wp-navigation button { padding: 6px 12px; cursor: pointer; }\n .wp-graph-details { position: fixed; z-index: 1100; box-sizing: border-box; max-width: min(520px, calc(100vw - 24px)); max-height: min(480px, calc(100vh - 24px)); overflow: auto; background: white; padding: 12px; border: 1px solid #aaa; border-radius: 6px; box-shadow: 0 4px 16px #0003; font: 14px/1.5 Arial, sans-serif; overflow-wrap: anywhere; }\n .wp-graph-details li { margin-bottom: 8px; }\n .wp-api-detail { cursor: pointer; text-decoration: underline; }\n .wp-api-detail:focus { outline: 2px solid #1565c0; }\n `;\n }\n\n script(): string {\n return `new (${GraphNavigationController.toString()})();`;\n }\n}\n\n/** Serialized browser controller; dependencies are native DOM APIs only. */\nclass GraphNavigationController {\n private readonly host = document.getElementById('graph');\n private readonly toolbar = document.createElement('nav');\n private readonly status = document.createElement('output');\n private svg: SVGSVGElement | null = null;\n private scale = 1;\n\n constructor() {\n if (this.host === null) return;\n this.toolbar.className = 'wp-navigation';\n this.toolbar.setAttribute('aria-label', 'Graph navigation');\n this.host.before(this.toolbar);\n this.button('Zoom in', () => this.zoom(1.25));\n this.button('Zoom out', () => this.zoom(0.8));\n this.button('Readable reset', () => this.reset());\n this.button('Fit overview', () => this.fit());\n this.toolbar.append(this.status);\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n window.addEventListener('resize', () => this.changed());\n this.host.addEventListener('scroll', () => this.changed());\n }\n\n private button(label: string, action: () => void): void {\n const button = document.createElement('button');\n button.type = 'button';\n button.textContent = label;\n button.addEventListener('click', action);\n this.toolbar.append(button);\n }\n\n private changed(): void {\n document.dispatchEvent(new Event('wp-graph-transform'));\n }\n\n private resize(): void {\n if (this.svg === null) return;\n const box = this.svg.viewBox.baseVal;\n this.svg.style.width = `${box.width * this.scale}px`;\n this.svg.style.height = `${box.height * this.scale}px`;\n this.status.textContent = `${Math.round(this.scale * 100)}%`;\n this.changed();\n }\n\n private zoom(factor: number): void {\n if (this.host === null) return;\n const before = this.scale;\n this.scale = Math.max(0.1, Math.min(4, this.scale * factor));\n const centerX = this.host.scrollLeft + this.host.clientWidth / 2;\n const centerY = this.host.scrollTop + this.host.clientHeight / 2;\n this.resize();\n this.host.scrollLeft = (centerX * this.scale) / before - this.host.clientWidth / 2;\n this.host.scrollTop = (centerY * this.scale) / before - this.host.clientHeight / 2;\n }\n\n private reset(): void {\n this.scale = 1;\n this.resize();\n this.scrollOrigin();\n }\n\n private scrollOrigin(): void {\n if (this.host === null) return;\n this.host.scrollLeft = 0;\n this.host.scrollTop = 0;\n }\n\n private fit(): void {\n if (this.svg === null || this.host === null) return;\n this.scale = Math.min(\n 1,\n this.host.clientWidth / this.svg.viewBox.baseVal.width,\n this.host.clientHeight / this.svg.viewBox.baseVal.height,\n );\n this.resize();\n this.scrollOrigin();\n }\n\n private attach(): void {\n const next = this.host?.querySelector('svg');\n if (next === null || next === undefined || next === this.svg) return;\n this.svg = next;\n this.reset();\n }\n}\n"]}
|
|
@@ -45,6 +45,7 @@ export declare class GraphNodeMenu {
|
|
|
45
45
|
* menu. Opening a menu closes any other, so at most one is ever on screen.
|
|
46
46
|
*/
|
|
47
47
|
private menuScript;
|
|
48
|
+
private keyboardScript;
|
|
48
49
|
/**
|
|
49
50
|
* The lock the DESIGN pages and the RUNTIME page use: neither has a dropdown or a responsibilities
|
|
50
51
|
* list, so locking a box dims every other box in that graph and lights the locked one.
|
|
@@ -151,13 +151,18 @@ class GraphNodeMenu {
|
|
|
151
151
|
menu.style.left = left + 'px';
|
|
152
152
|
menu.style.top = (box.bottom + window.scrollY + 4) + 'px';
|
|
153
153
|
}
|
|
154
|
+
${this.keyboardScript()}
|
|
154
155
|
static wire(svg, itemsFor) {
|
|
155
156
|
svg.querySelectorAll('g.node').forEach(function (node) {
|
|
156
157
|
const title = node.querySelector('title');
|
|
157
158
|
const name = title === null || title.textContent === null
|
|
158
159
|
? '' : title.textContent.trim();
|
|
159
160
|
if (name === '') return;
|
|
161
|
+
if (node.classList.contains('wp-layout')) return;
|
|
160
162
|
node.classList.add('wp-node-clickable');
|
|
163
|
+
node.setAttribute('tabindex', '0');
|
|
164
|
+
node.setAttribute('role', 'button');
|
|
165
|
+
WpNodeMenu.wireKeyboard(node, name, itemsFor);
|
|
161
166
|
node.addEventListener('click', function (ev) {
|
|
162
167
|
ev.preventDefault();
|
|
163
168
|
ev.stopPropagation();
|
|
@@ -167,6 +172,17 @@ class GraphNodeMenu {
|
|
|
167
172
|
}
|
|
168
173
|
}`;
|
|
169
174
|
}
|
|
175
|
+
keyboardScript() {
|
|
176
|
+
return `static wireKeyboard(node, name, itemsFor) {
|
|
177
|
+
node.addEventListener('keydown', function (ev) {
|
|
178
|
+
if (ev.key !== 'Enter' && ev.key !== ' ') return;
|
|
179
|
+
ev.preventDefault();
|
|
180
|
+
ev.stopPropagation();
|
|
181
|
+
WpNodeMenu.open(node, name, itemsFor(name, node));
|
|
182
|
+
document.querySelector('#wp-node-menu button')?.focus();
|
|
183
|
+
});
|
|
184
|
+
}`;
|
|
185
|
+
}
|
|
170
186
|
/**
|
|
171
187
|
* The lock the DESIGN pages and the RUNTIME page use: neither has a dropdown or a responsibilities
|
|
172
188
|
* list, so locking a box dims every other box in that graph and lights the locked one.
|
|
@@ -175,6 +191,19 @@ class GraphNodeMenu {
|
|
|
175
191
|
return `
|
|
176
192
|
class WpNodeLock {
|
|
177
193
|
constructor(svg) { this.svg = svg; this.locked = null; }
|
|
194
|
+
rebind(svg) {
|
|
195
|
+
const name = this.locked;
|
|
196
|
+
this.svg = svg;
|
|
197
|
+
this.clear();
|
|
198
|
+
this.locked = name;
|
|
199
|
+
if (name === null) return;
|
|
200
|
+
for (const node of svg.querySelectorAll('g.node')) {
|
|
201
|
+
if (node.querySelector('title')?.textContent === name) {
|
|
202
|
+
this.lock(name, node);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
178
207
|
isLocked(name) { return this.locked === name; }
|
|
179
208
|
toggle(name, nodeEl) {
|
|
180
209
|
if (this.locked === name) this.clear();
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-node-menu.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-node-menu.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,aAAa;IACtB;;;;OAIG;IACH,MAAM;QACF,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8CL,CAAC;IACP,CAAC;IAED;;;;;;OAMG;IACH,SAAS,CAAC,KAAa;QACnB,OAAO;UACL,KAAK,WAAW,KAAK;UACrB,KAAK;UACL,KAAK;UACL,KAAK;UACL,KAAK;UACL,KAAK,yCAAyC,CAAC;IACrD,CAAC;IAED,+FAA+F;IAC/F,MAAM;QACF,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;IACjF,CAAC;IAED;;;;OAIG;IACK,UAAU;QACd,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA2DL,CAAC;IACP,CAAC;IAED;;;OAGG;IACK,UAAU;QACd,OAAO;;;;;;;;;;;;;;;;;;;;;UAqBL,CAAC;IACP,CAAC;IAED,2EAA2E;IACnE,aAAa;QACjB,OAAO;;;;YAIH,CAAC;IACT,CAAC;CACJ;AAvLD,sCAuLC","sourcesContent":["/**\n * The floating per-node menu, shared by ALL THREE generated graphs.\n *\n * There is exactly ONE implementation of the menu — its CSS, its positioning, its dismissal and the\n * click wiring that opens it — and `architecture/dependencies.html` (graph-visualizer.ts), every\n * project's `design.html` (di-graph/design-visualizer.ts) and `tmp/webpieces/runtime-architecture.html`\n * (runtime-visualizer.ts) all inline these same bytes. Duplicating it into the three emitters is what\n * this module exists to prevent.\n *\n * Why the browser code is emitted as a STRING here rather than living in a compiled `*.client.ts`\n * beside its emitter: `generateDesignHTML` is called straight from a source checkout (by the\n * di-graph-generate executor's unit tests) and has no injection seam for the client text, so a\n * `readCompiledClient` sibling would make the design page unbuildable from source. A string keeps one\n * copy reachable by every emitter with no build-order coupling. The two pages that DO have a compiled\n * client (`graph-visualizer.client.ts`, `runtime-visualizer.client.ts`) call into these classes as\n * ambient globals — see viz-client-globals.d.ts.\n *\n * What the menu does NOT own is the LOCK BEHAVIOUR, because the pages genuinely differ:\n * - the architecture page already has a `#wp-lock` dropdown backed by GraphHighlighter (chain\n * highlight + responsibilities filter), and its menu item drives that, so the two stay in sync;\n * - a design page and the runtime page have neither dropdown nor responsibilities, so they use\n * `WpNodeLock` below — dim every other box in that graph, light the locked one.\n * All three spell the dim with the SAME class names and the SAME CSS from `dimStyles()`.\n */\nexport class GraphNodeMenu {\n /**\n * Menu chrome + the \"this box is clickable\" affordance. Every node is clickable now (the menu\n * replaced direct navigation), so the cursor/glow is on every box rather than only the ones with\n * a design page.\n */\n styles(): string {\n return `\n .wp-node-menu {\n position: absolute;\n z-index: 1000;\n min-width: 150px;\n padding: 4px 0;\n background: white;\n border: 1px solid #cfcfcf;\n border-radius: 6px;\n box-shadow: 0 4px 14px rgba(0,0,0,0.22);\n font-family: Arial, sans-serif;\n font-size: 14px;\n }\n .wp-node-menu-title {\n padding: 4px 14px 6px;\n color: #777;\n font-size: 12px;\n font-family: monospace;\n border-bottom: 1px solid #eee;\n margin-bottom: 4px;\n }\n .wp-node-menu-item {\n display: block;\n width: 100%;\n padding: 7px 14px;\n border: 0;\n background: none;\n color: #1565C0;\n font: inherit;\n text-align: left;\n cursor: pointer;\n }\n .wp-node-menu-item:hover { background: #E3F2FD; }\n g.wp-node-clickable { cursor: pointer; }\n /* Every shape Graphviz (or the runtime page's own queue-cylinder redraw) can emit for a node\n * body: box/record -> polygon, circle -> ellipse, cylinder and the redrawn queue -> path. A\n * shape left out of this list would be a box that is clickable but never looks it. */\n g.wp-node-clickable polygon,\n g.wp-node-clickable ellipse,\n g.wp-node-clickable path { transition: stroke-width 0.12s ease, filter 0.12s ease; }\n g.wp-node-clickable:hover polygon,\n g.wp-node-clickable:hover ellipse,\n g.wp-node-clickable:hover path {\n stroke: #1976d2;\n stroke-width: 5;\n filter: drop-shadow(0 0 6px rgba(25, 118, 210, 0.85));\n }`;\n }\n\n /**\n * The dim/undim rules a lock (or a hover) toggles, scoped to whatever element holds the rendered\n * SVG on this page (`#graph` on the architecture page, `.graph` on a design page).\n *\n * We ONLY dim: the lit subgraph keeps its exact normal look. The un-dim rules repeat\n * `svg.wp-dim` so they out-specify the dim rule, which carries an extra type selector.\n */\n dimStyles(scope: string): string {\n return `\n ${scope} .node, ${scope} .edge { transition: opacity 0.12s ease; }\n ${scope} svg.wp-dim .node,\n ${scope} svg.wp-dim .edge { opacity: 0.15; }\n ${scope} svg.wp-dim .node.wp-focus,\n ${scope} svg.wp-dim .node.wp-neighbor,\n ${scope} svg.wp-dim .edge.wp-hl { opacity: 1; }`;\n }\n\n /** The whole browser side: the menu, the design-page lock, and the global dismiss handlers. */\n script(): string {\n return `${this.menuScript()}\\n${this.lockScript()}\\n${this.dismissScript()}`;\n }\n\n /**\n * `WpNodeMenuItem` + `WpNodeMenu`. Anchored at the node's own bounding box (bottom-left corner,\n * 4px below it) and clamped so a box at the right edge of a very wide graph still shows its whole\n * menu. Opening a menu closes any other, so at most one is ever on screen.\n */\n private menuScript(): string {\n return `\n class WpNodeMenuItem {\n constructor(label, onSelect) { this.label = label; this.onSelect = onSelect; }\n }\n class WpNodeMenu {\n static close() {\n const open = document.getElementById('wp-node-menu');\n if (open !== null) open.remove();\n }\n static open(nodeEl, name, items) {\n WpNodeMenu.close();\n const menu = document.createElement('div');\n menu.id = 'wp-node-menu';\n menu.className = 'wp-node-menu';\n menu.addEventListener('click', function (ev) { ev.stopPropagation(); });\n const heading = document.createElement('div');\n heading.className = 'wp-node-menu-title';\n heading.textContent = name;\n menu.appendChild(heading);\n for (const item of items) menu.appendChild(WpNodeMenu.button(item));\n document.body.appendChild(menu);\n WpNodeMenu.place(menu, nodeEl);\n }\n static button(item) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'wp-node-menu-item';\n button.textContent = item.label;\n button.addEventListener('click', function (ev) {\n ev.preventDefault();\n ev.stopPropagation();\n WpNodeMenu.close();\n item.onSelect();\n });\n return button;\n }\n static place(menu, nodeEl) {\n const box = nodeEl.getBoundingClientRect();\n const own = menu.getBoundingClientRect();\n const maxLeft = window.scrollX + document.documentElement.clientWidth - own.width - 8;\n let left = box.left + window.scrollX;\n if (left > maxLeft) left = Math.max(window.scrollX + 8, maxLeft);\n menu.style.left = left + 'px';\n menu.style.top = (box.bottom + window.scrollY + 4) + 'px';\n }\n static wire(svg, itemsFor) {\n svg.querySelectorAll('g.node').forEach(function (node) {\n const title = node.querySelector('title');\n const name = title === null || title.textContent === null\n ? '' : title.textContent.trim();\n if (name === '') return;\n node.classList.add('wp-node-clickable');\n node.addEventListener('click', function (ev) {\n ev.preventDefault();\n ev.stopPropagation();\n WpNodeMenu.open(node, name, itemsFor(name, node));\n });\n });\n }\n }`;\n }\n\n /**\n * The lock the DESIGN pages and the RUNTIME page use: neither has a dropdown or a responsibilities\n * list, so locking a box dims every other box in that graph and lights the locked one.\n */\n private lockScript(): string {\n return `\n class WpNodeLock {\n constructor(svg) { this.svg = svg; this.locked = null; }\n isLocked(name) { return this.locked === name; }\n toggle(name, nodeEl) {\n if (this.locked === name) this.clear();\n else this.lock(name, nodeEl);\n }\n lock(name, nodeEl) {\n this.clear();\n this.locked = name;\n this.svg.classList.add('wp-dim');\n nodeEl.classList.add('wp-focus');\n }\n clear() {\n this.locked = null;\n this.svg.classList.remove('wp-dim');\n this.svg.querySelectorAll('.wp-focus').forEach(function (el) {\n el.classList.remove('wp-focus');\n });\n }\n }`;\n }\n\n /** Outside click and Escape both dismiss — wired once, on the document. */\n private dismissScript(): string {\n return `\n document.addEventListener('click', function () { WpNodeMenu.close(); });\n document.addEventListener('keydown', function (ev) {\n if (ev.key === 'Escape') WpNodeMenu.close();\n });`;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"graph-node-menu.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-node-menu.ts"],"names":[],"mappings":";;;AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAa,aAAa;IACtB;;;;OAIG;IACH,MAAM;QACF,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA8CL,CAAC;IACP,CAAC;IAED;;;;;;OAMG;IACH,SAAS,CAAC,KAAa;QACnB,OAAO;UACL,KAAK,WAAW,KAAK;UACrB,KAAK;UACL,KAAK;UACL,KAAK;UACL,KAAK;UACL,KAAK,yCAAyC,CAAC;IACrD,CAAC;IAED,+FAA+F;IAC/F,MAAM;QACF,OAAO,GAAG,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC,UAAU,EAAE,KAAK,IAAI,CAAC,aAAa,EAAE,EAAE,CAAC;IACjF,CAAC;IAED;;;;OAIG;IACK,UAAU;QACd,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA6CD,IAAI,CAAC,cAAc,EAAE;;;;;;;;;;;;;;;;;;;UAmBzB,CAAC;IACP,CAAC;IAEO,cAAc;QAClB,OAAO;;;;;;;;UAQL,CAAC;IACP,CAAC;IAED;;;OAGG;IACK,UAAU;QACd,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCL,CAAC;IACP,CAAC;IAED,2EAA2E;IACnE,aAAa;QACjB,OAAO;;;;YAIH,CAAC;IACT,CAAC;CACJ;AArND,sCAqNC","sourcesContent":["/**\n * The floating per-node menu, shared by ALL THREE generated graphs.\n *\n * There is exactly ONE implementation of the menu — its CSS, its positioning, its dismissal and the\n * click wiring that opens it — and `architecture/dependencies.html` (graph-visualizer.ts), every\n * project's `design.html` (di-graph/design-visualizer.ts) and `tmp/webpieces/runtime-architecture.html`\n * (runtime-visualizer.ts) all inline these same bytes. Duplicating it into the three emitters is what\n * this module exists to prevent.\n *\n * Why the browser code is emitted as a STRING here rather than living in a compiled `*.client.ts`\n * beside its emitter: `generateDesignHTML` is called straight from a source checkout (by the\n * di-graph-generate executor's unit tests) and has no injection seam for the client text, so a\n * `readCompiledClient` sibling would make the design page unbuildable from source. A string keeps one\n * copy reachable by every emitter with no build-order coupling. The two pages that DO have a compiled\n * client (`graph-visualizer.client.ts`, `runtime-visualizer.client.ts`) call into these classes as\n * ambient globals — see viz-client-globals.d.ts.\n *\n * What the menu does NOT own is the LOCK BEHAVIOUR, because the pages genuinely differ:\n * - the architecture page already has a `#wp-lock` dropdown backed by GraphHighlighter (chain\n * highlight + responsibilities filter), and its menu item drives that, so the two stay in sync;\n * - a design page and the runtime page have neither dropdown nor responsibilities, so they use\n * `WpNodeLock` below — dim every other box in that graph, light the locked one.\n * All three spell the dim with the SAME class names and the SAME CSS from `dimStyles()`.\n */\nexport class GraphNodeMenu {\n /**\n * Menu chrome + the \"this box is clickable\" affordance. Every node is clickable now (the menu\n * replaced direct navigation), so the cursor/glow is on every box rather than only the ones with\n * a design page.\n */\n styles(): string {\n return `\n .wp-node-menu {\n position: absolute;\n z-index: 1000;\n min-width: 150px;\n padding: 4px 0;\n background: white;\n border: 1px solid #cfcfcf;\n border-radius: 6px;\n box-shadow: 0 4px 14px rgba(0,0,0,0.22);\n font-family: Arial, sans-serif;\n font-size: 14px;\n }\n .wp-node-menu-title {\n padding: 4px 14px 6px;\n color: #777;\n font-size: 12px;\n font-family: monospace;\n border-bottom: 1px solid #eee;\n margin-bottom: 4px;\n }\n .wp-node-menu-item {\n display: block;\n width: 100%;\n padding: 7px 14px;\n border: 0;\n background: none;\n color: #1565C0;\n font: inherit;\n text-align: left;\n cursor: pointer;\n }\n .wp-node-menu-item:hover { background: #E3F2FD; }\n g.wp-node-clickable { cursor: pointer; }\n /* Every shape Graphviz (or the runtime page's own queue-cylinder redraw) can emit for a node\n * body: box/record -> polygon, circle -> ellipse, cylinder and the redrawn queue -> path. A\n * shape left out of this list would be a box that is clickable but never looks it. */\n g.wp-node-clickable polygon,\n g.wp-node-clickable ellipse,\n g.wp-node-clickable path { transition: stroke-width 0.12s ease, filter 0.12s ease; }\n g.wp-node-clickable:hover polygon,\n g.wp-node-clickable:hover ellipse,\n g.wp-node-clickable:hover path {\n stroke: #1976d2;\n stroke-width: 5;\n filter: drop-shadow(0 0 6px rgba(25, 118, 210, 0.85));\n }`;\n }\n\n /**\n * The dim/undim rules a lock (or a hover) toggles, scoped to whatever element holds the rendered\n * SVG on this page (`#graph` on the architecture page, `.graph` on a design page).\n *\n * We ONLY dim: the lit subgraph keeps its exact normal look. The un-dim rules repeat\n * `svg.wp-dim` so they out-specify the dim rule, which carries an extra type selector.\n */\n dimStyles(scope: string): string {\n return `\n ${scope} .node, ${scope} .edge { transition: opacity 0.12s ease; }\n ${scope} svg.wp-dim .node,\n ${scope} svg.wp-dim .edge { opacity: 0.15; }\n ${scope} svg.wp-dim .node.wp-focus,\n ${scope} svg.wp-dim .node.wp-neighbor,\n ${scope} svg.wp-dim .edge.wp-hl { opacity: 1; }`;\n }\n\n /** The whole browser side: the menu, the design-page lock, and the global dismiss handlers. */\n script(): string {\n return `${this.menuScript()}\\n${this.lockScript()}\\n${this.dismissScript()}`;\n }\n\n /**\n * `WpNodeMenuItem` + `WpNodeMenu`. Anchored at the node's own bounding box (bottom-left corner,\n * 4px below it) and clamped so a box at the right edge of a very wide graph still shows its whole\n * menu. Opening a menu closes any other, so at most one is ever on screen.\n */\n private menuScript(): string {\n return `\n class WpNodeMenuItem {\n constructor(label, onSelect) { this.label = label; this.onSelect = onSelect; }\n }\n class WpNodeMenu {\n static close() {\n const open = document.getElementById('wp-node-menu');\n if (open !== null) open.remove();\n }\n static open(nodeEl, name, items) {\n WpNodeMenu.close();\n const menu = document.createElement('div');\n menu.id = 'wp-node-menu';\n menu.className = 'wp-node-menu';\n menu.addEventListener('click', function (ev) { ev.stopPropagation(); });\n const heading = document.createElement('div');\n heading.className = 'wp-node-menu-title';\n heading.textContent = name;\n menu.appendChild(heading);\n for (const item of items) menu.appendChild(WpNodeMenu.button(item));\n document.body.appendChild(menu);\n WpNodeMenu.place(menu, nodeEl);\n }\n static button(item) {\n const button = document.createElement('button');\n button.type = 'button';\n button.className = 'wp-node-menu-item';\n button.textContent = item.label;\n button.addEventListener('click', function (ev) {\n ev.preventDefault();\n ev.stopPropagation();\n WpNodeMenu.close();\n item.onSelect();\n });\n return button;\n }\n static place(menu, nodeEl) {\n const box = nodeEl.getBoundingClientRect();\n const own = menu.getBoundingClientRect();\n const maxLeft = window.scrollX + document.documentElement.clientWidth - own.width - 8;\n let left = box.left + window.scrollX;\n if (left > maxLeft) left = Math.max(window.scrollX + 8, maxLeft);\n menu.style.left = left + 'px';\n menu.style.top = (box.bottom + window.scrollY + 4) + 'px';\n }\n ${this.keyboardScript()}\n static wire(svg, itemsFor) {\n svg.querySelectorAll('g.node').forEach(function (node) {\n const title = node.querySelector('title');\n const name = title === null || title.textContent === null\n ? '' : title.textContent.trim();\n if (name === '') return;\n if (node.classList.contains('wp-layout')) return;\n node.classList.add('wp-node-clickable');\n node.setAttribute('tabindex', '0');\n node.setAttribute('role', 'button');\n WpNodeMenu.wireKeyboard(node, name, itemsFor);\n node.addEventListener('click', function (ev) {\n ev.preventDefault();\n ev.stopPropagation();\n WpNodeMenu.open(node, name, itemsFor(name, node));\n });\n });\n }\n }`;\n }\n\n private keyboardScript(): string {\n return `static wireKeyboard(node, name, itemsFor) {\n node.addEventListener('keydown', function (ev) {\n if (ev.key !== 'Enter' && ev.key !== ' ') return;\n ev.preventDefault();\n ev.stopPropagation();\n WpNodeMenu.open(node, name, itemsFor(name, node));\n document.querySelector('#wp-node-menu button')?.focus();\n });\n }`;\n }\n\n /**\n * The lock the DESIGN pages and the RUNTIME page use: neither has a dropdown or a responsibilities\n * list, so locking a box dims every other box in that graph and lights the locked one.\n */\n private lockScript(): string {\n return `\n class WpNodeLock {\n constructor(svg) { this.svg = svg; this.locked = null; }\n rebind(svg) {\n const name = this.locked;\n this.svg = svg;\n this.clear();\n this.locked = name;\n if (name === null) return;\n for (const node of svg.querySelectorAll('g.node')) {\n if (node.querySelector('title')?.textContent === name) {\n this.lock(name, node);\n break;\n }\n }\n }\n isLocked(name) { return this.locked === name; }\n toggle(name, nodeEl) {\n if (this.locked === name) this.clear();\n else this.lock(name, nodeEl);\n }\n lock(name, nodeEl) {\n this.clear();\n this.locked = name;\n this.svg.classList.add('wp-dim');\n nodeEl.classList.add('wp-focus');\n }\n clear() {\n this.locked = null;\n this.svg.classList.remove('wp-dim');\n this.svg.querySelectorAll('.wp-focus').forEach(function (el) {\n el.classList.remove('wp-focus');\n });\n }\n }`;\n }\n\n /** Outside click and Escape both dismiss — wired once, on the document. */\n private dismissScript(): string {\n return `\n document.addEventListener('click', function () { WpNodeMenu.close(); });\n document.addEventListener('keydown', function (ev) {\n if (ev.key === 'Escape') WpNodeMenu.close();\n });`;\n }\n}\n"]}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { LevelBand } from './graph-level-bands';
|
|
2
|
+
/** Renderer-owned records, captured while emitting DOT, never recovered by parsing labels. */
|
|
3
|
+
export declare class RenderNode {
|
|
4
|
+
readonly id: string;
|
|
5
|
+
readonly dot: string;
|
|
6
|
+
constructor(id: string, dot: string);
|
|
7
|
+
}
|
|
8
|
+
export declare class RenderEdge {
|
|
9
|
+
readonly from: string;
|
|
10
|
+
readonly to: string;
|
|
11
|
+
readonly dot: string;
|
|
12
|
+
readonly id: string;
|
|
13
|
+
constructor(from: string, to: string, dot: string, id: string);
|
|
14
|
+
}
|
|
15
|
+
/** The immutable drawable snapshot carried by a generated page. */
|
|
16
|
+
export declare class GraphRenderModel {
|
|
17
|
+
readonly nodes: RenderNode[];
|
|
18
|
+
readonly edges: RenderEdge[];
|
|
19
|
+
bands: LevelBand[];
|
|
20
|
+
fullDot: string;
|
|
21
|
+
header: string;
|
|
22
|
+
footer: string;
|
|
23
|
+
node(id: string, dot: string): string;
|
|
24
|
+
completeEndpoints(): void;
|
|
25
|
+
edge(from: string, to: string, dot: string): string;
|
|
26
|
+
}
|
|
27
|
+
/** Shared filter chrome and script-safe JSON for pages opened directly from disk. */
|
|
28
|
+
export declare class GraphFilterAssets {
|
|
29
|
+
json<T>(value: T): string;
|
|
30
|
+
html(): string;
|
|
31
|
+
styles(): string;
|
|
32
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.GraphFilterAssets = exports.GraphRenderModel = exports.RenderEdge = exports.RenderNode = void 0;
|
|
4
|
+
const dot_syntax_1 = require("./dot-syntax");
|
|
5
|
+
/** Renderer-owned records, captured while emitting DOT, never recovered by parsing labels. */
|
|
6
|
+
class RenderNode {
|
|
7
|
+
id;
|
|
8
|
+
dot;
|
|
9
|
+
constructor(id, dot) {
|
|
10
|
+
this.id = id;
|
|
11
|
+
this.dot = dot;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.RenderNode = RenderNode;
|
|
15
|
+
class RenderEdge {
|
|
16
|
+
from;
|
|
17
|
+
to;
|
|
18
|
+
dot;
|
|
19
|
+
id;
|
|
20
|
+
constructor(from, to, dot, id) {
|
|
21
|
+
this.from = from;
|
|
22
|
+
this.to = to;
|
|
23
|
+
this.dot = dot;
|
|
24
|
+
this.id = id;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
exports.RenderEdge = RenderEdge;
|
|
28
|
+
/** The immutable drawable snapshot carried by a generated page. */
|
|
29
|
+
class GraphRenderModel {
|
|
30
|
+
nodes = [];
|
|
31
|
+
edges = [];
|
|
32
|
+
bands = [];
|
|
33
|
+
fullDot = '';
|
|
34
|
+
header = '';
|
|
35
|
+
footer = '';
|
|
36
|
+
node(id, dot) {
|
|
37
|
+
this.nodes.push(new RenderNode(id, dot));
|
|
38
|
+
return dot;
|
|
39
|
+
}
|
|
40
|
+
completeEndpoints() {
|
|
41
|
+
const ids = new Set(this.nodes.map((node) => node.id));
|
|
42
|
+
for (const edge of this.edges) {
|
|
43
|
+
for (const id of [edge.from, edge.to]) {
|
|
44
|
+
if (ids.has(id))
|
|
45
|
+
continue;
|
|
46
|
+
ids.add(id);
|
|
47
|
+
this.node(id, ` "${(0, dot_syntax_1.dotValue)(id)}";\n`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
edge(from, to, dot) {
|
|
52
|
+
const id = `wp-real-edge-${this.edges.length}`;
|
|
53
|
+
const statement = `${dot.slice(0, -2)} [id="${id}"];\n`;
|
|
54
|
+
this.edges.push(new RenderEdge(from, to, statement, id));
|
|
55
|
+
return statement;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
exports.GraphRenderModel = GraphRenderModel;
|
|
59
|
+
/** Shared filter chrome and script-safe JSON for pages opened directly from disk. */
|
|
60
|
+
class GraphFilterAssets {
|
|
61
|
+
json(value) {
|
|
62
|
+
return JSON.stringify(value).replace(/</g, '\\u003c');
|
|
63
|
+
}
|
|
64
|
+
html() {
|
|
65
|
+
return ('<p id="wp-filter-status" hidden>Filtering: <strong id="wp-filter-anchor"></strong> ' +
|
|
66
|
+
'<button id="wp-filter-off" type="button">Turn off Filter</button></p>' +
|
|
67
|
+
'<p id="wp-graph-error" role="alert" hidden></p>');
|
|
68
|
+
}
|
|
69
|
+
styles() {
|
|
70
|
+
return ('#wp-filter-status { text-align:center; } #wp-graph-error { color:#b71c1c; } ' +
|
|
71
|
+
'#graph .wp-filter-anchor polygon, #graph .wp-filter-anchor path, ' +
|
|
72
|
+
'#graph .wp-filter-anchor ellipse { stroke:#1565c0; stroke-width:3; }');
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
exports.GraphFilterAssets = GraphFilterAssets;
|
|
76
|
+
//# sourceMappingURL=graph-render-model.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"graph-render-model.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-render-model.ts"],"names":[],"mappings":";;;AAAA,6CAAwC;AAGxC,8FAA8F;AAC9F,MAAa,UAAU;IAEC;IACA;IAFpB,YACoB,EAAU,EACV,GAAW;QADX,OAAE,GAAF,EAAE,CAAQ;QACV,QAAG,GAAH,GAAG,CAAQ;IAC5B,CAAC;CACP;AALD,gCAKC;AAED,MAAa,UAAU;IAEC;IACA;IACA;IACA;IAJpB,YACoB,IAAY,EACZ,EAAU,EACV,GAAW,EACX,EAAU;QAHV,SAAI,GAAJ,IAAI,CAAQ;QACZ,OAAE,GAAF,EAAE,CAAQ;QACV,QAAG,GAAH,GAAG,CAAQ;QACX,OAAE,GAAF,EAAE,CAAQ;IAC3B,CAAC;CACP;AAPD,gCAOC;AAED,mEAAmE;AACnE,MAAa,gBAAgB;IAChB,KAAK,GAAiB,EAAE,CAAC;IACzB,KAAK,GAAiB,EAAE,CAAC;IAClC,KAAK,GAAgB,EAAE,CAAC;IACxB,OAAO,GAAG,EAAE,CAAC;IACb,MAAM,GAAG,EAAE,CAAC;IACZ,MAAM,GAAG,EAAE,CAAC;IAEZ,IAAI,CAAC,EAAU,EAAE,GAAW;QACxB,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC,CAAC;QACzC,OAAO,GAAG,CAAC;IACf,CAAC;IAED,iBAAiB;QACb,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QACvD,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC5B,KAAK,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;gBACpC,IAAI,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBAAE,SAAS;gBAC1B,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;gBACZ,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,MAAM,IAAA,qBAAQ,EAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAC5C,CAAC;QACL,CAAC;IACL,CAAC;IAED,IAAI,CAAC,IAAY,EAAE,EAAU,EAAE,GAAW;QACtC,MAAM,EAAE,GAAG,gBAAgB,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;QAC/C,MAAM,SAAS,GAAG,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,EAAE,OAAO,CAAC;QACxD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,UAAU,CAAC,IAAI,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,CAAC,CAAC,CAAC;QACzD,OAAO,SAAS,CAAC;IACrB,CAAC;CACJ;AA9BD,4CA8BC;AAED,qFAAqF;AACrF,MAAa,iBAAiB;IAC1B,IAAI,CAAI,KAAQ;QACZ,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI;QACA,OAAO,CACH,qFAAqF;YACrF,uEAAuE;YACvE,iDAAiD,CACpD,CAAC;IACN,CAAC;IAED,MAAM;QACF,OAAO,CACH,8EAA8E;YAC9E,mEAAmE;YACnE,sEAAsE,CACzE,CAAC;IACN,CAAC;CACJ;AApBD,8CAoBC","sourcesContent":["import { dotValue } from './dot-syntax';\nimport { LevelBand } from './graph-level-bands';\n\n/** Renderer-owned records, captured while emitting DOT, never recovered by parsing labels. */\nexport class RenderNode {\n constructor(\n public readonly id: string,\n public readonly dot: string,\n ) {}\n}\n\nexport class RenderEdge {\n constructor(\n public readonly from: string,\n public readonly to: string,\n public readonly dot: string,\n public readonly id: string,\n ) {}\n}\n\n/** The immutable drawable snapshot carried by a generated page. */\nexport class GraphRenderModel {\n readonly nodes: RenderNode[] = [];\n readonly edges: RenderEdge[] = [];\n bands: LevelBand[] = [];\n fullDot = '';\n header = '';\n footer = '';\n\n node(id: string, dot: string): string {\n this.nodes.push(new RenderNode(id, dot));\n return dot;\n }\n\n completeEndpoints(): void {\n const ids = new Set(this.nodes.map((node) => node.id));\n for (const edge of this.edges) {\n for (const id of [edge.from, edge.to]) {\n if (ids.has(id)) continue;\n ids.add(id);\n this.node(id, ` \"${dotValue(id)}\";\\n`);\n }\n }\n }\n\n edge(from: string, to: string, dot: string): string {\n const id = `wp-real-edge-${this.edges.length}`;\n const statement = `${dot.slice(0, -2)} [id=\"${id}\"];\\n`;\n this.edges.push(new RenderEdge(from, to, statement, id));\n return statement;\n }\n}\n\n/** Shared filter chrome and script-safe JSON for pages opened directly from disk. */\nexport class GraphFilterAssets {\n json<T>(value: T): string {\n return JSON.stringify(value).replace(/</g, '\\\\u003c');\n }\n\n html(): string {\n return (\n '<p id=\"wp-filter-status\" hidden>Filtering: <strong id=\"wp-filter-anchor\"></strong> ' +\n '<button id=\"wp-filter-off\" type=\"button\">Turn off Filter</button></p>' +\n '<p id=\"wp-graph-error\" role=\"alert\" hidden></p>'\n );\n }\n\n styles(): string {\n return (\n '#wp-filter-status { text-align:center; } #wp-graph-error { color:#b71c1c; } ' +\n '#graph .wp-filter-anchor polygon, #graph .wp-filter-anchor path, ' +\n '#graph .wp-filter-anchor ellipse { stroke:#1565c0; stroke-width:3; }'\n );\n }\n}\n"]}
|
|
@@ -60,6 +60,8 @@ export interface GraphEntry {
|
|
|
60
60
|
responsibilitiesFile?: string;
|
|
61
61
|
designFile?: string;
|
|
62
62
|
apiRelations?: ProjectApiRelations;
|
|
63
|
+
runtimeDeclaration?: string;
|
|
64
|
+
runtimeComposition?: boolean;
|
|
63
65
|
/**
|
|
64
66
|
* The @webpieces runtime packages this project's OWN package.json declares — http-routing,
|
|
65
67
|
* http-server, http-client-node, http-client-browser, cloudtasks-client. Omitted when it
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph-sorter.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-sorter.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;
|
|
1
|
+
{"version":3,"file":"graph-sorter.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/graph-sorter.ts"],"names":[],"mappings":";AAAA;;;;;;;GAOG;;AAuFH,4DAyCC;AASD,wDAgBC;AAtJD,iDAAsD;AA2EtD;;;;;;;;GAQG;AACH,SAAgB,wBAAwB,CAAC,KAA+B;IACpE,MAAM,MAAM,GAAe,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,GAAG,EAAU,CAAC;IACpC,MAAM,WAAW,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEvC,OAAO,SAAS,CAAC,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,CAAC;QACzC,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,MAAM,OAAO,IAAI,WAAW,EAAE,CAAC;YAChC,IAAI,SAAS,CAAC,GAAG,CAAC,OAAO,CAAC;gBAAE,SAAS;YAErC,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAClC,uEAAuE;YACvE,MAAM,mBAAmB,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;YAEpE,IAAI,mBAAmB,EAAE,CAAC;gBACtB,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAC/B,CAAC;QACL,CAAC;QAED,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC5B,gFAAgF;YAChF,mFAAmF;YACnF,kFAAkF;YAClF,IAAI,mCAAoB,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,mBAAmB,CAAC,CAAC;YACrE,sFAAsF;YACtF,MAAM,SAAS,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAS,EAAW,EAAE,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;YAChF,MAAM,IAAI,KAAK,CACX,+EAA+E,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACxG,CAAC;QACN,CAAC;QAED,4DAA4D;QAC5D,YAAY,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;QAE1B,oBAAoB;QACpB,YAAY,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;IAClD,CAAC;IAED,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;;;;;GAMG;AACH,SAAgB,sBAAsB,CAAC,KAA+B;IAClE,MAAM,MAAM,GAAG,wBAAwB,CAAC,KAAK,CAAC,CAAC;IAC/C,MAAM,MAAM,GAAkB,EAAE,CAAC;IAEjC,+DAA+D;IAC/D,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,EAAE,EAAE;QACjC,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC1B,6CAA6C;YAC7C,MAAM,CAAC,OAAO,CAAC,GAAG;gBACd,KAAK,EAAE,UAAU;gBACjB,SAAS,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE;aAC3C,CAAC;QACN,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC;AAClB,CAAC","sourcesContent":["/**\n * Graph Sorter\n *\n * Performs topological sorting on the dependency graph to:\n * 1. Detect circular dependencies (fails if cycle found)\n * 2. Assign level numbers to each project (level 0 = no deps, level 1 = depends on level 0, etc.)\n * 3. Group projects into layers for deterministic ordering\n */\n\nimport type { ProjectApiRelations } from './api-usage/api-relations';\nimport { ProjectCycleDetector } from './graph-cycles';\n\n/**\n * Graph entry with level metadata plus AI-oriented metadata filled in by\n * enrichGraph() (lib/graph-metadata.ts) before the graph is saved:\n * - framework: the project's libType — the SET of runtime environments it is\n * validated to run in, drawn from browser | react | angular | node | express\n * (e.g. [\"browser\",\"node\"]); from its `framework:` nx tags (source of truth)\n * or inferred from package.json deps\n * - shortDescription: summary extracted from the project's responsibilities.md\n * - responsibilitiesFile: repo-relative path to the FULL responsibilities doc\n * - designFile: repo-relative path to the generated DI design.json (only for\n * project.json projects)\n * - apiRelations: for each api-lib in `dependsOn`, WHY the edge exists — the\n * APIs this project implements (serves) and/or uses (calls), each with its\n * transport (rpc | pubsub). Derived by scanning source (see api-usage/).\n */\nexport interface GraphEntry {\n level: number;\n dependsOn: string[];\n framework?: string[];\n role?: string;\n /**\n * The name CLIENTS address this app by at runtime (`new ClientConfig('helper-fsdb')`) — its\n * deployed service name, DECLARED in project.json as metadata.webpieces.serviceName. Absent for\n * anything nothing calls by name (libraries, browser apps). See service-name-resolver.ts.\n */\n serviceName?: string;\n /**\n * The service(s) this project's clients call when the call site cannot carry a literal\n * `ClientConfig` — the symmetric half of `serviceName`. DECLARED in project.json as\n * metadata.webpieces.callsService, either a single service name (every untargeted `uses` aims\n * there) or an `{ apiClassName: serviceName }` map (a client that calls several). Absent for a\n * project that never calls anything, or one whose call sites all carry literals.\n * See service-name-resolver.ts.\n */\n callsService?: string | Record<string, string>;\n /**\n * When false, the project is hidden from the rendered architecture graphs\n * (its box AND every edge touching it are omitted from dependencies.html and\n * the runtime graph). It stays in this JSON so the data view is complete.\n * Absent means drawn (the default). From the project's `drawOnGraph:` nx tag.\n */\n drawOnGraph?: boolean;\n /**\n * Target service names whose RUNTIME edge from this project is cut out of leveling and cycle\n * detection, from this project's `cutLegacyCycle:<target>` nx tags. Per EDGE, not per project.\n * An admission of legacy debt, never an approved pattern — see cut-legacy-cycle-resolver.ts.\n * Absent (the overwhelmingly normal case) means this project cuts nothing.\n */\n cutLegacyCycle?: string[];\n shortDescription?: string;\n responsibilitiesFile?: string;\n designFile?: string;\n apiRelations?: ProjectApiRelations;\n runtimeDeclaration?: string;\n runtimeComposition?: boolean;\n /**\n * The @webpieces runtime packages this project's OWN package.json declares — http-routing,\n * http-server, http-client-node, http-client-browser, cloudtasks-client. Omitted when it\n * declares none, so TOTAL absence across the file means the file predates this field and\n * nothing is auto-hidden (see runtime-graph.ts). Core/util/logging packages are deliberately\n * not markers; see runtime-participant-resolver.ts for why.\n *\n * The runtime graph ORs this over a node's library closure: a server whose whole webpieces\n * stack comes from a shared bootstrap lib participates even though it declares nothing itself.\n */\n webpiecesRuntime?: string[];\n}\n\n/**\n * Enhanced graph format with level information\n */\nexport type EnhancedGraph = Record<string, GraphEntry>;\n\n/**\n * Compute topological layers for dependency graph using Kahn's algorithm\n *\n * Projects are grouped into layers where each layer only depends on previous layers.\n * Throws an error if a circular dependency is detected.\n *\n * @param graph - Dependency graph { project: [deps] }\n * @returns Array of layers, each containing sorted project names\n */\nexport function computeTopologicalLayers(graph: Record<string, string[]>): string[][] {\n const layers: string[][] = [];\n const processed = new Set<string>();\n const allProjects = Object.keys(graph);\n\n while (processed.size < allProjects.length) {\n const currentLayer: string[] = [];\n\n for (const project of allProjects) {\n if (processed.has(project)) continue;\n\n const deps = graph[project] || [];\n // Check if all dependencies are in previous layers (already processed)\n const allDepsInPrevLayers = deps.every((dep) => processed.has(dep));\n\n if (allDepsInPrevLayers) {\n currentLayer.push(project);\n }\n }\n\n if (currentLayer.length === 0) {\n // No progress made = circular dependency. ProjectCycleDetector is the ONE cycle\n // implementation in this package — it names every chain, in project keys, with the\n // fix — so this reports through it rather than keeping a second, weaker DFS here.\n new ProjectCycleDetector().assertAcyclic(graph, 'the project graph');\n // Unreachable: Kahn only stalls on a cycle, and the assert above throws on every one.\n const remaining = allProjects.filter((p: string): boolean => !processed.has(p));\n throw new Error(\n `Topological sort stalled with no cycle to report — projects left unlayered: ${remaining.join(', ')}`,\n );\n }\n\n // Sort alphabetically within layer for deterministic output\n currentLayer.sort();\n layers.push(currentLayer);\n\n // Mark as processed\n currentLayer.forEach((p) => processed.add(p));\n }\n\n return layers;\n}\n\n/**\n * Sort graph in topological order with alphabetical sorting within layers\n * Returns enhanced format with level metadata\n *\n * @param graph - Unsorted dependency graph { project: [deps] }\n * @returns Sorted graph with level metadata { project: { level: number, dependsOn: [deps] } }\n */\nexport function sortGraphTopologically(graph: Record<string, string[]>): EnhancedGraph {\n const layers = computeTopologicalLayers(graph);\n const result: EnhancedGraph = {};\n\n // Add projects layer by layer (dependencies before dependents)\n layers.forEach((layer, levelIndex) => {\n for (const project of layer) {\n // Already sorted alphabetically within layer\n result[project] = {\n level: levelIndex,\n dependsOn: (graph[project] || []).sort(),\n };\n }\n });\n\n return result;\n}\n"]}
|
|
@@ -1,83 +1,24 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The class graph-visualizer.ts stamps on the invisible rank anchors, spacer bands and ordering
|
|
3
|
-
* edges that pin each level to its own row. They are layout scaffolding, not architecture, so the
|
|
4
|
-
* indexes below skip them: an anchor must never be walkable as a dependency, or hovering one box
|
|
5
|
-
* would light boxes it has nothing to do with.
|
|
6
|
-
*
|
|
7
|
-
* Graphviz drops `style=invis` elements from its SVG entirely (verified against the emitted SVG),
|
|
8
|
-
* so today nothing carrying this class reaches the DOM at all. The skip is the guarantee that the
|
|
9
|
-
* scaffolding stays inert if that ever stops being true — the DOT is the only place that decides
|
|
10
|
-
* what is scaffolding, and it says so with this class.
|
|
11
|
-
*/
|
|
12
|
-
declare const LAYOUT_CLASS = "wp-layout";
|
|
13
|
-
/** One traversal direction: the edges to light, and the nodes to walk on to. */
|
|
14
|
-
declare class Direction {
|
|
15
|
-
readonly nodes: Map<string, Set<string>>;
|
|
16
|
-
readonly edges: Map<string, Set<Element>>;
|
|
17
|
-
constructor(nodes: Map<string, Set<string>>, edges: Map<string, Set<Element>>);
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Indexes the rendered SVG and drives the dim/highlight/lock interaction.
|
|
21
|
-
*
|
|
22
|
-
* The adjacency is DIRECTED and kept as two maps per axis: `in*` is what points AT a node (ancestors,
|
|
23
|
-
* walked upward) and `out*` is what it points to (dependencies, walked downward). Highlighting walks
|
|
24
|
-
* both, so a hovered box lights its entire chain in both directions rather than just its neighbours.
|
|
25
|
-
*/
|
|
1
|
+
/** Architecture interactions bind to each new SVG; logical Lock/Filter state belongs to the page. */
|
|
26
2
|
declare class GraphHighlighter {
|
|
27
3
|
private readonly svg;
|
|
4
|
+
private readonly chain;
|
|
5
|
+
private readonly model;
|
|
6
|
+
private readonly page;
|
|
28
7
|
private readonly nodeByName;
|
|
29
|
-
|
|
30
|
-
private readonly outEdges;
|
|
31
|
-
private readonly inNodes;
|
|
32
|
-
private readonly outNodes;
|
|
33
|
-
/**
|
|
34
|
-
* The box the dropdown pinned, or null. Hover still works on top of a lock — leaving a box
|
|
35
|
-
* restores the LOCKED view instead of clearing, so the pinned subgraph stays visible while you
|
|
36
|
-
* scroll down to its responsibility cards.
|
|
37
|
-
*/
|
|
38
|
-
private locked;
|
|
39
|
-
constructor(svg: SVGSVGElement);
|
|
40
|
-
/** Index the SVG, then wire hover, the lock dropdown, and the per-node floating menu. */
|
|
8
|
+
constructor(svg: SVGSVGElement, chain: WpGraphChain, model: RenderModelJson, page: GraphPage);
|
|
41
9
|
wire(): void;
|
|
42
|
-
private indexNodes;
|
|
43
|
-
private indexEdges;
|
|
44
|
-
private ensureEdges;
|
|
45
|
-
private ensureNodes;
|
|
46
10
|
private clear;
|
|
47
|
-
/**
|
|
48
|
-
* Dim everything, then transitively light ancestors and descendants of `name`: edges reached get
|
|
49
|
-
* `wp-hl`, boxes get `wp-neighbor`. The `visited` set is what makes a cyclic graph terminate.
|
|
50
|
-
*/
|
|
51
11
|
private highlight;
|
|
52
|
-
|
|
53
|
-
/** Leaving a box restores the locked view rather than clearing it outright. */
|
|
54
|
-
private relight;
|
|
12
|
+
relight(): void;
|
|
55
13
|
private wireHover;
|
|
56
|
-
private wireLock;
|
|
57
|
-
private lockSelect;
|
|
58
|
-
/** True when `name` is the box the page is currently locked on — what the menu labels itself by. */
|
|
59
|
-
isLocked(name: string): boolean;
|
|
60
|
-
/**
|
|
61
|
-
* THE one lock entry point, so the dropdown and the node menu can never disagree: it sets the
|
|
62
|
-
* dropdown's selection, re-highlights, and re-filters the cards whichever of the two asked.
|
|
63
|
-
* `null` is "All (no lock)".
|
|
64
|
-
*/
|
|
65
|
-
setLock(name: string | null): void;
|
|
66
|
-
private applyLock;
|
|
67
|
-
/**
|
|
68
|
-
* Every box opens the floating menu (the shared one both generated graphs use). "View Design" is
|
|
69
|
-
* only built when that project actually HAS a committed design.html; the bottom item is always
|
|
70
|
-
* Lock or Unlock, whichever applies to this box right now.
|
|
71
|
-
*/
|
|
72
14
|
private wireMenu;
|
|
73
|
-
/**
|
|
74
|
-
* Filter the responsibility cards to the locked box's chain by READING BACK the
|
|
75
|
-
* `.wp-focus`/`.wp-neighbor` classes highlight() just set — so there is no second graph walk and
|
|
76
|
-
* the cards can never disagree with the picture.
|
|
77
|
-
*/
|
|
78
|
-
private filterCards;
|
|
79
15
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
16
|
+
declare class GraphPage extends WpFilterPage {
|
|
17
|
+
private highlighter;
|
|
18
|
+
protected captureBinding(_svg: SVGSVGElement | null): () => void;
|
|
19
|
+
protected wireControls(): void;
|
|
20
|
+
protected wireSvg(svg: SVGSVGElement): void;
|
|
21
|
+
lockSelection(): string | null;
|
|
22
|
+
setLock(name: string | null): void;
|
|
23
|
+
private filterCards;
|
|
83
24
|
}
|