@webpieces/nx-webpieces-rules 0.4.893 → 0.4.895

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/package.json +7 -7
  2. package/src/executors/generate/executor.js +11 -7
  3. package/src/executors/generate/executor.js.map +1 -1
  4. package/src/executors/validate-architecture-unchanged/executor.d.ts +4 -2
  5. package/src/executors/validate-architecture-unchanged/executor.js +32 -5
  6. package/src/executors/validate-architecture-unchanged/executor.js.map +1 -1
  7. package/src/executors/validate-runtime-architecture/executor.js +1 -1
  8. package/src/executors/validate-runtime-architecture/executor.js.map +1 -1
  9. package/src/lib/graph-loader.d.ts +16 -2
  10. package/src/lib/graph-loader.js +34 -4
  11. package/src/lib/graph-loader.js.map +1 -1
  12. package/src/lib/remote-service-node-id.d.ts +8 -0
  13. package/src/lib/remote-service-node-id.js +15 -0
  14. package/src/lib/remote-service-node-id.js.map +1 -0
  15. package/src/lib/remote-services.d.ts +53 -0
  16. package/src/lib/remote-services.js +123 -0
  17. package/src/lib/remote-services.js.map +1 -0
  18. package/src/lib/runtime-details.d.ts +7 -0
  19. package/src/lib/runtime-details.js +27 -0
  20. package/src/lib/runtime-details.js.map +1 -1
  21. package/src/lib/runtime-graph-model.d.ts +28 -0
  22. package/src/lib/runtime-graph-model.js.map +1 -1
  23. package/src/lib/runtime-graph.d.ts +3 -2
  24. package/src/lib/runtime-graph.js +15 -3
  25. package/src/lib/runtime-graph.js.map +1 -1
  26. package/src/lib/runtime-remote-calls.d.ts +35 -0
  27. package/src/lib/runtime-remote-calls.js +87 -0
  28. package/src/lib/runtime-remote-calls.js.map +1 -0
  29. package/src/lib/runtime-visualizer.js +41 -0
  30. package/src/lib/runtime-visualizer.js.map +1 -1
  31. package/src/lib/runtime-viz-theme.d.ts +7 -0
  32. package/src/lib/runtime-viz-theme.js +11 -1
  33. package/src/lib/runtime-viz-theme.js.map +1 -1
  34. package/src/lib/runtime-wiring/approved-graph.js +2 -29
  35. package/src/lib/runtime-wiring/approved-graph.js.map +1 -1
  36. package/src/lib/runtime-wiring/service-targets.d.ts +22 -0
  37. package/src/lib/runtime-wiring/service-targets.js +95 -0
  38. package/src/lib/runtime-wiring/service-targets.js.map +1 -0
