@webpieces/nx-webpieces-rules 0.4.907 → 0.4.909
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/README.md +10 -1
- package/package.json +7 -7
- package/src/lib/api-usage/external-systems.js +2 -0
- package/src/lib/api-usage/external-systems.js.map +1 -1
- package/src/lib/runtime-details.js +7 -1
- package/src/lib/runtime-details.js.map +1 -1
- package/src/lib/runtime-external-triggers.d.ts +14 -0
- package/src/lib/runtime-external-triggers.js +45 -0
- package/src/lib/runtime-external-triggers.js.map +1 -0
- package/src/lib/runtime-graph-io.js +29 -1
- package/src/lib/runtime-graph-io.js.map +1 -1
- package/src/lib/runtime-graph-levels.d.ts +2 -20
- package/src/lib/runtime-graph-levels.js +8 -30
- package/src/lib/runtime-graph-levels.js.map +1 -1
- package/src/lib/runtime-graph-model.d.ts +21 -13
- package/src/lib/runtime-graph-model.js +18 -0
- package/src/lib/runtime-graph-model.js.map +1 -1
- package/src/lib/runtime-graph.d.ts +1 -1
- package/src/lib/runtime-graph.js +15 -6
- package/src/lib/runtime-graph.js.map +1 -1
- package/src/lib/runtime-node-levels.d.ts +13 -0
- package/src/lib/runtime-node-levels.js +118 -0
- package/src/lib/runtime-node-levels.js.map +1 -0
- package/src/lib/runtime-remote-calls.js +2 -0
- package/src/lib/runtime-remote-calls.js.map +1 -1
- package/src/lib/runtime-viewer-adapter.js +40 -5
- package/src/lib/runtime-viewer-adapter.js.map +1 -1
- package/src/lib/runtime-visualizer.js +46 -45
- package/src/lib/runtime-visualizer.js.map +1 -1
- package/src/lib/runtime-viz-theme.js +2 -2
- package/src/lib/runtime-viz-theme.js.map +1 -1
package/README.md
CHANGED
|
@@ -124,7 +124,16 @@ Every row is one level. A library's level is its dependency depth, but every ser
|
|
|
124
124
|
app (`role:server` / `role:client` / `role:app`) is pinned to the TOP row, one above the highest
|
|
125
125
|
library, so the top row is exactly the entry points (a `role:bundle` sits above the apps it
|
|
126
126
|
aggregates; a server orchestrator above the servers it boots). The `level` in `dependencies.json`
|
|
127
|
-
is that promoted level. The runtime graph
|
|
127
|
+
is that promoted level. The runtime graph stores a level and `levelBecause` for every service, queue, external system,
|
|
128
|
+
remote service and trigger in `runtime-dependencies.json`. Outbound systems and remote services are
|
|
129
|
+
L0; services start at L1 and sit one above their highest callee. Foreign queues sit one above their
|
|
130
|
+
consumer and one below their foreign producers. A self-queue shares its consumer's level and does
|
|
131
|
+
not raise it. Contract-only pubsub flows are stored under `contractQueues`, without inventing method
|
|
132
|
+
producers. Foreign queue cycles fail with the queue named in the cycle path; existing explicit legacy
|
|
133
|
+
RPC cuts remain honored. Every trigger shares the top row, one above the highest non-trigger node.
|
|
134
|
+
`maxLevel` records the highest generated level. The renderer ranks all visible node kinds using these
|
|
135
|
+
stored numbers; inbound callers and outbound sinks for the same vendor have separate boxes.
|
|
136
|
+
After upgrading, regenerate old RPC-only JSON with `pnpm nx run architecture:generate`.
|
|
128
137
|
|
|
129
138
|
- **Runtime** — where the code can run. One runtime is a solid fill. One runtime plus a
|
|
130
139
|
specialization (angular/react inside browser, express inside node) is nested: a frame in the
|
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.909",
|
|
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.909",
|
|
24
|
+
"@webpieces/core-util": "0.4.909",
|
|
25
|
+
"@webpieces/eslint-rules": "0.4.909",
|
|
26
|
+
"@webpieces/rules-config": "0.4.909",
|
|
27
27
|
"madge": "8.0.0",
|
|
28
|
-
"@webpieces/rules-sdk": "0.4.
|
|
28
|
+
"@webpieces/rules-sdk": "0.4.909"
|
|
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.909"
|
|
45
45
|
},
|
|
46
46
|
"types": "./src/index.d.ts",
|
|
47
47
|
"bin": {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"external-systems.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/external-systems.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * External system declarations\n *\n * A system OUTSIDE this repo that a service talks to — a database, a bucket, a cache. Until now\n * every one of them collapsed into the same grey dashed box, so `lib-firestore` (a datastore) was\n * indistinguishable from an HTTP service the repo happens not to implement. Declaring what a system\n * IS lets the runtime viz draw it as the thing it is.\n *\n * Split out of api-scanner.ts and runtime-graph.ts (both already at their file-size limit) so the\n * two halves of the feature — DECLARING a system and RESOLVING it to arrows — sit together.\n *\n * The two declaration sites exist because the two real cases differ in whether a contract exists:\n *\n * - **wrapped** — the repo has a vendor seam (`FirestoreAdminApi`), so the kind is declared with an\n * `@externalSystem <kind> [label]` JSDoc tag on the CONTRACT. A TS `interface` cannot carry a\n * decorator, and these seams are interfaces, so JSDoc is the only marker that fits in place.\n * - **unwrapped** — the service opens the connection itself (a `pg.Pool`, a TypeORM `DataSource`)\n * and there is no contract to mark, so the declaration is an `external:<kind>:<identity>` nx tag\n * on that PROJECT. Wrapping such a datastore purely to gain a marker is not worth it: a TypeORM\n * facade never closes, unlike the ~8 hand-picked methods a firestore seam needs.\n *\n * Both resolve to the same `(kind, identity)` pair, and identity is the NODE identity — two projects\n * declaring `postgres` converge on one cylinder with an arrow each, rather than drawing a database\n * apiece.\n */\n\nimport type { ApiClassInfo, ExternalSystemDecl, ExternalSystemDecls, ExternalSystemKind } from './api-relations';\nimport { isExternalSystemKind } from './api-relations';\nimport type { ProjectInfo } from '../project-info';\nimport type { RuntimeExternalSystem, RuntimeGraph, RuntimeService } from '../runtime-graph-model';\n\n/** nx tag prefix declaring an external system: `external:<kind>:<identity>`. */\nconst EXTERNAL_TAG_PREFIX = 'external:';\n\n/** The two halves of a parsed `external:<kind>:<identity>` nx tag. */\nclass ExternalTag {\n constructor(\n public readonly kind: ExternalSystemKind,\n public readonly identity: string,\n ) {}\n}\n\n/**\n * `external:<kind>:<identity>` -> its parts, or null for any other tag.\n *\n * Returns null (rather than throwing) on an unknown kind: this prefix shares a tag list with\n * `framework:` and `role:`, and hard-failing the whole graph generation over one malformed tag is a\n * worse outcome than not drawing one node.\n */\n// webpieces-disable no-function-outside-class -- pure parser, matching the sibling builders in this file\nfunction parseExternalTag(tag: string): ExternalTag | null {\n if (!tag.startsWith(EXTERNAL_TAG_PREFIX)) return null;\n const parts = tag.slice(EXTERNAL_TAG_PREFIX.length).split(':');\n if (parts.length !== 2) return null;\n const kind = parts[0].toLowerCase();\n const identity = parts[1].trim();\n if (!isExternalSystemKind(kind) || identity === '') return null;\n return new ExternalTag(kind, identity);\n}\n\n/**\n * The committed `externalSystems` table for architecture/dependencies.json, merging both declaration\n * sites into one identity-keyed map.\n *\n * A system may legitimately carry both: a repo can wrap a datastore behind a contract in one service\n * and open it directly in another. Takes the api index rather than the whole scan result so this\n * module never has to import api-scanner, which imports it.\n */\n// webpieces-disable no-function-outside-class -- table builder, mirrors buildApiContracts in api-scanner.ts\nexport function buildExternalSystems(\n apiIndex: Map<string, ApiClassInfo>,\n projectInfos: Map<string, ProjectInfo>,\n): ExternalSystemDecls {\n const systems: ExternalSystemDecls = {};\n // First declaration wins the kind, so a later typo cannot silently reshape an already-drawn node.\n const ensure = (identity: string, kind: ExternalSystemKind, label: string): ExternalSystemDecl => {\n if (systems[identity] === undefined) systems[identity] = { kind, label, apis: [], projects: [] };\n return systems[identity];\n };\n\n for (const api of [...apiIndex.keys()].sort()) {\n const declared = apiIndex.get(api)!.externalSystem;\n if (declared === undefined) continue;\n ensure(declared.label, declared.kind, declared.label).apis.push(api);\n }\n\n for (const name of [...projectInfos.keys()].sort()) {\n for (const tag of projectInfos.get(name)!.tags) {\n const parsed = parseExternalTag(tag);\n if (parsed === null) continue;\n ensure(parsed.identity, parsed.kind, parsed.identity).projects.push(name);\n }\n }\n return systems;\n}\n\n/**\n * Resolve declarations into drawable nodes: which services actually get an arrow to each system.\n *\n * Two resolutions, matching the two declaration sites. A CONTRACT-declared system is reached by\n * every service that `uses` one of its contracts, so the arrows follow real call sites. A\n * TAG-declared system is reached ONLY by the tagged project itself — a tag asserts \"I open this\n * connection\", and fanning it out to dependents would invent arrows nobody wrote (a service that\n * depends on the entity library only for a DTO type does not talk to the database).\n *\n * A system nothing reaches is dropped rather than drawn floating: a declaration whose users all\n * disappeared is stale, and an unconnected node on the graph reads as a live dependency.\n */\n// webpieces-disable no-function-outside-class -- pure resolver, matching the sibling builders in this file\nexport function resolveExternalSystems(\n decls: ExternalSystemDecls,\n services: Record<string, RuntimeService>,\n): Record<string, RuntimeExternalSystem> {\n const out: Record<string, RuntimeExternalSystem> = {};\n for (const identity of Object.keys(decls).sort()) {\n const decl = decls[identity];\n const usedBy = new Set<string>();\n for (const name of Object.keys(services)) {\n if (services[name].uses.some((api: string) => decl.apis.includes(api))) usedBy.add(name);\n }\n // Only when the tagged project is itself a runtime node — tagging a plain library would\n // otherwise draw an arrow leaving a box that does not exist on this graph.\n for (const project of decl.projects) {\n if (services[project] !== undefined) usedBy.add(project);\n }\n if (usedBy.size === 0) continue;\n out[identity] = {\n kind: decl.kind,\n label: decl.label,\n usedBy: [...usedBy].sort(),\n apis: [...decl.apis].sort(),\n };\n }\n return out;\n}\n\n/**\n * Hang the resolved systems off the graph, and STAMP each declaring contract with its declaration.\n *\n * The stamp is what stops the same system being drawn twice: the visualizer skips an\n * `unresolvedUses` entry whose contract carries one, because that contract has already been drawn\n * with a real shape rather than as the generic grey box it would otherwise fall back to.\n *\n * A graph with nothing declared is left completely untouched — no empty key is written — so a repo\n * that adopts none of this keeps a byte-identical runtime-dependencies.json.\n */\n// webpieces-disable no-function-outside-class -- pure graph mutator, matching the sibling builders in this file\nexport function attachExternalSystems(graph: RuntimeGraph, systems: Record<string, RuntimeExternalSystem>): void {\n if (Object.keys(systems).length === 0) return;\n graph.externalSystems = systems;\n for (const system of Object.values(systems)) {\n for (const api of system.apis) {\n const entry = graph.apis[api];\n if (entry !== undefined) entry.externalSystem = { kind: system.kind, label: system.label };\n }\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"external-systems.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/external-systems.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * External system declarations\n *\n * A system OUTSIDE this repo that a service talks to — a database, a bucket, a cache. Until now\n * every one of them collapsed into the same grey dashed box, so `lib-firestore` (a datastore) was\n * indistinguishable from an HTTP service the repo happens not to implement. Declaring what a system\n * IS lets the runtime viz draw it as the thing it is.\n *\n * Split out of api-scanner.ts and runtime-graph.ts (both already at their file-size limit) so the\n * two halves of the feature — DECLARING a system and RESOLVING it to arrows — sit together.\n *\n * The two declaration sites exist because the two real cases differ in whether a contract exists:\n *\n * - **wrapped** — the repo has a vendor seam (`FirestoreAdminApi`), so the kind is declared with an\n * `@externalSystem <kind> [label]` JSDoc tag on the CONTRACT. A TS `interface` cannot carry a\n * decorator, and these seams are interfaces, so JSDoc is the only marker that fits in place.\n * - **unwrapped** — the service opens the connection itself (a `pg.Pool`, a TypeORM `DataSource`)\n * and there is no contract to mark, so the declaration is an `external:<kind>:<identity>` nx tag\n * on that PROJECT. Wrapping such a datastore purely to gain a marker is not worth it: a TypeORM\n * facade never closes, unlike the ~8 hand-picked methods a firestore seam needs.\n *\n * Both resolve to the same `(kind, identity)` pair, and identity is the NODE identity — two projects\n * declaring `postgres` converge on one cylinder with an arrow each, rather than drawing a database\n * apiece.\n */\n\nimport type { ApiClassInfo, ExternalSystemDecl, ExternalSystemDecls, ExternalSystemKind } from './api-relations';\nimport { isExternalSystemKind } from './api-relations';\nimport type { ProjectInfo } from '../project-info';\nimport type { RuntimeExternalSystem, RuntimeGraph, RuntimeService } from '../runtime-graph-model';\n\n/** nx tag prefix declaring an external system: `external:<kind>:<identity>`. */\nconst EXTERNAL_TAG_PREFIX = 'external:';\n\n/** The two halves of a parsed `external:<kind>:<identity>` nx tag. */\nclass ExternalTag {\n constructor(\n public readonly kind: ExternalSystemKind,\n public readonly identity: string,\n ) {}\n}\n\n/**\n * `external:<kind>:<identity>` -> its parts, or null for any other tag.\n *\n * Returns null (rather than throwing) on an unknown kind: this prefix shares a tag list with\n * `framework:` and `role:`, and hard-failing the whole graph generation over one malformed tag is a\n * worse outcome than not drawing one node.\n */\n// webpieces-disable no-function-outside-class -- pure parser, matching the sibling builders in this file\nfunction parseExternalTag(tag: string): ExternalTag | null {\n if (!tag.startsWith(EXTERNAL_TAG_PREFIX)) return null;\n const parts = tag.slice(EXTERNAL_TAG_PREFIX.length).split(':');\n if (parts.length !== 2) return null;\n const kind = parts[0].toLowerCase();\n const identity = parts[1].trim();\n if (!isExternalSystemKind(kind) || identity === '') return null;\n return new ExternalTag(kind, identity);\n}\n\n/**\n * The committed `externalSystems` table for architecture/dependencies.json, merging both declaration\n * sites into one identity-keyed map.\n *\n * A system may legitimately carry both: a repo can wrap a datastore behind a contract in one service\n * and open it directly in another. Takes the api index rather than the whole scan result so this\n * module never has to import api-scanner, which imports it.\n */\n// webpieces-disable no-function-outside-class -- table builder, mirrors buildApiContracts in api-scanner.ts\nexport function buildExternalSystems(\n apiIndex: Map<string, ApiClassInfo>,\n projectInfos: Map<string, ProjectInfo>,\n): ExternalSystemDecls {\n const systems: ExternalSystemDecls = {};\n // First declaration wins the kind, so a later typo cannot silently reshape an already-drawn node.\n const ensure = (identity: string, kind: ExternalSystemKind, label: string): ExternalSystemDecl => {\n if (systems[identity] === undefined) systems[identity] = { kind, label, apis: [], projects: [] };\n return systems[identity];\n };\n\n for (const api of [...apiIndex.keys()].sort()) {\n const declared = apiIndex.get(api)!.externalSystem;\n if (declared === undefined) continue;\n ensure(declared.label, declared.kind, declared.label).apis.push(api);\n }\n\n for (const name of [...projectInfos.keys()].sort()) {\n for (const tag of projectInfos.get(name)!.tags) {\n const parsed = parseExternalTag(tag);\n if (parsed === null) continue;\n ensure(parsed.identity, parsed.kind, parsed.identity).projects.push(name);\n }\n }\n return systems;\n}\n\n/**\n * Resolve declarations into drawable nodes: which services actually get an arrow to each system.\n *\n * Two resolutions, matching the two declaration sites. A CONTRACT-declared system is reached by\n * every service that `uses` one of its contracts, so the arrows follow real call sites. A\n * TAG-declared system is reached ONLY by the tagged project itself — a tag asserts \"I open this\n * connection\", and fanning it out to dependents would invent arrows nobody wrote (a service that\n * depends on the entity library only for a DTO type does not talk to the database).\n *\n * A system nothing reaches is dropped rather than drawn floating: a declaration whose users all\n * disappeared is stale, and an unconnected node on the graph reads as a live dependency.\n */\n// webpieces-disable no-function-outside-class -- pure resolver, matching the sibling builders in this file\nexport function resolveExternalSystems(\n decls: ExternalSystemDecls,\n services: Record<string, RuntimeService>,\n): Record<string, RuntimeExternalSystem> {\n const out: Record<string, RuntimeExternalSystem> = {};\n for (const identity of Object.keys(decls).sort()) {\n const decl = decls[identity];\n const usedBy = new Set<string>();\n for (const name of Object.keys(services)) {\n if (services[name].uses.some((api: string) => decl.apis.includes(api))) usedBy.add(name);\n }\n // Only when the tagged project is itself a runtime node — tagging a plain library would\n // otherwise draw an arrow leaving a box that does not exist on this graph.\n for (const project of decl.projects) {\n if (services[project] !== undefined) usedBy.add(project);\n }\n if (usedBy.size === 0) continue;\n out[identity] = {\n kind: decl.kind,\n label: decl.label,\n level: 0,\n levelBecause: 'outbound sink',\n usedBy: [...usedBy].sort(),\n apis: [...decl.apis].sort(),\n };\n }\n return out;\n}\n\n/**\n * Hang the resolved systems off the graph, and STAMP each declaring contract with its declaration.\n *\n * The stamp is what stops the same system being drawn twice: the visualizer skips an\n * `unresolvedUses` entry whose contract carries one, because that contract has already been drawn\n * with a real shape rather than as the generic grey box it would otherwise fall back to.\n *\n * A graph with nothing declared is left completely untouched — no empty key is written — so a repo\n * that adopts none of this keeps a byte-identical runtime-dependencies.json.\n */\n// webpieces-disable no-function-outside-class -- pure graph mutator, matching the sibling builders in this file\nexport function attachExternalSystems(graph: RuntimeGraph, systems: Record<string, RuntimeExternalSystem>): void {\n if (Object.keys(systems).length === 0) return;\n graph.externalSystems = systems;\n for (const system of Object.values(systems)) {\n for (const api of system.apis) {\n const entry = graph.apis[api];\n if (entry !== undefined) entry.externalSystem = { kind: system.kind, label: system.label };\n }\n }\n}\n"]}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RuntimeDetails = exports.RuntimeNodeDetails = void 0;
|
|
4
|
+
const runtime_external_triggers_1 = require("./runtime-external-triggers");
|
|
4
5
|
const remote_service_node_id_1 = require("./remote-service-node-id");
|
|
5
6
|
class RuntimeNodeDetails {
|
|
6
7
|
implemented;
|
|
@@ -55,6 +56,11 @@ class RuntimeDetails {
|
|
|
55
56
|
}
|
|
56
57
|
this.nodes[name] = new RuntimeNodeDetails(implemented, [...new Set(used)]);
|
|
57
58
|
}
|
|
59
|
+
for (const group of new runtime_external_triggers_1.RuntimeExternalTriggers(graph.triggers).groups) {
|
|
60
|
+
this.edges[`${group.from}->${group.to}`] = group
|
|
61
|
+
.apis()
|
|
62
|
+
.map((api) => `${graph.apis[api]?.owner ?? '?'}#${api}`);
|
|
63
|
+
}
|
|
58
64
|
this.remoteNodes(graph);
|
|
59
65
|
if (model !== null)
|
|
60
66
|
this.renderEdges(graph, model);
|
|
@@ -203,7 +209,7 @@ class RuntimeDetailsController {
|
|
|
203
209
|
for (const group of Array.from(this.host?.querySelectorAll('g.edge') ?? [])) {
|
|
204
210
|
const identity = group.querySelector('title')?.textContent ?? '';
|
|
205
211
|
const entries = this.data.edges[identity];
|
|
206
|
-
if (entries === undefined || entries.length
|
|
212
|
+
if (entries === undefined || entries.length < 2)
|
|
207
213
|
continue;
|
|
208
214
|
const element = group.querySelector('text');
|
|
209
215
|
if (element !== null && element.dataset['wpDetails'] !== 'true')
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-details.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-details.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GraphRenderModel } from './graph-render-model';\nimport type { RuntimeGraph, RuntimeRemoteCall } from './runtime-graph-model';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\n\nexport class RuntimeNodeDetails {\n constructor(\n public readonly implemented: string[],\n public readonly used: string[],\n ) {}\n}\n\n/** View data stays complete even when external destinations are hidden from DOT. */\nexport class RuntimeDetails {\n readonly nodes: Record<string, RuntimeNodeDetails> = {};\n readonly edges: Record<string, string[]> = {};\n\n constructor(\n graph: RuntimeGraph,\n showExternalNodes: boolean,\n model: GraphRenderModel | null = null,\n ) {\n for (const [name, service] of Object.entries(graph.services)) {\n // implements is the complete derived API list; wiringImplements is provenance only.\n const implemented = [...new Set(service.implements)].map(\n (api) => `${graph.apis[api]?.owner ?? '?'}#${api}`,\n );\n const used: string[] = [];\n for (const edge of graph.runtimeEdges.filter((edge) => edge.from === name)) {\n const entries = edge.via.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${edge.to} (${edge.type ?? 'rpc'})` +\n (edge.queue === undefined ? '' : ` via ${edge.queue}`),\n );\n if (service.wiringUses === undefined) used.push(...entries);\n if (edge.type !== 'pubsub')\n this.edges[`${edge.from}->${edge.to}`] = edge.via.map(\n (api) => `${graph.apis[api]?.owner ?? '?'}#${api}`,\n );\n }\n const remoteApis = this.remoteCalls(\n graph,\n name,\n service.wiringUses === undefined ? used : [],\n );\n for (const api of service.wiringUses === undefined ? service.uses : []) {\n if (graph.runtimeEdges.some((edge) => edge.from === name && edge.via.includes(api)))\n continue;\n if (remoteApis.has(api)) continue;\n const contract = graph.apis[api];\n const external =\n contract?.type === 'external' || contract?.implementedBy.length === 0;\n used.push(\n `${contract?.owner ?? '?'}#${api} → ${external ? (contract?.externalSystem?.label ?? contract?.owner ?? 'unknown external') : 'unknown / inferred target'}` +\n ` (${contract?.type ?? 'rpc'})${external && !showExternalNodes ? ' — destination hidden' : ''}`,\n );\n }\n used.push(...this.wiringUses(graph, name));\n for (const [identity, system] of Object.entries(graph.externalSystems ?? {})) {\n if (!system.usedBy.includes(name)) continue;\n this.edges[`${name}->system__${identity.replace(/[^a-zA-Z0-9_]/g, '_')}`] =\n system.apis.map((api) => `${graph.apis[api]?.owner ?? '?'}#${api}`);\n }\n for (const use of graph.unresolvedUses.filter((use) => use.service === name)) {\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n const owner = graph.apis[use.api]?.owner ?? use.api;\n const key = `${name}->external__${owner}`;\n (this.edges[key] ??= []).push(`${owner}#${use.api}`);\n }\n this.nodes[name] = new RuntimeNodeDetails(implemented, [...new Set(used)]);\n }\n this.remoteNodes(graph);\n if (model !== null) this.renderEdges(graph, model);\n }\n\n private wiringUses(graph: RuntimeGraph, name: string): string[] {\n return (graph.services[name].wiringUses ?? []).map(\n (ref) =>\n `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} → ${ref.targetService ?? 'unknown external target'} (${ref.type})` +\n (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`),\n );\n }\n\n /** A reviewed remote service's node details: the contracts its declaration says it implements. */\n private remoteNodes(graph: RuntimeGraph): void {\n for (const [name, remote] of Object.entries(graph.remoteServices ?? {}))\n this.nodes[new RemoteServiceNodeId().of(name)] = new RuntimeNodeDetails(\n [...new Set(remote.implements)],\n [],\n );\n }\n\n /**\n * The edge details for `name`'s calls into reviewed remote services (#1203), also appended to\n * `used`; returns the contracts covered so they are not reported as an unknown target.\n */\n private remoteCalls(graph: RuntimeGraph, name: string, used: string[]): Set<string> {\n const covered = new Set<string>();\n for (const [remoteName, remote] of Object.entries(graph.remoteServices ?? {}))\n for (const call of remote.calledBy.filter(\n (entry: RuntimeRemoteCall) => entry.from === name,\n )) {\n const entries = call.via.map(\n (api: string) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${remoteName} (remote service, ${call.type})`,\n );\n for (const api of call.via) covered.add(api);\n used.push(...entries);\n const key = `${name}->${new RemoteServiceNodeId().of(remoteName)}`;\n this.edges[key] = [\n ...(this.edges[key] ?? []),\n ...call.via.map((api) => `${graph.apis[api]?.owner ?? '?'}#${api}`),\n ];\n }\n return covered;\n }\n\n /** Bind detail facts to renderer-owned identities, including grouped queue hops. */\n private renderEdges(graph: RuntimeGraph, model: GraphRenderModel): void {\n for (const edge of model.edges) {\n const identity = `${edge.from}->${edge.to}`;\n const service = graph.services[edge.from] === undefined ? edge.to : edge.from;\n const queue = model.nodes.find(\n (node) => node.id === (edge.from.startsWith('queue__') ? edge.from : edge.to),\n );\n const via = graph.runtimeEdges.filter((call) => {\n if (call.type !== 'pubsub') return call.from === edge.from && call.to === edge.to;\n if (call.from !== service && call.to !== service) return false;\n return (\n queue !== undefined &&\n (call.queue === undefined\n ? call.via.some((api) => queue.queueKeys.includes(`queue:${api}`))\n : queue.queueKeys.includes(call.queue))\n );\n });\n const entries = [...(this.edges[identity] ?? [])];\n for (const call of via) {\n for (const api of call.via) {\n if (\n call.queue === undefined &&\n call.type === 'pubsub' &&\n !queue?.queueKeys.includes(`queue:${api}`)\n )\n continue;\n entries.push(`${graph.apis[api]?.owner ?? '?'}#${api}`);\n }\n }\n this.edges[identity] = [...new Set(entries)];\n }\n }\n\n script(): string {\n const encoded = JSON.stringify(this).replace(/</g, '\\\\u003c');\n return `new (${RuntimeDetailsController.toString()})(${encoded});`;\n }\n}\n\n/** Explicit click controller; redraws rebind edge controls and dismiss stale anchors. */\nclass RuntimeDetailsController {\n private readonly host = document.getElementById('graph');\n private readonly panel = document.createElement('div');\n private anchor: SVGElement | null = null;\n private identity = '';\n private implemented = false;\n\n constructor(private readonly data: RuntimeDetails) {\n if (this.host === null) return;\n this.panel.className = 'wp-graph-details';\n this.panel.hidden = true;\n this.panel.setAttribute('role', 'dialog');\n this.panel.setAttribute('aria-label', 'API relationships');\n document.body.append(this.panel);\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n document.addEventListener('wp-runtime-implements', (event) => {\n const detail = (event as CustomEvent<RuntimeImplementsPayload>).detail;\n this.open(\n detail.node,\n this.data.nodes[detail.name]?.implemented ?? [],\n detail.name,\n true,\n );\n });\n document.addEventListener('keydown', (event) => {\n if (event.key === 'Escape' && !this.panel.hidden) {\n event.stopPropagation();\n this.close();\n }\n });\n document.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n !this.panel.contains(event.target) &&\n !this.anchor?.contains(event.target)\n )\n this.close();\n });\n document.addEventListener('wp-graph-transform', () => this.position());\n this.host.addEventListener('scroll', () => this.position());\n window.addEventListener('resize', () => this.position());\n }\n\n private position(): void {\n if (this.anchor === null || this.panel.hidden) return;\n const rect = this.anchor.getBoundingClientRect();\n this.panel.style.left = `${Math.max(12, Math.min(rect.left, innerWidth - this.panel.offsetWidth - 12))}px`;\n this.panel.style.top = `${Math.max(12, Math.min(rect.bottom + 6, innerHeight - this.panel.offsetHeight - 12))}px`;\n }\n\n private close(): void {\n if (this.panel.hidden) return;\n const old = this.anchor;\n this.panel.hidden = true;\n this.anchor = null;\n if (old?.isConnected) old.focus();\n }\n\n private open(\n element: SVGElement,\n entries: string[],\n identity: string,\n implemented: boolean,\n ): void {\n this.anchor = element;\n this.identity = identity;\n this.implemented = implemented;\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = 'Close';\n close.onclick = () => this.close();\n const list = document.createElement('ul');\n for (const entry of entries.length === 0\n ? ['No implemented contracts recorded in this saved snapshot.']\n : entries) {\n const item = document.createElement('li');\n item.textContent = entry;\n list.append(item);\n }\n this.panel.replaceChildren(close, list);\n this.panel.hidden = false;\n this.position();\n close.focus({ preventScroll: true });\n }\n\n private attach(): void {\n for (const group of Array.from(this.host?.querySelectorAll<SVGGElement>('g.edge') ?? [])) {\n const identity = group.querySelector('title')?.textContent ?? '';\n const entries = this.data.edges[identity];\n if (entries === undefined || entries.length === 0) continue;\n const element = group.querySelector('text');\n if (element !== null && element.dataset['wpDetails'] !== 'true')\n this.attachElement(element, entries, identity);\n }\n if (this.panel.hidden || this.anchor?.isConnected) return;\n const groups = Array.from(\n this.host?.querySelectorAll<SVGGElement>(this.implemented ? 'g.node' : 'g.edge') ?? [],\n );\n const group = groups.find(\n (node) => node.querySelector('title')?.textContent === this.identity,\n );\n const replacement = this.implemented ? group : group?.querySelector('text');\n if (replacement) {\n this.anchor = replacement;\n this.position();\n } else this.close();\n }\n\n private attachElement(element: SVGTextElement, entries: string[], identity: string): void {\n element.dataset['wpDetails'] = 'true';\n element.classList.add('wp-api-detail');\n element.setAttribute('tabindex', '0');\n element.setAttribute('role', 'button');\n element.setAttribute('aria-label', `Uses: ${identity}`);\n element.setAttribute('aria-haspopup', 'dialog');\n element.addEventListener('click', (event) => {\n event.stopPropagation();\n this.open(element, entries, identity, false);\n });\n element.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n this.open(element, entries, identity, false);\n }\n });\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime-details.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-details.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GraphRenderModel } from './graph-render-model';\nimport type { RuntimeGraph, RuntimeRemoteCall } from './runtime-graph-model';\nimport { RuntimeExternalTriggers } from './runtime-external-triggers';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\n\nexport class RuntimeNodeDetails {\n constructor(\n public readonly implemented: string[],\n public readonly used: string[],\n ) {}\n}\n\n/** View data stays complete even when external destinations are hidden from DOT. */\nexport class RuntimeDetails {\n readonly nodes: Record<string, RuntimeNodeDetails> = {};\n readonly edges: Record<string, string[]> = {};\n\n constructor(\n graph: RuntimeGraph,\n showExternalNodes: boolean,\n model: GraphRenderModel | null = null,\n ) {\n for (const [name, service] of Object.entries(graph.services)) {\n // implements is the complete derived API list; wiringImplements is provenance only.\n const implemented = [...new Set(service.implements)].map(\n (api) => `${graph.apis[api]?.owner ?? '?'}#${api}`,\n );\n const used: string[] = [];\n for (const edge of graph.runtimeEdges.filter((edge) => edge.from === name)) {\n const entries = edge.via.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${edge.to} (${edge.type ?? 'rpc'})` +\n (edge.queue === undefined ? '' : ` via ${edge.queue}`),\n );\n if (service.wiringUses === undefined) used.push(...entries);\n if (edge.type !== 'pubsub')\n this.edges[`${edge.from}->${edge.to}`] = edge.via.map(\n (api) => `${graph.apis[api]?.owner ?? '?'}#${api}`,\n );\n }\n const remoteApis = this.remoteCalls(\n graph,\n name,\n service.wiringUses === undefined ? used : [],\n );\n for (const api of service.wiringUses === undefined ? service.uses : []) {\n if (graph.runtimeEdges.some((edge) => edge.from === name && edge.via.includes(api)))\n continue;\n if (remoteApis.has(api)) continue;\n const contract = graph.apis[api];\n const external =\n contract?.type === 'external' || contract?.implementedBy.length === 0;\n used.push(\n `${contract?.owner ?? '?'}#${api} → ${external ? (contract?.externalSystem?.label ?? contract?.owner ?? 'unknown external') : 'unknown / inferred target'}` +\n ` (${contract?.type ?? 'rpc'})${external && !showExternalNodes ? ' — destination hidden' : ''}`,\n );\n }\n used.push(...this.wiringUses(graph, name));\n for (const [identity, system] of Object.entries(graph.externalSystems ?? {})) {\n if (!system.usedBy.includes(name)) continue;\n this.edges[`${name}->system__${identity.replace(/[^a-zA-Z0-9_]/g, '_')}`] =\n system.apis.map((api) => `${graph.apis[api]?.owner ?? '?'}#${api}`);\n }\n for (const use of graph.unresolvedUses.filter((use) => use.service === name)) {\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n const owner = graph.apis[use.api]?.owner ?? use.api;\n const key = `${name}->external__${owner}`;\n (this.edges[key] ??= []).push(`${owner}#${use.api}`);\n }\n this.nodes[name] = new RuntimeNodeDetails(implemented, [...new Set(used)]);\n }\n for (const group of new RuntimeExternalTriggers(graph.triggers).groups) {\n this.edges[`${group.from}->${group.to}`] = group\n .apis()\n .map((api: string) => `${graph.apis[api]?.owner ?? '?'}#${api}`);\n }\n this.remoteNodes(graph);\n if (model !== null) this.renderEdges(graph, model);\n }\n\n private wiringUses(graph: RuntimeGraph, name: string): string[] {\n return (graph.services[name].wiringUses ?? []).map(\n (ref) =>\n `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} → ${ref.targetService ?? 'unknown external target'} (${ref.type})` +\n (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`),\n );\n }\n\n /** A reviewed remote service's node details: the contracts its declaration says it implements. */\n private remoteNodes(graph: RuntimeGraph): void {\n for (const [name, remote] of Object.entries(graph.remoteServices ?? {}))\n this.nodes[new RemoteServiceNodeId().of(name)] = new RuntimeNodeDetails(\n [...new Set(remote.implements)],\n [],\n );\n }\n\n /**\n * The edge details for `name`'s calls into reviewed remote services (#1203), also appended to\n * `used`; returns the contracts covered so they are not reported as an unknown target.\n */\n private remoteCalls(graph: RuntimeGraph, name: string, used: string[]): Set<string> {\n const covered = new Set<string>();\n for (const [remoteName, remote] of Object.entries(graph.remoteServices ?? {}))\n for (const call of remote.calledBy.filter(\n (entry: RuntimeRemoteCall) => entry.from === name,\n )) {\n const entries = call.via.map(\n (api: string) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${remoteName} (remote service, ${call.type})`,\n );\n for (const api of call.via) covered.add(api);\n used.push(...entries);\n const key = `${name}->${new RemoteServiceNodeId().of(remoteName)}`;\n this.edges[key] = [\n ...(this.edges[key] ?? []),\n ...call.via.map((api) => `${graph.apis[api]?.owner ?? '?'}#${api}`),\n ];\n }\n return covered;\n }\n\n /** Bind detail facts to renderer-owned identities, including grouped queue hops. */\n private renderEdges(graph: RuntimeGraph, model: GraphRenderModel): void {\n for (const edge of model.edges) {\n const identity = `${edge.from}->${edge.to}`;\n const service = graph.services[edge.from] === undefined ? edge.to : edge.from;\n const queue = model.nodes.find(\n (node) => node.id === (edge.from.startsWith('queue__') ? edge.from : edge.to),\n );\n const via = graph.runtimeEdges.filter((call) => {\n if (call.type !== 'pubsub') return call.from === edge.from && call.to === edge.to;\n if (call.from !== service && call.to !== service) return false;\n return (\n queue !== undefined &&\n (call.queue === undefined\n ? call.via.some((api) => queue.queueKeys.includes(`queue:${api}`))\n : queue.queueKeys.includes(call.queue))\n );\n });\n const entries = [...(this.edges[identity] ?? [])];\n for (const call of via) {\n for (const api of call.via) {\n if (\n call.queue === undefined &&\n call.type === 'pubsub' &&\n !queue?.queueKeys.includes(`queue:${api}`)\n )\n continue;\n entries.push(`${graph.apis[api]?.owner ?? '?'}#${api}`);\n }\n }\n this.edges[identity] = [...new Set(entries)];\n }\n }\n\n script(): string {\n const encoded = JSON.stringify(this).replace(/</g, '\\\\u003c');\n return `new (${RuntimeDetailsController.toString()})(${encoded});`;\n }\n}\n\n/** Explicit click controller; redraws rebind edge controls and dismiss stale anchors. */\nclass RuntimeDetailsController {\n private readonly host = document.getElementById('graph');\n private readonly panel = document.createElement('div');\n private anchor: SVGElement | null = null;\n private identity = '';\n private implemented = false;\n\n constructor(private readonly data: RuntimeDetails) {\n if (this.host === null) return;\n this.panel.className = 'wp-graph-details';\n this.panel.hidden = true;\n this.panel.setAttribute('role', 'dialog');\n this.panel.setAttribute('aria-label', 'API relationships');\n document.body.append(this.panel);\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n document.addEventListener('wp-runtime-implements', (event) => {\n const detail = (event as CustomEvent<RuntimeImplementsPayload>).detail;\n this.open(\n detail.node,\n this.data.nodes[detail.name]?.implemented ?? [],\n detail.name,\n true,\n );\n });\n document.addEventListener('keydown', (event) => {\n if (event.key === 'Escape' && !this.panel.hidden) {\n event.stopPropagation();\n this.close();\n }\n });\n document.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n !this.panel.contains(event.target) &&\n !this.anchor?.contains(event.target)\n )\n this.close();\n });\n document.addEventListener('wp-graph-transform', () => this.position());\n this.host.addEventListener('scroll', () => this.position());\n window.addEventListener('resize', () => this.position());\n }\n\n private position(): void {\n if (this.anchor === null || this.panel.hidden) return;\n const rect = this.anchor.getBoundingClientRect();\n this.panel.style.left = `${Math.max(12, Math.min(rect.left, innerWidth - this.panel.offsetWidth - 12))}px`;\n this.panel.style.top = `${Math.max(12, Math.min(rect.bottom + 6, innerHeight - this.panel.offsetHeight - 12))}px`;\n }\n\n private close(): void {\n if (this.panel.hidden) return;\n const old = this.anchor;\n this.panel.hidden = true;\n this.anchor = null;\n if (old?.isConnected) old.focus();\n }\n\n private open(\n element: SVGElement,\n entries: string[],\n identity: string,\n implemented: boolean,\n ): void {\n this.anchor = element;\n this.identity = identity;\n this.implemented = implemented;\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = 'Close';\n close.onclick = () => this.close();\n const list = document.createElement('ul');\n for (const entry of entries.length === 0\n ? ['No implemented contracts recorded in this saved snapshot.']\n : entries) {\n const item = document.createElement('li');\n item.textContent = entry;\n list.append(item);\n }\n this.panel.replaceChildren(close, list);\n this.panel.hidden = false;\n this.position();\n close.focus({ preventScroll: true });\n }\n\n private attach(): void {\n for (const group of Array.from(this.host?.querySelectorAll<SVGGElement>('g.edge') ?? [])) {\n const identity = group.querySelector('title')?.textContent ?? '';\n const entries = this.data.edges[identity];\n if (entries === undefined || entries.length < 2) continue;\n const element = group.querySelector('text');\n if (element !== null && element.dataset['wpDetails'] !== 'true')\n this.attachElement(element, entries, identity);\n }\n if (this.panel.hidden || this.anchor?.isConnected) return;\n const groups = Array.from(\n this.host?.querySelectorAll<SVGGElement>(this.implemented ? 'g.node' : 'g.edge') ?? [],\n );\n const group = groups.find(\n (node) => node.querySelector('title')?.textContent === this.identity,\n );\n const replacement = this.implemented ? group : group?.querySelector('text');\n if (replacement) {\n this.anchor = replacement;\n this.position();\n } else this.close();\n }\n\n private attachElement(element: SVGTextElement, entries: string[], identity: string): void {\n element.dataset['wpDetails'] = 'true';\n element.classList.add('wp-api-detail');\n element.setAttribute('tabindex', '0');\n element.setAttribute('role', 'button');\n element.setAttribute('aria-label', `Uses: ${identity}`);\n element.setAttribute('aria-haspopup', 'dialog');\n element.addEventListener('click', (event) => {\n event.stopPropagation();\n this.open(element, entries, identity, false);\n });\n element.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n this.open(element, entries, identity, false);\n }\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { RuntimeTrigger } from './runtime-graph-model';
|
|
2
|
+
/** One external caller/service pair, shared by DOT emission and popup data. */
|
|
3
|
+
export declare class RuntimeExternalTriggerGroup {
|
|
4
|
+
readonly from: string;
|
|
5
|
+
readonly to: string;
|
|
6
|
+
readonly triggers: RuntimeTrigger[];
|
|
7
|
+
constructor(from: string, to: string);
|
|
8
|
+
apis(): string[];
|
|
9
|
+
label(): string;
|
|
10
|
+
}
|
|
11
|
+
export declare class RuntimeExternalTriggers {
|
|
12
|
+
readonly groups: RuntimeExternalTriggerGroup[];
|
|
13
|
+
constructor(triggers: RuntimeTrigger[]);
|
|
14
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RuntimeExternalTriggers = exports.RuntimeExternalTriggerGroup = void 0;
|
|
4
|
+
/** One external caller/service pair, shared by DOT emission and popup data. */
|
|
5
|
+
class RuntimeExternalTriggerGroup {
|
|
6
|
+
from;
|
|
7
|
+
to;
|
|
8
|
+
triggers = [];
|
|
9
|
+
constructor(from, to) {
|
|
10
|
+
this.from = from;
|
|
11
|
+
this.to = to;
|
|
12
|
+
}
|
|
13
|
+
apis() {
|
|
14
|
+
return [...new Set(this.triggers.map((trigger) => trigger.api))].sort();
|
|
15
|
+
}
|
|
16
|
+
label() {
|
|
17
|
+
const apis = this.apis();
|
|
18
|
+
if (apis.length > 1)
|
|
19
|
+
return `Uses (${apis.length}) ▾`;
|
|
20
|
+
const methods = new Set(this.triggers.map((trigger) => trigger.method));
|
|
21
|
+
return methods.size === 1 ? `${apis[0]}.${this.triggers[0].method}` : apis[0];
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
exports.RuntimeExternalTriggerGroup = RuntimeExternalTriggerGroup;
|
|
25
|
+
class RuntimeExternalTriggers {
|
|
26
|
+
groups;
|
|
27
|
+
constructor(triggers) {
|
|
28
|
+
const groups = new Map();
|
|
29
|
+
for (const trigger of triggers) {
|
|
30
|
+
if (trigger.kind !== 'external')
|
|
31
|
+
continue;
|
|
32
|
+
const caller = trigger.caller;
|
|
33
|
+
const from = caller === undefined
|
|
34
|
+
? `inbound__${trigger.api.replace(/[^A-Za-z0-9_]/g, '_')}`
|
|
35
|
+
: `system__caller__${caller.label.replace(/[^A-Za-z0-9_]/g, '_')}`;
|
|
36
|
+
const key = JSON.stringify([from, trigger.service]);
|
|
37
|
+
if (!groups.has(key))
|
|
38
|
+
groups.set(key, new RuntimeExternalTriggerGroup(from, trigger.service));
|
|
39
|
+
groups.get(key).triggers.push(trigger);
|
|
40
|
+
}
|
|
41
|
+
this.groups = [...groups.values()];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
exports.RuntimeExternalTriggers = RuntimeExternalTriggers;
|
|
45
|
+
//# sourceMappingURL=runtime-external-triggers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime-external-triggers.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-external-triggers.ts"],"names":[],"mappings":";;;AAEA,+EAA+E;AAC/E,MAAa,2BAA2B;IAIvB;IACA;IAJJ,QAAQ,GAAqB,EAAE,CAAC;IAEzC,YACa,IAAY,EACZ,EAAU;QADV,SAAI,GAAJ,IAAI,CAAQ;QACZ,OAAE,GAAF,EAAE,CAAQ;IACpB,CAAC;IAEJ,IAAI;QACA,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAuB,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;IAC5F,CAAC;IAED,KAAK;QACD,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,EAAE,CAAC;QACzB,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,SAAS,IAAI,CAAC,MAAM,KAAK,CAAC;QACtD,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAuB,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;QACxF,OAAO,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClF,CAAC;CACJ;AAlBD,kEAkBC;AAED,MAAa,uBAAuB;IACvB,MAAM,CAAgC;IAE/C,YAAY,QAA0B;QAClC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAuC,CAAC;QAC9D,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC7B,IAAI,OAAO,CAAC,IAAI,KAAK,UAAU;gBAAE,SAAS;YAC1C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;YAC9B,MAAM,IAAI,GACN,MAAM,KAAK,SAAS;gBAChB,CAAC,CAAC,YAAY,OAAO,CAAC,GAAG,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE;gBAC1D,CAAC,CAAC,mBAAmB,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC;YAC3E,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YACpD,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;gBAChB,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,2BAA2B,CAAC,IAAI,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YAC5E,MAAM,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,MAAM,GAAG,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;CACJ;AAnBD,0DAmBC","sourcesContent":["import type { RuntimeTrigger } from './runtime-graph-model';\n\n/** One external caller/service pair, shared by DOT emission and popup data. */\nexport class RuntimeExternalTriggerGroup {\n readonly triggers: RuntimeTrigger[] = [];\n\n constructor(\n readonly from: string,\n readonly to: string,\n ) {}\n\n apis(): string[] {\n return [...new Set(this.triggers.map((trigger: RuntimeTrigger) => trigger.api))].sort();\n }\n\n label(): string {\n const apis = this.apis();\n if (apis.length > 1) return `Uses (${apis.length}) ▾`;\n const methods = new Set(this.triggers.map((trigger: RuntimeTrigger) => trigger.method));\n return methods.size === 1 ? `${apis[0]}.${this.triggers[0].method}` : apis[0];\n }\n}\n\nexport class RuntimeExternalTriggers {\n readonly groups: RuntimeExternalTriggerGroup[];\n\n constructor(triggers: RuntimeTrigger[]) {\n const groups = new Map<string, RuntimeExternalTriggerGroup>();\n for (const trigger of triggers) {\n if (trigger.kind !== 'external') continue;\n const caller = trigger.caller;\n const from =\n caller === undefined\n ? `inbound__${trigger.api.replace(/[^A-Za-z0-9_]/g, '_')}`\n : `system__caller__${caller.label.replace(/[^A-Za-z0-9_]/g, '_')}`;\n const key = JSON.stringify([from, trigger.service]);\n if (!groups.has(key))\n groups.set(key, new RuntimeExternalTriggerGroup(from, trigger.service));\n groups.get(key)!.triggers.push(trigger);\n }\n this.groups = [...groups.values()];\n }\n}\n"]}
|
|
@@ -18,6 +18,8 @@ exports.serializeRuntimeGraph = serializeRuntimeGraph;
|
|
|
18
18
|
const tslib_1 = require("tslib");
|
|
19
19
|
const fs = tslib_1.__importStar(require("fs"));
|
|
20
20
|
const path = tslib_1.__importStar(require("path"));
|
|
21
|
+
const rules_config_1 = require("@webpieces/rules-config");
|
|
22
|
+
const runtime_config_1 = require("./runtime-config");
|
|
21
23
|
const toError_1 = require("../toError");
|
|
22
24
|
exports.DEFAULT_RUNTIME_GRAPH_PATH = 'architecture/runtime-dependencies.json';
|
|
23
25
|
/** Deterministic JSON (sorted keys + arrays already sorted during assembly). */
|
|
@@ -42,18 +44,44 @@ function loadRuntimeGraph(workspaceRoot, graphPath = exports.DEFAULT_RUNTIME_GRA
|
|
|
42
44
|
const fullPath = path.join(workspaceRoot, graphPath);
|
|
43
45
|
if (!fs.existsSync(fullPath))
|
|
44
46
|
return null;
|
|
47
|
+
let graph;
|
|
45
48
|
// eslint-disable-next-line @webpieces/no-unmanaged-exceptions
|
|
46
49
|
try {
|
|
47
|
-
|
|
50
|
+
graph = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
|
|
48
51
|
}
|
|
49
52
|
catch (err) {
|
|
50
53
|
const error = (0, toError_1.toError)(err);
|
|
51
54
|
throw new Error(`Failed to load runtime graph from ${fullPath}`, { cause: error });
|
|
52
55
|
}
|
|
56
|
+
new RuntimeLevelSchema().validate(graph);
|
|
57
|
+
return graph;
|
|
53
58
|
}
|
|
54
59
|
/** Serialize for an in-memory equality check (matches the on-disk format). */
|
|
55
60
|
// webpieces-disable no-function-outside-class -- module-scope file IO, matching saveGraph/loadBlessedGraph in graph-loader.ts
|
|
56
61
|
function serializeRuntimeGraph(graph) {
|
|
57
62
|
return formatRuntimeJson(graph);
|
|
58
63
|
}
|
|
64
|
+
/** Old RPC-only JSON must be regenerated, never silently rendered with missing ranks. */
|
|
65
|
+
class RuntimeLevelSchema {
|
|
66
|
+
validate(graph) {
|
|
67
|
+
const nodes = [
|
|
68
|
+
...Object.values(graph.services),
|
|
69
|
+
...Object.values(graph.queues),
|
|
70
|
+
...Object.values(graph.contractQueues ?? {}),
|
|
71
|
+
...graph.triggers,
|
|
72
|
+
...Object.values(graph.externalSystems ?? {}),
|
|
73
|
+
...Object.values(graph.remoteServices ?? {}),
|
|
74
|
+
...graph.unresolvedUses,
|
|
75
|
+
];
|
|
76
|
+
if (graph.contractQueues !== undefined &&
|
|
77
|
+
Number.isInteger(graph.maxLevel) &&
|
|
78
|
+
nodes.every((node) => Number.isInteger(node.level) &&
|
|
79
|
+
node.level >= 0 &&
|
|
80
|
+
typeof node.levelBecause === 'string'))
|
|
81
|
+
return;
|
|
82
|
+
throw new rules_config_1.RuleFailError(runtime_config_1.RUNTIME_RULE_NAME, 'runtime-dependencies.json requires levels for every node and contractQueues. Regenerate with architecture:generate.', undefined, undefined, [
|
|
83
|
+
new rules_config_1.Option('Run pnpm nx run architecture:generate and commit runtime-dependencies.json.', true),
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
59
87
|
//# sourceMappingURL=runtime-graph-io.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-graph-io.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-io.ts"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;;;
|
|
1
|
+
{"version":3,"file":"runtime-graph-io.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-io.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Runtime Graph persistence\n *\n * Reading, writing and canonical-serializing architecture/runtime-dependencies.json. Split out of\n * runtime-graph.ts (which owns the derivation and had grown past the file-size limit).\n *\n * `formatRuntimeJson` is the ONE canonical rendering: validate-runtime-architecture compares the\n * freshly derived graph to the committed one as STRINGS, so any second way of writing this file\n * would show up as unfixable drift.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { RuleFailError, Option } from '@webpieces/rules-config';\nimport { RUNTIME_RULE_NAME } from './runtime-config';\nimport type { RuntimeGraph } from './runtime-graph-model';\nimport { toError } from '../toError';\n\nexport const DEFAULT_RUNTIME_GRAPH_PATH = 'architecture/runtime-dependencies.json';\n\n/** Deterministic JSON (sorted keys + arrays already sorted during assembly). */\n// webpieces-disable no-function-outside-class -- module-scope file IO, matching saveGraph/loadBlessedGraph in graph-loader.ts\nfunction formatRuntimeJson(graph: RuntimeGraph): string {\n return JSON.stringify(graph, null, 4) + '\\n';\n}\n\n// webpieces-disable no-function-outside-class -- module-scope file IO, matching saveGraph/loadBlessedGraph in graph-loader.ts\nexport function saveRuntimeGraph(\n graph: RuntimeGraph,\n workspaceRoot: string,\n graphPath: string = DEFAULT_RUNTIME_GRAPH_PATH,\n): void {\n const fullPath = path.join(workspaceRoot, graphPath);\n const dir = path.dirname(fullPath);\n if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });\n fs.writeFileSync(fullPath, formatRuntimeJson(graph), 'utf-8');\n}\n\n// webpieces-disable no-function-outside-class -- module-scope file IO, matching saveGraph/loadBlessedGraph in graph-loader.ts\nexport function runtimeGraphFileExists(\n workspaceRoot: string,\n graphPath: string = DEFAULT_RUNTIME_GRAPH_PATH,\n): boolean {\n return fs.existsSync(path.join(workspaceRoot, graphPath));\n}\n\n// webpieces-disable no-function-outside-class -- module-scope file IO, matching saveGraph/loadBlessedGraph in graph-loader.ts\nexport function loadRuntimeGraph(\n workspaceRoot: string,\n graphPath: string = DEFAULT_RUNTIME_GRAPH_PATH,\n): RuntimeGraph | null {\n const fullPath = path.join(workspaceRoot, graphPath);\n if (!fs.existsSync(fullPath)) return null;\n let graph: RuntimeGraph;\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n graph = JSON.parse(fs.readFileSync(fullPath, 'utf-8')) as RuntimeGraph;\n } catch (err: unknown) {\n const error = toError(err);\n throw new Error(`Failed to load runtime graph from ${fullPath}`, { cause: error });\n }\n new RuntimeLevelSchema().validate(graph);\n return graph;\n}\n\n/** Serialize for an in-memory equality check (matches the on-disk format). */\n// webpieces-disable no-function-outside-class -- module-scope file IO, matching saveGraph/loadBlessedGraph in graph-loader.ts\nexport function serializeRuntimeGraph(graph: RuntimeGraph): string {\n return formatRuntimeJson(graph);\n}\n\n/** Old RPC-only JSON must be regenerated, never silently rendered with missing ranks. */\nclass RuntimeLevelSchema {\n validate(graph: RuntimeGraph): void {\n const nodes = [\n ...Object.values(graph.services),\n ...Object.values(graph.queues),\n ...Object.values(graph.contractQueues ?? {}),\n ...graph.triggers,\n ...Object.values(graph.externalSystems ?? {}),\n ...Object.values(graph.remoteServices ?? {}),\n ...graph.unresolvedUses,\n ];\n if (\n graph.contractQueues !== undefined &&\n Number.isInteger(graph.maxLevel) &&\n nodes.every(\n (node) =>\n Number.isInteger(node.level) &&\n node.level >= 0 &&\n typeof node.levelBecause === 'string',\n )\n )\n return;\n throw new RuleFailError(\n RUNTIME_RULE_NAME,\n 'runtime-dependencies.json requires levels for every node and contractQueues. Regenerate with architecture:generate.',\n undefined,\n undefined,\n [\n new Option(\n 'Run pnpm nx run architecture:generate and commit runtime-dependencies.json.',\n true,\n ),\n ],\n );\n }\n}\n"]}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { EnhancedGraph } from './graph-sorter';
|
|
2
|
-
import type { RuntimeEdge
|
|
2
|
+
import type { RuntimeEdge } from './runtime-graph-model';
|
|
3
3
|
/**
|
|
4
4
|
* LEVELS and ADJACENCY for the runtime graph — how deep each service sits, and who calls whom.
|
|
5
5
|
*
|
|
@@ -8,23 +8,7 @@ import type { RuntimeEdge, RuntimeGraph } from './runtime-graph-model';
|
|
|
8
8
|
* computation over the result, with no knowledge of contracts at all. Splitting it also keeps
|
|
9
9
|
* runtime-graph.ts under the file-size limit, which is the visible symptom of the same thing.
|
|
10
10
|
*/
|
|
11
|
-
/**
|
|
12
|
-
* Adjacency (service -> [targets]) used for leveling + cycle checks.
|
|
13
|
-
*
|
|
14
|
-
* TWO KINDS OF EDGE ARE EXCLUDED, and the second is modelled on the first:
|
|
15
|
-
*
|
|
16
|
-
* PUBSUB EDGES. A queue is precisely the thing that decouples producer from consumer: the producer
|
|
17
|
-
* returns as soon as the task is enqueued and never waits on the consumer, so a queued hop is not a
|
|
18
|
-
* runtime dependency in the sense levels and cycle detection mean. Counting them would make the
|
|
19
|
-
* common and correct `A → queue → A` (a service deferring its own work) an architecture cycle, and
|
|
20
|
-
* would rank services by an ordering that does not constrain deploy or startup.
|
|
21
|
-
*
|
|
22
|
-
* EDGES STAMPED `cutLegacyCycle`. The calling project carries a `cutLegacyCycle:<target>` nx tag
|
|
23
|
-
* (see cut-legacy-cycle-resolver.ts) ADMITTING that this hop closes a real cycle which is being
|
|
24
|
-
* tolerated as legacy debt. Unlike a queued hop the coupling is real, so the edge is still drawn —
|
|
25
|
-
* it is only kept out of leveling and cycle detection, which are the two things a cycle makes
|
|
26
|
-
* meaningless.
|
|
27
|
-
*/
|
|
11
|
+
/** RPC adjacency for callers working with an edge list alone. Full runtime layering uses RuntimeNodeLevels. */
|
|
28
12
|
export declare function adjacencyFromEdges(serviceNames: string[], edges: RuntimeEdge[]): Record<string, string[]>;
|
|
29
13
|
/**
|
|
30
14
|
* Stamp `cutLegacyCycle` on every runtime edge a project declared a cut for — the DECLARATION
|
|
@@ -49,8 +33,6 @@ export declare function adjacencyFromEdges(serviceNames: string[], edges: Runtim
|
|
|
49
33
|
* target, so a tag may name a module name or a declared serviceName.
|
|
50
34
|
*/
|
|
51
35
|
export declare function applyCycleCuts(edges: RuntimeEdge[], projects: EnhancedGraph, nodeByServiceName: Map<string, string>): string[];
|
|
52
|
-
/** Adjacency (service -> [targets]) from a loaded runtime graph. */
|
|
53
|
-
export declare function runtimeAdjacency(graph: RuntimeGraph): Record<string, string[]>;
|
|
54
36
|
/**
|
|
55
37
|
* Assign levels via topological sort — and THROW on a cycle rather than levelling one.
|
|
56
38
|
*
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.adjacencyFromEdges = adjacencyFromEdges;
|
|
4
4
|
exports.applyCycleCuts = applyCycleCuts;
|
|
5
|
-
exports.runtimeAdjacency = runtimeAdjacency;
|
|
6
5
|
exports.assignLevels = assignLevels;
|
|
7
6
|
const rules_config_1 = require("@webpieces/rules-config");
|
|
8
7
|
const graph_sorter_1 = require("./graph-sorter");
|
|
@@ -17,23 +16,7 @@ const runtime_config_1 = require("./runtime-config");
|
|
|
17
16
|
* computation over the result, with no knowledge of contracts at all. Splitting it also keeps
|
|
18
17
|
* runtime-graph.ts under the file-size limit, which is the visible symptom of the same thing.
|
|
19
18
|
*/
|
|
20
|
-
/**
|
|
21
|
-
* Adjacency (service -> [targets]) used for leveling + cycle checks.
|
|
22
|
-
*
|
|
23
|
-
* TWO KINDS OF EDGE ARE EXCLUDED, and the second is modelled on the first:
|
|
24
|
-
*
|
|
25
|
-
* PUBSUB EDGES. A queue is precisely the thing that decouples producer from consumer: the producer
|
|
26
|
-
* returns as soon as the task is enqueued and never waits on the consumer, so a queued hop is not a
|
|
27
|
-
* runtime dependency in the sense levels and cycle detection mean. Counting them would make the
|
|
28
|
-
* common and correct `A → queue → A` (a service deferring its own work) an architecture cycle, and
|
|
29
|
-
* would rank services by an ordering that does not constrain deploy or startup.
|
|
30
|
-
*
|
|
31
|
-
* EDGES STAMPED `cutLegacyCycle`. The calling project carries a `cutLegacyCycle:<target>` nx tag
|
|
32
|
-
* (see cut-legacy-cycle-resolver.ts) ADMITTING that this hop closes a real cycle which is being
|
|
33
|
-
* tolerated as legacy debt. Unlike a queued hop the coupling is real, so the edge is still drawn —
|
|
34
|
-
* it is only kept out of leveling and cycle detection, which are the two things a cycle makes
|
|
35
|
-
* meaningless.
|
|
36
|
-
*/
|
|
19
|
+
/** RPC adjacency for callers working with an edge list alone. Full runtime layering uses RuntimeNodeLevels. */
|
|
37
20
|
// webpieces-disable no-function-outside-class -- pure graph helper, matches the sibling helpers in this file
|
|
38
21
|
function adjacencyFromEdges(serviceNames, edges) {
|
|
39
22
|
const adj = {};
|
|
@@ -85,8 +68,7 @@ function applyCycleCuts(edges, projects, nodeByServiceName) {
|
|
|
85
68
|
`nothing exempts nothing.`);
|
|
86
69
|
continue;
|
|
87
70
|
}
|
|
88
|
-
//
|
|
89
|
-
// never needs cutting; only a direct call edge is cuttable.
|
|
71
|
+
// Legacy cuts apply to direct calls only. Foreign queues remain visible to cycle detection.
|
|
90
72
|
const cut = edges.filter((e) => e.from === name && e.to === node && e.type !== 'pubsub');
|
|
91
73
|
if (cut.length === 0) {
|
|
92
74
|
problems.push(`${name} declares ${cut_legacy_cycle_resolver_1.CUT_LEGACY_CYCLE_TAG_PREFIX}${target}, but it has no direct ` +
|
|
@@ -99,11 +81,6 @@ function applyCycleCuts(edges, projects, nodeByServiceName) {
|
|
|
99
81
|
}
|
|
100
82
|
return problems;
|
|
101
83
|
}
|
|
102
|
-
/** Adjacency (service -> [targets]) from a loaded runtime graph. */
|
|
103
|
-
// webpieces-disable no-function-outside-class -- pure graph helper, sibling of the one above
|
|
104
|
-
function runtimeAdjacency(graph) {
|
|
105
|
-
return adjacencyFromEdges(Object.keys(graph.services), graph.runtimeEdges);
|
|
106
|
-
}
|
|
107
84
|
/** The cures for a cyclic runtime graph, honest ones first and the IOU last. */
|
|
108
85
|
// webpieces-disable no-function-outside-class -- cure list for the throw below, sibling of the helpers in this file
|
|
109
86
|
function cycleCures() {
|
|
@@ -111,14 +88,13 @@ function cycleCures() {
|
|
|
111
88
|
new rules_config_1.Option('Retag a node that is not really a deployed service. A project tagged `role:server` in ' +
|
|
112
89
|
'its project.json that is in fact a library should carry `role:lib` instead, which ' +
|
|
113
90
|
'takes it out of the runtime graph entirely and removes every edge through it.', true),
|
|
114
|
-
new rules_config_1.Option('
|
|
115
|
-
'
|
|
116
|
-
'because the producer returns without waiting on the consumer.'),
|
|
91
|
+
new rules_config_1.Option('Break the feedback path through the named queue or RPC hop. Foreign queues count ' +
|
|
92
|
+
'toward runtime layering; only a service enqueueing to itself is exempt.'),
|
|
117
93
|
new rules_config_1.Option('Extract the shared contract into a `role:api-lib` project that both sides depend on, ' +
|
|
118
94
|
'so the call runs in one direction only.'),
|
|
119
95
|
new rules_config_1.Option('Last resort — admit the debt. Add `' +
|
|
120
96
|
cut_legacy_cycle_resolver_1.CUT_LEGACY_CYCLE_TAG_PREFIX +
|
|
121
|
-
|
|
97
|
+
"<targetService>` to the CALLING project's project.json tags, naming the service on " +
|
|
122
98
|
'the other end of the one edge you are cutting. This does NOT say the edge is ' +
|
|
123
99
|
'harmless; it records that the cycle is real and is being tolerated as legacy debt. ' +
|
|
124
100
|
'The edge stays on the drawing as a dashed "legacy cycle" arrow, and ' +
|
|
@@ -144,7 +120,9 @@ function cycleCures() {
|
|
|
144
120
|
function assignLevels(adjacency) {
|
|
145
121
|
const cycles = new graph_cycles_1.ProjectCycleDetector().find(adjacency);
|
|
146
122
|
if (cycles.length > 0) {
|
|
147
|
-
const listed = cycles
|
|
123
|
+
const listed = cycles
|
|
124
|
+
.map((cycle) => ` ${cycle.describe()}`)
|
|
125
|
+
.join('\n');
|
|
148
126
|
const plural = cycles.length === 1 ? 'cycle' : 'cycles';
|
|
149
127
|
throw new rules_config_1.RuleFailError(runtime_config_1.RUNTIME_RULE_NAME, `The runtime service graph contains ${cycles.length} ${plural}:\n${listed}\n` +
|
|
150
128
|
'CD deploys services in dependency order and a cycle has no such order — whichever ' +
|