@webpieces/nx-webpieces-rules 0.4.861 ā 0.4.863
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/validate-architecture-unchanged/executor.js +2 -0
- package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
- package/src/lib/runtime-wiring/canonical-clients.d.ts +19 -0
- package/src/lib/runtime-wiring/canonical-clients.js +178 -0
- package/src/lib/runtime-wiring/canonical-clients.js.map +1 -0
- package/src/lib/runtime-wiring/source-extractor.js +10 -1
- package/src/lib/runtime-wiring/source-extractor.js.map +1 -1
- package/src/rule-help.js +1 -1
- package/src/rule-help.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.863",
|
|
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.863",
|
|
24
|
+
"@webpieces/core-util": "0.4.863",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.863",
|
|
26
|
+
"@webpieces/rules-config": "0.4.863",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.863"
|
|
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.863"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -19,6 +19,7 @@ const graph_comparator_1 = require("../../lib/graph-comparator");
|
|
|
19
19
|
const graph_loader_1 = require("../../lib/graph-loader");
|
|
20
20
|
const graph_metadata_1 = require("../../lib/graph-metadata");
|
|
21
21
|
const tag_truth_1 = require("../../lib/tag-truth");
|
|
22
|
+
const canonical_clients_1 = require("../../lib/runtime-wiring/canonical-clients");
|
|
22
23
|
const approved_graph_1 = require("../../lib/runtime-wiring/approved-graph");
|
|
23
24
|
const rule_gate_1 = require("../../lib/rule-gate");
|
|
24
25
|
const toError_1 = require("../../toError");
|
|
@@ -117,6 +118,7 @@ class CurrentGraphBuilder {
|
|
|
117
118
|
const currentGraph = (0, graph_sorter_1.sortGraphTopologically)(reducedGraph);
|
|
118
119
|
console.log('š·ļø Enriching graph with framework + responsibilities metadata...');
|
|
119
120
|
const projectInfos = await (0, graph_metadata_1.collectProjectInfo)();
|
|
121
|
+
new canonical_clients_1.WorkspaceClientBindings().assert(workspaceRoot, projectInfos);
|
|
120
122
|
(0, graph_metadata_1.enrichGraph)(currentGraph, projectInfos, workspaceRoot);
|
|
121
123
|
new tag_truth_1.TagTruthCheck().assertTrue(currentGraph, projectInfos, workspaceRoot);
|
|
122
124
|
new approved_graph_1.ApprovedWiringGraph().attach(workspaceRoot, currentGraph, projectInfos, 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":";AAAA;;;;;;;;GAQG;;;AA+FH,gDAOC;AAuFD,8BAkEC;AA5PD,0DAKiC;AACjC,+DAAiE;AACjE,yDAAgE;AAChE,iEAA2D;AAC3D,yDAA2E;AAE3E,6DAAoG;AACpG,mDAAoD;AACpD,4EAA8E;AAI9E,mDAA+C;AAE/C,2CAAwC;AAUxC,MAAM,WAAW,GAAG,2BAA2B,CAAC;AAEhD;;;GAGG;AACH,SAAS,wBAAwB,CAAC,aAAqB;IACnD,MAAM,MAAM,GAAG,IAAA,4BAAa,EAAC,aAAa,EAAE,WAAW,CAAC,CAAC;IAEzD,OAAO,MAAM,CAAC;AAClB,CAAC;AAED;;GAEG;AACH,SAAS,cAAc,CAAC,OAAe,EAAE,aAAqB;IAC1D,MAAM,MAAM,GAAG,wBAAwB,CAAC,aAAa,CAAC,CAAC;IAEvD,OAAO,CAAC,KAAK,CAAC,+CAA+C,CAAC,CAAC;IAC/D,OAAO,CAAC,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAChC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACvB,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,mBAAmB,GAAG,MAAM,GAAG,wCAAwC,CAAC,CAAC;IACvF,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;IAClB,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;IACzB,OAAO,CAAC,KAAK,CAAC,+BAA+B,CAAC,CAAC;IAC/C,OAAO,CAAC,KAAK,CACT,oGAAoG,CACvG,CAAC;IACF,OAAO,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,2GAA2G;AAC3G,SAAS,yBAAyB,CAC9B,OAA4B,EAC5B,KAA0B;IAE1B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,SAAS;YACf,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,uDAAuD,CAAC,CAAC;aAChF,IAAI,CAAC,KAAK,SAAS;YACpB,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,mDAAmD,CAAC,CAAC;aAC5E,IAAI,CAAC,KAAK,CAAC;YAAE,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,iCAAiC,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;IAC7F,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,2BAA2B,OAAO,CAAC,MAAM,cAAc,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACvF,CAAC;AAED;;;;GAIG;AACH,+GAA+G;AAC/G,SAAgB,kBAAkB,CAC9B,OAA4B,EAC5B,KAAuB;IAEvB,MAAM,SAAS,GAAG,yBAAyB,CAAC,OAAO,CAAC,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;IAC9F,IAAI,SAAS,KAAK,IAAI;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,2BAA2B,CAAC,OAAO,CAAC,eAAe,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;AACvF,CAAC;AAED;;;GAGG;AACH,+GAA+G;AAC/G,SAAS,2BAA2B,CAChC,OAA4B,EAC5B,KAA0B;IAE1B,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IACpF,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACvB,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;QACxB,MAAM,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,SAAS;YACf,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,yDAAyD,CAAC,CAAC;aAClF,IAAI,CAAC,KAAK,SAAS;YACpB,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,yDAAyD,CAAC,CAAC;aAClF,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;YAC5C,OAAO,CAAC,IAAI,CAAC,OAAO,IAAI,gCAAgC,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACtC,OAAO,0BAA0B,OAAO,CAAC,MAAM,iBAAiB,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AACzF,CAAC;AAED;;;;GAIG;AACH,MAAa,mBAAmB;IAC5B,KAAK,CAAC,KAAK,CAAC,aAAqB,EAAE,SAAkB;QACjD,OAAO,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;QACzD,MAAM,YAAY,GAAG,MAAM,IAAA,sCAAoB,GAAE,CAAC;QAClD,OAAO,CAAC,GAAG,CAAC,oCAAoC,CAAC,CAAC;QAClD,MAAM,YAAY,GAAG,IAAA,qCAAsB,EAAC,YAAY,CAAC,CAAC;QAC1D,OAAO,CAAC,GAAG,CAAC,oEAAoE,CAAC,CAAC;QAClF,MAAM,YAAY,GAAG,MAAM,IAAA,mCAAkB,GAAE,CAAC;QAChD,IAAA,4BAAW,EAAC,YAAY,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;QACvD,IAAI,yBAAa,EAAE,CAAC,UAAU,CAAC,YAAY,EAAE,YAAY,EAAE,aAAa,CAAC,CAAC;QAC1E,IAAI,oCAAmB,EAAE,CAAC,MAAM,CAAC,aAAa,EAAE,YAAY,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;QACvF,MAAM,KAAK,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,KAAK,KAAK,IAAI;YACd,MAAM,IAAI,4BAAa,CACnB,+BAA+B,EAC/B,yBAAyB,EACzB,SAAS,EACT,SAAS,EACT;gBACI,IAAI,qBAAM,CACN,gFAAgF,EAChF,IAAI,CACP;aACJ,CACJ,CAAC;QACN,OAAO,IAAI,mBAAmB,CAAC,YAAY,EAAE,KAAK,CAAC,gBAAgB,EAAE,KAAK,CAAC,eAAe,CAAC,CAAC;IAChG,CAAC;CACJ;AA3BD,kDA2BC;AAED,gGAAgG;AAChG,MAAa,mBAAmB;IAER;IACA;IACA;IAHpB,YACoB,KAAoB,EACpB,gBAAqC,EACrC,eAAoC;QAFpC,UAAK,GAAL,KAAK,CAAe;QACpB,qBAAgB,GAAhB,gBAAgB,CAAqB;QACrC,oBAAe,GAAf,eAAe,CAAqB;IACrD,CAAC;CACP;AAND,kDAMC;AAED,yFAAyF;AACzF,2GAA2G;AAC3G,SAAS,kBAAkB;IACvB,MAAM,IAAI,4BAAa,CACnB,+BAA+B,EAC/B,kDAAkD,EAClD,SAAS,EACT,SAAS,EACT;QACI,IAAI,qBAAM,CACN,mXAAmX,EACnX,IAAI,CACP;KACJ,CACJ,CAAC;AACN,CAAC;AAEc,KAAK,UAAU,WAAW,CACrC,OAA6C,EAC7C,OAAwB;IAExB,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,CAAC;IACpC,MAAM,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAEnC,yFAAyF;IACzF,yFAAyF;IACzF,IAAI,IAAI,oBAAQ,EAAE,CAAC,UAAU,CAAC,aAAa,EAAE,iCAAiC,EAAE,IAAI,CAAC,EAAE,CAAC;QACpF,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;IAC7B,CAAC;IAED,OAAO,CAAC,GAAG,CAAC,0CAA0C,CAAC,CAAC;IAExD,8DAA8D;IAC9D,IAAI,CAAC;QACD,8BAA8B;QAC9B,IAAI,CAAC,IAAA,8BAAe,EAAC,aAAa,EAAE,SAAS,CAAC,EAAE,CAAC;YAC7C,kBAAkB,EAAE,CAAC;YACrB,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,wEAAwE;QACxE,2CAA2C;QAC3C,MAAM,YAAY,GAAG,MAAM,IAAI,mBAAmB,EAAE,CAAC,KAAK,CAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAErF,2BAA2B;QAC3B,OAAO,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAC;QACzC,MAAM,UAAU,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAE9D,IAAI,CAAC,UAAU,EAAE,CAAC;YACd,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAC;YAC9C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,yBAAyB;QACzB,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAC5D,MAAM,UAAU,GAAG,IAAA,gCAAa,EAAC,YAAY,CAAC,KAAK,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC;QAE1E,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YACxB,cAAc,CAAC,UAAU,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;YAClD,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,6FAA6F;QAC7F,0FAA0F;QAC1F,sEAAsE;QACtE,MAAM,UAAU,GAAG,kBAAkB,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;QAChE,IAAI,UAAU,KAAK,IAAI,EAAE,CAAC;YACtB,cAAc,CAAC,UAAU,EAAE,aAAa,CAAC,CAAC;YAC1C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;QAC9B,CAAC;QAED,OAAO,CAAC,GAAG,CAAC,8DAA8D,CAAC,CAAC;QAC5E,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,yFAAyF;QACzF,wFAAwF;QACxF,MAAM,QAAQ,GACV,KAAK,YAAY,4BAAa,CAAC,CAAC,CAAC,IAAA,qCAAsB,EAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC;QACnF,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,QAAQ,CAAC,CAAC;QAC7D,qBAAqB,CAAC,KAAK,EAAE,aAAa,CAAC,CAAC;QAC5C,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IAC9B,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,SAAS,qBAAqB,CAAC,KAAY,EAAE,aAAqB;IAC9D,IAAI,KAAK,YAAY,wCAAuB,EAAE,CAAC;QAC3C,MAAM,MAAM,GAAG,IAAA,4BAAa,EAAC,aAAa,EAAE,+BAA+B,CAAC,CAAC;QAC7E,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;QAClB,OAAO,CAAC,KAAK,CACT,mBAAmB,GAAG,MAAM,GAAG,qDAAqD,CACvF,CAAC;IACN,CAAC;AACL,CAAC","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 { 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 return describeExternalSystemDrift(current.externalSystems, saved.externalSystems);\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(currentGraph, saved.apiContractFiles, saved.externalSystems);\n }\n}\n\n/** The regenerated graph plus the two 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 ) {}\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\"}, and reviewed externalSystems. 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 { 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 { WorkspaceClientBindings } from '../../lib/runtime-wiring/canonical-clients';\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 { 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 return describeExternalSystemDrift(current.externalSystems, saved.externalSystems);\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 new WorkspaceClientBindings().assert(workspaceRoot, projectInfos);\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(currentGraph, saved.apiContractFiles, saved.externalSystems);\n }\n}\n\n/** The regenerated graph plus the two 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 ) {}\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\"}, and reviewed externalSystems. 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"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import * as ts from 'typescript';
|
|
2
|
+
import type { ProjectInfo } from '../project-info';
|
|
3
|
+
/** ALL-CODE grammar check: no git diff, line exemptions, or wiring.ts carve-out. */
|
|
4
|
+
export declare class CanonicalClientBindings {
|
|
5
|
+
private readonly checker;
|
|
6
|
+
constructor(checker: ts.TypeChecker);
|
|
7
|
+
problems(file: ts.SourceFile): string[];
|
|
8
|
+
assert(problems: readonly string[]): void;
|
|
9
|
+
private checkFactory;
|
|
10
|
+
private singleton;
|
|
11
|
+
private property;
|
|
12
|
+
private method;
|
|
13
|
+
private expand;
|
|
14
|
+
}
|
|
15
|
+
/** Architecture validation scans every participating owner, even when its build isn't affected. */
|
|
16
|
+
export declare class WorkspaceClientBindings {
|
|
17
|
+
private program;
|
|
18
|
+
assert(workspaceRoot: string, infos: ReadonlyMap<string, ProjectInfo>): void;
|
|
19
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.WorkspaceClientBindings = exports.CanonicalClientBindings = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const path = tslib_1.__importStar(require("path"));
|
|
6
|
+
const ts = tslib_1.__importStar(require("typescript"));
|
|
7
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
8
|
+
const program_1 = require("../di-graph/program");
|
|
9
|
+
/** ALL-CODE grammar check: no git diff, line exemptions, or wiring.ts carve-out. */
|
|
10
|
+
class CanonicalClientBindings {
|
|
11
|
+
checker;
|
|
12
|
+
constructor(checker) {
|
|
13
|
+
this.checker = checker;
|
|
14
|
+
}
|
|
15
|
+
problems(file) {
|
|
16
|
+
const problems = [];
|
|
17
|
+
const visit = (node) => {
|
|
18
|
+
if (ts.isCallExpression(node) && this.method(node.expression) === 'toDynamicValue') {
|
|
19
|
+
const bind = this.expand(node.expression);
|
|
20
|
+
const receiver = ts.isPropertyAccessExpression(bind) || ts.isElementAccessExpression(bind)
|
|
21
|
+
? this.expand(bind.expression)
|
|
22
|
+
: undefined;
|
|
23
|
+
if (receiver !== undefined &&
|
|
24
|
+
ts.isCallExpression(receiver) &&
|
|
25
|
+
this.method(receiver.expression) === 'bind' &&
|
|
26
|
+
this.singleton(node)) {
|
|
27
|
+
this.checkFactory(node.arguments[0], receiver.arguments[0], false, file, problems);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
if (ts.isObjectLiteralExpression(node)) {
|
|
31
|
+
const token = this.property(node, 'provide');
|
|
32
|
+
const factory = this.property(node, 'useFactory');
|
|
33
|
+
if (token !== undefined && factory !== undefined)
|
|
34
|
+
this.checkFactory(factory, token, true, file, problems);
|
|
35
|
+
}
|
|
36
|
+
ts.forEachChild(node, visit);
|
|
37
|
+
};
|
|
38
|
+
visit(file);
|
|
39
|
+
return problems;
|
|
40
|
+
}
|
|
41
|
+
assert(problems) {
|
|
42
|
+
if (problems.length === 0)
|
|
43
|
+
return;
|
|
44
|
+
throw new rules_config_1.RuleFailError('validate-runtime-architecture', problems.join('\n'), undefined, undefined, [
|
|
45
|
+
new rules_config_1.Option('Replace every supported hand-written registration, including unchanged code in src/wiring.ts. Use RuntimeClients.bindRpc with rpcTarget; RuntimeTaskClients.bindPubSub for task clients; provideRpcClient for browser providers. Preserve tokens, filters and lazy singleton resolution.', true),
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
checkFactory(factory, token, browser, file, problems) {
|
|
49
|
+
if (factory === undefined || token === undefined)
|
|
50
|
+
return;
|
|
51
|
+
const value = this.expand(factory);
|
|
52
|
+
if (!ts.isArrowFunction(value) && !ts.isFunctionExpression(value))
|
|
53
|
+
return;
|
|
54
|
+
const result = ts.isBlock(value.body) &&
|
|
55
|
+
value.body.statements.length === 1 &&
|
|
56
|
+
ts.isReturnStatement(value.body.statements[0])
|
|
57
|
+
? value.body.statements[0].expression
|
|
58
|
+
: ts.isBlock(value.body)
|
|
59
|
+
? undefined
|
|
60
|
+
: value.body;
|
|
61
|
+
if (result === undefined)
|
|
62
|
+
return; // Side effects/custom adapters have no helper equivalent.
|
|
63
|
+
const call = this.expand(result);
|
|
64
|
+
if (!ts.isCallExpression(call))
|
|
65
|
+
return;
|
|
66
|
+
const declaration = this.checker.getResolvedSignature(call)?.declaration;
|
|
67
|
+
const owner = declaration?.parent;
|
|
68
|
+
if (owner === undefined || !ts.isClassDeclaration(owner))
|
|
69
|
+
return;
|
|
70
|
+
const name = owner.name?.text;
|
|
71
|
+
const method = this.method(call.expression);
|
|
72
|
+
const rpc = name === 'ClientHttpFactory' && method === 'createRpcClient' && !browser;
|
|
73
|
+
const task = name === 'ClientCloudTasksFactory' && method === 'createPubSubClient' && !browser;
|
|
74
|
+
const provider = name === 'ClientHttpBrowserFactory' && method === 'createRpcClient' && browser;
|
|
75
|
+
if (!rpc && !task && !provider)
|
|
76
|
+
return;
|
|
77
|
+
if (call.arguments.length < 2 || call.arguments.length > (rpc ? 3 : 2))
|
|
78
|
+
return;
|
|
79
|
+
const config = this.expand(call.arguments[1]);
|
|
80
|
+
if (!ts.isNewExpression(config) ||
|
|
81
|
+
this.method(config.expression) !== (task ? 'TaskClientConfig' : 'ClientConfig') ||
|
|
82
|
+
config.arguments?.length !== 1)
|
|
83
|
+
return; // Custom config options aren't supported by helpers.
|
|
84
|
+
const api = call.arguments[0].getText();
|
|
85
|
+
const target = config.arguments[0].getText();
|
|
86
|
+
const filters = call.arguments[2] === undefined ? '' : `, ${call.arguments[2].getText()}`;
|
|
87
|
+
const replacement = task
|
|
88
|
+
? `new RuntimeTaskClients(options).bindPubSub(${token.getText()}, ${api}, ${target});`
|
|
89
|
+
: provider
|
|
90
|
+
? `provideRpcClient(${token.getText()}, ${api}, rpcTarget(${api}, ${target}))`
|
|
91
|
+
: `new RuntimeClients(options).bindRpc(${token.getText()}, ${api}, rpcTarget(${api}, ${target})${filters});`;
|
|
92
|
+
const location = file.getLineAndCharacterOfPosition(factory.getStart(file));
|
|
93
|
+
problems.push(`${file.fileName}:${location.line + 1}:${location.character + 1}: canonical client registration required: ${replacement}`);
|
|
94
|
+
}
|
|
95
|
+
singleton(call) {
|
|
96
|
+
let node = call;
|
|
97
|
+
while (node.parent !== undefined &&
|
|
98
|
+
(ts.isPropertyAccessExpression(node.parent) ||
|
|
99
|
+
ts.isElementAccessExpression(node.parent) ||
|
|
100
|
+
ts.isCallExpression(node.parent))) {
|
|
101
|
+
node = node.parent;
|
|
102
|
+
if (ts.isCallExpression(node) && this.method(node.expression) === 'inSingletonScope')
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
property(node, name) {
|
|
108
|
+
for (const property of node.properties) {
|
|
109
|
+
if (ts.isPropertyAssignment(property) &&
|
|
110
|
+
(ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)) &&
|
|
111
|
+
property.name.text === name)
|
|
112
|
+
return property.initializer;
|
|
113
|
+
if (ts.isShorthandPropertyAssignment(property) && property.name.text === name)
|
|
114
|
+
return property.name;
|
|
115
|
+
}
|
|
116
|
+
return undefined;
|
|
117
|
+
}
|
|
118
|
+
method(expression) {
|
|
119
|
+
const value = this.expand(expression);
|
|
120
|
+
if (ts.isPropertyAccessExpression(value))
|
|
121
|
+
return value.name.text;
|
|
122
|
+
if (ts.isElementAccessExpression(value) && ts.isStringLiteralLike(value.argumentExpression))
|
|
123
|
+
return value.argumentExpression.text;
|
|
124
|
+
const symbol = this.checker.getSymbolAtLocation(value);
|
|
125
|
+
return symbol !== undefined && (symbol.flags & ts.SymbolFlags.Alias) !== 0
|
|
126
|
+
? this.checker.getAliasedSymbol(symbol).getName()
|
|
127
|
+
: (symbol?.getName() ?? value.getText());
|
|
128
|
+
}
|
|
129
|
+
expand(expression, seen = new Set()) {
|
|
130
|
+
if (seen.has(expression))
|
|
131
|
+
return expression;
|
|
132
|
+
seen.add(expression);
|
|
133
|
+
if (ts.isParenthesizedExpression(expression) ||
|
|
134
|
+
ts.isAsExpression(expression) ||
|
|
135
|
+
ts.isNonNullExpression(expression) ||
|
|
136
|
+
ts.isSatisfiesExpression(expression))
|
|
137
|
+
return this.expand(expression.expression, seen);
|
|
138
|
+
const declaration = this.checker.getSymbolAtLocation(expression)?.valueDeclaration;
|
|
139
|
+
if (declaration !== undefined &&
|
|
140
|
+
ts.isVariableDeclaration(declaration) &&
|
|
141
|
+
declaration.initializer !== undefined)
|
|
142
|
+
return this.expand(declaration.initializer, seen);
|
|
143
|
+
return expression;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
exports.CanonicalClientBindings = CanonicalClientBindings;
|
|
147
|
+
/** Architecture validation scans every participating owner, even when its build isn't affected. */
|
|
148
|
+
class WorkspaceClientBindings {
|
|
149
|
+
program(root) {
|
|
150
|
+
const configured = (0, program_1.createProjectProgram)(root);
|
|
151
|
+
const files = ts.sys.readDirectory(path.join(root, 'src'), ['.ts', '.tsx'], ['**/*.spec.ts', '**/*.test.ts', '**/__tests__/**', '**/node_modules/**']);
|
|
152
|
+
return ts.createProgram(files, configured?.getCompilerOptions() ?? {});
|
|
153
|
+
}
|
|
154
|
+
assert(workspaceRoot, infos) {
|
|
155
|
+
const problems = [];
|
|
156
|
+
for (const info of infos.values()) {
|
|
157
|
+
if (!info.tags.some((tag) => tag === 'webpieces' || tag === 'webpieces-lib'))
|
|
158
|
+
continue;
|
|
159
|
+
const program = this.program(path.resolve(workspaceRoot, info.root));
|
|
160
|
+
const check = new CanonicalClientBindings(program.getTypeChecker());
|
|
161
|
+
const root = path.resolve(workspaceRoot, info.root) + path.sep;
|
|
162
|
+
for (const file of program.getSourceFiles()) {
|
|
163
|
+
if (file.isDeclarationFile ||
|
|
164
|
+
!file.fileName.startsWith(root) ||
|
|
165
|
+
/(?:\.spec\.|\.test\.|\/__tests__\/|\/node_modules\/)/.test(file.fileName))
|
|
166
|
+
continue;
|
|
167
|
+
problems.push(...check.problems(file));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
// No source program is needed to render a structured aggregate failure.
|
|
171
|
+
if (problems.length > 0)
|
|
172
|
+
throw new rules_config_1.RuleFailError('validate-runtime-architecture', problems.join('\n'), undefined, undefined, [
|
|
173
|
+
new rules_config_1.Option('Migrate all reported registrations using the canonical helpers and rpcTarget, preserving tokens and filters. This checks ALL CODE in every participating runtime owner, irrespective of the current diff.', true),
|
|
174
|
+
]);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
exports.WorkspaceClientBindings = WorkspaceClientBindings;
|
|
178
|
+
//# sourceMappingURL=canonical-clients.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"canonical-clients.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/canonical-clients.ts"],"names":[],"mappings":";;;;AAAA,mDAA6B;AAC7B,uDAAiC;AACjC,0DAAgE;AAEhE,iDAA2D;AAE3D,oFAAoF;AACpF,MAAa,uBAAuB;IACH;IAA7B,YAA6B,OAAuB;QAAvB,YAAO,GAAP,OAAO,CAAgB;IAAG,CAAC;IAExD,QAAQ,CAAC,IAAmB;QACxB,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,MAAM,KAAK,GAAG,CAAC,IAAa,EAAQ,EAAE;YAClC,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,gBAAgB,EAAE,CAAC;gBACjF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC1C,MAAM,QAAQ,GACV,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;oBACrE,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC;oBAC9B,CAAC,CAAC,SAAS,CAAC;gBACpB,IACI,QAAQ,KAAK,SAAS;oBACtB,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC;oBAC7B,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,MAAM;oBAC3C,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EACtB,CAAC;oBACC,IAAI,CAAC,YAAY,CACb,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EACjB,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EACrB,KAAK,EACL,IAAI,EACJ,QAAQ,CACX,CAAC;gBACN,CAAC;YACL,CAAC;YACD,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,EAAE,CAAC;gBACrC,MAAM,KAAK,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;gBAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC;gBAClD,IAAI,KAAK,KAAK,SAAS,IAAI,OAAO,KAAK,SAAS;oBAC5C,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;YAChE,CAAC;YACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;QACjC,CAAC,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,CAAC;QACZ,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,MAAM,CAAC,QAA2B;QAC9B,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO;QAClC,MAAM,IAAI,4BAAa,CACnB,+BAA+B,EAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EACnB,SAAS,EACT,SAAS,EACT;YACI,IAAI,qBAAM,CACN,0RAA0R,EAC1R,IAAI,CACP;SACJ,CACJ,CAAC;IACN,CAAC;IAEO,YAAY,CAChB,OAAkC,EAClC,KAAgC,EAChC,OAAgB,EAChB,IAAmB,EACnB,QAAkB;QAElB,IAAI,OAAO,KAAK,SAAS,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO;QACzD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACnC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC;YAAE,OAAO;QAC1E,MAAM,MAAM,GACR,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;YACtB,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;YAClC,EAAE,CAAC,iBAAiB,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;YAC1C,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU;YACrC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;gBACtB,CAAC,CAAC,SAAS;gBACX,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC;QACvB,IAAI,MAAM,KAAK,SAAS;YAAE,OAAO,CAAC,0DAA0D;QAC5F,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACjC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAAE,OAAO;QACvC,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,oBAAoB,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC;QACzE,MAAM,KAAK,GAAG,WAAW,EAAE,MAAM,CAAC;QAClC,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,KAAK,CAAC;YAAE,OAAO;QACjE,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,CAAC;QAC9B,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;QAC5C,MAAM,GAAG,GAAG,IAAI,KAAK,mBAAmB,IAAI,MAAM,KAAK,iBAAiB,IAAI,CAAC,OAAO,CAAC;QACrF,MAAM,IAAI,GACN,IAAI,KAAK,yBAAyB,IAAI,MAAM,KAAK,oBAAoB,IAAI,CAAC,OAAO,CAAC;QACtF,MAAM,QAAQ,GACV,IAAI,KAAK,0BAA0B,IAAI,MAAM,KAAK,iBAAiB,IAAI,OAAO,CAAC;QACnF,IAAI,CAAC,GAAG,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ;YAAE,OAAO;QACvC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAAE,OAAO;QAC/E,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAC9C,IACI,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC;YAC3B,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAC,CAAC,cAAc,CAAC;YAC/E,MAAM,CAAC,SAAS,EAAE,MAAM,KAAK,CAAC;YAE9B,OAAO,CAAC,qDAAqD;QACjE,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QACxC,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC;QAC7C,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC;QAC1F,MAAM,WAAW,GAAG,IAAI;YACpB,CAAC,CAAC,8CAA8C,KAAK,CAAC,OAAO,EAAE,KAAK,GAAG,KAAK,MAAM,IAAI;YACtF,CAAC,CAAC,QAAQ;gBACR,CAAC,CAAC,oBAAoB,KAAK,CAAC,OAAO,EAAE,KAAK,GAAG,eAAe,GAAG,KAAK,MAAM,IAAI;gBAC9E,CAAC,CAAC,uCAAuC,KAAK,CAAC,OAAO,EAAE,KAAK,GAAG,eAAe,GAAG,KAAK,MAAM,IAAI,OAAO,IAAI,CAAC;QACnH,MAAM,QAAQ,GAAG,IAAI,CAAC,6BAA6B,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;QAC5E,QAAQ,CAAC,IAAI,CACT,GAAG,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,IAAI,QAAQ,CAAC,SAAS,GAAG,CAAC,6CAA6C,WAAW,EAAE,CAC5H,CAAC;IACN,CAAC;IAEO,SAAS,CAAC,IAAuB;QACrC,IAAI,IAAI,GAAY,IAAI,CAAC;QACzB,OACI,IAAI,CAAC,MAAM,KAAK,SAAS;YACzB,CAAC,EAAE,CAAC,0BAA0B,CAAC,IAAI,CAAC,MAAM,CAAC;gBACvC,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC,MAAM,CAAC;gBACzC,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,EACvC,CAAC;YACC,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC;YACnB,IAAI,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,kBAAkB;gBAChF,OAAO,IAAI,CAAC;QACpB,CAAC;QACD,OAAO,KAAK,CAAC;IACjB,CAAC;IAEO,QAAQ,CAAC,IAAgC,EAAE,IAAY;QAC3D,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACrC,IACI,EAAE,CAAC,oBAAoB,CAAC,QAAQ,CAAC;gBACjC,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;gBACzE,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI;gBAE3B,OAAO,QAAQ,CAAC,WAAW,CAAC;YAChC,IAAI,EAAE,CAAC,6BAA6B,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI;gBACzE,OAAO,QAAQ,CAAC,IAAI,CAAC;QAC7B,CAAC;QACD,OAAO,SAAS,CAAC;IACrB,CAAC;IAEO,MAAM,CAAC,UAAyB;QACpC,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACtC,IAAI,EAAE,CAAC,0BAA0B,CAAC,KAAK,CAAC;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC;QACjE,IAAI,EAAE,CAAC,yBAAyB,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,mBAAmB,CAAC,KAAK,CAAC,kBAAkB,CAAC;YACvF,OAAO,KAAK,CAAC,kBAAkB,CAAC,IAAI,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC;QACvD,OAAO,MAAM,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,KAAK,GAAG,EAAE,CAAC,WAAW,CAAC,KAAK,CAAC,KAAK,CAAC;YACtE,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,gBAAgB,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;YACjD,CAAC,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;IACjD,CAAC;IAEO,MAAM,CAAC,UAAyB,EAAE,OAAqB,IAAI,GAAG,EAAE;QACpE,IAAI,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC;YAAE,OAAO,UAAU,CAAC;QAC5C,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACrB,IACI,EAAE,CAAC,yBAAyB,CAAC,UAAU,CAAC;YACxC,EAAE,CAAC,cAAc,CAAC,UAAU,CAAC;YAC7B,EAAE,CAAC,mBAAmB,CAAC,UAAU,CAAC;YAClC,EAAE,CAAC,qBAAqB,CAAC,UAAU,CAAC;YAEpC,OAAO,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC;QACpD,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,mBAAmB,CAAC,UAAU,CAAC,EAAE,gBAAgB,CAAC;QACnF,IACI,WAAW,KAAK,SAAS;YACzB,EAAE,CAAC,qBAAqB,CAAC,WAAW,CAAC;YACrC,WAAW,CAAC,WAAW,KAAK,SAAS;YAErC,OAAO,IAAI,CAAC,MAAM,CAAC,WAAW,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;QACtD,OAAO,UAAU,CAAC;IACtB,CAAC;CACJ;AAxKD,0DAwKC;AAED,mGAAmG;AACnG,MAAa,uBAAuB;IACxB,OAAO,CAAC,IAAY;QACxB,MAAM,UAAU,GAAG,IAAA,8BAAoB,EAAC,IAAI,CAAC,CAAC;QAC9C,MAAM,KAAK,GAAG,EAAE,CAAC,GAAG,CAAC,aAAa,CAC9B,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,EACtB,CAAC,KAAK,EAAE,MAAM,CAAC,EACf,CAAC,cAAc,EAAE,cAAc,EAAE,iBAAiB,EAAE,oBAAoB,CAAC,CAC5E,CAAC;QACF,OAAO,EAAE,CAAC,aAAa,CAAC,KAAK,EAAE,UAAU,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED,MAAM,CAAC,aAAqB,EAAE,KAAuC;QACjE,MAAM,QAAQ,GAAa,EAAE,CAAC;QAC9B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAChC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,KAAK,WAAW,IAAI,GAAG,KAAK,eAAe,CAAC;gBAAE,SAAS;YACvF,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YACrE,MAAM,KAAK,GAAG,IAAI,uBAAuB,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;YACpE,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC;YAC/D,KAAK,MAAM,IAAI,IAAI,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1C,IACI,IAAI,CAAC,iBAAiB;oBACtB,CAAC,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,CAAC;oBAC/B,sDAAsD,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC;oBAE1E,SAAS;gBACb,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC;YAC3C,CAAC;QACL,CAAC;QACD,wEAAwE;QACxE,IAAI,QAAQ,CAAC,MAAM,GAAG,CAAC;YACnB,MAAM,IAAI,4BAAa,CACnB,+BAA+B,EAC/B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EACnB,SAAS,EACT,SAAS,EACT;gBACI,IAAI,qBAAM,CACN,2MAA2M,EAC3M,IAAI,CACP;aACJ,CACJ,CAAC;IACV,CAAC;CACJ;AA3CD,0DA2CC","sourcesContent":["import * as path from 'path';\nimport * as ts from 'typescript';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport type { ProjectInfo } from '../project-info';\nimport { createProjectProgram } from '../di-graph/program';\n\n/** ALL-CODE grammar check: no git diff, line exemptions, or wiring.ts carve-out. */\nexport class CanonicalClientBindings {\n constructor(private readonly checker: ts.TypeChecker) {}\n\n problems(file: ts.SourceFile): string[] {\n const problems: string[] = [];\n const visit = (node: ts.Node): void => {\n if (ts.isCallExpression(node) && this.method(node.expression) === 'toDynamicValue') {\n const bind = this.expand(node.expression);\n const receiver =\n ts.isPropertyAccessExpression(bind) || ts.isElementAccessExpression(bind)\n ? this.expand(bind.expression)\n : undefined;\n if (\n receiver !== undefined &&\n ts.isCallExpression(receiver) &&\n this.method(receiver.expression) === 'bind' &&\n this.singleton(node)\n ) {\n this.checkFactory(\n node.arguments[0],\n receiver.arguments[0],\n false,\n file,\n problems,\n );\n }\n }\n if (ts.isObjectLiteralExpression(node)) {\n const token = this.property(node, 'provide');\n const factory = this.property(node, 'useFactory');\n if (token !== undefined && factory !== undefined)\n this.checkFactory(factory, token, true, file, problems);\n }\n ts.forEachChild(node, visit);\n };\n visit(file);\n return problems;\n }\n\n assert(problems: readonly string[]): void {\n if (problems.length === 0) return;\n throw new RuleFailError(\n 'validate-runtime-architecture',\n problems.join('\\n'),\n undefined,\n undefined,\n [\n new Option(\n 'Replace every supported hand-written registration, including unchanged code in src/wiring.ts. Use RuntimeClients.bindRpc with rpcTarget; RuntimeTaskClients.bindPubSub for task clients; provideRpcClient for browser providers. Preserve tokens, filters and lazy singleton resolution.',\n true,\n ),\n ],\n );\n }\n\n private checkFactory(\n factory: ts.Expression | undefined,\n token: ts.Expression | undefined,\n browser: boolean,\n file: ts.SourceFile,\n problems: string[],\n ): void {\n if (factory === undefined || token === undefined) return;\n const value = this.expand(factory);\n if (!ts.isArrowFunction(value) && !ts.isFunctionExpression(value)) return;\n const result =\n ts.isBlock(value.body) &&\n value.body.statements.length === 1 &&\n ts.isReturnStatement(value.body.statements[0])\n ? value.body.statements[0].expression\n : ts.isBlock(value.body)\n ? undefined\n : value.body;\n if (result === undefined) return; // Side effects/custom adapters have no helper equivalent.\n const call = this.expand(result);\n if (!ts.isCallExpression(call)) return;\n const declaration = this.checker.getResolvedSignature(call)?.declaration;\n const owner = declaration?.parent;\n if (owner === undefined || !ts.isClassDeclaration(owner)) return;\n const name = owner.name?.text;\n const method = this.method(call.expression);\n const rpc = name === 'ClientHttpFactory' && method === 'createRpcClient' && !browser;\n const task =\n name === 'ClientCloudTasksFactory' && method === 'createPubSubClient' && !browser;\n const provider =\n name === 'ClientHttpBrowserFactory' && method === 'createRpcClient' && browser;\n if (!rpc && !task && !provider) return;\n if (call.arguments.length < 2 || call.arguments.length > (rpc ? 3 : 2)) return;\n const config = this.expand(call.arguments[1]);\n if (\n !ts.isNewExpression(config) ||\n this.method(config.expression) !== (task ? 'TaskClientConfig' : 'ClientConfig') ||\n config.arguments?.length !== 1\n )\n return; // Custom config options aren't supported by helpers.\n const api = call.arguments[0].getText();\n const target = config.arguments[0].getText();\n const filters = call.arguments[2] === undefined ? '' : `, ${call.arguments[2].getText()}`;\n const replacement = task\n ? `new RuntimeTaskClients(options).bindPubSub(${token.getText()}, ${api}, ${target});`\n : provider\n ? `provideRpcClient(${token.getText()}, ${api}, rpcTarget(${api}, ${target}))`\n : `new RuntimeClients(options).bindRpc(${token.getText()}, ${api}, rpcTarget(${api}, ${target})${filters});`;\n const location = file.getLineAndCharacterOfPosition(factory.getStart(file));\n problems.push(\n `${file.fileName}:${location.line + 1}:${location.character + 1}: canonical client registration required: ${replacement}`,\n );\n }\n\n private singleton(call: ts.CallExpression): boolean {\n let node: ts.Node = call;\n while (\n node.parent !== undefined &&\n (ts.isPropertyAccessExpression(node.parent) ||\n ts.isElementAccessExpression(node.parent) ||\n ts.isCallExpression(node.parent))\n ) {\n node = node.parent;\n if (ts.isCallExpression(node) && this.method(node.expression) === 'inSingletonScope')\n return true;\n }\n return false;\n }\n\n private property(node: ts.ObjectLiteralExpression, name: string): ts.Expression | undefined {\n for (const property of node.properties) {\n if (\n ts.isPropertyAssignment(property) &&\n (ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)) &&\n property.name.text === name\n )\n return property.initializer;\n if (ts.isShorthandPropertyAssignment(property) && property.name.text === name)\n return property.name;\n }\n return undefined;\n }\n\n private method(expression: ts.Expression): string {\n const value = this.expand(expression);\n if (ts.isPropertyAccessExpression(value)) return value.name.text;\n if (ts.isElementAccessExpression(value) && ts.isStringLiteralLike(value.argumentExpression))\n return value.argumentExpression.text;\n const symbol = this.checker.getSymbolAtLocation(value);\n return symbol !== undefined && (symbol.flags & ts.SymbolFlags.Alias) !== 0\n ? this.checker.getAliasedSymbol(symbol).getName()\n : (symbol?.getName() ?? value.getText());\n }\n\n private expand(expression: ts.Expression, seen: Set<ts.Node> = new Set()): ts.Expression {\n if (seen.has(expression)) return expression;\n seen.add(expression);\n if (\n ts.isParenthesizedExpression(expression) ||\n ts.isAsExpression(expression) ||\n ts.isNonNullExpression(expression) ||\n ts.isSatisfiesExpression(expression)\n )\n return this.expand(expression.expression, seen);\n const declaration = this.checker.getSymbolAtLocation(expression)?.valueDeclaration;\n if (\n declaration !== undefined &&\n ts.isVariableDeclaration(declaration) &&\n declaration.initializer !== undefined\n )\n return this.expand(declaration.initializer, seen);\n return expression;\n }\n}\n\n/** Architecture validation scans every participating owner, even when its build isn't affected. */\nexport class WorkspaceClientBindings {\n private program(root: string): ts.Program {\n const configured = createProjectProgram(root);\n const files = ts.sys.readDirectory(\n path.join(root, 'src'),\n ['.ts', '.tsx'],\n ['**/*.spec.ts', '**/*.test.ts', '**/__tests__/**', '**/node_modules/**'],\n );\n return ts.createProgram(files, configured?.getCompilerOptions() ?? {});\n }\n\n assert(workspaceRoot: string, infos: ReadonlyMap<string, ProjectInfo>): void {\n const problems: string[] = [];\n for (const info of infos.values()) {\n if (!info.tags.some((tag) => tag === 'webpieces' || tag === 'webpieces-lib')) continue;\n const program = this.program(path.resolve(workspaceRoot, info.root));\n const check = new CanonicalClientBindings(program.getTypeChecker());\n const root = path.resolve(workspaceRoot, info.root) + path.sep;\n for (const file of program.getSourceFiles()) {\n if (\n file.isDeclarationFile ||\n !file.fileName.startsWith(root) ||\n /(?:\\.spec\\.|\\.test\\.|\\/__tests__\\/|\\/node_modules\\/)/.test(file.fileName)\n )\n continue;\n problems.push(...check.problems(file));\n }\n }\n // No source program is needed to render a structured aggregate failure.\n if (problems.length > 0)\n throw new RuleFailError(\n 'validate-runtime-architecture',\n problems.join('\\n'),\n undefined,\n undefined,\n [\n new Option(\n 'Migrate all reported registrations using the canonical helpers and rpcTarget, preserving tokens and filters. This checks ALL CODE in every participating runtime owner, irrespective of the current diff.',\n true,\n ),\n ],\n );\n }\n}\n"]}
|
|
@@ -8,6 +8,7 @@ const rules_config_1 = require("@webpieces/rules-config");
|
|
|
8
8
|
const program_1 = require("../di-graph/program");
|
|
9
9
|
const declaration_1 = require("./declaration");
|
|
10
10
|
const source_values_1 = require("./source-values");
|
|
11
|
+
const canonical_clients_1 = require("./canonical-clients");
|
|
11
12
|
/** Build-only extraction. It never imports application modules or invokes their constructors. */
|
|
12
13
|
class WiringSourceExtractor {
|
|
13
14
|
workspaceRoot;
|
|
@@ -26,6 +27,14 @@ class WiringSourceExtractor {
|
|
|
26
27
|
this.values = new source_values_1.WiringSourceValues(workspaceRoot, infos, program.getTypeChecker());
|
|
27
28
|
}
|
|
28
29
|
extract() {
|
|
30
|
+
const clients = new canonical_clients_1.CanonicalClientBindings(this.program.getTypeChecker());
|
|
31
|
+
const problems = this.program
|
|
32
|
+
.getSourceFiles()
|
|
33
|
+
.filter((file) => !file.isDeclarationFile &&
|
|
34
|
+
this.owns(file.fileName) &&
|
|
35
|
+
!/(?:\.spec\.|\.test\.|\/__tests__\/)/.test(file.fileName))
|
|
36
|
+
.flatMap((file) => clients.problems(file));
|
|
37
|
+
clients.assert(problems);
|
|
29
38
|
const wiringPath = path.resolve(this.workspaceRoot, this.info.root, 'src/wiring.ts');
|
|
30
39
|
const wiring = this.program.getSourceFile(wiringPath);
|
|
31
40
|
if (wiring === undefined)
|
|
@@ -228,7 +237,7 @@ class WiringSourceExtractor {
|
|
|
228
237
|
}
|
|
229
238
|
fail(message) {
|
|
230
239
|
throw new rules_config_1.RuleFailError('validate-runtime-architecture', message, undefined, undefined, [
|
|
231
|
-
new rules_config_1.Option('Move topology into canonical src/wiring.ts and use the documented static plan grammar; do not execute configuration to extract it.', true),
|
|
240
|
+
new rules_config_1.Option('Move topology into canonical src/wiring.ts using RuntimeClients.bindRpc(token, Api, rpcTarget(Api, deployment)), RuntimeTaskClients.bindPubSub, or provideRpcClient for supported registrations, and use the documented static plan grammar; do not execute configuration to extract it.', true),
|
|
232
241
|
]);
|
|
233
242
|
}
|
|
234
243
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"source-extractor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/source-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport * as ts from 'typescript';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport type { ProjectInfo } from '../project-info';\nimport { createProjectProgram } from '../di-graph/program';\nimport {\n ContractIdentity,\n ImplementsFacts,\n UsesFacts,\n RuntimeDeclaration,\n WiringExport,\n WiringRelationship,\n WiringSelection,\n} from './declaration';\nimport { WiringSourceValues } from './source-values';\n\n/** Build-only extraction. It never imports application modules or invokes their constructors. */\nexport class WiringSourceExtractor {\n private readonly values: WiringSourceValues;\n private readonly exported: Record<string, WiringExport> = {};\n private entry: string | undefined;\n private host: string | undefined;\n\n constructor(\n private readonly workspaceRoot: string,\n private readonly info: ProjectInfo,\n private readonly infos: ReadonlyMap<string, ProjectInfo>,\n private readonly program: ts.Program,\n ) {\n this.values = new WiringSourceValues(workspaceRoot, infos, program.getTypeChecker());\n }\n\n extract(): RuntimeDeclaration {\n const wiringPath = path.resolve(this.workspaceRoot, this.info.root, 'src/wiring.ts');\n const wiring = this.program.getSourceFile(wiringPath);\n if (wiring === undefined) this.fail(`Missing canonical ${wiringPath}.`);\n for (const file of this.program.getSourceFiles()) {\n if (\n file.isDeclarationFile ||\n !this.owns(file.fileName) ||\n /(?:\\.spec\\.|\\.test\\.|\\/__tests__\\/)/.test(file.fileName)\n )\n continue;\n this.visit(file, file.fileName === wiringPath);\n }\n const framework = this.info.tags.includes('framework:angular')\n ? 'angular'\n : this.info.tags.some((tag) => tag === 'framework:node' || tag === 'framework:express')\n ? 'node'\n : 'browser';\n if (this.info.tags.includes('webpieces') && this.entry === undefined)\n this.fail(`${this.info.name} requires getRuntimeWiring() returning a typed plan.`);\n return new RuntimeDeclaration(\n this.info.name,\n framework,\n this.exported,\n this.entry,\n this.host,\n );\n }\n\n private visit(node: ts.Node, inWiring: boolean): void {\n if (\n !inWiring &&\n (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) &&\n [\n 'addRoutes',\n 'createRpcClient',\n 'createPubSubClient',\n 'bindRpc',\n 'bindPubSub',\n ].includes(this.values.symbolName(node))\n )\n this.fail(\n `${node.getSourceFile().fileName}: topology method references belong in src/wiring.ts, including aliases.`,\n );\n if (\n inWiring &&\n ts.isNewExpression(node) &&\n this.values.symbolName(node.expression) === 'ExternalContractUse'\n )\n this.recordExternal(node);\n if (ts.isCallExpression(node)) {\n const name = this.values.symbolName(node.expression);\n if (\n [\n 'addRoutes',\n 'createRpcClient',\n 'createPubSubClient',\n 'bindRpc',\n 'bindPubSub',\n 'provideRpcClient',\n ].includes(name)\n ) {\n if (!inWiring)\n this.fail(\n `${node.getSourceFile().fileName}: ${name} must be owned by src/wiring.ts.`,\n );\n this.recordRelationship(node, name);\n }\n }\n if (inWiring && ts.isMethodDeclaration(node) && node.name.getText() === 'getRuntimeWiring')\n this.recordPlan(node);\n ts.forEachChild(node, (child) => this.visit(child, inWiring));\n }\n\n private recordExternal(expression: ts.NewExpression): void {\n const owner = this.exportOwner(expression);\n const identity = expression.arguments?.[0];\n if (owner === undefined || identity === undefined || !ts.isStringLiteralLike(identity))\n this.fail(\n 'ExternalContractUse belongs to a selected exported module and requires a literal project#contract identity.',\n );\n const qualified = (identity as ts.StringLiteralLike).text.split('#');\n const project = qualified[0];\n const exportedName = qualified[1];\n const extra = qualified[2];\n if (!project || !exportedName || extra !== undefined)\n this.fail('ExternalContractUse requires one qualified project#contract identity.');\n if (!this.values.hasExternalConsumer(this.info.name, project, exportedName, this.program))\n this.fail(\n `${project}#${exportedName} has no production business consumer in ${this.info.name}; adapters and test doubles do not establish uses.`,\n );\n const exported = this.exported[owner!] ?? new WiringExport('binding', [], []);\n exported.relationships.push(\n new WiringRelationship(\n new ContractIdentity(project, exportedName),\n new UsesFacts('external', {\n kind: 'unknown',\n reason: 'Vendor interface; destination classified by approved contract metadata',\n }),\n ),\n );\n this.exported[owner!] = exported;\n }\n\n private recordRelationship(call: ts.CallExpression, method: string): void {\n const owner = this.exportOwner(call);\n // The generic Angular integration adapter defines the primitive; its invocations own facts.\n if (owner === 'provideRpcClient' && method === 'createRpcClient') return;\n if (owner === undefined)\n this.fail(`Unowned ${method} at ${call.getSourceFile().fileName}:${call.getStart()}.`);\n const apiIndex =\n method === 'bindRpc' || method === 'bindPubSub' || method === 'provideRpcClient'\n ? 1\n : 0;\n const api = call.arguments[apiIndex];\n if (api === undefined) this.fail(`${method} requires an explicit API contract.`);\n const contract = this.values.identity(api!);\n const direction = method === 'addRoutes' ? 'implements' : 'uses';\n const contractTransport = this.values.transport(api!);\n const transport =\n method === 'addRoutes'\n ? contractTransport\n : method === 'bindPubSub' || method === 'createPubSubClient'\n ? 'pubsub'\n : 'rpc';\n if (transport !== contractTransport)\n this.fail(\n `${method} transport disagrees with ${contract.project}#${contract.exportedName}.`,\n );\n const target =\n direction === 'implements'\n ? undefined\n : this.values.target(call.arguments[apiIndex + 1]);\n const kind =\n direction === 'implements'\n ? 'routing'\n : method === 'provideRpcClient'\n ? 'providers'\n : 'binding';\n const exported = this.exported[owner!] ?? new WiringExport(kind, [], []);\n exported.relationships.push(\n new WiringRelationship(\n contract,\n direction === 'implements'\n ? new ImplementsFacts(transport, this.values.condition(call))\n : new UsesFacts(transport, target!, this.values.condition(call)),\n ),\n );\n this.exported[owner!] = exported;\n }\n\n private recordPlan(method: ts.MethodDeclaration): void {\n const owner = this.exportOwner(method);\n if (owner === undefined) this.fail('The plan class must be exported from src/wiring.ts.');\n const returns = this.planReturns(method, owner!);\n const expression = returns[0].expression as ts.NewExpression;\n const name = this.values.symbolName(expression.expression);\n const args = expression.arguments ?? [];\n let arrays: readonly ts.Expression[];\n if (name === 'ServerWiring') {\n const host = this.values.target(args[0]);\n if (host.kind !== 'service')\n this.fail('ServerWiring requires a literal host identity.');\n this.host = host.service;\n if (\n args[1] === undefined ||\n !ts.isNewExpression(args[1]) ||\n this.values.symbolName(args[1].expression) !== 'ServerWiringOptions'\n )\n this.fail(\n 'Use ServerWiringOptions with separate bindingModules and routingModules arrays.',\n );\n arrays = (args[1] as ts.NewExpression).arguments ?? [];\n } else if (name === 'BrowserWiring') arrays = args;\n else this.fail(`Unsupported plan ${name}; use ServerWiring or BrowserWiring.`);\n const selections: WiringSelection[] = [];\n for (const array of arrays) {\n if (!ts.isArrayLiteralExpression(array))\n this.fail(\n 'Plan module/provider lists must be static arrays; dynamic composition needs an explicit supported policy.',\n );\n for (const element of (array as ts.ArrayLiteralExpression).elements) {\n if (ts.isObjectLiteralExpression(element)) continue; // ordinary Angular providers; API factories are checked independently\n if (\n ts.isCallExpression(element) &&\n this.values.symbolName(element.expression) === 'provideRpcClient'\n )\n continue;\n const invocation =\n ts.isNewExpression(element) || ts.isCallExpression(element)\n ? element\n : undefined;\n const identity = this.values.identity(\n invocation === undefined ? element : invocation.expression,\n );\n const targets = invocation === undefined ? {} : this.values.arguments(invocation);\n selections.push(\n new WiringSelection(\n identity.project,\n identity.exportedName,\n targets,\n invocation === undefined ? {} : this.values.policies(invocation),\n ),\n );\n if (\n identity.project === this.info.name &&\n this.exported[identity.exportedName] === undefined\n )\n this.exported[identity.exportedName] = new WiringExport('binding', [], []);\n }\n }\n this.exported[owner!] = new WiringExport('plan', [], selections);\n this.entry = owner;\n }\n\n private planReturns(method: ts.MethodDeclaration, owner: string): ts.ReturnStatement[] {\n const returns: ts.ReturnStatement[] = [];\n const visit = (node: ts.Node): void => {\n if (ts.isReturnStatement(node)) returns.push(node);\n else ts.forEachChild(node, visit);\n };\n if (method.body !== undefined) visit(method.body);\n if (\n returns.length !== 1 ||\n returns[0].expression === undefined ||\n !ts.isNewExpression(returns[0].expression)\n )\n this.fail(`${owner}.getRuntimeWiring must return one statically composed plan.`);\n return returns;\n }\n\n private exportOwner(node: ts.Node): string | undefined {\n let current: ts.Node | undefined = node;\n while (current !== undefined) {\n if (\n ts.isClassDeclaration(current) &&\n current.name !== undefined &&\n (ts.getModifiers(current) ?? []).some(\n (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,\n )\n )\n return current.name.text;\n if (\n ts.isFunctionDeclaration(current) &&\n current.name !== undefined &&\n (ts.getModifiers(current) ?? []).some(\n (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,\n )\n )\n return current.name.text;\n if (ts.isVariableDeclaration(current) && ts.isIdentifier(current.name)) {\n const statement = current.parent.parent;\n if (\n ts.isVariableStatement(statement) &&\n (ts.getModifiers(statement) ?? []).some(\n (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,\n )\n )\n return current.name.text;\n }\n current = current.parent;\n }\n return undefined;\n }\n\n private owns(file: string): boolean {\n const root = path.resolve(this.workspaceRoot, this.info.root) + path.sep;\n return path.resolve(file).startsWith(root) && !file.includes('/node_modules/');\n }\n\n private fail(message: string): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(\n 'Move topology into canonical src/wiring.ts and use the documented static plan grammar; do not execute configuration to extract it.',\n true,\n ),\n ]);\n }\n}\n\n/** Factory kept separate so graph assembly cannot accidentally create a compiler. */\nexport class WiringProjectProgram {\n create(workspaceRoot: string, info: ProjectInfo): ts.Program {\n const root = path.resolve(workspaceRoot, info.root);\n const configured = createProjectProgram(root);\n if (configured !== null) return configured;\n const config = ts.findConfigFile(root, ts.sys.fileExists, 'tsconfig.json');\n const parsed =\n config === undefined ? undefined : ts.readConfigFile(config, ts.sys.readFile);\n const options =\n parsed === undefined\n ? {}\n : ts.parseJsonConfigFileContent(parsed.config, ts.sys, root).options;\n const files = ts.sys.readDirectory(\n path.join(root, 'src'),\n ['.ts'],\n ['**/*.spec.ts', '**/*.test.ts'],\n );\n return ts.createProgram(files, options);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"source-extractor.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/source-extractor.ts"],"names":[],"mappings":"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","sourcesContent":["import * as path from 'path';\nimport * as ts from 'typescript';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport type { ProjectInfo } from '../project-info';\nimport { createProjectProgram } from '../di-graph/program';\nimport {\n ContractIdentity,\n ImplementsFacts,\n UsesFacts,\n RuntimeDeclaration,\n WiringExport,\n WiringRelationship,\n WiringSelection,\n} from './declaration';\nimport { WiringSourceValues } from './source-values';\nimport { CanonicalClientBindings } from './canonical-clients';\n\n/** Build-only extraction. It never imports application modules or invokes their constructors. */\nexport class WiringSourceExtractor {\n private readonly values: WiringSourceValues;\n private readonly exported: Record<string, WiringExport> = {};\n private entry: string | undefined;\n private host: string | undefined;\n\n constructor(\n private readonly workspaceRoot: string,\n private readonly info: ProjectInfo,\n private readonly infos: ReadonlyMap<string, ProjectInfo>,\n private readonly program: ts.Program,\n ) {\n this.values = new WiringSourceValues(workspaceRoot, infos, program.getTypeChecker());\n }\n\n extract(): RuntimeDeclaration {\n const clients = new CanonicalClientBindings(this.program.getTypeChecker());\n const problems = this.program\n .getSourceFiles()\n .filter(\n (file) =>\n !file.isDeclarationFile &&\n this.owns(file.fileName) &&\n !/(?:\\.spec\\.|\\.test\\.|\\/__tests__\\/)/.test(file.fileName),\n )\n .flatMap((file) => clients.problems(file));\n clients.assert(problems);\n const wiringPath = path.resolve(this.workspaceRoot, this.info.root, 'src/wiring.ts');\n const wiring = this.program.getSourceFile(wiringPath);\n if (wiring === undefined) this.fail(`Missing canonical ${wiringPath}.`);\n for (const file of this.program.getSourceFiles()) {\n if (\n file.isDeclarationFile ||\n !this.owns(file.fileName) ||\n /(?:\\.spec\\.|\\.test\\.|\\/__tests__\\/)/.test(file.fileName)\n )\n continue;\n this.visit(file, file.fileName === wiringPath);\n }\n const framework = this.info.tags.includes('framework:angular')\n ? 'angular'\n : this.info.tags.some((tag) => tag === 'framework:node' || tag === 'framework:express')\n ? 'node'\n : 'browser';\n if (this.info.tags.includes('webpieces') && this.entry === undefined)\n this.fail(`${this.info.name} requires getRuntimeWiring() returning a typed plan.`);\n return new RuntimeDeclaration(\n this.info.name,\n framework,\n this.exported,\n this.entry,\n this.host,\n );\n }\n\n private visit(node: ts.Node, inWiring: boolean): void {\n if (\n !inWiring &&\n (ts.isPropertyAccessExpression(node) || ts.isElementAccessExpression(node)) &&\n [\n 'addRoutes',\n 'createRpcClient',\n 'createPubSubClient',\n 'bindRpc',\n 'bindPubSub',\n ].includes(this.values.symbolName(node))\n )\n this.fail(\n `${node.getSourceFile().fileName}: topology method references belong in src/wiring.ts, including aliases.`,\n );\n if (\n inWiring &&\n ts.isNewExpression(node) &&\n this.values.symbolName(node.expression) === 'ExternalContractUse'\n )\n this.recordExternal(node);\n if (ts.isCallExpression(node)) {\n const name = this.values.symbolName(node.expression);\n if (\n [\n 'addRoutes',\n 'createRpcClient',\n 'createPubSubClient',\n 'bindRpc',\n 'bindPubSub',\n 'provideRpcClient',\n ].includes(name)\n ) {\n if (!inWiring)\n this.fail(\n `${node.getSourceFile().fileName}: ${name} must be owned by src/wiring.ts.`,\n );\n this.recordRelationship(node, name);\n }\n }\n if (inWiring && ts.isMethodDeclaration(node) && node.name.getText() === 'getRuntimeWiring')\n this.recordPlan(node);\n ts.forEachChild(node, (child) => this.visit(child, inWiring));\n }\n\n private recordExternal(expression: ts.NewExpression): void {\n const owner = this.exportOwner(expression);\n const identity = expression.arguments?.[0];\n if (owner === undefined || identity === undefined || !ts.isStringLiteralLike(identity))\n this.fail(\n 'ExternalContractUse belongs to a selected exported module and requires a literal project#contract identity.',\n );\n const qualified = (identity as ts.StringLiteralLike).text.split('#');\n const project = qualified[0];\n const exportedName = qualified[1];\n const extra = qualified[2];\n if (!project || !exportedName || extra !== undefined)\n this.fail('ExternalContractUse requires one qualified project#contract identity.');\n if (!this.values.hasExternalConsumer(this.info.name, project, exportedName, this.program))\n this.fail(\n `${project}#${exportedName} has no production business consumer in ${this.info.name}; adapters and test doubles do not establish uses.`,\n );\n const exported = this.exported[owner!] ?? new WiringExport('binding', [], []);\n exported.relationships.push(\n new WiringRelationship(\n new ContractIdentity(project, exportedName),\n new UsesFacts('external', {\n kind: 'unknown',\n reason: 'Vendor interface; destination classified by approved contract metadata',\n }),\n ),\n );\n this.exported[owner!] = exported;\n }\n\n private recordRelationship(call: ts.CallExpression, method: string): void {\n const owner = this.exportOwner(call);\n // The generic Angular integration adapter defines the primitive; its invocations own facts.\n if (owner === 'provideRpcClient' && method === 'createRpcClient') return;\n if (owner === undefined)\n this.fail(`Unowned ${method} at ${call.getSourceFile().fileName}:${call.getStart()}.`);\n const apiIndex =\n method === 'bindRpc' || method === 'bindPubSub' || method === 'provideRpcClient'\n ? 1\n : 0;\n const api = call.arguments[apiIndex];\n if (api === undefined) this.fail(`${method} requires an explicit API contract.`);\n const contract = this.values.identity(api!);\n const direction = method === 'addRoutes' ? 'implements' : 'uses';\n const contractTransport = this.values.transport(api!);\n const transport =\n method === 'addRoutes'\n ? contractTransport\n : method === 'bindPubSub' || method === 'createPubSubClient'\n ? 'pubsub'\n : 'rpc';\n if (transport !== contractTransport)\n this.fail(\n `${method} transport disagrees with ${contract.project}#${contract.exportedName}.`,\n );\n const target =\n direction === 'implements'\n ? undefined\n : this.values.target(call.arguments[apiIndex + 1]);\n const kind =\n direction === 'implements'\n ? 'routing'\n : method === 'provideRpcClient'\n ? 'providers'\n : 'binding';\n const exported = this.exported[owner!] ?? new WiringExport(kind, [], []);\n exported.relationships.push(\n new WiringRelationship(\n contract,\n direction === 'implements'\n ? new ImplementsFacts(transport, this.values.condition(call))\n : new UsesFacts(transport, target!, this.values.condition(call)),\n ),\n );\n this.exported[owner!] = exported;\n }\n\n private recordPlan(method: ts.MethodDeclaration): void {\n const owner = this.exportOwner(method);\n if (owner === undefined) this.fail('The plan class must be exported from src/wiring.ts.');\n const returns = this.planReturns(method, owner!);\n const expression = returns[0].expression as ts.NewExpression;\n const name = this.values.symbolName(expression.expression);\n const args = expression.arguments ?? [];\n let arrays: readonly ts.Expression[];\n if (name === 'ServerWiring') {\n const host = this.values.target(args[0]);\n if (host.kind !== 'service')\n this.fail('ServerWiring requires a literal host identity.');\n this.host = host.service;\n if (\n args[1] === undefined ||\n !ts.isNewExpression(args[1]) ||\n this.values.symbolName(args[1].expression) !== 'ServerWiringOptions'\n )\n this.fail(\n 'Use ServerWiringOptions with separate bindingModules and routingModules arrays.',\n );\n arrays = (args[1] as ts.NewExpression).arguments ?? [];\n } else if (name === 'BrowserWiring') arrays = args;\n else this.fail(`Unsupported plan ${name}; use ServerWiring or BrowserWiring.`);\n const selections: WiringSelection[] = [];\n for (const array of arrays) {\n if (!ts.isArrayLiteralExpression(array))\n this.fail(\n 'Plan module/provider lists must be static arrays; dynamic composition needs an explicit supported policy.',\n );\n for (const element of (array as ts.ArrayLiteralExpression).elements) {\n if (ts.isObjectLiteralExpression(element)) continue; // ordinary Angular providers; API factories are checked independently\n if (\n ts.isCallExpression(element) &&\n this.values.symbolName(element.expression) === 'provideRpcClient'\n )\n continue;\n const invocation =\n ts.isNewExpression(element) || ts.isCallExpression(element)\n ? element\n : undefined;\n const identity = this.values.identity(\n invocation === undefined ? element : invocation.expression,\n );\n const targets = invocation === undefined ? {} : this.values.arguments(invocation);\n selections.push(\n new WiringSelection(\n identity.project,\n identity.exportedName,\n targets,\n invocation === undefined ? {} : this.values.policies(invocation),\n ),\n );\n if (\n identity.project === this.info.name &&\n this.exported[identity.exportedName] === undefined\n )\n this.exported[identity.exportedName] = new WiringExport('binding', [], []);\n }\n }\n this.exported[owner!] = new WiringExport('plan', [], selections);\n this.entry = owner;\n }\n\n private planReturns(method: ts.MethodDeclaration, owner: string): ts.ReturnStatement[] {\n const returns: ts.ReturnStatement[] = [];\n const visit = (node: ts.Node): void => {\n if (ts.isReturnStatement(node)) returns.push(node);\n else ts.forEachChild(node, visit);\n };\n if (method.body !== undefined) visit(method.body);\n if (\n returns.length !== 1 ||\n returns[0].expression === undefined ||\n !ts.isNewExpression(returns[0].expression)\n )\n this.fail(`${owner}.getRuntimeWiring must return one statically composed plan.`);\n return returns;\n }\n\n private exportOwner(node: ts.Node): string | undefined {\n let current: ts.Node | undefined = node;\n while (current !== undefined) {\n if (\n ts.isClassDeclaration(current) &&\n current.name !== undefined &&\n (ts.getModifiers(current) ?? []).some(\n (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,\n )\n )\n return current.name.text;\n if (\n ts.isFunctionDeclaration(current) &&\n current.name !== undefined &&\n (ts.getModifiers(current) ?? []).some(\n (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,\n )\n )\n return current.name.text;\n if (ts.isVariableDeclaration(current) && ts.isIdentifier(current.name)) {\n const statement = current.parent.parent;\n if (\n ts.isVariableStatement(statement) &&\n (ts.getModifiers(statement) ?? []).some(\n (modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword,\n )\n )\n return current.name.text;\n }\n current = current.parent;\n }\n return undefined;\n }\n\n private owns(file: string): boolean {\n const root = path.resolve(this.workspaceRoot, this.info.root) + path.sep;\n return path.resolve(file).startsWith(root) && !file.includes('/node_modules/');\n }\n\n private fail(message: string): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(\n 'Move topology into canonical src/wiring.ts using RuntimeClients.bindRpc(token, Api, rpcTarget(Api, deployment)), RuntimeTaskClients.bindPubSub, or provideRpcClient for supported registrations, and use the documented static plan grammar; do not execute configuration to extract it.',\n true,\n ),\n ]);\n }\n}\n\n/** Factory kept separate so graph assembly cannot accidentally create a compiler. */\nexport class WiringProjectProgram {\n create(workspaceRoot: string, info: ProjectInfo): ts.Program {\n const root = path.resolve(workspaceRoot, info.root);\n const configured = createProjectProgram(root);\n if (configured !== null) return configured;\n const config = ts.findConfigFile(root, ts.sys.fileExists, 'tsconfig.json');\n const parsed =\n config === undefined ? undefined : ts.readConfigFile(config, ts.sys.readFile);\n const options =\n parsed === undefined\n ? {}\n : ts.parseJsonConfigFileContent(parsed.config, ts.sys, root).options;\n const files = ts.sys.readDirectory(\n path.join(root, 'src'),\n ['.ts'],\n ['**/*.spec.ts', '**/*.test.ts'],\n );\n return ts.createProgram(files, options);\n }\n}\n"]}
|
package/src/rule-help.js
CHANGED
|
@@ -9,7 +9,7 @@ exports.ruleHelp = {
|
|
|
9
9
|
'nx-wiring': new rules_sdk_1.RuleHelp('Wire the required validation targets into the Nx execution graph.', 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.'),
|
|
10
10
|
'di-graph': new rules_sdk_1.RuleHelp('Generate and validate dependency-injection design graphs.', 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.'),
|
|
11
11
|
'missing-design-annotation': new rules_sdk_1.RuleHelp('Declare the application design roots required for DI analysis.', 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.'),
|
|
12
|
-
'validate-architecture-unchanged': new rules_sdk_1.RuleHelp('Keep the committed architecture graph aligned with current dependencies.', '
|
|
12
|
+
'validate-architecture-unchanged': new rules_sdk_1.RuleHelp('Keep the committed architecture graph aligned with current dependencies and enforce canonical client registrations across ALL CODE in participating runtime owners.', 'In src/wiring.ts use new RuntimeClients(options).bindRpc(token, Api, rpcTarget(Api, deployment), filters?) from @webpieces/http-client-node, new RuntimeTaskClients(options).bindPubSub(token, Api, deployment) from @webpieces/cloudtasks-client, and provideRpcClient(token, Api, rpcTarget(Api, deployment)) from @webpieces/http-client-browser. Migrate every supported raw singleton/provider factory, including unchanged owners; preserve tokens and filters. Review graph changes and runtime-deps.json candidates explicitly.'),
|
|
13
13
|
'validate-no-architecture-cycles': new rules_sdk_1.RuleHelp('Keep the project dependency architecture free of cycles.', 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.'),
|
|
14
14
|
'validate-packagejson': new rules_sdk_1.RuleHelp('Align package dependencies with declared project dependencies.', 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.'),
|
|
15
15
|
'validate-versions-locked': new rules_sdk_1.RuleHelp('Pin dependency versions consistently across governed projects.', 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.'),
|
package/src/rule-help.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rule-help.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/rule-help.ts"],"names":[],"mappings":";;;AAAA,oDAAgD;AAEhD,gEAAgE;AACnD,QAAA,QAAQ,GAAuC;IACxD,uBAAuB,EAAE,IAAI,oBAAQ,CACjC,kDAAkD,EAClD,sIAAsI,CACzI;IACD,sBAAsB,EAAE,IAAI,oBAAQ,CAChC,2DAA2D,EAC3D,sIAAsI,CACzI;IACD,WAAW,EAAE,IAAI,oBAAQ,CACrB,mEAAmE,EACnE,sIAAsI,CACzI;IACD,UAAU,EAAE,IAAI,oBAAQ,CACpB,2DAA2D,EAC3D,sIAAsI,CACzI;IACD,2BAA2B,EAAE,IAAI,oBAAQ,CACrC,gEAAgE,EAChE,sIAAsI,CACzI;IACD,iCAAiC,EAAE,IAAI,oBAAQ,CAC3C,
|
|
1
|
+
{"version":3,"file":"rule-help.js","sourceRoot":"","sources":["../../../../../packages/tooling/nx-webpieces-rules/src/rule-help.ts"],"names":[],"mappings":";;;AAAA,oDAAgD;AAEhD,gEAAgE;AACnD,QAAA,QAAQ,GAAuC;IACxD,uBAAuB,EAAE,IAAI,oBAAQ,CACjC,kDAAkD,EAClD,sIAAsI,CACzI;IACD,sBAAsB,EAAE,IAAI,oBAAQ,CAChC,2DAA2D,EAC3D,sIAAsI,CACzI;IACD,WAAW,EAAE,IAAI,oBAAQ,CACrB,mEAAmE,EACnE,sIAAsI,CACzI;IACD,UAAU,EAAE,IAAI,oBAAQ,CACpB,2DAA2D,EAC3D,sIAAsI,CACzI;IACD,2BAA2B,EAAE,IAAI,oBAAQ,CACrC,gEAAgE,EAChE,sIAAsI,CACzI;IACD,iCAAiC,EAAE,IAAI,oBAAQ,CAC3C,qKAAqK,EACrK,ygBAAygB,CAC5gB;IACD,iCAAiC,EAAE,IAAI,oBAAQ,CAC3C,0DAA0D,EAC1D,sIAAsI,CACzI;IACD,sBAAsB,EAAE,IAAI,oBAAQ,CAChC,gEAAgE,EAChE,sIAAsI,CACzI;IACD,0BAA0B,EAAE,IAAI,oBAAQ,CACpC,gEAAgE,EAChE,sIAAsI,CACzI;IACD,sBAAsB,EAAE,IAAI,oBAAQ,CAChC,8EAA8E,EAC9E,sIAAsI,CACzI;IACD,wBAAwB,EAAE,IAAI,oBAAQ,CAClC,qEAAqE,EACrE,sIAAsI,CACzI;IACD,uBAAuB,EAAE,IAAI,oBAAQ,CACjC,uEAAuE,EACvE,sIAAsI,CACzI;IACD,mBAAmB,EAAE,IAAI,oBAAQ,CAC7B,mEAAmE,EACnE,sIAAsI,CACzI;IACD,sBAAsB,EAAE,IAAI,oBAAQ,CAChC,+DAA+D,EAC/D,sIAAsI,CACzI;IACD,cAAc,EAAE,IAAI,oBAAQ,CACxB,sDAAsD,EACtD,sIAAsI,CACzI;IACD,kBAAkB,EAAE,IAAI,oBAAQ,CAC5B,gEAAgE,EAChE,sIAAsI,CACzI;CACJ,CAAC","sourcesContent":["import { RuleHelp } from '@webpieces/rules-sdk';\n\n/** Owner-authored guidance consumed by the registry catalog. */\nexport const ruleHelp: Readonly<Record<string, RuleHelp>> = {\n 'no-file-import-cycles': new RuleHelp(\n 'Keep each project free of circular file imports.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'runtime-architecture': new RuleHelp(\n 'Validate runtime service and API dependency declarations.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'nx-wiring': new RuleHelp(\n 'Wire the required validation targets into the Nx execution graph.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'di-graph': new RuleHelp(\n 'Generate and validate dependency-injection design graphs.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'missing-design-annotation': new RuleHelp(\n 'Declare the application design roots required for DI analysis.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'validate-architecture-unchanged': new RuleHelp(\n 'Keep the committed architecture graph aligned with current dependencies and enforce canonical client registrations across ALL CODE in participating runtime owners.',\n 'In src/wiring.ts use new RuntimeClients(options).bindRpc(token, Api, rpcTarget(Api, deployment), filters?) from @webpieces/http-client-node, new RuntimeTaskClients(options).bindPubSub(token, Api, deployment) from @webpieces/cloudtasks-client, and provideRpcClient(token, Api, rpcTarget(Api, deployment)) from @webpieces/http-client-browser. Migrate every supported raw singleton/provider factory, including unchanged owners; preserve tokens and filters. Review graph changes and runtime-deps.json candidates explicitly.',\n ),\n 'validate-no-architecture-cycles': new RuleHelp(\n 'Keep the project dependency architecture free of cycles.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'validate-packagejson': new RuleHelp(\n 'Align package dependencies with declared project dependencies.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'validate-versions-locked': new RuleHelp(\n 'Pin dependency versions consistently across governed projects.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'validate-eslint-sync': new RuleHelp(\n 'Keep managed ESLint configuration synchronized with its installed templates.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'no-root-union-api-type': new RuleHelp(\n 'Use a stable named root API contract rather than a root union type.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'api-rules-for-openapi': new RuleHelp(\n 'Validate API contracts against their configured OpenAPI requirements.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'api-rules-for-mcp': new RuleHelp(\n 'Validate API contracts against their configured MCP requirements.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'api-lib-dependencies': new RuleHelp(\n 'Validate the configured API library dependency relationships.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'api-lib-path': new RuleHelp(\n 'Use the configured paths for API contract libraries.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n 'framework-folder': new RuleHelp(\n 'Align framework declarations with their project folder layout.',\n 'Apply this policy in source, or review its explicit settings in {configFile}. An intentional opt-out requires complete OFF settings.',\n ),\n};\n"]}
|