@webpieces/nx-webpieces-rules 0.4.920 → 0.4.922
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/package.json +7 -7
- package/src/executors/generate/executor.d.ts +7 -0
- package/src/executors/generate/executor.js +13 -5
- package/src/executors/generate/executor.js.map +1 -1
- package/src/executors/validate-architecture-unchanged/executor.d.ts +1 -20
- package/src/executors/validate-architecture-unchanged/executor.js +2 -45
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/executors/visualize/executor.d.ts +8 -1
- package/src/executors/visualize/executor.js +11 -3
- package/src/executors/visualize/executor.js.map +1 -1
- package/src/executors/visualize-runtime/executor.js +2 -1
- package/src/executors/visualize-runtime/executor.js.map +1 -1
- package/src/executors/visualize-runtime-goal/executor.js +2 -1
- package/src/executors/visualize-runtime-goal/executor.js.map +1 -1
- package/src/lib/current-architecture.d.ts +26 -0
- package/src/lib/current-architecture.js +56 -0
- package/src/lib/current-architecture.js.map +1 -0
- package/src/lib/graph-edge-diff.d.ts +115 -0
- package/src/lib/graph-edge-diff.js +263 -0
- package/src/lib/graph-edge-diff.js.map +1 -0
- package/src/lib/graph-impact.d.ts +21 -4
- package/src/lib/graph-impact.js +33 -6
- package/src/lib/graph-impact.js.map +1 -1
- package/src/lib/graph-legend.d.ts +4 -0
- package/src/lib/graph-legend.js +14 -1
- package/src/lib/graph-legend.js.map +1 -1
- package/src/lib/graph-mode-state.client.d.ts +64 -0
- package/src/lib/graph-mode-state.client.js +209 -3
- package/src/lib/graph-mode-state.client.js.map +1 -1
- package/src/lib/graph-navigation.js +4 -2
- package/src/lib/graph-navigation.js.map +1 -1
- package/src/lib/graph-page-shell.d.ts +3 -1
- package/src/lib/graph-page-shell.js +3 -0
- package/src/lib/graph-page-shell.js.map +1 -1
- package/src/lib/graph-visualizer.client.d.ts +3 -2
- package/src/lib/graph-visualizer.client.js +26 -13
- package/src/lib/graph-visualizer.client.js.map +1 -1
- package/src/lib/runtime-html-page.js +2 -2
- package/src/lib/runtime-html-page.js.map +1 -1
- package/src/native-actions.js +1 -1
- package/src/native-actions.js.map +1 -1
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.922",
|
|
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.922",
|
|
24
|
+
"@webpieces/core-util": "0.4.922",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.922",
|
|
26
|
+
"@webpieces/rules-config": "0.4.922",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.922"
|
|
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.922"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -26,5 +26,12 @@ export declare class ArchitectureGenerator {
|
|
|
26
26
|
private readonly impact;
|
|
27
27
|
constructor(visualizer: GraphVisualizer, impact?: ImpactRefresh);
|
|
28
28
|
generate(workspaceRoot: string, graphPath: string | undefined): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* The page's Impact mode — nx affected for "Changed on this branch" ONLY, into a gitignored
|
|
31
|
+
* sidecar beside the html (never into the committed files), plus the edges gained and lost since
|
|
32
|
+
* the fork point, against the two graphs just built. Generate never scans the last commit; that
|
|
33
|
+
* comparison is arch:visualize's. Optional: it reports, it never throws.
|
|
34
|
+
*/
|
|
35
|
+
private refreshImpact;
|
|
29
36
|
}
|
|
30
37
|
export default function runExecutor(options: GenerateExecutorOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -15,6 +15,7 @@ const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
|
15
15
|
const tag_truth_1 = require("../../lib/tag-truth");
|
|
16
16
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
17
17
|
const graph_impact_1 = require("../../lib/graph-impact");
|
|
18
|
+
const graph_edge_diff_1 = require("../../lib/graph-edge-diff");
|
|
18
19
|
const path = tslib_1.__importStar(require("path"));
|
|
19
20
|
const runtime_graph_1 = require("../../lib/runtime-graph");
|
|
20
21
|
const runtime_participant_resolver_1 = require("../../lib/runtime-participant-resolver");
|
|
@@ -46,6 +47,7 @@ function generateRuntimeGraph(workspaceRoot, graph, hiddenProjects, apiContracts
|
|
|
46
47
|
console.error(` • ${problem}`);
|
|
47
48
|
console.error(' This FAILS architecture:validate-runtime-architecture.');
|
|
48
49
|
}
|
|
50
|
+
return report.graph;
|
|
49
51
|
}
|
|
50
52
|
/** Print the derivation's guessed-edge warnings (nothing at all when the graph is fully targeted). */
|
|
51
53
|
// webpieces-disable no-function-outside-class -- executor step helper, like the rest of this executor file
|
|
@@ -152,16 +154,22 @@ class ArchitectureGenerator {
|
|
|
152
154
|
const productDependencies = tag_truth_1.TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;
|
|
153
155
|
const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot, productDependencies);
|
|
154
156
|
console.log(`✅ Wrote ${vizPaths.htmlPath}`);
|
|
155
|
-
// Step 4c: The page's Impact mode — nx affected for "Changed on this branch" ONLY, into a
|
|
156
|
-
// gitignored sidecar beside the html (never into the committed files). Generate never scans
|
|
157
|
-
// the last commit; that comparison is arch:visualize's. Optional: it reports, it never throws.
|
|
158
|
-
console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, enhancedGraph, graph_impact_1.GENERATE_IMPACT_KINDS));
|
|
159
157
|
// Step 5: Generate the runtime microservice graph from the same scan.
|
|
160
158
|
// Projects tagged drawOnGraph:false are threaded through so the runtime
|
|
161
159
|
// graph hides them too (they stay flagged in runtime-dependencies.json).
|
|
162
|
-
generateRuntimeGraph(workspaceRoot, enhancedGraph, hiddenProjectsIn(enhancedGraph), apiContracts, scanned.externalSystems, scanned.remoteServices);
|
|
160
|
+
const runtimeGraph = generateRuntimeGraph(workspaceRoot, enhancedGraph, hiddenProjectsIn(enhancedGraph), apiContracts, scanned.externalSystems, scanned.remoteServices);
|
|
161
|
+
await this.refreshImpact(path.dirname(vizPaths.htmlPath), workspaceRoot, new graph_edge_diff_1.LiveGraphs(enhancedGraph, runtimeGraph));
|
|
163
162
|
printGraphSummary(enhancedGraph);
|
|
164
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* The page's Impact mode — nx affected for "Changed on this branch" ONLY, into a gitignored
|
|
166
|
+
* sidecar beside the html (never into the committed files), plus the edges gained and lost since
|
|
167
|
+
* the fork point, against the two graphs just built. Generate never scans the last commit; that
|
|
168
|
+
* comparison is arch:visualize's. Optional: it reports, it never throws.
|
|
169
|
+
*/
|
|
170
|
+
async refreshImpact(architectureDir, workspaceRoot, live) {
|
|
171
|
+
console.log(await this.impact.run(architectureDir, workspaceRoot, live.projects, graph_impact_1.GENERATE_IMPACT_KINDS, new graph_edge_diff_1.FixedLiveGraphs(live)));
|
|
172
|
+
}
|
|
165
173
|
}
|
|
166
174
|
exports.ArchitectureGenerator = ArchitectureGenerator;
|
|
167
175
|
// webpieces-disable no-function-outside-class -- persist the approved graph and contract tables together
|
|
@@ -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":["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, TagTruthRules } 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 type { RemoteServices } from '../../lib/remote-services';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { GENERATE_IMPACT_KINDS, ImpactRefresh } from '../../lib/graph-impact';\nimport * as path from 'path';\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 remoteServices: RemoteServices,\n): void {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(\n graph,\n hiddenProjects,\n apiContracts,\n externalSystems,\n remoteServices,\n );\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 saved.remoteServices,\n );\n}\n\n/** The 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 /** Reviewed remote services (#1203): read from the saved file, written back verbatim. */\n public readonly remoteServices: RemoteServices,\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 */\nexport class ArchitectureGenerator {\n constructor(\n private readonly visualizer: GraphVisualizer,\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n ) {}\n\n async generate(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(\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 // enrichGraph pinned servers, clients and apps to the top row (#1179): prove the promotion\n // kept every dependency strictly below its dependent before anything is written.\n cycles.assertLevelsDescend(reducedGraph, cycles.levelsOf(enhancedGraph), 'the entry-point-promoted graph');\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: the committed, clickable HTML view, regenerated in lock-step with dependencies.json.\n const productDependencies = TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot, productDependencies);\n console.log(`✅ Wrote ${vizPaths.htmlPath}`);\n\n // Step 4c: The page's Impact mode — nx affected for \"Changed on this branch\" ONLY, into a\n // gitignored sidecar beside the html (never into the committed files). Generate never scans\n // the last commit; that comparison is arch:visualize's. Optional: it reports, it never throws.\n console.log(\n await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, enhancedGraph, GENERATE_IMPACT_KINDS),\n );\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 scanned.remoteServices,\n );\n\n printGraphSummary(enhancedGraph);\n }\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 scanned.remoteServices,\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 new ArchitectureGenerator(new GraphVisualizer()).generate(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"]}
|
|
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, TagTruthRules } 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 type { RemoteServices } from '../../lib/remote-services';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { GENERATE_IMPACT_KINDS, ImpactRefresh } from '../../lib/graph-impact';\nimport { FixedLiveGraphs, LiveGraphs } from '../../lib/graph-edge-diff';\nimport type { RuntimeGraph } from '../../lib/runtime-graph-model';\nimport * as path from 'path';\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 remoteServices: RemoteServices,\n): RuntimeGraph {\n console.log('📡 Deriving runtime graph from dependencies.json (implements × uses per API)...');\n const report = deriveRuntimeGraphReport(\n graph,\n hiddenProjects,\n apiContracts,\n externalSystems,\n remoteServices,\n );\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 return report.graph;\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 saved.remoteServices,\n );\n}\n\n/** The 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 /** Reviewed remote services (#1203): read from the saved file, written back verbatim. */\n public readonly remoteServices: RemoteServices,\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 */\nexport class ArchitectureGenerator {\n constructor(\n private readonly visualizer: GraphVisualizer,\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n ) {}\n\n async generate(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(\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 // enrichGraph pinned servers, clients and apps to the top row (#1179): prove the promotion\n // kept every dependency strictly below its dependent before anything is written.\n cycles.assertLevelsDescend(reducedGraph, cycles.levelsOf(enhancedGraph), 'the entry-point-promoted graph');\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: the committed, clickable HTML view, regenerated in lock-step with dependencies.json.\n const productDependencies = TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;\n const vizPaths = this.visualizer.writeVisualization(enhancedGraph, workspaceRoot, productDependencies);\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 const runtimeGraph = generateRuntimeGraph(\n workspaceRoot,\n enhancedGraph,\n hiddenProjectsIn(enhancedGraph),\n apiContracts,\n scanned.externalSystems,\n scanned.remoteServices,\n );\n\n await this.refreshImpact(path.dirname(vizPaths.htmlPath), workspaceRoot, new LiveGraphs(enhancedGraph, runtimeGraph));\n\n printGraphSummary(enhancedGraph);\n }\n\n /**\n * The page's Impact mode — nx affected for \"Changed on this branch\" ONLY, into a gitignored\n * sidecar beside the html (never into the committed files), plus the edges gained and lost since\n * the fork point, against the two graphs just built. Generate never scans the last commit; that\n * comparison is arch:visualize's. Optional: it reports, it never throws.\n */\n private async refreshImpact(architectureDir: string, workspaceRoot: string, live: LiveGraphs): Promise<void> {\n console.log(\n await this.impact.run(architectureDir, workspaceRoot, live.projects, GENERATE_IMPACT_KINDS, new FixedLiveGraphs(live)),\n );\n }\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 scanned.remoteServices,\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 new ArchitectureGenerator(new GraphVisualizer()).generate(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"]}
|
|
@@ -9,10 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
import type { ExecutorContext } from '@nx/devkit';
|
|
11
11
|
import type { DependenciesFile } from '../../lib/graph-loader';
|
|
12
|
-
import
|
|
13
|
-
import type { ApiContractFileRefs } from '../../lib/api-contract-files';
|
|
14
|
-
import type { RemoteServices } from '../../lib/remote-services';
|
|
15
|
-
import type { EnhancedGraph } from '../../lib/graph-sorter';
|
|
12
|
+
import { CurrentArchitecture } from '../../lib/current-architecture';
|
|
16
13
|
export interface ValidateArchitectureUnchangedOptions {
|
|
17
14
|
graphPath?: string;
|
|
18
15
|
}
|
|
@@ -25,20 +22,4 @@ export interface ExecutorResult {
|
|
|
25
22
|
* fixing it is the same single command either way, so listing both adds noise rather than information.
|
|
26
23
|
*/
|
|
27
24
|
export declare function describeTableDrift(current: CurrentArchitecture, saved: DependenciesFile): string | null;
|
|
28
|
-
/**
|
|
29
|
-
* Build the current dependency graph exactly as the generator does: reduce the nx
|
|
30
|
-
* graph, sort into levels, enrich with metadata, and attach the derived
|
|
31
|
-
* apiRelations — so this validator compares like-for-like against the committed file.
|
|
32
|
-
*/
|
|
33
|
-
export declare class CurrentGraphBuilder {
|
|
34
|
-
build(workspaceRoot: string, graphPath?: string): Promise<CurrentArchitecture>;
|
|
35
|
-
}
|
|
36
|
-
/** The regenerated graph plus the tables beside it — everything dependencies.json holds. */
|
|
37
|
-
export declare class CurrentArchitecture {
|
|
38
|
-
readonly graph: EnhancedGraph;
|
|
39
|
-
readonly apiContractFiles: ApiContractFileRefs;
|
|
40
|
-
readonly externalSystems: ExternalSystemDecls;
|
|
41
|
-
readonly remoteServices: RemoteServices;
|
|
42
|
-
constructor(graph: EnhancedGraph, apiContractFiles: ApiContractFileRefs, externalSystems: ExternalSystemDecls, remoteServices: RemoteServices);
|
|
43
|
-
}
|
|
44
25
|
export default function runExecutor(options: ValidateArchitectureUnchangedOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -9,18 +9,14 @@
|
|
|
9
9
|
* nx run architecture:validate-architecture-unchanged
|
|
10
10
|
*/
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.CurrentArchitecture = exports.CurrentGraphBuilder = void 0;
|
|
13
12
|
exports.describeTableDrift = describeTableDrift;
|
|
14
13
|
exports.default = runExecutor;
|
|
15
14
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
16
|
-
const graph_generator_1 = require("../../lib/graph-generator");
|
|
17
|
-
const graph_sorter_1 = require("../../lib/graph-sorter");
|
|
18
15
|
const graph_comparator_1 = require("../../lib/graph-comparator");
|
|
19
16
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
20
17
|
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
21
|
-
const tag_truth_1 = require("../../lib/tag-truth");
|
|
22
|
-
const approved_graph_1 = require("../../lib/runtime-wiring/approved-graph");
|
|
23
18
|
const rule_gate_1 = require("../../lib/rule-gate");
|
|
19
|
+
const current_architecture_1 = require("../../lib/current-architecture");
|
|
24
20
|
const toError_1 = require("../../toError");
|
|
25
21
|
const TMP_MD_FILE = 'webpieces.dependencies.md';
|
|
26
22
|
/**
|
|
@@ -129,45 +125,6 @@ function describeExternalSystemDrift(current, saved) {
|
|
|
129
125
|
return null;
|
|
130
126
|
return `externalSystems drift (${changes.length} system(s)):\n${changes.join('\n')}`;
|
|
131
127
|
}
|
|
132
|
-
/**
|
|
133
|
-
* Build the current dependency graph exactly as the generator does: reduce the nx
|
|
134
|
-
* graph, sort into levels, enrich with metadata, and attach the derived
|
|
135
|
-
* apiRelations — so this validator compares like-for-like against the committed file.
|
|
136
|
-
*/
|
|
137
|
-
class CurrentGraphBuilder {
|
|
138
|
-
async build(workspaceRoot, graphPath) {
|
|
139
|
-
console.log('📊 Generating current dependency graph...');
|
|
140
|
-
const reducedGraph = await (0, graph_generator_1.generateReducedGraph)();
|
|
141
|
-
console.log('🔄 Computing topological layers...');
|
|
142
|
-
const currentGraph = (0, graph_sorter_1.sortGraphTopologically)(reducedGraph);
|
|
143
|
-
console.log('🏷️ Enriching graph with framework + responsibilities metadata...');
|
|
144
|
-
const projectInfos = await (0, graph_metadata_1.collectProjectInfo)();
|
|
145
|
-
(0, graph_metadata_1.enrichGraph)(currentGraph, projectInfos, workspaceRoot);
|
|
146
|
-
new tag_truth_1.TagTruthCheck().assertTrue(currentGraph, projectInfos, workspaceRoot);
|
|
147
|
-
new approved_graph_1.ApprovedWiringGraph().attach(workspaceRoot, currentGraph, projectInfos, graphPath);
|
|
148
|
-
const saved = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
149
|
-
if (saved === null)
|
|
150
|
-
throw new rules_config_1.RuleFailError('validate-runtime-architecture', 'Missing approved graph.', undefined, undefined, [
|
|
151
|
-
new rules_config_1.Option('Migrate src/wiring.ts and review runtime-deps.json and API contract approvals.', true),
|
|
152
|
-
]);
|
|
153
|
-
return new CurrentArchitecture(currentGraph, saved.apiContractFiles, saved.externalSystems, saved.remoteServices);
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
exports.CurrentGraphBuilder = CurrentGraphBuilder;
|
|
157
|
-
/** The regenerated graph plus the tables beside it — everything dependencies.json holds. */
|
|
158
|
-
class CurrentArchitecture {
|
|
159
|
-
graph;
|
|
160
|
-
apiContractFiles;
|
|
161
|
-
externalSystems;
|
|
162
|
-
remoteServices;
|
|
163
|
-
constructor(graph, apiContractFiles, externalSystems, remoteServices) {
|
|
164
|
-
this.graph = graph;
|
|
165
|
-
this.apiContractFiles = apiContractFiles;
|
|
166
|
-
this.externalSystems = externalSystems;
|
|
167
|
-
this.remoteServices = remoteServices;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
exports.CurrentArchitecture = CurrentArchitecture;
|
|
171
128
|
/** The bootstrap path: there is nothing to diff against yet, so say how to create it. */
|
|
172
129
|
// webpieces-disable no-function-outside-class -- executor step helper, matches reportMismatch in this file
|
|
173
130
|
function reportMissingGraph() {
|
|
@@ -193,7 +150,7 @@ async function runExecutor(options, context) {
|
|
|
193
150
|
}
|
|
194
151
|
// Steps 1-3: build + enrich + scan the current graph (same pipeline the
|
|
195
152
|
// generator runs, so any drift is caught).
|
|
196
|
-
const currentGraph = await new CurrentGraphBuilder().build(workspaceRoot, graphPath);
|
|
153
|
+
const currentGraph = await new current_architecture_1.CurrentGraphBuilder().build(workspaceRoot, graphPath);
|
|
197
154
|
// Step 4: Load saved graph
|
|
198
155
|
console.log('📂 Loading saved graph...');
|
|
199
156
|
const savedGraph = (0, graph_loader_1.loadBlessedGraph)(workspaceRoot, graphPath);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-architecture-unchanged/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Architecture Unchanged Executor\n *\n * Validates that the current architecture graph matches the saved blessed graph.\n * This ensures no unapproved architecture changes have been made.\n *\n * Usage:\n * nx run architecture:validate-architecture-unchanged\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 { compareGraphs } from '../../lib/graph-comparator';\nimport { loadBlessedGraph, graphFileExists } from '../../lib/graph-loader';\nimport type { DependenciesFile } from '../../lib/graph-loader';\nimport { collectProjectInfo, enrichGraph, MetadataValidationError } from '../../lib/graph-metadata';\nimport { TagTruthCheck } from '../../lib/tag-truth';\nimport { ApprovedWiringGraph } from '../../lib/runtime-wiring/approved-graph';\nimport type { ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport { ApiContractFiles } from '../../lib/api-contract-files';\nimport type { ApiContractFileRefs } from '../../lib/api-contract-files';\nimport type { RemoteServices } from '../../lib/remote-services';\nimport { RuleGate } from '../../lib/rule-gate';\nimport type { EnhancedGraph } from '../../lib/graph-sorter';\nimport { toError } from '../../toError';\n\nexport interface ValidateArchitectureUnchangedOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst TMP_MD_FILE = 'webpieces.dependencies.md';\n\n/**\n * Write the instructions documentation to .webpieces/instruct-ai/.\n * Sourced from @webpieces/rules-config.\n */\nfunction writeTmpInstructionsFile(workspaceRoot: string): string {\n const mdPath = writeTemplate(workspaceRoot, TMP_MD_FILE);\n\n return mdPath;\n}\n\n/**\n * Report a current-vs-saved graph mismatch and write the AI instructions file.\n */\nfunction reportMismatch(summary: string, workspaceRoot: string): void {\n const mdPath = writeTmpInstructionsFile(workspaceRoot);\n\n console.error('❌ Architecture has changed since last update!');\n console.error('\\nDifferences:');\n console.error(summary);\n console.error('');\n console.error('⚠️ *** Refer to ' + mdPath + ' for instructions on how to fix *** ⚠️');\n console.error('');\n console.error('To fix:');\n console.error(' 1. Review the changes above');\n console.error(\n ' 2. If intentional, ASK USER to run: nx run architecture:generate since this is a critical change',\n );\n console.error(' 3. Commit the updated architecture/dependencies.json');\n}\n\n/**\n * Which APIs dependencies.json links to a contract file, compared by LINK only — an added or removed\n * API. The endpoints inside `architecture/apis/<Api>.json` are deliberately NOT compared (#949): an\n * endpoint change must not fail this rule. Null when the links match.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches reportMismatch in this file\nfunction describeContractLinkDrift(\n current: ApiContractFileRefs,\n saved: ApiContractFileRefs,\n): string | null {\n const names = [...new Set([...Object.keys(current), ...Object.keys(saved)])].sort();\n const changes: string[] = [];\n for (const name of names) {\n const a = current[name];\n const b = saved[name];\n if (a === undefined)\n changes.push(` - ${name}: linked in dependencies.json but no longer in source`);\n else if (b === undefined)\n changes.push(` + ${name}: in source but not linked from dependencies.json`);\n else if (a !== b) changes.push(` ~ ${name}: contract file link changed (${b} -> ${a})`);\n }\n if (changes.length === 0) return null;\n return `apiContractFiles drift (${changes.length} api(s)):\\n${changes.join('\\n')}`;\n}\n\n/**\n * Drift in EITHER side table of dependencies.json — the api contract-file links or the\n * external-system declarations — or null when both match. The first difference found is reported;\n * fixing it is the same single command either way, so listing both adds noise rather than information.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches describeContractLinkDrift above\nexport function describeTableDrift(\n current: CurrentArchitecture,\n saved: DependenciesFile,\n): string | null {\n const linkDrift = describeContractLinkDrift(current.apiContractFiles, saved.apiContractFiles);\n if (linkDrift !== null) return linkDrift;\n const systemDrift = describeExternalSystemDrift(current.externalSystems, saved.externalSystems);\n if (systemDrift !== null) return systemDrift;\n return describeRemoteServiceDrift(current.remoteServices, saved.remoteServices);\n}\n\n/**\n * The same drift report for the reviewed remote services (#1203), or null when they match. The table\n * is approval metadata, so a difference means the file changed under the generated graph.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches describeContractLinkDrift above\nfunction describeRemoteServiceDrift(current: RemoteServices, saved: RemoteServices): string | null {\n const names = [...new Set([...Object.keys(current), ...Object.keys(saved)])].sort();\n const changes: string[] = [];\n for (const name of names) {\n const a = current[name];\n const b = saved[name];\n if (a === undefined) changes.push(` - ${name}: in dependencies.json but no longer approved`);\n else if (b === undefined) changes.push(` + ${name}: approved but missing from dependencies.json`);\n else if (JSON.stringify(a) !== JSON.stringify(b))\n changes.push(` ~ ${name}: label/implements changed`);\n }\n if (changes.length === 0) return null;\n return `remoteServices drift (${changes.length} service(s)):\\n${changes.join('\\n')}`;\n}\n\n/**\n * The same drift report for the declared external systems, or null when they match. Named per\n * system so the message points at the database that changed rather than dumping two JSON blobs.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches describeContractLinkDrift above\nfunction describeExternalSystemDrift(\n current: ExternalSystemDecls,\n saved: ExternalSystemDecls,\n): string | null {\n const names = [...new Set([...Object.keys(current), ...Object.keys(saved)])].sort();\n const changes: string[] = [];\n for (const name of names) {\n const a = current[name];\n const b = saved[name];\n if (a === undefined)\n changes.push(` - ${name}: in dependencies.json but no longer declared in source`);\n else if (b === undefined)\n changes.push(` + ${name}: declared in source but missing from dependencies.json`);\n else if (JSON.stringify(a) !== JSON.stringify(b))\n changes.push(` ~ ${name}: kind/label/declarers changed`);\n }\n if (changes.length === 0) return null;\n return `externalSystems drift (${changes.length} system(s)):\\n${changes.join('\\n')}`;\n}\n\n/**\n * Build the current dependency graph exactly as the generator does: reduce the nx\n * graph, sort into levels, enrich with metadata, and attach the derived\n * apiRelations — so this validator compares like-for-like against the committed file.\n */\nexport class CurrentGraphBuilder {\n async build(workspaceRoot: string, graphPath?: string): Promise<CurrentArchitecture> {\n console.log('📊 Generating current dependency graph...');\n const reducedGraph = await generateReducedGraph();\n console.log('🔄 Computing topological layers...');\n const currentGraph = sortGraphTopologically(reducedGraph);\n console.log('🏷️ Enriching graph with framework + responsibilities metadata...');\n const projectInfos = await collectProjectInfo();\n enrichGraph(currentGraph, projectInfos, workspaceRoot);\n new TagTruthCheck().assertTrue(currentGraph, projectInfos, workspaceRoot);\n new ApprovedWiringGraph().attach(workspaceRoot, currentGraph, projectInfos, graphPath);\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing approved graph.',\n undefined,\n undefined,\n [\n new Option(\n 'Migrate src/wiring.ts and review runtime-deps.json and API contract approvals.',\n true,\n ),\n ],\n );\n return new CurrentArchitecture(\n currentGraph,\n saved.apiContractFiles,\n saved.externalSystems,\n saved.remoteServices,\n );\n }\n}\n\n/** The regenerated graph plus the tables beside it — everything dependencies.json holds. */\nexport class CurrentArchitecture {\n constructor(\n public readonly graph: EnhancedGraph,\n public readonly apiContractFiles: ApiContractFileRefs,\n public readonly externalSystems: ExternalSystemDecls,\n public readonly remoteServices: RemoteServices,\n ) {}\n}\n\n/** The bootstrap path: there is nothing to diff against yet, so say how to create it. */\n// webpieces-disable no-function-outside-class -- executor step helper, matches reportMismatch in this file\nfunction reportMissingGraph(): never {\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing reviewed architecture/dependencies.json.',\n undefined,\n undefined,\n [\n new Option(\n '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\"}, reviewed externalSystems, and (only when a uses targets a service outside this workspace) reviewed remoteServices. Approve tagged owners runtime-deps.json candidates, then run architecture:generate and review the resulting graph.',\n true,\n ),\n ],\n );\n}\n\nexport default async function runExecutor(\n options: ValidateArchitectureUnchangedOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n // Epoch-gateable: this rule diffs against the blessed architecture/dependencies.json, so\n // \"grandfather the current drift until <epoch>\" is meaningful — hence honorEpoch = true.\n if (new RuleGate().isDisabled(workspaceRoot, 'validate-architecture-unchanged', true)) {\n return { success: true };\n }\n\n console.log('\\n🔍 Validating Architecture Unchanged\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Check if saved graph exists\n if (!graphFileExists(workspaceRoot, graphPath)) {\n reportMissingGraph();\n return { success: false };\n }\n\n // Steps 1-3: build + enrich + scan the current graph (same pipeline the\n // generator runs, so any drift is caught).\n const currentGraph = await new CurrentGraphBuilder().build(workspaceRoot, graphPath);\n\n // Step 4: Load saved graph\n console.log('📂 Loading saved graph...');\n const savedGraph = loadBlessedGraph(workspaceRoot, graphPath);\n\n if (!savedGraph) {\n console.error('❌ Could not load saved graph');\n return { success: false };\n }\n\n // Step 5: Compare graphs\n console.log('🔍 Comparing current graph to saved graph...');\n const comparison = compareGraphs(currentGraph.graph, savedGraph.projects);\n\n if (!comparison.identical) {\n reportMismatch(comparison.summary, workspaceRoot);\n return { success: false };\n }\n\n // Step 6: Compare the two side tables as well: which APIs are linked to a contract file, and\n // the external-system declarations. The endpoints inside architecture/apis/<Api>.json are\n // NOT compared — an endpoint change must never fail this rule (#949).\n const tableDrift = describeTableDrift(currentGraph, savedGraph);\n if (tableDrift !== null) {\n reportMismatch(tableDrift, workspaceRoot);\n return { success: false };\n }\n\n console.log('✅ Architecture unchanged - current graph matches saved graph');\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError (e.g. a dependencies.json still carrying the moved `apiContracts` key)\n // carries its cures in Option[]; render them rather than dropping them with `.message`.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Architecture validation failed:', rendered);\n reportMetadataFailure(error, workspaceRoot);\n return { success: false };\n }\n}\n\n// webpieces-disable no-function-outside-class -- executor diagnostic formatting\nfunction reportMetadataFailure(error: Error, workspaceRoot: string): void {\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' + mdPath + ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/validate-architecture-unchanged/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Validate Architecture Unchanged Executor\n *\n * Validates that the current architecture graph matches the saved blessed graph.\n * This ensures no unapproved architecture changes have been made.\n *\n * Usage:\n * nx run architecture:validate-architecture-unchanged\n */\n\nimport type { ExecutorContext } from '@nx/devkit';\nimport {\n writeTemplate,\n Option,\n RuleFailError,\n renderRuleFailForHuman,\n} from '@webpieces/rules-config';\nimport { compareGraphs } from '../../lib/graph-comparator';\nimport { loadBlessedGraph, graphFileExists } from '../../lib/graph-loader';\nimport type { DependenciesFile } from '../../lib/graph-loader';\nimport { MetadataValidationError } from '../../lib/graph-metadata';\nimport type { ExternalSystemDecls } from '../../lib/api-usage/api-relations';\nimport type { ApiContractFileRefs } from '../../lib/api-contract-files';\nimport type { RemoteServices } from '../../lib/remote-services';\nimport { RuleGate } from '../../lib/rule-gate';\nimport { CurrentArchitecture, CurrentGraphBuilder } from '../../lib/current-architecture';\nimport { toError } from '../../toError';\n\nexport interface ValidateArchitectureUnchangedOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nconst TMP_MD_FILE = 'webpieces.dependencies.md';\n\n/**\n * Write the instructions documentation to .webpieces/instruct-ai/.\n * Sourced from @webpieces/rules-config.\n */\nfunction writeTmpInstructionsFile(workspaceRoot: string): string {\n const mdPath = writeTemplate(workspaceRoot, TMP_MD_FILE);\n\n return mdPath;\n}\n\n/**\n * Report a current-vs-saved graph mismatch and write the AI instructions file.\n */\nfunction reportMismatch(summary: string, workspaceRoot: string): void {\n const mdPath = writeTmpInstructionsFile(workspaceRoot);\n\n console.error('❌ Architecture has changed since last update!');\n console.error('\\nDifferences:');\n console.error(summary);\n console.error('');\n console.error('⚠️ *** Refer to ' + mdPath + ' for instructions on how to fix *** ⚠️');\n console.error('');\n console.error('To fix:');\n console.error(' 1. Review the changes above');\n console.error(\n ' 2. If intentional, ASK USER to run: nx run architecture:generate since this is a critical change',\n );\n console.error(' 3. Commit the updated architecture/dependencies.json');\n}\n\n/**\n * Which APIs dependencies.json links to a contract file, compared by LINK only — an added or removed\n * API. The endpoints inside `architecture/apis/<Api>.json` are deliberately NOT compared (#949): an\n * endpoint change must not fail this rule. Null when the links match.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches reportMismatch in this file\nfunction describeContractLinkDrift(\n current: ApiContractFileRefs,\n saved: ApiContractFileRefs,\n): string | null {\n const names = [...new Set([...Object.keys(current), ...Object.keys(saved)])].sort();\n const changes: string[] = [];\n for (const name of names) {\n const a = current[name];\n const b = saved[name];\n if (a === undefined)\n changes.push(` - ${name}: linked in dependencies.json but no longer in source`);\n else if (b === undefined)\n changes.push(` + ${name}: in source but not linked from dependencies.json`);\n else if (a !== b) changes.push(` ~ ${name}: contract file link changed (${b} -> ${a})`);\n }\n if (changes.length === 0) return null;\n return `apiContractFiles drift (${changes.length} api(s)):\\n${changes.join('\\n')}`;\n}\n\n/**\n * Drift in EITHER side table of dependencies.json — the api contract-file links or the\n * external-system declarations — or null when both match. The first difference found is reported;\n * fixing it is the same single command either way, so listing both adds noise rather than information.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches describeContractLinkDrift above\nexport function describeTableDrift(\n current: CurrentArchitecture,\n saved: DependenciesFile,\n): string | null {\n const linkDrift = describeContractLinkDrift(current.apiContractFiles, saved.apiContractFiles);\n if (linkDrift !== null) return linkDrift;\n const systemDrift = describeExternalSystemDrift(current.externalSystems, saved.externalSystems);\n if (systemDrift !== null) return systemDrift;\n return describeRemoteServiceDrift(current.remoteServices, saved.remoteServices);\n}\n\n/**\n * The same drift report for the reviewed remote services (#1203), or null when they match. The table\n * is approval metadata, so a difference means the file changed under the generated graph.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches describeContractLinkDrift above\nfunction describeRemoteServiceDrift(current: RemoteServices, saved: RemoteServices): string | null {\n const names = [...new Set([...Object.keys(current), ...Object.keys(saved)])].sort();\n const changes: string[] = [];\n for (const name of names) {\n const a = current[name];\n const b = saved[name];\n if (a === undefined) changes.push(` - ${name}: in dependencies.json but no longer approved`);\n else if (b === undefined) changes.push(` + ${name}: approved but missing from dependencies.json`);\n else if (JSON.stringify(a) !== JSON.stringify(b))\n changes.push(` ~ ${name}: label/implements changed`);\n }\n if (changes.length === 0) return null;\n return `remoteServices drift (${changes.length} service(s)):\\n${changes.join('\\n')}`;\n}\n\n/**\n * The same drift report for the declared external systems, or null when they match. Named per\n * system so the message points at the database that changed rather than dumping two JSON blobs.\n */\n// webpieces-disable no-function-outside-class -- executor step helper, matches describeContractLinkDrift above\nfunction describeExternalSystemDrift(\n current: ExternalSystemDecls,\n saved: ExternalSystemDecls,\n): string | null {\n const names = [...new Set([...Object.keys(current), ...Object.keys(saved)])].sort();\n const changes: string[] = [];\n for (const name of names) {\n const a = current[name];\n const b = saved[name];\n if (a === undefined)\n changes.push(` - ${name}: in dependencies.json but no longer declared in source`);\n else if (b === undefined)\n changes.push(` + ${name}: declared in source but missing from dependencies.json`);\n else if (JSON.stringify(a) !== JSON.stringify(b))\n changes.push(` ~ ${name}: kind/label/declarers changed`);\n }\n if (changes.length === 0) return null;\n return `externalSystems drift (${changes.length} system(s)):\\n${changes.join('\\n')}`;\n}\n\n/** The bootstrap path: there is nothing to diff against yet, so say how to create it. */\n// webpieces-disable no-function-outside-class -- executor step helper, matches reportMismatch in this file\nfunction reportMissingGraph(): never {\n throw new RuleFailError(\n 'validate-runtime-architecture',\n 'Missing reviewed architecture/dependencies.json.',\n undefined,\n undefined,\n [\n new Option(\n '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\"}, reviewed externalSystems, and (only when a uses targets a service outside this workspace) reviewed remoteServices. Approve tagged owners runtime-deps.json candidates, then run architecture:generate and review the resulting graph.',\n true,\n ),\n ],\n );\n}\n\nexport default async function runExecutor(\n options: ValidateArchitectureUnchangedOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const graphPath = options.graphPath;\n const workspaceRoot = context.root;\n\n // Epoch-gateable: this rule diffs against the blessed architecture/dependencies.json, so\n // \"grandfather the current drift until <epoch>\" is meaningful — hence honorEpoch = true.\n if (new RuleGate().isDisabled(workspaceRoot, 'validate-architecture-unchanged', true)) {\n return { success: true };\n }\n\n console.log('\\n🔍 Validating Architecture Unchanged\\n');\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n // Check if saved graph exists\n if (!graphFileExists(workspaceRoot, graphPath)) {\n reportMissingGraph();\n return { success: false };\n }\n\n // Steps 1-3: build + enrich + scan the current graph (same pipeline the\n // generator runs, so any drift is caught).\n const currentGraph = await new CurrentGraphBuilder().build(workspaceRoot, graphPath);\n\n // Step 4: Load saved graph\n console.log('📂 Loading saved graph...');\n const savedGraph = loadBlessedGraph(workspaceRoot, graphPath);\n\n if (!savedGraph) {\n console.error('❌ Could not load saved graph');\n return { success: false };\n }\n\n // Step 5: Compare graphs\n console.log('🔍 Comparing current graph to saved graph...');\n const comparison = compareGraphs(currentGraph.graph, savedGraph.projects);\n\n if (!comparison.identical) {\n reportMismatch(comparison.summary, workspaceRoot);\n return { success: false };\n }\n\n // Step 6: Compare the two side tables as well: which APIs are linked to a contract file, and\n // the external-system declarations. The endpoints inside architecture/apis/<Api>.json are\n // NOT compared — an endpoint change must never fail this rule (#949).\n const tableDrift = describeTableDrift(currentGraph, savedGraph);\n if (tableDrift !== null) {\n reportMismatch(tableDrift, workspaceRoot);\n return { success: false };\n }\n\n console.log('✅ Architecture unchanged - current graph matches saved graph');\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n // A RuleFailError (e.g. a dependencies.json still carrying the moved `apiContracts` key)\n // carries its cures in Option[]; render them rather than dropping them with `.message`.\n const rendered =\n error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Architecture validation failed:', rendered);\n reportMetadataFailure(error, workspaceRoot);\n return { success: false };\n }\n}\n\n// webpieces-disable no-function-outside-class -- executor diagnostic formatting\nfunction reportMetadataFailure(error: Error, workspaceRoot: string): void {\n if (error instanceof MetadataValidationError) {\n const mdPath = writeTemplate(workspaceRoot, 'webpieces.responsibilities.md');\n console.error('');\n console.error(\n '⚠️ *** Refer to ' + mdPath + ' for how to author responsibilities.md files *** ⚠️',\n );\n }\n}\n"]}
|
|
@@ -10,6 +10,11 @@
|
|
|
10
10
|
* working tree). On main or a detached HEAD only the last commit exists, so checking out a commit
|
|
11
11
|
* and running `pnpm arch:visualize` shows what that commit changed.
|
|
12
12
|
*
|
|
13
|
+
* The Impact sidecar also carries the edges this branch gained and lost (#1264): the graph computed
|
|
14
|
+
* LIVE here (generate's pipeline, writing nothing — never HEAD's committed architecture/*.json, which
|
|
15
|
+
* would show nothing once the branch regenerated it) against the fork point's committed graph. The
|
|
16
|
+
* drawn boxes stay the saved snapshot; the changed edges are drawn over them in Impact mode.
|
|
17
|
+
*
|
|
13
18
|
* Usage:
|
|
14
19
|
* nx run architecture:visualize (pnpm arch:visualize)
|
|
15
20
|
*/
|
|
@@ -17,6 +22,7 @@ import type { ExecutorContext } from '@nx/devkit';
|
|
|
17
22
|
import { SavedSnapshot } from '../../lib/saved-snapshot';
|
|
18
23
|
import { GraphVisualizer } from '../../lib/graph-visualizer';
|
|
19
24
|
import { ImpactRefresh } from '../../lib/graph-impact';
|
|
25
|
+
import { LiveGraphSource } from '../../lib/graph-edge-diff';
|
|
20
26
|
export interface VisualizeExecutorOptions {
|
|
21
27
|
graphPath?: string;
|
|
22
28
|
}
|
|
@@ -28,7 +34,8 @@ export declare class SavedGraphViewer {
|
|
|
28
34
|
private readonly visualizer;
|
|
29
35
|
private readonly impact;
|
|
30
36
|
private readonly snapshot;
|
|
31
|
-
|
|
37
|
+
private readonly live;
|
|
38
|
+
constructor(visualizer?: GraphVisualizer, impact?: ImpactRefresh, snapshot?: SavedSnapshot, live?: LiveGraphSource);
|
|
32
39
|
view(options: VisualizeExecutorOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
33
40
|
}
|
|
34
41
|
export default function runExecutor(options: VisualizeExecutorOptions, context: ExecutorContext): Promise<ExecutorResult>;
|
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* working tree). On main or a detached HEAD only the last commit exists, so checking out a commit
|
|
12
12
|
* and running `pnpm arch:visualize` shows what that commit changed.
|
|
13
13
|
*
|
|
14
|
+
* The Impact sidecar also carries the edges this branch gained and lost (#1264): the graph computed
|
|
15
|
+
* LIVE here (generate's pipeline, writing nothing — never HEAD's committed architecture/*.json, which
|
|
16
|
+
* would show nothing once the branch regenerated it) against the fork point's committed graph. The
|
|
17
|
+
* drawn boxes stay the saved snapshot; the changed edges are drawn over them in Impact mode.
|
|
18
|
+
*
|
|
14
19
|
* Usage:
|
|
15
20
|
* nx run architecture:visualize (pnpm arch:visualize)
|
|
16
21
|
*/
|
|
@@ -24,6 +29,7 @@ const graph_loader_1 = require("../../lib/graph-loader");
|
|
|
24
29
|
const graph_visualizer_1 = require("../../lib/graph-visualizer");
|
|
25
30
|
const tag_truth_1 = require("../../lib/tag-truth");
|
|
26
31
|
const graph_impact_1 = require("../../lib/graph-impact");
|
|
32
|
+
const graph_edge_diff_1 = require("../../lib/graph-edge-diff");
|
|
27
33
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
28
34
|
const toError_1 = require("../../toError");
|
|
29
35
|
/** The view pipeline: load the saved graph, write the page, refresh Impact, open the page. */
|
|
@@ -31,10 +37,12 @@ class SavedGraphViewer {
|
|
|
31
37
|
visualizer;
|
|
32
38
|
impact;
|
|
33
39
|
snapshot;
|
|
34
|
-
|
|
40
|
+
live;
|
|
41
|
+
constructor(visualizer = new graph_visualizer_1.GraphVisualizer(), impact = new graph_impact_1.ImpactRefresh(), snapshot = new saved_snapshot_1.SavedSnapshot(), live = new graph_edge_diff_1.LiveGraphSource()) {
|
|
35
42
|
this.visualizer = visualizer;
|
|
36
43
|
this.impact = impact;
|
|
37
44
|
this.snapshot = snapshot;
|
|
45
|
+
this.live = live;
|
|
38
46
|
}
|
|
39
47
|
async view(options, context) {
|
|
40
48
|
const graphPath = options.graphPath ?? graph_loader_1.DEFAULT_GRAPH_PATH;
|
|
@@ -50,8 +58,8 @@ class SavedGraphViewer {
|
|
|
50
58
|
const vizPaths = this.visualizer.writeVisualization(graph, workspaceRoot, productDependencies);
|
|
51
59
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
52
60
|
// Impact is optional: it reports, it never throws, and it never touches committed files.
|
|
53
|
-
console.log('🟠 Refreshing impact (nx affected: changed on this branch, and since the last commit)...');
|
|
54
|
-
console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, graph, graph_impact_1.VISUALIZE_IMPACT_KINDS));
|
|
61
|
+
console.log('🟠 Refreshing impact (nx affected: changed on this branch, and since the last commit; edges gained and lost since the fork point, against the graph computed live now)...');
|
|
62
|
+
console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, graph, graph_impact_1.VISUALIZE_IMPACT_KINDS, this.live));
|
|
55
63
|
console.log('\n🌐 Opening visualization in browser...');
|
|
56
64
|
if (this.visualizer.openVisualization(vizPaths.htmlPath)) {
|
|
57
65
|
console.log('✅ Browser opened');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Visualize Executor\n *\n * Renders the SAVED architecture graph (DOT + HTML), refreshes the Impact sidecar, and opens the\n * visualization in a browser. It never regenerates the graph: the architecture facts stay the saved\n * snapshot. Impact, though, is per-checkout and gitignored (graph-impact.ts) — it is exactly the thing\n * that goes stale when you switch branches or check out a commit — so viewing refreshes it against\n * the saved graph, with one `nx show projects --affected --base=<base>` scan per comparison that\n * exists here: \"Changed on this branch\" (fork point → working tree) and \"Last commit\" (HEAD^ →\n * working tree). On main or a detached HEAD only the last commit exists, so checking out a commit\n * and running `pnpm arch:visualize` shows what that commit changed.\n *\n * The Impact sidecar also carries the edges this branch gained and lost (#1264): the graph computed\n * LIVE here (generate's pipeline, writing nothing — never HEAD's committed architecture/*.json, which\n * would show nothing once the branch regenerated it) against the fork point's committed graph. The\n * drawn boxes stay the saved snapshot; the changed edges are drawn over them in Impact mode.\n *\n * Usage:\n * nx run architecture:visualize (pnpm arch:visualize)\n */\n\nimport * as path from 'path';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { TagTruthRules } from '../../lib/tag-truth';\nimport { ImpactRefresh, VISUALIZE_IMPACT_KINDS } from '../../lib/graph-impact';\nimport { LiveGraphSource } from '../../lib/graph-edge-diff';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { toError } from '../../toError';\n\nexport interface VisualizeExecutorOptions {\n graphPath?: string;\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\n/** The view pipeline: load the saved graph, write the page, refresh Impact, open the page. */\nexport class SavedGraphViewer {\n constructor(\n private readonly visualizer: GraphVisualizer = new GraphVisualizer(),\n private readonly impact: ImpactRefresh = new ImpactRefresh(),\n private readonly snapshot: SavedSnapshot = new SavedSnapshot(),\n private readonly live: LiveGraphSource = new LiveGraphSource(),\n ) {}\n\n async view(options: VisualizeExecutorOptions, context: ExecutorContext): Promise<ExecutorResult> {\n const graphPath = options.graphPath ?? DEFAULT_GRAPH_PATH;\n const workspaceRoot = context.root;\n\n console.log('\\n🎨 Architecture Visualization\\n');\n console.log(this.snapshot.message());\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx\n try {\n console.log('📂 Loading saved graph...');\n const graph = this.snapshot.loadProjects(workspaceRoot, graphPath).projects;\n\n console.log('🎨 Generating visualization...');\n const productDependencies = TagTruthRules.fromConfig(workspaceRoot).productTags?.dependencies ?? null;\n const vizPaths = this.visualizer.writeVisualization(graph, workspaceRoot, productDependencies);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n\n // Impact is optional: it reports, it never throws, and it never touches committed files.\n console.log('🟠 Refreshing impact (nx affected: changed on this branch, and since the last commit; edges gained and lost since the fork point, against the graph computed live now)...');\n console.log(await this.impact.run(path.dirname(vizPaths.htmlPath), workspaceRoot, graph, VISUALIZE_IMPACT_KINDS, this.live));\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (this.visualizer.openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error(`❌ Visualization failed for ${graphPath}:`, rendered);\n return { success: false };\n }\n }\n}\n\n// webpieces-disable no-function-outside-class -- nx executor module: nx resolves a default-export function here\nexport default async function runExecutor(\n options: VisualizeExecutorOptions,\n context: ExecutorContext\n): Promise<ExecutorResult> {\n return new SavedGraphViewer().view(options, context);\n}\n"]}
|
|
@@ -16,6 +16,7 @@ const fs = tslib_1.__importStar(require("fs"));
|
|
|
16
16
|
const path = tslib_1.__importStar(require("path"));
|
|
17
17
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
18
18
|
const graph_impact_1 = require("../../lib/graph-impact");
|
|
19
|
+
const graph_edge_diff_1 = require("../../lib/graph-edge-diff");
|
|
19
20
|
const saved_snapshot_1 = require("../../lib/saved-snapshot");
|
|
20
21
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
21
22
|
const runtime_visualizer_1 = require("../../lib/runtime-visualizer");
|
|
@@ -41,7 +42,7 @@ async function runExecutor(_options, context) {
|
|
|
41
42
|
console.log(`✅ Generated: ${vizPaths.dotPath}`);
|
|
42
43
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
43
44
|
if (projects !== null)
|
|
44
|
-
console.log(await new graph_impact_1.ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, graph_impact_1.VISUALIZE_IMPACT_KINDS));
|
|
45
|
+
console.log(await new graph_impact_1.ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, graph_impact_1.VISUALIZE_IMPACT_KINDS, new graph_edge_diff_1.LiveGraphSource()));
|
|
45
46
|
else {
|
|
46
47
|
new graph_impact_1.ImpactSidecar().remove(path.dirname(vizPaths.htmlPath));
|
|
47
48
|
console.log('Impact and framework metadata unavailable: no saved project graph. Refresh explicitly with architecture:generate.');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;
|
|
1
|
+
{"version":3,"file":"executor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/executors/visualize-runtime/executor.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Visualize Runtime Executor\n *\n * Renders the runtime microservice graph (committed\n * architecture/runtime-dependencies.json) to DOT + HTML and opens it. When a runtime goal\n * (architecture/runtime-dependencies-goal.json, #1239) is present, the page also carries the\n * Goal vs Current comparison; when it is absent or unusable, the page says why instead.\n *\n * Usage: nx run architecture:visualize-runtime\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { DEFAULT_GRAPH_PATH } from '../../lib/graph-loader';\nimport { ImpactRefresh, ImpactSidecar, VISUALIZE_IMPACT_KINDS } from '../../lib/graph-impact';\nimport { LiveGraphSource } from '../../lib/graph-edge-diff';\nimport type { ExecutorContext } from '@nx/devkit';\nimport { SavedSnapshot } from '../../lib/saved-snapshot';\nimport { RuleFailError, renderRuleFailForHuman } from '@webpieces/rules-config';\nimport { writeRuntimeVisualization, RuntimeVizOptions, RuntimePageKind } from '../../lib/runtime-visualizer';\nimport { loadRuntimeConfig } from '../../lib/runtime-config';\nimport { GraphVisualizer } from '../../lib/graph-visualizer';\nimport { RuntimeComparisonInputs } from '../../lib/runtime-goal-view';\nimport { toError } from '../../toError';\n\nexport interface VisualizeRuntimeOptions {\n // No options.\n}\n\nexport interface ExecutorResult {\n success: boolean;\n}\n\nexport default async function runExecutor(\n _options: VisualizeRuntimeOptions,\n context: ExecutorContext,\n): Promise<ExecutorResult> {\n const workspaceRoot = context.root;\n const snapshot: SavedSnapshot = new SavedSnapshot();\n\n console.log('\\n🎨 Runtime Microservice Visualization\\n');\n console.log(snapshot.message());\n\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- Nx command rendering boundary; success:false propagates failure to Nx\n try {\n const graph = snapshot.loadRuntime(workspaceRoot);\n const config = loadRuntimeConfig(workspaceRoot);\n const projects = fs.existsSync(path.join(workspaceRoot, DEFAULT_GRAPH_PATH)) ? snapshot.loadProjects(workspaceRoot, DEFAULT_GRAPH_PATH).projects : null;\n const options = new RuntimeVizOptions(config.showExternalNodes, projects, workspaceRoot, RuntimePageKind.CURRENT);\n const inputs = new RuntimeComparisonInputs(snapshot);\n const comparison = inputs.view(graph, inputs.goal(workspaceRoot), options);\n console.log(comparison.available ? `Goal vs Current: ${comparison.summary}` : `Goal vs Current unavailable: ${comparison.reason}`);\n const vizPaths = writeRuntimeVisualization(graph, workspaceRoot, 'WebPieces Runtime Architecture', options, comparison);\n console.log(`✅ Generated: ${vizPaths.dotPath}`);\n console.log(`✅ Generated: ${vizPaths.htmlPath}`);\n if (projects !== null) console.log(await new ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, VISUALIZE_IMPACT_KINDS, new LiveGraphSource()));\n else { new ImpactSidecar().remove(path.dirname(vizPaths.htmlPath)); console.log('Impact and framework metadata unavailable: no saved project graph. Refresh explicitly with architecture:generate.'); }\n\n console.log('\\n🌐 Opening visualization in browser...');\n if (new GraphVisualizer().openVisualization(vizPaths.htmlPath)) {\n console.log('✅ Browser opened');\n } else {\n console.log(`⚠️ Could not auto-open. Open manually: ${vizPaths.htmlPath}`);\n }\n\n return { success: true };\n } catch (err: unknown) {\n const error = toError(err);\n const rendered = error instanceof RuleFailError ? renderRuleFailForHuman(error) : error.message;\n console.error('❌ Runtime visualization failed for architecture/runtime-dependencies.json:', rendered);\n return { success: false };\n }\n}\n"]}
|
|
@@ -25,6 +25,7 @@ const path = tslib_1.__importStar(require("path"));
|
|
|
25
25
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
26
26
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
27
27
|
const graph_impact_1 = require("../../lib/graph-impact");
|
|
28
|
+
const graph_edge_diff_1 = require("../../lib/graph-edge-diff");
|
|
28
29
|
const saved_snapshot_1 = require("../../lib/saved-snapshot");
|
|
29
30
|
const runtime_visualizer_1 = require("../../lib/runtime-visualizer");
|
|
30
31
|
const runtime_config_1 = require("../../lib/runtime-config");
|
|
@@ -53,7 +54,7 @@ async function runExecutor(options, context) {
|
|
|
53
54
|
console.log(`✅ Generated: ${vizPaths.dotPath}`);
|
|
54
55
|
console.log(`✅ Generated: ${vizPaths.htmlPath}`);
|
|
55
56
|
if (projects !== null)
|
|
56
|
-
console.log(await new graph_impact_1.ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, graph_impact_1.VISUALIZE_IMPACT_KINDS));
|
|
57
|
+
console.log(await new graph_impact_1.ImpactRefresh().run(path.dirname(vizPaths.htmlPath), workspaceRoot, projects, graph_impact_1.VISUALIZE_IMPACT_KINDS, new graph_edge_diff_1.LiveGraphSource()));
|
|
57
58
|
else {
|
|
58
59
|
new graph_impact_1.ImpactSidecar().remove(path.dirname(vizPaths.htmlPath));
|
|
59
60
|
console.log('Impact and framework metadata unavailable: no saved project graph. Proposed services carry their goal facts only.');
|