@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.
Files changed (110) hide show
  1. package/README.md +14 -0
  2. package/executors.json +5 -0
  3. package/package.json +7 -7
  4. package/src/executors/generate/executor.js +31 -55
  5. package/src/executors/generate/executor.js.map +1 -1
  6. package/src/executors/runtime-wiring-check/executor.d.ts +10 -0
  7. package/src/executors/runtime-wiring-check/executor.js +42 -0
  8. package/src/executors/runtime-wiring-check/executor.js.map +1 -0
  9. package/src/executors/runtime-wiring-check/schema.json +14 -0
  10. package/src/executors/validate-api-lib-tag/executor.js +5 -3
  11. package/src/executors/validate-api-lib-tag/executor.js.map +1 -1
  12. package/src/executors/validate-api-relations/executor.js +11 -8
  13. package/src/executors/validate-api-relations/executor.js.map +1 -1
  14. package/src/executors/validate-architecture-unchanged/executor.d.ts +1 -1
  15. package/src/executors/validate-architecture-unchanged/executor.js +22 -24
  16. package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
  17. package/src/index.d.ts +1 -0
  18. package/src/index.js +5 -1
  19. package/src/index.js.map +1 -1
  20. package/src/lib/api-usage/api-contract-approval.d.ts +7 -0
  21. package/src/lib/api-usage/api-contract-approval.js +50 -0
  22. package/src/lib/api-usage/api-contract-approval.js.map +1 -0
  23. package/src/lib/api-usage/api-contract-evidence.d.ts +6 -0
  24. package/src/lib/api-usage/api-contract-evidence.js +41 -0
  25. package/src/lib/api-usage/api-contract-evidence.js.map +1 -0
  26. package/src/lib/api-usage/api-relations-validator.d.ts +13 -2
  27. package/src/lib/api-usage/api-relations-validator.js +23 -0
  28. package/src/lib/api-usage/api-relations-validator.js.map +1 -1
  29. package/src/lib/api-usage/api-relations.d.ts +3 -0
  30. package/src/lib/api-usage/api-relations.js.map +1 -1
  31. package/src/lib/api-usage/api-scanner.d.ts +40 -0
  32. package/src/lib/api-usage/api-scanner.js +7 -1
  33. package/src/lib/api-usage/api-scanner.js.map +1 -1
  34. package/src/lib/graph-comparator.js +11 -1
  35. package/src/lib/graph-comparator.js.map +1 -1
  36. package/src/lib/graph-filter.client.d.ts +40 -0
  37. package/src/lib/graph-filter.client.js +190 -0
  38. package/src/lib/graph-filter.client.js.map +1 -0
  39. package/src/lib/graph-level-bands.d.ts +0 -18
  40. package/src/lib/graph-level-bands.js +4 -3
  41. package/src/lib/graph-level-bands.js.map +1 -1
  42. package/src/lib/graph-loader.js +12 -6
  43. package/src/lib/graph-loader.js.map +1 -1
  44. package/src/lib/graph-navigation.d.ts +5 -0
  45. package/src/lib/graph-navigation.js +102 -0
  46. package/src/lib/graph-navigation.js.map +1 -0
  47. package/src/lib/graph-node-menu.d.ts +1 -0
  48. package/src/lib/graph-node-menu.js +29 -0
  49. package/src/lib/graph-node-menu.js.map +1 -1
  50. package/src/lib/graph-render-model.d.ts +32 -0
  51. package/src/lib/graph-render-model.js +76 -0
  52. package/src/lib/graph-render-model.js.map +1 -0
  53. package/src/lib/graph-sorter.d.ts +2 -0
  54. package/src/lib/graph-sorter.js.map +1 -1
  55. package/src/lib/graph-visualizer.client.d.ts +14 -73
  56. package/src/lib/graph-visualizer.client.js +71 -243
  57. package/src/lib/graph-visualizer.client.js.map +1 -1
  58. package/src/lib/graph-visualizer.d.ts +8 -15
  59. package/src/lib/graph-visualizer.js +48 -26
  60. package/src/lib/graph-visualizer.js.map +1 -1
  61. package/src/lib/runtime-details.d.ts +13 -0
  62. package/src/lib/runtime-details.js +191 -0
  63. package/src/lib/runtime-details.js.map +1 -0
  64. package/src/lib/runtime-graph-model.d.ts +4 -1
  65. package/src/lib/runtime-graph-model.js.map +1 -1
  66. package/src/lib/runtime-graph.js +10 -4
  67. package/src/lib/runtime-graph.js.map +1 -1
  68. package/src/lib/runtime-html-page.d.ts +7 -3
  69. package/src/lib/runtime-html-page.js +19 -6
  70. package/src/lib/runtime-html-page.js.map +1 -1
  71. package/src/lib/runtime-visualizer.client.d.ts +8 -19
  72. package/src/lib/runtime-visualizer.client.js +28 -41
  73. package/src/lib/runtime-visualizer.client.js.map +1 -1
  74. package/src/lib/runtime-visualizer.d.ts +3 -45
  75. package/src/lib/runtime-visualizer.js +77 -79
  76. package/src/lib/runtime-visualizer.js.map +1 -1
  77. package/src/lib/runtime-viz-theme.js +4 -3
  78. package/src/lib/runtime-viz-theme.js.map +1 -1
  79. package/src/lib/runtime-wiring/DeclarationCompileAssertions.d.ts +4 -0
  80. package/src/lib/runtime-wiring/DeclarationCompileAssertions.js +27 -0
  81. package/src/lib/runtime-wiring/DeclarationCompileAssertions.js.map +1 -0
  82. package/src/lib/runtime-wiring/approved-graph.d.ts +8 -0
  83. package/src/lib/runtime-wiring/approved-graph.js +115 -0
  84. package/src/lib/runtime-wiring/approved-graph.js.map +1 -0
  85. package/src/lib/runtime-wiring/assembler.d.ts +24 -0
  86. package/src/lib/runtime-wiring/assembler.js +116 -0
  87. package/src/lib/runtime-wiring/assembler.js.map +1 -0
  88. package/src/lib/runtime-wiring/codec.d.ts +14 -0
  89. package/src/lib/runtime-wiring/codec.js +162 -0
  90. package/src/lib/runtime-wiring/codec.js.map +1 -0
  91. package/src/lib/runtime-wiring/declaration.d.ts +68 -0
  92. package/src/lib/runtime-wiring/declaration.js +88 -0
  93. package/src/lib/runtime-wiring/declaration.js.map +1 -0
  94. package/src/lib/runtime-wiring/source-extractor.d.ts +28 -0
  95. package/src/lib/runtime-wiring/source-extractor.js +253 -0
  96. package/src/lib/runtime-wiring/source-extractor.js.map +1 -0
  97. package/src/lib/runtime-wiring/source-values.d.ts +21 -0
  98. package/src/lib/runtime-wiring/source-values.js +204 -0
  99. package/src/lib/runtime-wiring/source-values.js.map +1 -0
  100. package/src/lib/runtime-wiring/verification.d.ts +9 -0
  101. package/src/lib/runtime-wiring/verification.js +56 -0
  102. package/src/lib/runtime-wiring/verification.js.map +1 -0
  103. package/src/plugin.d.ts +0 -21
  104. package/src/plugin.js +12 -7
  105. package/src/plugin.js.map +1 -1
  106. package/src/runtime-wiring-targets.d.ts +7 -0
  107. package/src/runtime-wiring-targets.js +58 -0
  108. package/src/runtime-wiring-targets.js.map +1 -0
  109. package/src/validation-targets.js +10 -2
  110. 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.853",
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.853",
24
- "@webpieces/core-util": "0.4.853",
25
- "@webpieces/eslint-rules": "0.4.853",
26
- "@webpieces/rules-config": "0.4.853",
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.853"
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.853"
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 graph_loader_1 = require("../../lib/graph-loader");
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('🔎 Scanning source for implements/uses API relations...');
76
- const externalApiPaths = (0, runtime_config_1.loadRuntimeConfig)(workspaceRoot).externalApiPaths;
77
- const scan = (0, api_scanner_1.scanAndAttachApiRelations)(workspaceRoot, graph, projectInfos, externalApiPaths);
78
- if (scan.unresolvedApiCalls.length > 0)
79
- console.warn((0, api_contract_errors_1.describeUnresolvedApiCalls)(scan.unresolvedApiCalls));
80
- // #1014: restated on EVERY run, green or red. An exclusion announced once, at the moment
81
- // somebody adds it, is read by the one person who already knows the decision.
82
- if (scan.apiDocRules.mcpExclusions.length > 0) {
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 effectiveGraphPath = graphPath ?? graph_loader_1.DEFAULT_GRAPH_PATH;
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, graph_loader_1.saveGraph)(enhancedGraph, workspaceRoot, effectiveGraphPath, contractRefs, scanned.externalSystems);
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 ' + mdPath + ' for how to author responsibilities.md files *** ⚠️');
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 api_scanner_1 = require("../../lib/api-usage/api-scanner");
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 api_scanner_1.ApiUsageScanner(workspaceRoot, projectInfos).scan();
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;;AAiBH,8BA8BC;AA5CD,6DAA8D;AAC9D,iEAAkE;AAClE,qFAAgH;AAChH,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,6BAAe,CAAC,aAAa,EAAE,YAAY,CAAC,CAAC,IAAI,EAAE,CAAC;QACrE,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,CAAC,4HAA4H,CAAC,CAAC;YAC1I,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,CAAC,kCAAkC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACjE,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 type { ExecutorContext } from '@nx/devkit';\nimport { collectProjectInfo } from '../../lib/graph-metadata';\nimport { ApiUsageScanner } from '../../lib/api-usage/api-scanner';\nimport { findApiLibTagViolations, describeApiLibTagViolation } 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 ApiUsageScanner(workspaceRoot, projectInfos).scan();\n const violations = findApiLibTagViolations(projectInfos, scan, workspaceRoot);\n\n if (violations.length === 0) {\n console.log('✅ role:api-lib / role:api-client match the code (every api library exports a contract or only wire types, and vice-versa).');\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('❌ api-lib tag validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
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 api_scanner_1 = require("../../lib/api-usage/api-scanner");
19
- const runtime_config_1 = require("../../lib/runtime-config");
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
- // Same externalApiPaths as generate: a vendor lib must be classified by the SAME rules here,
34
- // or a server depending on one would be reported unclassified purely because this scan was
35
- // configured differently from the one that wrote the graph.
36
- const externalApiPaths = (0, runtime_config_1.loadRuntimeConfig)(workspaceRoot).externalApiPaths;
37
- const scan = (0, api_scanner_1.scanAndAttachApiRelations)(workspaceRoot, graph, projectInfos, externalApiPaths);
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;;AAoBH,8BAsCC;AAvDD,+DAA0D;AAC1D,yDAAgE;AAChE,6DAA8D;AAC9D,iEAA4E;AAC5E,6DAA6D;AAC7D,yFAAkH;AAClH,2CAAwC;AAUxC,uHAAuH;AACxG,KAAK,UAAU,WAAW,CACrC,QAAqC,EACrC,OAAwB;IAExB,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,OAAO,CAAC,GAAG,CAAC,0EAA0E,CAAC,CAAC;IAExF,8DAA8D;IAC9D,IAAI,CAAC;QACD,4EAA4E;QAC5E,8DAA8D;QAC9D,MAAM,QAAQ,GAAG,MAAM,IAAA,+BAAa,GAAE,CAAC;QACvC,MAAM,KAAK,GAAG,IAAA,qCAAsB,EAAC,QAAQ,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,MAAM,IAAA,mCAAkB,GAAE,CAAC;QAChD,6FAA6F;QAC7F,2FAA2F;QAC3F,4DAA4D;QAC5D,MAAM,gBAAgB,GAAG,IAAA,kCAAiB,EAAC,aAAa,CAAC,CAAC,gBAAgB,CAAC;QAC3E,MAAM,IAAI,GAAG,IAAA,uCAAyB,EAAC,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,gBAAgB,CAAC,CAAC;QAE7F,MAAM,UAAU,GAAG,IAAA,iDAAuB,EAAC,KAAK,EAAE,YAAY,EAAE,IAAI,CAAC,CAAC;QACtE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC1B,OAAO,CAAC,GAAG,CAAC,kEAAkE,CAAC,CAAC;YAChF,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,oDAA0B,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,CAAC,oCAAoC,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;QACnE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC","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 type { ExecutorContext } from '@nx/devkit';\nimport { generateGraph } from '../../lib/graph-generator';\nimport { sortGraphTopologically } from '../../lib/graph-sorter';\nimport { collectProjectInfo } from '../../lib/graph-metadata';\nimport { scanAndAttachApiRelations } from '../../lib/api-usage/api-scanner';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport { findUnclassifiedApiDeps, describeUnclassifiedApiDep } 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 // Same externalApiPaths as generate: a vendor lib must be classified by the SAME rules here,\n // or a server depending on one would be reported unclassified purely because this scan was\n // configured differently from the one that wrote the graph.\n const externalApiPaths = loadRuntimeConfig(workspaceRoot).externalApiPaths;\n const scan = scanAndAttachApiRelations(workspaceRoot, graph, projectInfos, externalApiPaths);\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('❌ API relations validation failed:', error.message);\n return { success: false };\n }\n}\n"]}
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 {