@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
package/README.md
CHANGED
|
@@ -105,3 +105,17 @@ as a fallback. A failed generation remains a failure, and viewing any saved file
|
|
|
105
105
|
afterward does not establish that refresh succeeded. Browser-open failures print
|
|
106
106
|
the HTML path for manual opening. Architecture/API validation and CI still run
|
|
107
107
|
their existing checks independently of visualization.
|
|
108
|
+
|
|
109
|
+
### Compact graph filtering
|
|
110
|
+
|
|
111
|
+
Architecture and runtime node menus offer **Filter Unconnected** alongside
|
|
112
|
+
Lock/Unlock. Filtering retains the selected node and its separate transitive
|
|
113
|
+
incoming and outgoing chains, then reruns Graphviz. Architecture L-number rows keep
|
|
114
|
+
their original identities; runtime chains include rendered queues, clocks, external
|
|
115
|
+
APIs and systems. The anchor indicator and all surviving node menus offer **Turn off
|
|
116
|
+
Filter**, restoring the full layout and independent Lock state. Responsibilities
|
|
117
|
+
follow the retained set intersected with architecture Lock. Hover and menu dismissal
|
|
118
|
+
do not clear filtering, and no filter is persisted across reloads. Saved graph facts
|
|
119
|
+
and visibility options remain untouched. See
|
|
120
|
+
[dependency graphs](../../../docs/architecture/dependency-graphs.md#filter-unconnected)
|
|
121
|
+
for traversal examples and browser verification.
|
package/executors.json
CHANGED
|
@@ -109,6 +109,11 @@
|
|
|
109
109
|
"implementation": "./src/executors/validate-nx-wiring/executor",
|
|
110
110
|
"schema": "./src/executors/validate-nx-wiring/schema.json",
|
|
111
111
|
"description": "Validate the webpieces validators are wired into the build via nx.json targetDefaults dependsOn"
|
|
112
|
+
},
|
|
113
|
+
"runtime-wiring-check": {
|
|
114
|
+
"implementation": "./src/executors/runtime-wiring-check/executor",
|
|
115
|
+
"schema": "./src/executors/runtime-wiring-check/schema.json",
|
|
116
|
+
"description": "Verify canonical per-project wiring; write candidates only to build output"
|
|
112
117
|
}
|
|
113
118
|
}
|
|
114
119
|
}
|
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.855",
|
|
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.855",
|
|
24
|
+
"@webpieces/core-util": "0.4.855",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.855",
|
|
26
|
+
"@webpieces/rules-config": "0.4.855",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.855"
|
|
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.855"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -1,29 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Generate Executor
|
|
4
|
-
*
|
|
5
|
-
* Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus
|
|
6
|
-
* one contract file per API under architecture/apis/ (stale ones are deleted).
|
|
7
|
-
*
|
|
8
|
-
* Usage:
|
|
9
|
-
* nx run architecture:generate
|
|
10
|
-
*/
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.default = runExecutor;
|
|
4
|
+
const approved_graph_1 = require("../../lib/runtime-wiring/approved-graph");
|
|
5
|
+
const graph_loader_1 = require("../../lib/graph-loader");
|
|
13
6
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
14
7
|
const graph_generator_1 = require("../../lib/graph-generator");
|
|
15
8
|
const graph_sorter_1 = require("../../lib/graph-sorter");
|
|
16
9
|
const graph_cycles_1 = require("../../lib/graph-cycles");
|
|
17
|
-
const
|
|
10
|
+
const graph_loader_2 = require("../../lib/graph-loader");
|
|
18
11
|
const api_contract_files_1 = require("../../lib/api-contract-files");
|
|
19
12
|
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
20
13
|
const tag_truth_1 = require("../../lib/tag-truth");
|
|
21
|
-
const api_scanner_1 = require("../../lib/api-usage/api-scanner");
|
|
22
|
-
// The unresolved-contract report moved beside the other contract reports when api-scanner.ts reached
|
|
23
|
-
// its file-size limit.
|
|
24
|
-
const api_contract_errors_1 = require("../../lib/api-usage/api-contract-errors");
|
|
25
|
-
const external_systems_1 = require("../../lib/api-usage/external-systems");
|
|
26
|
-
const runtime_config_1 = require("../../lib/runtime-config");
|
|
27
14
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
28
15
|
const runtime_graph_1 = require("../../lib/runtime-graph");
|
|
29
16
|
const runtime_participant_resolver_1 = require("../../lib/runtime-participant-resolver");
|
|
@@ -71,32 +58,15 @@ function printRuntimeWarnings(warnings) {
|
|
|
71
58
|
* a green run that quietly omits a service gets committed, rendered, and trusted.
|
|
72
59
|
*/
|
|
73
60
|
// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file
|
|
74
|
-
function scanApiRelations(workspaceRoot, graph, projectInfos) {
|
|
75
|
-
console.log('
|
|
76
|
-
|
|
77
|
-
const
|
|
78
|
-
if (
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
console.log((0, api_contract_errors_1.describeMcpExclusions)(scan.apiDocRules.mcpExclusions));
|
|
84
|
-
}
|
|
85
|
-
// A decorator argument we could not read costs the graph a basePath, a method, or a whole
|
|
86
|
-
// contract — none of which leaves a trace in the output. Name them before anything is written.
|
|
87
|
-
if (scan.nonLiteralDecoratorArgs.length > 0) {
|
|
88
|
-
console.warn((0, api_scanner_1.describeNonLiteralDecoratorArgs)(scan.nonLiteralDecoratorArgs));
|
|
89
|
-
}
|
|
90
|
-
const contracts = (0, api_scanner_1.buildApiContracts)(scan);
|
|
91
|
-
// An endpoint whose declared trigger its api kind cannot deliver would silently draw a queue or a
|
|
92
|
-
// clock that nothing could ever fire — name it here, where the fix is one decorator away.
|
|
93
|
-
const mismatches = (0, api_scanner_1.describeMismatchedEndpointKinds)(contracts);
|
|
94
|
-
if (mismatches.length > 0) {
|
|
95
|
-
console.error(`❌ ${mismatches.length} @Endpoint kind(s) conflict with their api kind:`);
|
|
96
|
-
for (const mismatch of mismatches)
|
|
97
|
-
console.error(` • ${mismatch}`);
|
|
98
|
-
}
|
|
99
|
-
return new ScannedTables(contracts, (0, external_systems_1.buildExternalSystems)(scan.apiIndex, projectInfos));
|
|
61
|
+
function scanApiRelations(workspaceRoot, graph, projectInfos, graphPath = graph_loader_2.DEFAULT_GRAPH_PATH) {
|
|
62
|
+
console.log('📖 Assembling approved runtime-deps.json declarations (no source extraction)...');
|
|
63
|
+
new approved_graph_1.ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos, graphPath);
|
|
64
|
+
const saved = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
65
|
+
if (saved === null)
|
|
66
|
+
throw new rules_config_1.RuleFailError('validate-runtime-architecture', 'Missing approved API contract references.', undefined, undefined, [
|
|
67
|
+
new rules_config_1.Option('Migrate src/wiring.ts and review runtime-deps.json and API contract approvals before generation.', true),
|
|
68
|
+
]);
|
|
69
|
+
return new ScannedTables(new api_contract_files_1.ApiContractFiles().load(workspaceRoot, graphPath, saved.apiContractFiles), saved.externalSystems);
|
|
100
70
|
}
|
|
101
71
|
/** The two committed tables one scan produces, kept together so callers cannot persist just one. */
|
|
102
72
|
class ScannedTables {
|
|
@@ -158,12 +128,25 @@ async function generateEverything(workspaceRoot, graphPath) {
|
|
|
158
128
|
// Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by
|
|
159
129
|
// scanning source, so dependencies.json + the viz + the runtime graph all
|
|
160
130
|
// read the same derived truth.
|
|
161
|
-
const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos);
|
|
131
|
+
const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);
|
|
162
132
|
const apiContracts = scanned.apiContracts;
|
|
133
|
+
saveApprovedGraph(workspaceRoot, graphPath ?? graph_loader_2.DEFAULT_GRAPH_PATH, enhancedGraph, scanned);
|
|
134
|
+
// Step 4b: Write the committed, clickable HTML view next to the JSON so
|
|
135
|
+
// dependencies.html regenerates in lock-step with dependencies.json.
|
|
136
|
+
const vizPaths = new graph_visualizer_1.GraphVisualizer().writeVisualization(enhancedGraph, workspaceRoot);
|
|
137
|
+
console.log(`✅ Wrote ${vizPaths.htmlPath}`);
|
|
138
|
+
// Step 5: Generate the runtime microservice graph from the same scan.
|
|
139
|
+
// Projects tagged drawOnGraph:false are threaded through so the runtime
|
|
140
|
+
// graph hides them too (they stay flagged in runtime-dependencies.json).
|
|
141
|
+
generateRuntimeGraph(workspaceRoot, enhancedGraph, hiddenProjectsIn(enhancedGraph), apiContracts, scanned.externalSystems);
|
|
142
|
+
printGraphSummary(enhancedGraph);
|
|
143
|
+
}
|
|
144
|
+
// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together
|
|
145
|
+
function saveApprovedGraph(workspaceRoot, effectiveGraphPath, enhancedGraph, scanned) {
|
|
163
146
|
// Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of
|
|
164
147
|
// APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites
|
|
165
148
|
// an api file and never the dependency graph. The runtime validator reads these files back.
|
|
166
|
-
const
|
|
149
|
+
const apiContracts = scanned.apiContracts;
|
|
167
150
|
const contractFiles = new api_contract_files_1.ApiContractFiles();
|
|
168
151
|
const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);
|
|
169
152
|
console.log(`✅ Wrote ${written.written.length} api contract file(s)`);
|
|
@@ -174,17 +157,8 @@ async function generateEverything(workspaceRoot, graphPath) {
|
|
|
174
157
|
// validate from the files, so anything not written here would make the two disagree.
|
|
175
158
|
console.log('💾 Saving graph to architecture/dependencies.json...');
|
|
176
159
|
const contractRefs = contractFiles.refsFor(apiContracts);
|
|
177
|
-
(0,
|
|
160
|
+
(0, graph_loader_2.saveGraph)(enhancedGraph, workspaceRoot, effectiveGraphPath, contractRefs, scanned.externalSystems);
|
|
178
161
|
console.log('✅ Graph saved successfully');
|
|
179
|
-
// Step 4b: Write the committed, clickable HTML view next to the JSON so
|
|
180
|
-
// dependencies.html regenerates in lock-step with dependencies.json.
|
|
181
|
-
const vizPaths = new graph_visualizer_1.GraphVisualizer().writeVisualization(enhancedGraph, workspaceRoot);
|
|
182
|
-
console.log(`✅ Wrote ${vizPaths.htmlPath}`);
|
|
183
|
-
// Step 5: Generate the runtime microservice graph from the same scan.
|
|
184
|
-
// Projects tagged drawOnGraph:false are threaded through so the runtime
|
|
185
|
-
// graph hides them too (they stay flagged in runtime-dependencies.json).
|
|
186
|
-
generateRuntimeGraph(workspaceRoot, enhancedGraph, hiddenProjectsIn(enhancedGraph), apiContracts, scanned.externalSystems);
|
|
187
|
-
printGraphSummary(enhancedGraph);
|
|
188
162
|
}
|
|
189
163
|
async function runExecutor(options, context) {
|
|
190
164
|
const graphPath = options.graphPath;
|
|
@@ -205,7 +179,9 @@ async function runExecutor(options, context) {
|
|
|
205
179
|
if (error instanceof graph_metadata_1.MetadataValidationError) {
|
|
206
180
|
const mdPath = (0, rules_config_1.writeTemplate)(workspaceRoot, 'webpieces.responsibilities.md');
|
|
207
181
|
console.error('');
|
|
208
|
-
console.error('⚠️ *** Refer to ' +
|
|
182
|
+
console.error('⚠️ *** Refer to ' +
|
|
183
|
+
mdPath +
|
|
184
|
+
' for how to author responsibilities.md files *** ⚠️');
|
|
209
185
|
}
|
|
210
186
|
return { success: false };
|
|
211
187
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus\n * one contract file per API under architecture/apis/ (stale ones are deleted).\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport { writeTemplate, RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { ProjectCycleDetector } from '../../lib/graph-cycles';\nimport { saveGraph, DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ProjectInfo } from '../../lib/project-info';\nimport {\n scanAndAttachApiRelations,\n describeNonLiteralDecoratorArgs,\n buildApiContracts,\n describeMismatchedEndpointKinds,\n} from '../../lib/api-usage/api-scanner';\n// The unresolved-contract report moved beside the other contract reports when api-scanner.ts reached\n// its file-size limit.\nimport {\n describeMcpExclusions,\n describeUnresolvedApiCalls,\n} from '../../lib/api-usage/api-contract-errors';\nimport { buildExternalSystems } from '../../lib/api-usage/external-systems';\nimport type { ApiContracts, ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport type { EnhancedGraph, GraphEntry } from '../../lib/graph-sorter';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { deriveRuntimeGraphReport, saveRuntimeGraph } from '../../lib/runtime-graph';\nimport { printAutoHiddenServers } from '../../lib/runtime-participant-resolver';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/**\n * Generate the runtime microservice graph alongside the compile-time graph, DERIVED from the same\n * dependencies.json (its per-project apiRelations) — one regenerate produces both committed files, and\n * validate derives from the SAME source so they can't diverge. rpc APIs become direct runtime edges;\n * pubsub APIs become edges the viz draws through a queue.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction generateRuntimeGraph(\n workspaceRoot: string,\n graph: EnhancedGraph,\n hiddenProjects: Set<string>,\n apiContracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(graph, hiddenProjects, apiContracts, externalSystems);\n saveRuntimeGraph(report.graph, workspaceRoot);\n const serviceCount = Object.keys(report.graph.services).length;\n const queueCount = Object.keys(report.graph.queues).length;\n console.log(\n `✅ Runtime graph saved (${serviceCount} services, ${report.graph.runtimeEdges.length} runtime edges, ` +\n `${queueCount} queues, ${report.graph.triggers.length} cron/external triggers)`,\n );\n // Every edge the derivation had to GUESS at. Loud on purpose: a fanned-out edge is committed and\n // then reasoned about as if it were derived, which is how a fictional call survives for months.\n printRuntimeWarnings(report.warnings);\n printAutoHiddenServers(report.autoHidden);\n // Printed here, FAILED by validate-runtime-architecture: generate must still write the graph,\n // or the validator would have nothing to compare against and the error would be unfixable.\n if (report.problems.length > 0) {\n console.error(`❌ ${report.problems.length} client call(s) name a service no module answers to:`);\n for (const problem of report.problems) console.error(` • ${problem}`);\n console.error(' This FAILS architecture:validate-runtime-architecture.');\n }\n}\n\n/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printRuntimeWarnings(warnings: string[]): void {\n if (warnings.length === 0) return;\n console.warn(`⚠️ ${warnings.length} runtime edge(s) could not be targeted to ONE service:`);\n for (const warning of warnings) console.warn(` • ${warning}`);\n}\n\n/**\n * Scan for api relations and attach them, surfacing any contract we could NOT map back to source.\n * Unresolved contracts mean the graph we are about to WRITE is incomplete, so they must be loud —\n * a green run that quietly omits a service gets committed, rendered, and trusted.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction scanApiRelations(\n workspaceRoot: string,\n graph: EnhancedGraph,\n projectInfos: Map<string, ProjectInfo>,\n): ScannedTables {\n console.log('🔎 Scanning source for implements/uses API relations...');\n const externalApiPaths = loadRuntimeConfig(workspaceRoot).externalApiPaths;\n const scan = scanAndAttachApiRelations(workspaceRoot, graph, projectInfos, externalApiPaths);\n if (scan.unresolvedApiCalls.length > 0) console.warn(describeUnresolvedApiCalls(scan.unresolvedApiCalls));\n // #1014: restated on EVERY run, green or red. An exclusion announced once, at the moment\n // somebody adds it, is read by the one person who already knows the decision.\n if (scan.apiDocRules.mcpExclusions.length > 0) {\n console.log(describeMcpExclusions(scan.apiDocRules.mcpExclusions));\n }\n // A decorator argument we could not read costs the graph a basePath, a method, or a whole\n // contract — none of which leaves a trace in the output. Name them before anything is written.\n if (scan.nonLiteralDecoratorArgs.length > 0) {\n console.warn(describeNonLiteralDecoratorArgs(scan.nonLiteralDecoratorArgs));\n }\n const contracts = buildApiContracts(scan);\n // An endpoint whose declared trigger its api kind cannot deliver would silently draw a queue or a\n // clock that nothing could ever fire — name it here, where the fix is one decorator away.\n const mismatches = describeMismatchedEndpointKinds(contracts);\n if (mismatches.length > 0) {\n console.error(`❌ ${mismatches.length} @Endpoint kind(s) conflict with their api kind:`);\n for (const mismatch of mismatches) console.error(` • ${mismatch}`);\n }\n return new ScannedTables(contracts, buildExternalSystems(scan.apiIndex, projectInfos));\n}\n\n/** The two committed tables one scan produces, kept together so callers cannot persist just one. */\nclass ScannedTables {\n constructor(\n public readonly apiContracts: ApiContracts,\n public readonly externalSystems: ExternalSystemDecls,\n ) {}\n}\n\n/** Projects tagged drawOnGraph:false — kept in the JSON, omitted from every rendered graph. */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction hiddenProjectsIn(graph: EnhancedGraph): Set<string> {\n const hidden = new Set<string>();\n for (const name of Object.keys(graph)) {\n if (graph[name].drawOnGraph === false) hidden.add(name);\n }\n return hidden;\n}\n\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printGraphSummary(graph: EnhancedGraph): void {\n const levels = new Set(Object.values(graph).map((entry: GraphEntry) => entry.level));\n console.log(`\\n📈 Graph Summary:`);\n console.log(` Projects: ${Object.keys(graph).length}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n}\n\n/**\n * The whole generation pipeline. Split out of runExecutor so that function stays a thin\n * try/catch-and-report shell — the error reporting is what a caller reads on failure, and it should\n * not be separated from the throw by fifty lines of steps.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nasync function generateEverything(workspaceRoot: string, graphPath: string | undefined): Promise<void> {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"📊 Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 1b: The graph is a BUILD graph — refuse a cyclic one, naming EVERY cycle. This runs\n // before the sort deliberately: the sort also refuses, but reports one cycle and an\n // undifferentiated list of everything tangled with it, so a repo with several cycles pays one\n // full regeneration per cycle to discover them.\n console.log('🔄 Checking the project graph is acyclic...');\n const cycles = new ProjectCycleDetector();\n cycles.assertAcyclic(reducedGraph, 'the nx project graph');\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('🔄 Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n // ...and assert the stratification it just produced actually holds: every dependency strictly\n // below its dependent. Safe to assert only because the graph was sorted a line ago — a stale\n // committed file is never checked this way.\n cycles.assertLevelsDescend(reducedGraph, cycles.levelsOf(enhancedGraph), 'the freshly sorted graph');\n\n // Step 3: Enrich with AI metadata (framework, shortDescription, file\n // pointers). This VALIDATES (responsibilities.md required per project)\n // and throws before any write, so a failure never clobbers the file.\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(enhancedGraph, projectInfos, workspaceRoot);\n new TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write\n\n // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by\n // scanning source, so dependencies.json + the viz + the runtime graph all\n // read the same derived truth.\n const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos);\n const apiContracts = scanned.apiContracts;\n\n // Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of\n // APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites\n // an api file and never the dependency graph. The runtime validator reads these files back.\n const effectiveGraphPath = graphPath ?? DEFAULT_GRAPH_PATH;\n const contractFiles = new ApiContractFiles();\n const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);\n console.log(`✅ Wrote ${written.written.length} api contract file(s)`);\n for (const deleted of written.deleted) console.log(`🗑️ Deleted stale api contract file ${deleted}`);\n\n // Step 4a: Save the graph, INCLUDING the contract-file links and the external-system\n // declarations the runtime derivation reads back — generate derives from the in-memory graph,\n // validate from the files, so anything not written here would make the two disagree.\n console.log('💾 Saving graph to architecture/dependencies.json...');\n const contractRefs = contractFiles.refsFor(apiContracts);\n saveGraph(enhancedGraph, workspaceRoot, effectiveGraphPath, contractRefs, scanned.externalSystems);\n console.log('✅ Graph saved successfully');\n\n // Step 4b: Write the committed, clickable HTML view next to the JSON so\n // dependencies.html regenerates in lock-step with dependencies.json.\n const vizPaths = new GraphVisualizer().writeVisualization(enhancedGraph, workspaceRoot);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 5: Generate the runtime microservice graph from the same scan.\n // Projects tagged drawOnGraph:false are threaded through so the runtime\n // graph hides them too (they stay flagged in runtime-dependencies.json).\n generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n );\n\n printGraphSummary(enhancedGraph);\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n📊 Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n await generateEverything(workspaceRoot, graphPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError carries its cures as Option[]; `Error.message` is only the aiMessage, so\n // printing that alone silently drops them. renderRuleFailForHuman is the ONE renderer that\n // numbers them, and this catch is the top-level handler for the nx target.\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Graph generation failed:', rendered);\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error('⚠️ *** Refer to ' + mdPath + ' for how to author responsibilities.md files *** ⚠️');\n }\n return { success: false };\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/generate/executor.ts"],"names":[],"mappings":"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","sourcesContent":["import { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\n/**\n * Generate Executor\n *\n * Generates the architecture dependency graph and saves it to architecture/dependencies.json, plus\n * one contract file per API under architecture/apis/ (stale ones are deleted).\n *\n * Usage:\n * nx run architecture:generate\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport {\n writeTemplate,\n Option,\n RuleFailError,\n renderRuleFailForHuman,\n} from '@webpieces/rules-config';\nimport { generateReducedGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { ProjectCycleDetector } from '../../lib/graph-cycles';\nimport { saveGraph, DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ProjectInfo } from '../../lib/project-info';\nimport type { ApiContracts, ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport type { EnhancedGraph, GraphEntry } from '../../lib/graph-sorter';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { deriveRuntimeGraphReport, saveRuntimeGraph } from '../../lib/runtime-graph';\nimport { printAutoHiddenServers } from '../../lib/runtime-participant-resolver';\nimport { toError } from '../../toError';\n\nexport interface GenerateExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/**\n * Generate the runtime microservice graph alongside the compile-time graph, DERIVED from the same\n * dependencies.json (its per-project apiRelations) — one regenerate produces both committed files, and\n * validate derives from the SAME source so they can't diverge. rpc APIs become direct runtime edges;\n * pubsub APIs become edges the viz draws through a queue.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction generateRuntimeGraph(\n workspaceRoot: string,\n graph: EnhancedGraph,\n hiddenProjects: Set<string>,\n apiContracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(graph, hiddenProjects, apiContracts, externalSystems);\n saveRuntimeGraph(report.graph, workspaceRoot);\n const serviceCount = Object.keys(report.graph.services).length;\n const queueCount = Object.keys(report.graph.queues).length;\n console.log(\n `✅ Runtime graph saved (${serviceCount} services, ${report.graph.runtimeEdges.length} runtime edges, ` +\n `${queueCount} queues, ${report.graph.triggers.length} cron/external triggers)`,\n );\n // Every edge the derivation had to GUESS at. Loud on purpose: a fanned-out edge is committed and\n // then reasoned about as if it were derived, which is how a fictional call survives for months.\n printRuntimeWarnings(report.warnings);\n printAutoHiddenServers(report.autoHidden);\n // Printed here, FAILED by validate-runtime-architecture: generate must still write the graph,\n // or the validator would have nothing to compare against and the error would be unfixable.\n if (report.problems.length > 0) {\n console.error(\n `❌ ${report.problems.length} client call(s) name a service no module answers to:`,\n );\n for (const problem of report.problems) console.error(` • ${problem}`);\n console.error(' This FAILS architecture:validate-runtime-architecture.');\n }\n}\n\n/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printRuntimeWarnings(warnings: string[]): void {\n if (warnings.length === 0) return;\n console.warn(`⚠️ ${warnings.length} runtime edge(s) could not be targeted to ONE service:`);\n for (const warning of warnings) console.warn(` • ${warning}`);\n}\n\n/**\n * Scan for api relations and attach them, surfacing any contract we could NOT map back to source.\n * Unresolved contracts mean the graph we are about to WRITE is incomplete, so they must be loud —\n * a green run that quietly omits a service gets committed, rendered, and trusted.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction scanApiRelations(\n workspaceRoot: string,\n graph: EnhancedGraph,\n projectInfos: Map<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n): ScannedTables {\n console.log('📖 Assembling approved runtime-deps.json declarations (no source extraction)...');\n new ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos, graphPath);\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing approved API contract references.',\n undefined,\n undefined,\n [\n new Option(\n 'Migrate src/wiring.ts and review runtime-deps.json and API contract approvals before generation.',\n true,\n ),\n ],\n );\n return new ScannedTables(\n new ApiContractFiles().load(workspaceRoot, graphPath, saved.apiContractFiles),\n saved.externalSystems,\n );\n}\n\n/** The two committed tables one scan produces, kept together so callers cannot persist just one. */\nclass ScannedTables {\n constructor(\n public readonly apiContracts: ApiContracts,\n public readonly externalSystems: ExternalSystemDecls,\n ) {}\n}\n\n/** Projects tagged drawOnGraph:false — kept in the JSON, omitted from every rendered graph. */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction hiddenProjectsIn(graph: EnhancedGraph): Set<string> {\n const hidden = new Set<string>();\n for (const name of Object.keys(graph)) {\n if (graph[name].drawOnGraph === false) hidden.add(name);\n }\n return hidden;\n}\n\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nfunction printGraphSummary(graph: EnhancedGraph): void {\n const levels = new Set(Object.values(graph).map((entry: GraphEntry) => entry.level));\n console.log(`\\n📈 Graph Summary:`);\n console.log(` Projects: ${Object.keys(graph).length}`);\n console.log(` Levels: ${levels.size} (0-${Math.max(...levels)})`);\n}\n\n/**\n * The whole generation pipeline. Split out of runExecutor so that function stays a thin\n * try/catch-and-report shell — the error reporting is what a caller reads on failure, and it should\n * not be separated from the throw by fifty lines of steps.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file\nasync function generateEverything(\n workspaceRoot: string,\n graphPath: string | undefined,\n): Promise<void> {\n // Step 1: Build the full graph from nx, then transitively reduce it to the view\n console.log(\"📊 Generating dependency graph from nx's project graph...\");\n const reducedGraph = await generateReducedGraph();\n\n // Step 1b: The graph is a BUILD graph — refuse a cyclic one, naming EVERY cycle. This runs\n // before the sort deliberately: the sort also refuses, but reports one cycle and an\n // undifferentiated list of everything tangled with it, so a repo with several cycles pays one\n // full regeneration per cycle to discover them.\n console.log('🔄 Checking the project graph is acyclic...');\n const cycles = new ProjectCycleDetector();\n cycles.assertAcyclic(reducedGraph, 'the nx project graph');\n\n // Step 2: Topological sort (to assign levels for visualization)\n console.log('🔄 Computing topological layers...');\n const enhancedGraph = sortGraphTopologically(reducedGraph);\n // ...and assert the stratification it just produced actually holds: every dependency strictly\n // below its dependent. Safe to assert only because the graph was sorted a line ago — a stale\n // committed file is never checked this way.\n cycles.assertLevelsDescend(\n reducedGraph,\n cycles.levelsOf(enhancedGraph),\n 'the freshly sorted graph',\n );\n\n // Step 3: Enrich with AI metadata (framework, shortDescription, file\n // pointers). This VALIDATES (responsibilities.md required per project)\n // and throws before any write, so a failure never clobbers the file.\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(enhancedGraph, projectInfos, workspaceRoot);\n new TagTruthCheck().assertTrue(enhancedGraph, projectInfos, workspaceRoot); // #1064, before any write\n\n // Step 3b: Classify each api-lib edge (implements/uses + rpc/pubsub) by\n // scanning source, so dependencies.json + the viz + the runtime graph all\n // read the same derived truth.\n const scanned = scanApiRelations(workspaceRoot, enhancedGraph, projectInfos, graphPath);\n const apiContracts = scanned.apiContracts;\n\n saveApprovedGraph(workspaceRoot, graphPath ?? DEFAULT_GRAPH_PATH, enhancedGraph, scanned);\n\n // Step 4b: Write the committed, clickable HTML view next to the JSON so\n // dependencies.html regenerates in lock-step with dependencies.json.\n const vizPaths = new GraphVisualizer().writeVisualization(enhancedGraph, workspaceRoot);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 5: Generate the runtime microservice graph from the same scan.\n // Projects tagged drawOnGraph:false are threaded through so the runtime\n // graph hides them too (they stay flagged in runtime-dependencies.json).\n generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n );\n\n printGraphSummary(enhancedGraph);\n}\n\n// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together\nfunction saveApprovedGraph(\n workspaceRoot: string,\n effectiveGraphPath: string,\n enhancedGraph: EnhancedGraph,\n scanned: ScannedTables,\n): void {\n // Step 4: Write one contract file per API (architecture/apis/<Api>.json) and delete the files of\n // APIs that no longer exist. dependencies.json only LINKS to them, so an endpoint change rewrites\n // an api file and never the dependency graph. The runtime validator reads these files back.\n const apiContracts = scanned.apiContracts;\n const contractFiles = new ApiContractFiles();\n const written = contractFiles.write(workspaceRoot, effectiveGraphPath, apiContracts);\n console.log(`✅ Wrote ${written.written.length} api contract file(s)`);\n for (const deleted of written.deleted)\n console.log(`🗑️ Deleted stale api contract file ${deleted}`);\n\n // Step 4a: Save the graph, INCLUDING the contract-file links and the external-system\n // declarations the runtime derivation reads back — generate derives from the in-memory graph,\n // validate from the files, so anything not written here would make the two disagree.\n console.log('💾 Saving graph to architecture/dependencies.json...');\n const contractRefs = contractFiles.refsFor(apiContracts);\n saveGraph(\n enhancedGraph,\n workspaceRoot,\n effectiveGraphPath,\n contractRefs,\n scanned.externalSystems,\n );\n console.log('✅ Graph saved successfully');\n}\n\nexport default async function runExecutor(\n options: GenerateExecutorOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n console.log('\\n📊 Architecture Graph Generator\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n await generateEverything(workspaceRoot, graphPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError carries its cures as Option[]; `Error.message` is only the aiMessage, so\n // printing that alone silently drops them. renderRuleFailForHuman is the ONE renderer that\n // numbers them, and this catch is the top-level handler for the nx target.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Graph generation failed:', rendered);\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' +\n mdPath +\n ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n return { success: false };\n }\n}\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ExecutorContext } from '@nx/devkit';
|
|
2
|
+
export declare class RuntimeWiringCheckOptions {
|
|
3
|
+
readonly outputPath: string;
|
|
4
|
+
constructor(outputPath: string);
|
|
5
|
+
}
|
|
6
|
+
export declare class RuntimeWiringCheckResult {
|
|
7
|
+
readonly success: boolean;
|
|
8
|
+
constructor(success: boolean);
|
|
9
|
+
}
|
|
10
|
+
export default function executor(options: RuntimeWiringCheckOptions, context: ExecutorContext): Promise<RuntimeWiringCheckResult>;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RuntimeWiringCheckResult = exports.RuntimeWiringCheckOptions = void 0;
|
|
4
|
+
exports.default = executor;
|
|
5
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
6
|
+
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
7
|
+
const verification_1 = require("../../lib/runtime-wiring/verification");
|
|
8
|
+
const toError_1 = require("../../toError");
|
|
9
|
+
class RuntimeWiringCheckOptions {
|
|
10
|
+
outputPath;
|
|
11
|
+
constructor(outputPath) {
|
|
12
|
+
this.outputPath = outputPath;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
exports.RuntimeWiringCheckOptions = RuntimeWiringCheckOptions;
|
|
16
|
+
class RuntimeWiringCheckResult {
|
|
17
|
+
success;
|
|
18
|
+
constructor(success) {
|
|
19
|
+
this.success = success;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.RuntimeWiringCheckResult = RuntimeWiringCheckResult;
|
|
23
|
+
// webpieces-disable no-function-outside-class -- Nx executor process entry point
|
|
24
|
+
async function executor(options, context) {
|
|
25
|
+
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
26
|
+
try {
|
|
27
|
+
const infos = await (0, graph_metadata_1.collectProjectInfo)();
|
|
28
|
+
const info = context.projectName === undefined ? undefined : infos.get(context.projectName);
|
|
29
|
+
if (info === undefined)
|
|
30
|
+
throw new rules_config_1.RuleFailError('validate-runtime-architecture', 'runtime-wiring-check requires a named project.', undefined, undefined, [
|
|
31
|
+
new rules_config_1.Option('Run nx run <project>:runtime-wiring-check on a tagged project.', true),
|
|
32
|
+
]);
|
|
33
|
+
new verification_1.RuntimeWiringVerification().verify(context.root, info, infos, options.outputPath);
|
|
34
|
+
return { success: true };
|
|
35
|
+
}
|
|
36
|
+
catch (err) {
|
|
37
|
+
const error = (0, toError_1.toError)(err);
|
|
38
|
+
console.error(error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message);
|
|
39
|
+
return { success: false };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=executor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/runtime-wiring-check/executor.ts"],"names":[],"mappings":";;;AAeA,2BA8BC;AA5CD,0DAAwF;AACxF,6DAA8D;AAC9D,wEAAkF;AAClF,2CAAwC;AAExC,MAAa,yBAAyB;IACN;IAA5B,YAA4B,UAAkB;QAAlB,eAAU,GAAV,UAAU,CAAQ;IAAG,CAAC;CACrD;AAFD,8DAEC;AAED,MAAa,wBAAwB;IACL;IAA5B,YAA4B,OAAgB;QAAhB,YAAO,GAAP,OAAO,CAAS;IAAG,CAAC;CACnD;AAFD,4DAEC;AAED,iFAAiF;AAClE,KAAK,UAAU,QAAQ,CAClC,OAAkC,EAClC,OAAwB;IAExB,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,IAAA,mCAAkB,GAAE,CAAC;QACzC,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAC5F,IAAI,IAAI,KAAK,SAAS;YAClB,MAAM,IAAI,4BAAa,CACnB,+BAA+B,EAC/B,gDAAgD,EAChD,SAAS,EACT,SAAS,EACT;gBACI,IAAI,qBAAM,CACN,gEAAgE,EAChE,IAAI,CACP;aACJ,CACJ,CAAC;QACN,IAAI,wCAAyB,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,CAAC;QACtF,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,OAAO,CAAC,KAAK,CACT,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CACjF,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["import type { ExecutorContext } from '@nx/devkit';\nimport { renderRuleFailForHuman, Option, RuleFailError } from '@webpieces/rules-config';\nimport { collectProjectInfo } from '../../lib/graph-metadata';\nimport { RuntimeWiringVerification } from '../../lib/runtime-wiring/verification';\nimport { toError } from '../../toError';\n\nexport class RuntimeWiringCheckOptions {\n constructor(public readonly outputPath: string) {}\n}\n\nexport class RuntimeWiringCheckResult {\n constructor(public readonly success: boolean) {}\n}\n\n// webpieces-disable no-function-outside-class -- Nx executor process entry point\nexport default async function executor(\n options: RuntimeWiringCheckOptions,\n context: ExecutorContext,\n): Promise<RuntimeWiringCheckResult> {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const infos = await collectProjectInfo();\n const info = context.projectName === undefined ? undefined : infos.get(context.projectName);\n if (info === undefined)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'runtime-wiring-check requires a named project.',\n undefined,\n undefined,\n [\n new Option(\n 'Run nx run <project>:runtime-wiring-check on a tagged project.',\n true,\n ),\n ],\n );\n new RuntimeWiringVerification().verify(context.root, info, infos, options.outputPath);\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n console.error(\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message,\n );\n return { success: false };\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/schema",
|
|
3
|
+
"version": 1,
|
|
4
|
+
"title": "Verify canonical runtime wiring against approved declarations",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"outputPath": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Configured project build output directory for candidates"
|
|
10
|
+
}
|
|
11
|
+
},
|
|
12
|
+
"required": ["outputPath"],
|
|
13
|
+
"additionalProperties": false
|
|
14
|
+
}
|
|
@@ -13,8 +13,10 @@
|
|
|
13
13
|
*/
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.default = runExecutor;
|
|
16
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
16
17
|
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
17
|
-
const
|
|
18
|
+
const api_contract_evidence_1 = require("../../lib/api-usage/api-contract-evidence");
|
|
19
|
+
const runtime_config_1 = require("../../lib/runtime-config");
|
|
18
20
|
const api_lib_tag_validator_1 = require("../../lib/api-usage/api-lib-tag-validator");
|
|
19
21
|
const toError_1 = require("../../toError");
|
|
20
22
|
// webpieces-disable no-function-outside-class -- nx executor entry point (default export), like every sibling executor
|
|
@@ -24,7 +26,7 @@ async function runExecutor(_options, context) {
|
|
|
24
26
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
25
27
|
try {
|
|
26
28
|
const projectInfos = await (0, graph_metadata_1.collectProjectInfo)();
|
|
27
|
-
const scan = new
|
|
29
|
+
const scan = new api_contract_evidence_1.ApiContractEvidence().collect(workspaceRoot, projectInfos, (0, runtime_config_1.loadRuntimeConfig)(workspaceRoot).externalApiPaths);
|
|
28
30
|
const violations = (0, api_lib_tag_validator_1.findApiLibTagViolations)(projectInfos, scan, workspaceRoot);
|
|
29
31
|
if (violations.length === 0) {
|
|
30
32
|
console.log('✅ role:api-lib / role:api-client match the code (every api library exports a contract or only wire types, and vice-versa).');
|
|
@@ -39,7 +41,7 @@ async function runExecutor(_options, context) {
|
|
|
39
41
|
}
|
|
40
42
|
catch (err) {
|
|
41
43
|
const error = (0, toError_1.toError)(err);
|
|
42
|
-
console.error('❌ api-lib tag validation failed:', error.message);
|
|
44
|
+
console.error('❌ api-lib tag validation failed:', error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message);
|
|
43
45
|
return { success: false };
|
|
44
46
|
}
|
|
45
47
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-api-lib-tag/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-api-lib-tag/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;AAsBH,8BAuCC;AA3DD,0DAAgF;AAEhF,6DAA8D;AAC9D,qFAAgF;AAChF,6DAA6D;AAC7D,qFAGmD;AACnD,2CAAwC;AAUxC,uHAAuH;AACxG,KAAK,UAAU,WAAW,CACrC,QAAkC,EAClC,OAAwB;IAExB,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,OAAO,CAAC,GAAG,CAAC,6DAA6D,CAAC,CAAC;IAE3E,8DAA8D;IAC9D,IAAI,CAAC;QACD,MAAM,YAAY,GAAG,MAAM,IAAA,mCAAkB,GAAE,CAAC;QAChD,MAAM,IAAI,GAAG,IAAI,2CAAmB,EAAE,CAAC,OAAO,CAC1C,aAAa,EACb,YAAY,EACZ,IAAA,kCAAiB,EAAC,aAAa,CAAC,CAAC,gBAAgB,CACpD,CAAC;QACF,MAAM,UAAU,GAAG,IAAA,+CAAuB,EAAC,YAAY,EAAE,IAAI,EAAE,aAAa,CAAC,CAAC;QAE9E,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CACP,4HAA4H,CAC/H,CAAC;YACF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;QAC7B,CAAC;QAED,OAAO,CAAC,KAAK,CAAC,KAAK,UAAU,CAAC,MAAM,wCAAwC,CAAC,CAAC;QAC9E,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;YACjC,OAAO,CAAC,KAAK,CAAC,IAAA,kDAA0B,EAAC,SAAS,CAAC,CAAC,CAAC;YACrD,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACpB,MAAM,KAAK,GAAG,IAAA,iBAAO,EAAC,GAAG,CAAC,CAAC;QAC3B,OAAO,CAAC,KAAK,CACT,kCAAkC,EAClC,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CACjF,CAAC;QACF,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","sourcesContent":["/**\n * Validate API-lib Tag Executor\n *\n * Fails when the api roles and the code disagree, in either direction: a project\n * tagged role:api-lib that exports neither a contract (@ApiPath/@Rpc/@PubSub,\n * @WpInternal/@WpIpcEndpoint, or an in-process abstract …Api) nor only wire types,\n * a role:api-client that exports no contract, or a project that exports an\n * @ApiPath/@Rpc/@PubSub abstract class but carries neither api role (#1064, D3).\n *\n * Usage:\n * nx run architecture:validate-api-lib-tag\n */\n\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { collectProjectInfo } from '../../lib/graph-metadata';\nimport { ApiContractEvidence } from '../../lib/api-usage/api-contract-evidence';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport {\n findApiLibTagViolations,\n describeApiLibTagViolation,\n} from '../../lib/api-usage/api-lib-tag-validator';\nimport { toError } from '../../toError';\n\nexport interface ValidateApiLibTagOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n// webpieces-disable no-function-outside-class -- nx executor entry point (default export), like every sibling executor\nexport default async function runExecutor(\n _options: ValidateApiLibTagOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n\n console.log('\\n🏷️ Validating role:api-lib tag ⇔ API contract exports\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n const projectInfos = await collectProjectInfo();\n const scan = new ApiContractEvidence().collect(\n workspaceRoot,\n projectInfos,\n loadRuntimeConfig(workspaceRoot).externalApiPaths,\n );\n const violations = findApiLibTagViolations(projectInfos, scan, workspaceRoot);\n\n if (violations.length === 0) {\n console.log(\n '✅ role:api-lib / role:api-client match the code (every api library exports a contract or only wire types, and vice-versa).',\n );\n return { success: true };\n }\n\n console.error(`❌ ${violations.length} role:api-lib tag/code mismatch(es):\\n`);\n for (const violation of violations) {\n console.error(describeApiLibTagViolation(violation));\n console.error('');\n }\n return { success: false };\n } catch (err: unknown) {\n const error = toError(err);\n console.error(\n '❌ api-lib tag validation failed:',\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message,\n );\n return { success: false };\n }\n}\n"]}
|
|
@@ -12,11 +12,13 @@
|
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
14
|
exports.default = runExecutor;
|
|
15
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
15
16
|
const graph_generator_1 = require("../../lib/graph-generator");
|
|
16
17
|
const graph_sorter_1 = require("../../lib/graph-sorter");
|
|
17
18
|
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
18
|
-
const
|
|
19
|
-
const
|
|
19
|
+
const approved_graph_1 = require("../../lib/runtime-wiring/approved-graph");
|
|
20
|
+
const graph_loader_1 = require("../../lib/graph-loader");
|
|
21
|
+
const api_contract_files_1 = require("../../lib/api-contract-files");
|
|
20
22
|
const api_relations_validator_1 = require("../../lib/api-usage/api-relations-validator");
|
|
21
23
|
const toError_1 = require("../../toError");
|
|
22
24
|
// webpieces-disable no-function-outside-class -- nx executor entry point (default export), like every sibling executor
|
|
@@ -30,11 +32,12 @@ async function runExecutor(_options, context) {
|
|
|
30
32
|
const rawGraph = await (0, graph_generator_1.generateGraph)();
|
|
31
33
|
const graph = (0, graph_sorter_1.sortGraphTopologically)(rawGraph);
|
|
32
34
|
const projectInfos = await (0, graph_metadata_1.collectProjectInfo)();
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
const
|
|
35
|
+
new approved_graph_1.ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos);
|
|
36
|
+
const saved = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot);
|
|
37
|
+
if (saved === null)
|
|
38
|
+
return { success: false };
|
|
39
|
+
const contracts = new api_contract_files_1.ApiContractFiles().load(workspaceRoot, 'architecture/dependencies.json', saved.apiContractFiles);
|
|
40
|
+
const scan = new api_relations_validator_1.ApiRelationEvidence(new Set(Object.keys(graph).filter((name) => graph[name].runtimeComposition)), new Set(Object.values(contracts).map((contract) => contract.owner)), [], new Map(Object.entries(contracts).map((entry) => [entry[0], new api_relations_validator_1.ApiOwnerIdentity(entry[0], entry[1].owner)])));
|
|
38
41
|
const violations = (0, api_relations_validator_1.findUnclassifiedApiDeps)(graph, projectInfos, scan);
|
|
39
42
|
if (violations.length === 0) {
|
|
40
43
|
console.log('✅ Every server/client api-lib dependency is implemented or used.');
|
|
@@ -49,7 +52,7 @@ async function runExecutor(_options, context) {
|
|
|
49
52
|
}
|
|
50
53
|
catch (err) {
|
|
51
54
|
const error = (0, toError_1.toError)(err);
|
|
52
|
-
console.error('❌ API relations validation failed:', error.message);
|
|
55
|
+
console.error('❌ API relations validation failed:', error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message);
|
|
53
56
|
return { success: false };
|
|
54
57
|
}
|
|
55
58
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-api-relations/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-api-relations/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate API Relations Executor\n *\n * Fails when a runnable project (role:server / role:client) depends on an api-lib\n * but the source scan finds it neither IMPLEMENTS (serves via addRoutes) nor USES\n * (calls via createRpcClient/createPubSubClient) any of that api-lib's contracts.\n * Such an edge is an unexplained dependency — a dead import or forgotten wiring.\n *\n * Usage:\n * nx run architecture:validate-api-relations\n */\n\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { generateGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { collectProjectInfo } from '../../lib/graph-metadata';\nimport { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport { loadBlessedGraph } from '../../lib/graph-loader';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport {\n ApiRelationEvidence,\n ApiOwnerIdentity,\n findUnclassifiedApiDeps,\n describeUnclassifiedApiDep,\n} from '../../lib/api-usage/api-relations-validator';\nimport { toError } from '../../toError';\n\nexport interface ValidateApiRelationsOptions {\n // No options needed\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n// webpieces-disable no-function-outside-class -- nx executor entry point (default export), like every sibling executor\nexport default async function runExecutor(\n _options: ValidateApiRelationsOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n\n console.log('\\n🔗 Validating API Relations (implements/uses per api-lib dependency)\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Full (un-reduced) graph so a direct api-lib dependency is never hidden by\n // transitive reduction, then attach the derived apiRelations.\n const rawGraph = await generateGraph();\n const graph = sortGraphTopologically(rawGraph);\n const projectInfos = await collectProjectInfo();\n new ApprovedWiringGraph().attach(workspaceRoot, graph, projectInfos);\n const saved = loadBlessedGraph(workspaceRoot);\n if (saved === null) return { success: false };\n const contracts = new ApiContractFiles().load(\n workspaceRoot,\n 'architecture/dependencies.json',\n saved.apiContractFiles,\n );\n const scan = new ApiRelationEvidence(\n new Set(Object.keys(graph).filter((name) => graph[name].runtimeComposition)),\n new Set(Object.values(contracts).map((contract) => contract.owner)),\n [],\n new Map(\n Object.entries(contracts).map(\n (entry) => [entry[0], new ApiOwnerIdentity(entry[0], entry[1].owner)] as const,\n ),\n ),\n );\n const violations = findUnclassifiedApiDeps(graph, projectInfos, scan);\n if (violations.length === 0) {\n console.log('✅ Every server/client api-lib dependency is implemented or used.');\n return { success: true };\n }\n\n console.error(`❌ ${violations.length} unexplained api-lib dependenc(ies):\\n`);\n for (const violation of violations) {\n console.error(describeUnclassifiedApiDep(violation));\n console.error('');\n }\n return { success: false };\n } catch (err: unknown) {\n const error = toError(err);\n console.error(\n '❌ API relations validation failed:',\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message,\n );\n return { success: false };\n }\n}\n"]}
|
|
@@ -30,7 +30,7 @@ export declare function describeTableDrift(current: CurrentArchitecture, saved:
|
|
|
30
30
|
* apiRelations — so this validator compares like-for-like against the committed file.
|
|
31
31
|
*/
|
|
32
32
|
export declare class CurrentGraphBuilder {
|
|
33
|
-
build(workspaceRoot: string): Promise<CurrentArchitecture>;
|
|
33
|
+
build(workspaceRoot: string, graphPath?: string): Promise<CurrentArchitecture>;
|
|
34
34
|
}
|
|
35
35
|
/** The regenerated graph plus the two tables beside it — everything dependencies.json holds. */
|
|
36
36
|
export declare class CurrentArchitecture {
|