@webpieces/nx-webpieces-rules 0.4.854 → 0.4.856
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/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/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-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-sorter.d.ts +2 -0
- package/src/lib/graph-sorter.js.map +1 -1
- package/src/lib/graph-visualizer.d.ts +0 -11
- package/src/lib/graph-visualizer.js +13 -9
- 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 +2 -1
- package/src/lib/runtime-html-page.js +6 -1
- package/src/lib/runtime-html-page.js.map +1 -1
- package/src/lib/runtime-visualizer.d.ts +0 -46
- package/src/lib/runtime-visualizer.js +29 -43
- 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/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.856",
|
|
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.856",
|
|
24
|
+
"@webpieces/core-util": "0.4.856",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.856",
|
|
26
|
+
"@webpieces/rules-config": "0.4.856",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.856"
|
|
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.856"
|
|
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 {
|
|
@@ -19,10 +19,7 @@ const graph_comparator_1 = require("../../lib/graph-comparator");
|
|
|
19
19
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
20
20
|
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
21
21
|
const tag_truth_1 = require("../../lib/tag-truth");
|
|
22
|
-
const
|
|
23
|
-
const external_systems_1 = require("../../lib/api-usage/external-systems");
|
|
24
|
-
const api_contract_files_1 = require("../../lib/api-contract-files");
|
|
25
|
-
const runtime_config_1 = require("../../lib/runtime-config");
|
|
22
|
+
const approved_graph_1 = require("../../lib/runtime-wiring/approved-graph");
|
|
26
23
|
const rule_gate_1 = require("../../lib/rule-gate");
|
|
27
24
|
const toError_1 = require("../../toError");
|
|
28
25
|
const TMP_MD_FILE = 'webpieces.dependencies.md';
|
|
@@ -113,7 +110,7 @@ function describeExternalSystemDrift(current, saved) {
|
|
|
113
110
|
* apiRelations — so this validator compares like-for-like against the committed file.
|
|
114
111
|
*/
|
|
115
112
|
class CurrentGraphBuilder {
|
|
116
|
-
async build(workspaceRoot) {
|
|
113
|
+
async build(workspaceRoot, graphPath) {
|
|
117
114
|
console.log('📊 Generating current dependency graph...');
|
|
118
115
|
const reducedGraph = await (0, graph_generator_1.generateReducedGraph)();
|
|
119
116
|
console.log('🔄 Computing topological layers...');
|
|
@@ -122,12 +119,13 @@ class CurrentGraphBuilder {
|
|
|
122
119
|
const projectInfos = await (0, graph_metadata_1.collectProjectInfo)();
|
|
123
120
|
(0, graph_metadata_1.enrichGraph)(currentGraph, projectInfos, workspaceRoot);
|
|
124
121
|
new tag_truth_1.TagTruthCheck().assertTrue(currentGraph, projectInfos, workspaceRoot);
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
122
|
+
new approved_graph_1.ApprovedWiringGraph().attach(workspaceRoot, currentGraph, projectInfos, graphPath);
|
|
123
|
+
const saved = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
124
|
+
if (saved === null)
|
|
125
|
+
throw new rules_config_1.RuleFailError('validate-runtime-architecture', 'Missing approved graph.', undefined, undefined, [
|
|
126
|
+
new rules_config_1.Option('Migrate src/wiring.ts and review runtime-deps.json and API contract approvals.', true),
|
|
127
|
+
]);
|
|
128
|
+
return new CurrentArchitecture(currentGraph, saved.apiContractFiles, saved.externalSystems);
|
|
131
129
|
}
|
|
132
130
|
}
|
|
133
131
|
exports.CurrentGraphBuilder = CurrentGraphBuilder;
|
|
@@ -146,13 +144,9 @@ exports.CurrentArchitecture = CurrentArchitecture;
|
|
|
146
144
|
/** The bootstrap path: there is nothing to diff against yet, so say how to create it. */
|
|
147
145
|
// webpieces-disable no-function-outside-class -- executor step helper, matches reportMismatch in this file
|
|
148
146
|
function reportMissingGraph() {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
console.error(' 1. Run: nx run architecture:generate');
|
|
153
|
-
console.error(' 2. Run: nx run architecture:visualize');
|
|
154
|
-
console.error(' 3. Manually inspect the generated graph to confirm it is the desired architecture');
|
|
155
|
-
console.error(' 4. Commit architecture/dependencies.json');
|
|
147
|
+
throw new rules_config_1.RuleFailError('validate-runtime-architecture', 'Missing reviewed architecture/dependencies.json.', undefined, undefined, [
|
|
148
|
+
new rules_config_1.Option('Run owning API source build checks to emit contract candidates, review and copy them into architecture/apis/<ApiName>.json, and create dependencies.json with projects:{}, apiContractFiles:{"<ApiName>":"apis/<ApiName>.json"}, and reviewed externalSystems. Approve tagged owners runtime-deps.json candidates, then run architecture:generate and review the resulting graph.', true),
|
|
149
|
+
]);
|
|
156
150
|
}
|
|
157
151
|
async function runExecutor(options, context) {
|
|
158
152
|
const graphPath = options.graphPath;
|
|
@@ -172,7 +166,7 @@ async function runExecutor(options, context) {
|
|
|
172
166
|
}
|
|
173
167
|
// Steps 1-3: build + enrich + scan the current graph (same pipeline the
|
|
174
168
|
// generator runs, so any drift is caught).
|
|
175
|
-
const currentGraph = await new CurrentGraphBuilder().build(workspaceRoot);
|
|
169
|
+
const currentGraph = await new CurrentGraphBuilder().build(workspaceRoot, graphPath);
|
|
176
170
|
// Step 4: Load saved graph
|
|
177
171
|
console.log('📂 Loading saved graph...');
|
|
178
172
|
const savedGraph = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
@@ -204,12 +198,16 @@ async function runExecutor(options, context) {
|
|
|
204
198
|
// carries its cures in Option[]; render them rather than dropping them with `.message`.
|
|
205
199
|
const rendered = error instanceof rules_config_1.RuleFailError ? (0, rules_config_1.renderRuleFailForHuman)(error) : error.message;
|
|
206
200
|
console.error('❌ Architecture validation failed:', rendered);
|
|
207
|
-
|
|
208
|
-
const mdPath = (0, rules_config_1.writeTemplate)(workspaceRoot, 'webpieces.responsibilities.md');
|
|
209
|
-
console.error('');
|
|
210
|
-
console.error('⚠️ *** Refer to ' + mdPath + ' for how to author responsibilities.md files *** ⚠️');
|
|
211
|
-
}
|
|
201
|
+
reportMetadataFailure(error, workspaceRoot);
|
|
212
202
|
return { success: false };
|
|
213
203
|
}
|
|
214
204
|
}
|
|
205
|
+
// webpieces-disable no-function-outside-class -- executor diagnostic formatting
|
|
206
|
+
function reportMetadataFailure(error, workspaceRoot) {
|
|
207
|
+
if (error instanceof graph_metadata_1.MetadataValidationError) {
|
|
208
|
+
const mdPath = (0, rules_config_1.writeTemplate)(workspaceRoot, 'webpieces.responsibilities.md');
|
|
209
|
+
console.error('');
|
|
210
|
+
console.error('⚠️ *** Refer to ' + mdPath + ' for how to author responsibilities.md files *** ⚠️');
|
|
211
|
+
}
|
|
212
|
+
}
|
|
215
213
|
//# sourceMappingURL=executor.js.map
|