@@ -1 +1 @@
1
- {"version":3,"file":"approved-graph.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/approved-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport { RuntimeDeclarationCodec } from './codec';\nimport { RuntimeWiringAssembler } from './assembler';\nimport type { RuntimeDeclaration } from './declaration';\nimport type { EnhancedGraph } from '../graph-sorter';\nimport type { ProjectInfo } from '../project-info';\nimport type { ApiRef, ProjectApiRelations } from '../api-usage/api-relations';\nimport { loadBlessedGraph, DEFAULT_GRAPH_PATH } from '../graph-loader';\nimport { ApiContractFiles } from '../api-contract-files';\n\n/** Assembly consumes approvals only. Source verification is a separate per-project build target. */\nexport class ApprovedWiringGraph {\n attach(\n workspaceRoot: string,\n graph: EnhancedGraph,\n infos: ReadonlyMap<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n ): void {\n const previous = loadBlessedGraph(workspaceRoot, graphPath);\n if (previous === null)\n this.fail(\n 'Missing saved graph. Complete the runtime declaration migration before generating architecture.',\n );\n const declarations = new Map<string, RuntimeDeclaration>();\n for (const project of infos) {\n const name = project[0];\n const info = project[1];\n const tagged = info.tags.includes('webpieces') || info.tags.includes('webpieces-lib');\n const existingRelations = previous.projects[name]?.apiRelations;\n if (!tagged && existingRelations !== undefined)\n this.fail(\n `${name} has saved runtime facts but is untagged. Migrate it before using declaration-only generation.`,\n );\n if (!tagged) continue;\n const file = path.join(workspaceRoot, info.root, 'runtime-deps.json');\n if (!fs.existsSync(file)) this.fail(`Missing approved declaration ${file}.`);\n declarations.set(\n name,\n new RuntimeDeclarationCodec().decode(fs.readFileSync(file, 'utf8'), name),\n );\n graph[name].runtimeDeclaration = `${info.root}/runtime-deps.json`;\n }\n const assembler = new RuntimeWiringAssembler(declarations);\n for (const project of declarations) {\n const name = project[0];\n const declaration = project[1];\n if (declaration.entry === undefined) continue;\n const entry = graph[name];\n if (declaration.host !== undefined && entry.serviceName !== declaration.host)\n this.fail(\n `${name}: plan host ${declaration.host} disagrees with project serviceName ${entry.serviceName}.`,\n );\n const relations: ProjectApiRelations = {};\n for (const resolved of assembler.assemble(name)) {\n const relation = relations[resolved.owner] ?? {\n kind: 'uses',\n implements: [],\n uses: [],\n };\n const ref: ApiRef = { api: resolved.api, type: resolved.transport };\n if (resolved.conditional !== undefined) ref.conditional = resolved.conditional;\n ref.declaredVia = resolved.via;\n if (resolved.target?.kind === 'service')\n ref.targetService = resolved.target.service;\n if (resolved.transport === 'pubsub' && resolved.direction === 'uses')\n ref.methodsInferred = true;\n relation[resolved.direction].push(ref);\n relation.kind =\n relation.implements.length === 0\n ? 'uses'\n : relation.uses.length === 0\n ? 'implements'\n : 'uses-implements';\n relations[resolved.owner] = relation;\n }\n entry.apiRelations = relations;\n entry.runtimeComposition = true;\n }\n this.validateTargets(workspaceRoot, graphPath, graph);\n }\n\n private validateTargets(workspaceRoot: string, graphPath: string, graph: EnhancedGraph): void {\n const hosts = new Map<string, string>();\n for (const [name, entry] of Object.entries(graph)) {\n if (entry.serviceName === undefined) continue;\n if (hosts.has(entry.serviceName))\n this.fail(\n `Duplicate service identity ${entry.serviceName}: ${hosts.get(entry.serviceName)} and ${name}.`,\n );\n hosts.set(entry.serviceName, name);\n }\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n this.fail('Missing saved contract references for approved runtime wiring.');\n const contracts = new ApiContractFiles().load(\n workspaceRoot,\n graphPath,\n saved.apiContractFiles,\n );\n for (const [name, entry] of Object.entries(graph))\n for (const [owner, relation] of Object.entries(entry.apiRelations ?? {})) {\n for (const ref of [...relation.implements, ...relation.uses]) {\n if (\n ref.type !== 'external' &&\n (contracts[ref.api]?.owner !== owner ||\n contracts[ref.api]?.apiKind !== ref.type)\n )\n this.fail(`${name}: unknown qualified contract ${owner}#${ref.api}.`);\n }\n for (const ref of relation.uses) {\n if (ref.targetService === undefined) continue;\n if (\n Object.values(saved!.externalSystems).some((system) =>\n system.apis.includes(ref.api),\n )\n )\n continue;\n const provider = hosts.get(ref.targetService);\n const implemented =\n provider === undefined\n ? undefined\n : graph[provider].apiRelations?.[owner]?.implements;\n if (!implemented?.some((api) => api.api === ref.api && api.type === ref.type))\n this.fail(\n `${name}: target ${ref.targetService} does not implement ${owner}#${ref.api} (${ref.type}).`,\n );\n }\n }\n }\n\n private fail(message: string): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(\n 'Migrate canonical src/wiring.ts, tag owners webpieces/webpieces-lib, and review runtime-deps.json candidates before generation. Saved visualizations remain available.',\n true,\n ),\n ]);\n }\n}\n"]}
1
+ {"version":3,"file":"approved-graph.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/approved-graph.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,0DAAgE;AAChE,mCAAkD;AAClD,2CAAqD;AAKrD,kDAAuE;AACvE,8DAAyD;AACzD,uDAA2D;AAE3D,oGAAoG;AACpG,MAAa,mBAAmB;IAC5B,MAAM,CACF,aAAqB,EACrB,KAAoB,EACpB,KAAuC,EACvC,YAAoB,iCAAkB;QAEtC,MAAM,QAAQ,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAC5D,IAAI,QAAQ,KAAK,IAAI;YACjB,IAAI,CAAC,IAAI,CACL,iGAAiG,CACpG,CAAC;QACN,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAC;QAC3D,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YACtF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;YAChE,IAAI,CAAC,MAAM,IAAI,iBAAiB,KAAK,SAAS;gBAC1C,IAAI,CAAC,IAAI,CACL,GAAG,IAAI,gGAAgG,CAC1G,CAAC;YACN,IAAI,CAAC,MAAM;gBAAE,SAAS;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;YACtE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,gCAAgC,IAAI,GAAG,CAAC,CAAC;YAC7E,YAAY,CAAC,GAAG,CACZ,IAAI,EACJ,IAAI,+BAAuB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAC5E,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,CAAC,kBAAkB,GAAG,GAAG,IAAI,CAAC,IAAI,oBAAoB,CAAC;QACtE,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,kCAAsB,CAAC,YAAY,CAAC,CAAC;QAC3D,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS;gBAAE,SAAS;YAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,WAAW,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,WAAW,KAAK,WAAW,CAAC,IAAI;gBACxE,IAAI,CAAC,IAAI,CACL,GAAG,IAAI,eAAe,WAAW,CAAC,IAAI,uCAAuC,KAAK,CAAC,WAAW,GAAG,CACpG,CAAC;YACN,MAAM,SAAS,GAAwB,EAAE,CAAC;YAC1C,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI;oBAC1C,IAAI,EAAE,MAAM;oBACZ,UAAU,EAAE,EAAE;oBACd,IAAI,EAAE,EAAE;iBACX,CAAC;gBACF,MAAM,GAAG,GAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACpE,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS;oBAAE,GAAG,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;gBAC/E,GAAG,CAAC,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC;gBAC/B,IAAI,QAAQ,CAAC,MAAM,EAAE,IAAI,KAAK,SAAS;oBACnC,GAAG,CAAC,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;gBAChD,IAAI,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,QAAQ,CAAC,SAAS,KAAK,MAAM;oBAChE,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC/B,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACvC,QAAQ,CAAC,IAAI;oBACT,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;wBAC5B,CAAC,CAAC,MAAM;wBACR,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;4BAC1B,CAAC,CAAC,YAAY;4BACd,CAAC,CAAC,iBAAiB,CAAC;gBAC9B,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;YACzC,CAAC;YACD,KAAK,CAAC,YAAY,GAAG,SAAS,CAAC;YAC/B,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;IAEO,eAAe,CAAC,aAAqB,EAAE,SAAiB,EAAE,KAAoB;QAClF,MAAM,KAAK,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,KAAK,KAAK,IAAI;YACd,IAAI,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAChF,MAAM,SAAS,GAAG,IAAI,qCAAgB,EAAE,CAAC,IAAI,CACzC,aAAa,EACb,SAAS,EACT,KAAK,CAAC,gBAAgB,CACzB,CAAC;QACF,IAAI,wCAAsB,EAAE,CAAC,QAAQ,CACjC,KAAK,EACL,SAAS,EACT,KAAK,CAAC,eAAe,EACrB,KAAK,CAAC,cAAc,CACvB,CAAC;IACN,CAAC;IAEO,IAAI,CAAC,OAAe;QACxB,MAAM,IAAI,4BAAa,CAAC,+BAA+B,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE;YACpF,IAAI,qBAAM,CACN,wKAAwK,EACxK,IAAI,CACP;SACJ,CAAC,CAAC;IACP,CAAC;CACJ;AA/FD,kDA+FC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport { RuntimeDeclarationCodec } from './codec';\nimport { RuntimeWiringAssembler } from './assembler';\nimport type { RuntimeDeclaration } from './declaration';\nimport type { EnhancedGraph } from '../graph-sorter';\nimport type { ProjectInfo } from '../project-info';\nimport type { ApiRef, ProjectApiRelations } from '../api-usage/api-relations';\nimport { loadBlessedGraph, DEFAULT_GRAPH_PATH } from '../graph-loader';\nimport { ApiContractFiles } from '../api-contract-files';\nimport { ServiceTargetValidator } from './service-targets';\n\n/** Assembly consumes approvals only. Source verification is a separate per-project build target. */\nexport class ApprovedWiringGraph {\n attach(\n workspaceRoot: string,\n graph: EnhancedGraph,\n infos: ReadonlyMap<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n ): void {\n const previous = loadBlessedGraph(workspaceRoot, graphPath);\n if (previous === null)\n this.fail(\n 'Missing saved graph. Complete the runtime declaration migration before generating architecture.',\n );\n const declarations = new Map<string, RuntimeDeclaration>();\n for (const project of infos) {\n const name = project[0];\n const info = project[1];\n const tagged = info.tags.includes('webpieces') || info.tags.includes('webpieces-lib');\n const existingRelations = previous.projects[name]?.apiRelations;\n if (!tagged && existingRelations !== undefined)\n this.fail(\n `${name} has saved runtime facts but is untagged. Migrate it before using declaration-only generation.`,\n );\n if (!tagged) continue;\n const file = path.join(workspaceRoot, info.root, 'runtime-deps.json');\n if (!fs.existsSync(file)) this.fail(`Missing approved declaration ${file}.`);\n declarations.set(\n name,\n new RuntimeDeclarationCodec().decode(fs.readFileSync(file, 'utf8'), name),\n );\n graph[name].runtimeDeclaration = `${info.root}/runtime-deps.json`;\n }\n const assembler = new RuntimeWiringAssembler(declarations);\n for (const project of declarations) {\n const name = project[0];\n const declaration = project[1];\n if (declaration.entry === undefined) continue;\n const entry = graph[name];\n if (declaration.host !== undefined && entry.serviceName !== declaration.host)\n this.fail(\n `${name}: plan host ${declaration.host} disagrees with project serviceName ${entry.serviceName}.`,\n );\n const relations: ProjectApiRelations = {};\n for (const resolved of assembler.assemble(name)) {\n const relation = relations[resolved.owner] ?? {\n kind: 'uses',\n implements: [],\n uses: [],\n };\n const ref: ApiRef = { api: resolved.api, type: resolved.transport };\n if (resolved.conditional !== undefined) ref.conditional = resolved.conditional;\n ref.declaredVia = resolved.via;\n if (resolved.target?.kind === 'service')\n ref.targetService = resolved.target.service;\n if (resolved.transport === 'pubsub' && resolved.direction === 'uses')\n ref.methodsInferred = true;\n relation[resolved.direction].push(ref);\n relation.kind =\n relation.implements.length === 0\n ? 'uses'\n : relation.uses.length === 0\n ? 'implements'\n : 'uses-implements';\n relations[resolved.owner] = relation;\n }\n entry.apiRelations = relations;\n entry.runtimeComposition = true;\n }\n this.validateTargets(workspaceRoot, graphPath, graph);\n }\n\n private validateTargets(workspaceRoot: string, graphPath: string, graph: EnhancedGraph): void {\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n this.fail('Missing saved contract references for approved runtime wiring.');\n const contracts = new ApiContractFiles().load(\n workspaceRoot,\n graphPath,\n saved.apiContractFiles,\n );\n new ServiceTargetValidator().validate(\n graph,\n contracts,\n saved.externalSystems,\n saved.remoteServices,\n );\n }\n\n private fail(message: string): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(\n 'Migrate canonical src/wiring.ts, tag owners webpieces/webpieces-lib, and review runtime-deps.json candidates before generation. Saved visualizations remain available.',\n true,\n ),\n ]);\n }\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import type { EnhancedGraph } from '../graph-sorter';
2
+ import type { ApiContracts, ExternalSystemDecls } from '../api-usage/api-relations';
3
+ import type { RemoteServices } from '../remote-services';
4
+ /**
5
+ * Every approved `uses` target must answer to its name and serve what is asked of it (#1203).
6
+ *
7
+ * A target is either a workspace project's `metadata.webpieces.serviceName`, or a REVIEWED remote
8
+ * service (another repo, a VM, partner endpoints) declared in dependencies.json `remoteServices`. The
9
+ * two name spaces must not overlap: a remote name that is also a workspace serviceName or project
10
+ * would make one name mean two services.
11
+ */
12
+ export declare class ServiceTargetValidator {
13
+ validate(graph: EnhancedGraph, contracts: ApiContracts, externalSystems: ExternalSystemDecls, remoteServices: RemoteServices): void;
14
+ private validateTarget;
15
+ /** serviceName -> the workspace project declaring it; a name declared twice fails. */
16
+ private hosts;
17
+ /** Each declared remote is outside the workspace and implements only approved contracts. */
18
+ private validateRemoteServices;
19
+ private assertApprovedContract;
20
+ private assertRemoteImplements;
21
+ private fail;
22
+ }
@@ -0,0 +1,95 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServiceTargetValidator = void 0;
4
+ const rules_config_1 = require("@webpieces/rules-config");
5
+ const MIGRATE_CURE = 'Migrate canonical src/wiring.ts, tag owners webpieces/webpieces-lib, and review runtime-deps.json candidates before generation. Saved visualizations remain available.';
6
+ const REMOTE_CURE = 'A uses may target a workspace project\'s metadata.webpieces.serviceName, or a service OUTSIDE this workspace declared in the reviewed remoteServices table of architecture/dependencies.json: "remoteServices": { "<serviceName>": { "label"?: "<display text>", "implements": [{ "api": "<ApiName>", "type": "rpc" | "pubsub" }] } }. Generation reads that table and never writes or infers it.';
7
+ /**
8
+ * Every approved `uses` target must answer to its name and serve what is asked of it (#1203).
9
+ *
10
+ * A target is either a workspace project's `metadata.webpieces.serviceName`, or a REVIEWED remote
11
+ * service (another repo, a VM, partner endpoints) declared in dependencies.json `remoteServices`. The
12
+ * two name spaces must not overlap: a remote name that is also a workspace serviceName or project
13
+ * would make one name mean two services.
14
+ */
15
+ class ServiceTargetValidator {
16
+ validate(graph, contracts, externalSystems, remoteServices) {
17
+ const hosts = this.hosts(graph);
18
+ this.validateRemoteServices(graph, hosts, contracts, remoteServices);
19
+ for (const [name, entry] of Object.entries(graph))
20
+ for (const [owner, relation] of Object.entries(entry.apiRelations ?? {})) {
21
+ for (const ref of [...relation.implements, ...relation.uses]) {
22
+ if (ref.type !== 'external' &&
23
+ (contracts[ref.api]?.owner !== owner ||
24
+ contracts[ref.api]?.apiKind !== ref.type))
25
+ this.fail(`${name}: unknown qualified contract ${owner}#${ref.api}.`);
26
+ }
27
+ for (const ref of relation.uses)
28
+ this.validateTarget(graph, hosts, externalSystems, remoteServices, name, owner, ref);
29
+ }
30
+ }
31
+ validateTarget(graph, hosts, externalSystems, remoteServices, name, owner, ref) {
32
+ const target = ref.targetService;
33
+ if (target === undefined)
34
+ return;
35
+ if (Object.values(externalSystems).some((system) => system.apis.includes(ref.api)))
36
+ return;
37
+ const provider = hosts.get(target);
38
+ if (provider === undefined) {
39
+ const remote = remoteServices[target];
40
+ if (remote === undefined)
41
+ this.fail(`${name}: target ${target} (for ${owner}#${ref.api} (${ref.type})) is neither a workspace serviceName nor a declared remote service in dependencies.json remoteServices.`, REMOTE_CURE);
42
+ this.assertRemoteImplements(name, owner, ref, target, remote);
43
+ return;
44
+ }
45
+ const implemented = graph[provider].apiRelations?.[owner]?.implements;
46
+ if (!implemented?.some((api) => api.api === ref.api && api.type === ref.type))
47
+ this.fail(`${name}: target ${target} does not implement ${owner}#${ref.api} (${ref.type}).`);
48
+ }
49
+ /** serviceName -> the workspace project declaring it; a name declared twice fails. */
50
+ hosts(graph) {
51
+ const hosts = new Map();
52
+ for (const [name, entry] of Object.entries(graph)) {
53
+ if (entry.serviceName === undefined)
54
+ continue;
55
+ if (hosts.has(entry.serviceName))
56
+ this.fail(`Duplicate service identity ${entry.serviceName}: ${hosts.get(entry.serviceName)} and ${name}.`);
57
+ hosts.set(entry.serviceName, name);
58
+ }
59
+ return hosts;
60
+ }
61
+ /** Each declared remote is outside the workspace and implements only approved contracts. */
62
+ validateRemoteServices(graph, hosts, contracts, remoteServices) {
63
+ for (const [remote, decl] of Object.entries(remoteServices)) {
64
+ const host = hosts.get(remote);
65
+ if (host !== undefined)
66
+ this.fail(`remoteServices.${remote} collides with the workspace serviceName of ${host}: a remote service must live OUTSIDE this workspace. Remove the remoteServices entry (targets of ${remote} resolve to ${host}) or rename one of them.`, REMOTE_CURE);
67
+ if (graph[remote] !== undefined)
68
+ this.fail(`remoteServices.${remote} collides with the workspace project ${remote}: a remote service must live OUTSIDE this workspace. Remove the remoteServices entry or rename it.`, REMOTE_CURE);
69
+ for (const api of decl.implements)
70
+ this.assertApprovedContract(remote, api, contracts);
71
+ }
72
+ }
73
+ assertApprovedContract(remote, api, contracts) {
74
+ const contract = contracts[api.api];
75
+ if (contract === undefined)
76
+ this.fail(`remoteServices.${remote}: unknown qualified contract ${api.api} (${api.type}) — no approved architecture/apis/${api.api}.json declares it.`, REMOTE_CURE);
77
+ if (contract.apiKind !== api.type)
78
+ this.fail(`remoteServices.${remote}: unknown qualified contract ${contract.owner}#${api.api} (${api.type}) — the approved contract is ${contract.apiKind}.`, REMOTE_CURE);
79
+ }
80
+ assertRemoteImplements(user, owner, ref, target, remote) {
81
+ if (remote.implementsApi(ref.api, ref.type))
82
+ return;
83
+ const declared = remote.implements
84
+ .map((entry) => `${entry.api} (${entry.type})`)
85
+ .join(', ');
86
+ this.fail(`${user}: remote service ${target} does not implement ${owner}#${ref.api} (${ref.type}); remoteServices.${target}.implements declares only ${declared}.`, REMOTE_CURE);
87
+ }
88
+ fail(message, cure = MIGRATE_CURE) {
89
+ throw new rules_config_1.RuleFailError('validate-runtime-architecture', message, undefined, undefined, [
90
+ new rules_config_1.Option(cure, true),
91
+ ]);
92
+ }
93
+ }
94
+ exports.ServiceTargetValidator = ServiceTargetValidator;
95
+ //# sourceMappingURL=service-targets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"service-targets.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/service-targets.ts"],"names":[],"mappings":"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","sourcesContent":["import { Option, RuleFailError } from '@webpieces/rules-config';\nimport type { EnhancedGraph } from '../graph-sorter';\nimport type {\n ApiContracts,\n ApiRef,\n ExternalSystemDecl,\n ExternalSystemDecls,\n} from '../api-usage/api-relations';\nimport type { RemoteService, RemoteServiceApi, RemoteServices } from '../remote-services';\n\nconst MIGRATE_CURE =\n 'Migrate canonical src/wiring.ts, tag owners webpieces/webpieces-lib, and review runtime-deps.json candidates before generation. Saved visualizations remain available.';\n\nconst REMOTE_CURE =\n 'A uses may target a workspace project\\'s metadata.webpieces.serviceName, or a service OUTSIDE this workspace declared in the reviewed remoteServices table of architecture/dependencies.json: \"remoteServices\": { \"<serviceName>\": { \"label\"?: \"<display text>\", \"implements\": [{ \"api\": \"<ApiName>\", \"type\": \"rpc\" | \"pubsub\" }] } }. Generation reads that table and never writes or infers it.';\n\n/**\n * Every approved `uses` target must answer to its name and serve what is asked of it (#1203).\n *\n * A target is either a workspace project's `metadata.webpieces.serviceName`, or a REVIEWED remote\n * service (another repo, a VM, partner endpoints) declared in dependencies.json `remoteServices`. The\n * two name spaces must not overlap: a remote name that is also a workspace serviceName or project\n * would make one name mean two services.\n */\nexport class ServiceTargetValidator {\n validate(\n graph: EnhancedGraph,\n contracts: ApiContracts,\n externalSystems: ExternalSystemDecls,\n remoteServices: RemoteServices,\n ): void {\n const hosts = this.hosts(graph);\n this.validateRemoteServices(graph, hosts, contracts, remoteServices);\n for (const [name, entry] of Object.entries(graph))\n for (const [owner, relation] of Object.entries(entry.apiRelations ?? {})) {\n for (const ref of [...relation.implements, ...relation.uses]) {\n if (\n ref.type !== 'external' &&\n (contracts[ref.api]?.owner !== owner ||\n contracts[ref.api]?.apiKind !== ref.type)\n )\n this.fail(`${name}: unknown qualified contract ${owner}#${ref.api}.`);\n }\n for (const ref of relation.uses)\n this.validateTarget(graph, hosts, externalSystems, remoteServices, name, owner, ref);\n }\n }\n\n private validateTarget(\n graph: EnhancedGraph,\n hosts: Map<string, string>,\n externalSystems: ExternalSystemDecls,\n remoteServices: RemoteServices,\n name: string,\n owner: string,\n ref: ApiRef,\n ): void {\n const target = ref.targetService;\n if (target === undefined) return;\n if (\n Object.values(externalSystems).some((system: ExternalSystemDecl) =>\n system.apis.includes(ref.api),\n )\n )\n return;\n const provider = hosts.get(target);\n if (provider === undefined) {\n const remote = remoteServices[target];\n if (remote === undefined)\n this.fail(\n `${name}: target ${target} (for ${owner}#${ref.api} (${ref.type})) is neither a workspace serviceName nor a declared remote service in dependencies.json remoteServices.`,\n REMOTE_CURE,\n );\n this.assertRemoteImplements(name, owner, ref, target, remote);\n return;\n }\n const implemented = graph[provider].apiRelations?.[owner]?.implements;\n if (!implemented?.some((api: ApiRef) => api.api === ref.api && api.type === ref.type))\n this.fail(`${name}: target ${target} does not implement ${owner}#${ref.api} (${ref.type}).`);\n }\n\n /** serviceName -> the workspace project declaring it; a name declared twice fails. */\n private hosts(graph: EnhancedGraph): Map<string, string> {\n const hosts = new Map<string, string>();\n for (const [name, entry] of Object.entries(graph)) {\n if (entry.serviceName === undefined) continue;\n if (hosts.has(entry.serviceName))\n this.fail(\n `Duplicate service identity ${entry.serviceName}: ${hosts.get(entry.serviceName)} and ${name}.`,\n );\n hosts.set(entry.serviceName, name);\n }\n return hosts;\n }\n\n /** Each declared remote is outside the workspace and implements only approved contracts. */\n private validateRemoteServices(\n graph: EnhancedGraph,\n hosts: Map<string, string>,\n contracts: ApiContracts,\n remoteServices: RemoteServices,\n ): void {\n for (const [remote, decl] of Object.entries(remoteServices)) {\n const host = hosts.get(remote);\n if (host !== undefined)\n this.fail(\n `remoteServices.${remote} collides with the workspace serviceName of ${host}: a remote service must live OUTSIDE this workspace. Remove the remoteServices entry (targets of ${remote} resolve to ${host}) or rename one of them.`,\n REMOTE_CURE,\n );\n if (graph[remote] !== undefined)\n this.fail(\n `remoteServices.${remote} collides with the workspace project ${remote}: a remote service must live OUTSIDE this workspace. Remove the remoteServices entry or rename it.`,\n REMOTE_CURE,\n );\n for (const api of decl.implements) this.assertApprovedContract(remote, api, contracts);\n }\n }\n\n private assertApprovedContract(remote: string, api: RemoteServiceApi, contracts: ApiContracts): void {\n const contract = contracts[api.api];\n if (contract === undefined)\n this.fail(\n `remoteServices.${remote}: unknown qualified contract ${api.api} (${api.type}) — no approved architecture/apis/${api.api}.json declares it.`,\n REMOTE_CURE,\n );\n if (contract.apiKind !== api.type)\n this.fail(\n `remoteServices.${remote}: unknown qualified contract ${contract.owner}#${api.api} (${api.type}) — the approved contract is ${contract.apiKind}.`,\n REMOTE_CURE,\n );\n }\n\n private assertRemoteImplements(\n user: string,\n owner: string,\n ref: ApiRef,\n target: string,\n remote: RemoteService,\n ): void {\n if (remote.implementsApi(ref.api, ref.type)) return;\n const declared = remote.implements\n .map((entry: RemoteServiceApi) => `${entry.api} (${entry.type})`)\n .join(', ');\n this.fail(\n `${user}: remote service ${target} does not implement ${owner}#${ref.api} (${ref.type}); remoteServices.${target}.implements declares only ${declared}.`,\n REMOTE_CURE,\n );\n }\n\n private fail(message: string, cure: string = MIGRATE_CURE): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(cure, true),\n ]);\n }\n}\n"]}