@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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-graph-levels.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-levels.ts"],"names":[],"mappings":"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","sourcesContent":["import { RuleFailError, Option } from '@webpieces/rules-config';\nimport { sortGraphTopologically } from './graph-sorter';\nimport type { GraphEntry, EnhancedGraph } from './graph-sorter';\nimport { ProjectCycleDetector, ProjectCycle } from './graph-cycles';\nimport { CUT_LEGACY_CYCLE_TAG_PREFIX } from './cut-legacy-cycle-resolver';\nimport { RUNTIME_RULE_NAME } from './runtime-config';\nimport type { RuntimeEdge, RuntimeGraph } from './runtime-graph-model';\n\n/**\n * LEVELS and ADJACENCY for the runtime graph — how deep each service sits, and who calls whom.\n *\n * Its own module rather than a block inside runtime-graph.ts: that file owns DERIVATION (turning\n * apiRelations into services, apis, edges, queues and triggers), and this is a pure graph\n * computation over the result, with no knowledge of contracts at all. Splitting it also keeps\n * runtime-graph.ts under the file-size limit, which is the visible symptom of the same thing.\n */\n/**\n * Adjacency (service -> [targets]) used for leveling + cycle checks.\n *\n * TWO KINDS OF EDGE ARE EXCLUDED, and the second is modelled on the first:\n *\n * PUBSUB EDGES. A queue is precisely the thing that decouples producer from consumer: the producer\n * returns as soon as the task is enqueued and never waits on the consumer, so a queued hop is not a\n * runtime dependency in the sense levels and cycle detection mean. Counting them would make the\n * common and correct `A → queue → A` (a service deferring its own work) an architecture cycle, and\n * would rank services by an ordering that does not constrain deploy or startup.\n *\n * EDGES STAMPED `cutLegacyCycle`. The calling project carries a `cutLegacyCycle:<target>` nx tag\n * (see cut-legacy-cycle-resolver.ts) ADMITTING that this hop closes a real cycle which is being\n * tolerated as legacy debt. Unlike a queued hop the coupling is real, so the edge is still drawn —\n * it is only kept out of leveling and cycle detection, which are the two things a cycle makes\n * meaningless.\n */\n// webpieces-disable no-function-outside-class -- pure graph helper, matches the sibling helpers in this file\nexport function adjacencyFromEdges(\n serviceNames: string[],\n edges: RuntimeEdge[],\n): Record<string, string[]> {\n const adj: Record<string, string[]> = {};\n for (const name of serviceNames) adj[name] = [];\n for (const edge of edges) {\n if (edge.type === 'pubsub') continue;\n if (edge.cutLegacyCycle === true) continue;\n if (!adj[edge.from]) adj[edge.from] = [];\n adj[edge.from].push(edge.to);\n }\n return adj;\n}\n\n/**\n * Stamp `cutLegacyCycle` on every runtime edge a project declared a cut for — the DECLARATION\n * (`cutLegacyCycle:<targetService>` nx tags, resolved into `GraphEntry.cutLegacyCycle` and committed\n * to dependencies.json) turned into the mark {@link adjacencyFromEdges} above reads.\n *\n * IT FAILS THE BUILD ON A TAG THAT CUTS NOTHING, in both directions:\n *\n * - the tag names a service no runtime node answers to — a typo or a rename. Silently doing nothing\n * is the worst outcome available: the check would look exempted while no edge was in fact cut, or\n * the tag would keep reading as a live IOU after the service it named was deleted. `serviceName`\n * is validated against real module names for exactly this reason.\n * - the tag resolves, but there is no direct edge to cut. The debt is PAID — delete the tag, so\n * `grep -rn cutLegacyCycle` keeps enumerating only cycles still actually tolerated.\n *\n * Returned as problems (the deriver's `problems` list) rather than thrown, so one run names every\n * bad tag instead of stopping at the first.\n *\n * @param edges the derived runtime edges, mutated in place.\n * @param projects the committed project entries carrying `cutLegacyCycle`.\n * @param nodeByServiceName addressable name -> runtime node, the SAME map that resolves a call\n * target, so a tag may name a module name or a declared serviceName.\n */\n// webpieces-disable no-function-outside-class -- pure pass over derived edges, sibling of the helpers here\nexport function applyCycleCuts(\n edges: RuntimeEdge[],\n projects: EnhancedGraph,\n nodeByServiceName: Map<string, string>,\n): string[] {\n const problems: string[] = [];\n for (const name of Object.keys(projects).sort()) {\n const entry: GraphEntry = projects[name];\n for (const target of entry.cutLegacyCycle ?? []) {\n const node = nodeByServiceName.get(target);\n if (node === undefined) {\n problems.push(\n `${name} declares ${CUT_LEGACY_CYCLE_TAG_PREFIX}${target}, but no runtime service ` +\n `answers to '${target}'. Fix the name or delete the tag — a cut that names ` +\n `nothing exempts nothing.`,\n );\n continue;\n }\n // A queued hop is already excluded from leveling, so it can never be part of a cycle and\n // never needs cutting; only a direct call edge is cuttable.\n const cut = edges.filter(\n (e: RuntimeEdge) => e.from === name && e.to === node && e.type !== 'pubsub',\n );\n if (cut.length === 0) {\n problems.push(\n `${name} declares ${CUT_LEGACY_CYCLE_TAG_PREFIX}${target}, but it has no direct ` +\n `runtime call edge to '${node}'. The debt is paid — delete the tag.`,\n );\n continue;\n }\n for (const e of cut) e.cutLegacyCycle = true;\n }\n }\n return problems;\n}\n\n/** Adjacency (service -> [targets]) from a loaded runtime graph. */\n// webpieces-disable no-function-outside-class -- pure graph helper, sibling of the one above\nexport function runtimeAdjacency(graph: RuntimeGraph): Record<string, string[]> {\n return adjacencyFromEdges(Object.keys(graph.services), graph.runtimeEdges);\n}\n\n/** The cures for a cyclic runtime graph, honest ones first and the IOU last. */\n// webpieces-disable no-function-outside-class -- cure list for the throw below, sibling of the helpers in this file\nfunction cycleCures(): Option[] {\n return [\n new Option(\n 'Retag a node that is not really a deployed service. A project tagged `role:server` in ' +\n 'its project.json that is in fact a library should carry `role:lib` instead, which ' +\n 'takes it out of the runtime graph entirely and removes every edge through it.',\n true,\n ),\n new Option(\n 'Make the hop asynchronous. A contract marked `@PubSub()` is delivered through a Cloud ' +\n 'Tasks queue, and queued edges are excluded from leveling and cycle detection ' +\n 'because the producer returns without waiting on the consumer.',\n ),\n new Option(\n 'Extract the shared contract into a `role:api-lib` project that both sides depend on, ' +\n 'so the call runs in one direction only.',\n ),\n new Option(\n 'Last resort — admit the debt. Add `' +\n CUT_LEGACY_CYCLE_TAG_PREFIX +\n '<targetService>` to the CALLING project\\'s project.json tags, naming the service on ' +\n 'the other end of the one edge you are cutting. This does NOT say the edge is ' +\n 'harmless; it records that the cycle is real and is being tolerated as legacy debt. ' +\n 'The edge stays on the drawing as a dashed \"legacy cycle\" arrow, and ' +\n '`grep -rn cutLegacyCycle` enumerates every one still outstanding.',\n ),\n ];\n}\n\n/**\n * Assign levels via topological sort — and THROW on a cycle rather than levelling one.\n *\n * This used to swallow the cycle and flatten EVERY service to level 0, which was the worst available\n * outcome twice over: one bad edge anywhere silently restratified the whole graph, and the reason\n * was discarded, so the diagram rendered as one legitimate-looking flat row.\n *\n * Cycles are not allowed. CD deploys services in dependency order, and a cyclic architecture has no\n * such order — whichever member rolls out first is calling one that is not up yet — so the graph is\n * not deployable and its level numbers mean nothing.\n *\n * Detection is {@link ProjectCycleDetector}, the ONE cycle detector in this package (it guards the\n * compile-time project graph too); only the audience and the cures differ, so only the message is\n * written here.\n */\n// webpieces-disable no-function-outside-class -- pure graph helper, sibling of the two above\nexport function assignLevels(adjacency: Record<string, string[]>): Record<string, number> {\n const cycles = new ProjectCycleDetector().find(adjacency);\n if (cycles.length > 0) {\n const listed = cycles.map((cycle: ProjectCycle): string => ` ${cycle.describe()}`).join('\\n');\n const plural = cycles.length === 1 ? 'cycle' : 'cycles';\n throw new RuleFailError(\n RUNTIME_RULE_NAME,\n `The runtime service graph contains ${cycles.length} ${plural}:\\n${listed}\\n` +\n 'CD deploys services in dependency order and a cycle has no such order — whichever ' +\n 'service in the chain rolls out first calls one that is not up yet — so an ' +\n 'architecture with cycles cannot be deployed and its level numbers are meaningless. ' +\n 'Cut every chain above.',\n undefined,\n undefined,\n cycleCures(),\n );\n }\n const levels: Record<string, number> = {};\n const sorted = sortGraphTopologically(adjacency);\n for (const name of Object.keys(sorted)) levels[name] = sorted[name].level;\n return levels;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime-graph-levels.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-levels.ts"],"names":[],"mappings":"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","sourcesContent":["import { RuleFailError, Option } from '@webpieces/rules-config';\nimport { sortGraphTopologically } from './graph-sorter';\nimport type { GraphEntry, EnhancedGraph } from './graph-sorter';\nimport { ProjectCycleDetector, ProjectCycle } from './graph-cycles';\nimport { CUT_LEGACY_CYCLE_TAG_PREFIX } from './cut-legacy-cycle-resolver';\nimport { RUNTIME_RULE_NAME } from './runtime-config';\nimport type { RuntimeEdge } from './runtime-graph-model';\n\n/**\n * LEVELS and ADJACENCY for the runtime graph — how deep each service sits, and who calls whom.\n *\n * Its own module rather than a block inside runtime-graph.ts: that file owns DERIVATION (turning\n * apiRelations into services, apis, edges, queues and triggers), and this is a pure graph\n * computation over the result, with no knowledge of contracts at all. Splitting it also keeps\n * runtime-graph.ts under the file-size limit, which is the visible symptom of the same thing.\n */\n/** RPC adjacency for callers working with an edge list alone. Full runtime layering uses RuntimeNodeLevels. */\n// webpieces-disable no-function-outside-class -- pure graph helper, matches the sibling helpers in this file\nexport function adjacencyFromEdges(\n serviceNames: string[],\n edges: RuntimeEdge[],\n): Record<string, string[]> {\n const adj: Record<string, string[]> = {};\n for (const name of serviceNames) adj[name] = [];\n for (const edge of edges) {\n if (edge.type === 'pubsub') continue;\n if (edge.cutLegacyCycle === true) continue;\n if (!adj[edge.from]) adj[edge.from] = [];\n adj[edge.from].push(edge.to);\n }\n return adj;\n}\n\n/**\n * Stamp `cutLegacyCycle` on every runtime edge a project declared a cut for — the DECLARATION\n * (`cutLegacyCycle:<targetService>` nx tags, resolved into `GraphEntry.cutLegacyCycle` and committed\n * to dependencies.json) turned into the mark {@link adjacencyFromEdges} above reads.\n *\n * IT FAILS THE BUILD ON A TAG THAT CUTS NOTHING, in both directions:\n *\n * - the tag names a service no runtime node answers to — a typo or a rename. Silently doing nothing\n * is the worst outcome available: the check would look exempted while no edge was in fact cut, or\n * the tag would keep reading as a live IOU after the service it named was deleted. `serviceName`\n * is validated against real module names for exactly this reason.\n * - the tag resolves, but there is no direct edge to cut. The debt is PAID — delete the tag, so\n * `grep -rn cutLegacyCycle` keeps enumerating only cycles still actually tolerated.\n *\n * Returned as problems (the deriver's `problems` list) rather than thrown, so one run names every\n * bad tag instead of stopping at the first.\n *\n * @param edges the derived runtime edges, mutated in place.\n * @param projects the committed project entries carrying `cutLegacyCycle`.\n * @param nodeByServiceName addressable name -> runtime node, the SAME map that resolves a call\n * target, so a tag may name a module name or a declared serviceName.\n */\n// webpieces-disable no-function-outside-class -- pure pass over derived edges, sibling of the helpers here\nexport function applyCycleCuts(\n edges: RuntimeEdge[],\n projects: EnhancedGraph,\n nodeByServiceName: Map<string, string>,\n): string[] {\n const problems: string[] = [];\n for (const name of Object.keys(projects).sort()) {\n const entry: GraphEntry = projects[name];\n for (const target of entry.cutLegacyCycle ?? []) {\n const node = nodeByServiceName.get(target);\n if (node === undefined) {\n problems.push(\n `${name} declares ${CUT_LEGACY_CYCLE_TAG_PREFIX}${target}, but no runtime service ` +\n `answers to '${target}'. Fix the name or delete the tag — a cut that names ` +\n `nothing exempts nothing.`,\n );\n continue;\n }\n // Legacy cuts apply to direct calls only. Foreign queues remain visible to cycle detection.\n const cut = edges.filter(\n (e: RuntimeEdge) => e.from === name && e.to === node && e.type !== 'pubsub',\n );\n if (cut.length === 0) {\n problems.push(\n `${name} declares ${CUT_LEGACY_CYCLE_TAG_PREFIX}${target}, but it has no direct ` +\n `runtime call edge to '${node}'. The debt is paid — delete the tag.`,\n );\n continue;\n }\n for (const e of cut) e.cutLegacyCycle = true;\n }\n }\n return problems;\n}\n\n/** The cures for a cyclic runtime graph, honest ones first and the IOU last. */\n// webpieces-disable no-function-outside-class -- cure list for the throw below, sibling of the helpers in this file\nfunction cycleCures(): Option[] {\n return [\n new Option(\n 'Retag a node that is not really a deployed service. A project tagged `role:server` in ' +\n 'its project.json that is in fact a library should carry `role:lib` instead, which ' +\n 'takes it out of the runtime graph entirely and removes every edge through it.',\n true,\n ),\n new Option(\n 'Break the feedback path through the named queue or RPC hop. Foreign queues count ' +\n 'toward runtime layering; only a service enqueueing to itself is exempt.',\n ),\n new Option(\n 'Extract the shared contract into a `role:api-lib` project that both sides depend on, ' +\n 'so the call runs in one direction only.',\n ),\n new Option(\n 'Last resort — admit the debt. Add `' +\n CUT_LEGACY_CYCLE_TAG_PREFIX +\n \"<targetService>` to the CALLING project's project.json tags, naming the service on \" +\n 'the other end of the one edge you are cutting. This does NOT say the edge is ' +\n 'harmless; it records that the cycle is real and is being tolerated as legacy debt. ' +\n 'The edge stays on the drawing as a dashed \"legacy cycle\" arrow, and ' +\n '`grep -rn cutLegacyCycle` enumerates every one still outstanding.',\n ),\n ];\n}\n\n/**\n * Assign levels via topological sort — and THROW on a cycle rather than levelling one.\n *\n * This used to swallow the cycle and flatten EVERY service to level 0, which was the worst available\n * outcome twice over: one bad edge anywhere silently restratified the whole graph, and the reason\n * was discarded, so the diagram rendered as one legitimate-looking flat row.\n *\n * Cycles are not allowed. CD deploys services in dependency order, and a cyclic architecture has no\n * such order — whichever member rolls out first is calling one that is not up yet — so the graph is\n * not deployable and its level numbers mean nothing.\n *\n * Detection is {@link ProjectCycleDetector}, the ONE cycle detector in this package (it guards the\n * compile-time project graph too); only the audience and the cures differ, so only the message is\n * written here.\n */\n// webpieces-disable no-function-outside-class -- pure graph helper, sibling of the two above\nexport function assignLevels(adjacency: Record<string, string[]>): Record<string, number> {\n const cycles = new ProjectCycleDetector().find(adjacency);\n if (cycles.length > 0) {\n const listed = cycles\n .map((cycle: ProjectCycle): string => ` ${cycle.describe()}`)\n .join('\\n');\n const plural = cycles.length === 1 ? 'cycle' : 'cycles';\n throw new RuleFailError(\n RUNTIME_RULE_NAME,\n `The runtime service graph contains ${cycles.length} ${plural}:\\n${listed}\\n` +\n 'CD deploys services in dependency order and a cycle has no such order — whichever ' +\n 'service in the chain rolls out first calls one that is not up yet — so an ' +\n 'architecture with cycles cannot be deployed and its level numbers are meaningless. ' +\n 'Cut every chain above.',\n undefined,\n undefined,\n cycleCures(),\n );\n }\n const levels: Record<string, number> = {};\n const sorted = sortGraphTopologically(adjacency);\n for (const name of Object.keys(sorted)) levels[name] = sorted[name].level;\n return levels;\n}\n"]}
|
|
@@ -10,8 +10,11 @@
|
|
|
10
10
|
* class would only ever be a shape assertion over a plain object, never a constructed instance.
|
|
11
11
|
*/
|
|
12
12
|
import type { ApiRef, ApiTransport, ExternalSystemDeclaration, ExternalSystemKind } from './api-usage/api-relations';
|
|
13
|
-
export
|
|
13
|
+
export declare class RuntimeLevel {
|
|
14
14
|
level: number;
|
|
15
|
+
levelBecause: string;
|
|
16
|
+
}
|
|
17
|
+
export interface RuntimeService extends RuntimeLevel {
|
|
15
18
|
/**
|
|
16
19
|
* The project's declared nx role — 'server' or 'client' (a library is never a node). Carried so
|
|
17
20
|
* the viz can LABEL the node with what it actually is: it used to infer the role from
|
|
@@ -110,13 +113,16 @@ export interface RuntimeEdge {
|
|
|
110
113
|
* Only exact method declarations may populate producer facts. Contract-only client wiring
|
|
111
114
|
* produces contract-level edges without a method queue, regardless of the endpoint table.
|
|
112
115
|
*/
|
|
113
|
-
export
|
|
116
|
+
export declare class RuntimeQueueFlow extends RuntimeLevel {
|
|
117
|
+
selfQueue: boolean;
|
|
118
|
+
producedBy: string[];
|
|
119
|
+
consumedBy: string[];
|
|
120
|
+
}
|
|
121
|
+
export interface RuntimeQueue extends RuntimeQueueFlow {
|
|
114
122
|
api: string;
|
|
115
123
|
method: string;
|
|
116
124
|
/** `@Queue(...)` override, else `${Api}-${method}` — the name Terraform must match 1:1. */
|
|
117
125
|
queueName: string;
|
|
118
|
-
producedBy: string[];
|
|
119
|
-
consumedBy: string[];
|
|
120
126
|
}
|
|
121
127
|
/**
|
|
122
128
|
* An endpoint driven by something that is NOT an in-repo caller — a clock or an outside system.
|
|
@@ -124,7 +130,7 @@ export interface RuntimeQueue {
|
|
|
124
130
|
* invisible until now: a nightly sweep and a GCP push subscription are real runtime entry points
|
|
125
131
|
* with real Terraform behind them, and the graph showed neither.
|
|
126
132
|
*/
|
|
127
|
-
export interface RuntimeTrigger {
|
|
133
|
+
export interface RuntimeTrigger extends RuntimeLevel {
|
|
128
134
|
/** 'cron' → a scheduler fires it; 'external' → a system outside this repo posts to it. */
|
|
129
135
|
kind: 'cron' | 'external';
|
|
130
136
|
api: string;
|
|
@@ -137,16 +143,15 @@ export interface RuntimeTrigger {
|
|
|
137
143
|
* Present for 'external': WHO outside this repo posts to it, declared as
|
|
138
144
|
* `@Endpoint(POST, p, WRITE, EXTERNAL, { calledBy: 'twilio' })`.
|
|
139
145
|
*
|
|
140
|
-
* The
|
|
141
|
-
*
|
|
142
|
-
* `label` is the identity here too, not display text.
|
|
146
|
+
* The caller is drawn separately from an outbound system with the same label: callers
|
|
147
|
+
* start the flow on the top row, while outbound systems are sinks on L0.
|
|
143
148
|
*
|
|
144
149
|
* Optional only for graphs generated BEFORE the caller was required; generation now fails rather
|
|
145
150
|
* than emitting an `external` method without one.
|
|
146
151
|
*/
|
|
147
152
|
caller?: ExternalSystemDeclaration;
|
|
148
153
|
}
|
|
149
|
-
export interface RuntimeUnresolved {
|
|
154
|
+
export interface RuntimeUnresolved extends RuntimeLevel {
|
|
150
155
|
service: string;
|
|
151
156
|
api: string;
|
|
152
157
|
}
|
|
@@ -167,7 +172,7 @@ export interface RuntimeUnresolved {
|
|
|
167
172
|
* `label` is the node IDENTITY, not just display text: two services declaring `postgres` converge on
|
|
168
173
|
* ONE node with two arrows into it, rather than drawing a database each.
|
|
169
174
|
*/
|
|
170
|
-
export interface RuntimeExternalSystem {
|
|
175
|
+
export interface RuntimeExternalSystem extends RuntimeLevel {
|
|
171
176
|
kind: ExternalSystemKind;
|
|
172
177
|
/** Display name AND node identity — declarations sharing a label share a node. */
|
|
173
178
|
label: string;
|
|
@@ -188,10 +193,10 @@ export interface RuntimeRemoteCall {
|
|
|
188
193
|
/**
|
|
189
194
|
* A service OUTSIDE this workspace (another repo, a VM, partner endpoints) declared in the reviewed
|
|
190
195
|
* `remoteServices` table of dependencies.json (#1203). It is a SERVICE — it implements our contracts —
|
|
191
|
-
* so it is drawn as a service node, distinct from a vendor external system.
|
|
192
|
-
*
|
|
196
|
+
* so it is drawn as a service node, distinct from a vendor external system. Its level is zero:
|
|
197
|
+
* outbound calls from outside this workspace are not part of this graph.
|
|
193
198
|
*/
|
|
194
|
-
export interface RuntimeRemoteService {
|
|
199
|
+
export interface RuntimeRemoteService extends RuntimeLevel {
|
|
195
200
|
/** Display text from the reviewed declaration; the key is the identity. */
|
|
196
201
|
label?: string;
|
|
197
202
|
/** The contracts it is declared to implement, as `Api (transport)`, in reviewed order. */
|
|
@@ -200,6 +205,9 @@ export interface RuntimeRemoteService {
|
|
|
200
205
|
calledBy: RuntimeRemoteCall[];
|
|
201
206
|
}
|
|
202
207
|
export interface RuntimeGraph {
|
|
208
|
+
maxLevel: number;
|
|
209
|
+
/** Contract-only pubsub flows; these do not assert any method-level producers. */
|
|
210
|
+
contractQueues: Record<string, RuntimeQueueFlow>;
|
|
203
211
|
services: Record<string, RuntimeService>;
|
|
204
212
|
apis: Record<string, RuntimeApi>;
|
|
205
213
|
runtimeEdges: RuntimeEdge[];
|
|
@@ -11,4 +11,22 @@
|
|
|
11
11
|
* class would only ever be a shape assertion over a plain object, never a constructed instance.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.RuntimeQueueFlow = exports.RuntimeLevel = void 0;
|
|
15
|
+
class RuntimeLevel {
|
|
16
|
+
level = 0;
|
|
17
|
+
levelBecause = '';
|
|
18
|
+
}
|
|
19
|
+
exports.RuntimeLevel = RuntimeLevel;
|
|
20
|
+
/**
|
|
21
|
+
* One Cloud Tasks queue: the async seam between a producer and a consumer, at METHOD granularity.
|
|
22
|
+
*
|
|
23
|
+
* Only exact method declarations may populate producer facts. Contract-only client wiring
|
|
24
|
+
* produces contract-level edges without a method queue, regardless of the endpoint table.
|
|
25
|
+
*/
|
|
26
|
+
class RuntimeQueueFlow extends RuntimeLevel {
|
|
27
|
+
selfQueue = false;
|
|
28
|
+
producedBy = [];
|
|
29
|
+
consumedBy = [];
|
|
30
|
+
}
|
|
31
|
+
exports.RuntimeQueueFlow = RuntimeQueueFlow;
|
|
14
32
|
//# sourceMappingURL=runtime-graph-model.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-graph-model.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG","sourcesContent":["/**\n * Runtime Graph model\n *\n * The serialization DTOs for architecture/runtime-dependencies.json. Split out of runtime-graph.ts,\n * which owns the DERIVATION (and had grown past the file-size limit), so the committed data shape\n * can be read on its own — it is what every consumer of the file, and any future Terraform\n * cross-check, actually programs against.\n *\n * Interfaces rather than classes: these are parsed straight out of JSON with `JSON.parse`, so a\n * class would only ever be a shape assertion over a plain object, never a constructed instance.\n */\n\nimport type {\n ApiRef,\n ApiTransport,\n ExternalSystemDeclaration,\n ExternalSystemKind,\n} from './api-usage/api-relations';\n\nexport interface RuntimeService {\n level: number;\n /**\n * The project's declared nx role — 'server' or 'client' (a library is never a node). Carried so\n * the viz can LABEL the node with what it actually is: it used to infer the role from\n * `implements.length > 0`, which labelled every relation-less server \"client\". Optional so a\n * runtime-dependencies.json written before this field existed still parses and still renders\n * exactly as it did (the visualizer falls back to the old inference).\n */\n role?: string;\n /**\n * The name clients address this service by (`new ClientConfig('helper-fsdb')`), declared in its\n * project.json. Absent for a service nothing calls by name (e.g. a browser app).\n */\n serviceName?: string;\n /**\n * The service(s) this node's clients call when the call site carries no literal `ClientConfig`,\n * declared in its project.json (metadata.webpieces.callsService). A single name, or an\n * `{ apiClassName: serviceName }` map. Absent when the node declares no target. Mirrors\n * GraphEntry.callsService; it is the CALLING-side counterpart of `serviceName`.\n */\n callsService?: string | Record<string, string>;\n implements: string[];\n /**\n * apiClassName -> the LIBRARY project whose apiRelations declared that implements, for the apis\n * this service serves through an embedded library rather than its own source (e.g. a shared\n * route-registration lib). Answers \"who implements WarmupApi, and where did that come from?\",\n * which previously required walking the dependsOn closure by hand.\n */\n implementsVia?: Record<string, string>;\n uses: string[];\n dependsOn: string[];\n /** Reviewed binding provenance and runtime conditions, retained for compact detail views. */\n wiringUses?: ApiRef[];\n wiringImplements?: ApiRef[];\n /**\n * When false, this service is hidden from the rendered runtime graph (its\n * node AND every edge touching it are omitted from the HTML/DOT). It stays\n * in runtime-dependencies.json so the data view is complete. Absent means\n * drawn (the default). Mirrors GraphEntry.drawOnGraph from the `drawOnGraph:`\n * nx tag.\n */\n drawOnGraph?: boolean;\n /**\n * The products this service belongs to (#1179), sorted: exactly its project's declared\n * `product:<name>` tags (#1216). Never derived from a runtime call or a queue; a call into another\n * product's service shows as the runtime edge, not as a product. Absent for a service whose\n * project is untagged. See graph-products.ts.\n */\n products?: string[];\n}\n\nexport interface RuntimeApi {\n implementedBy: string[];\n usedBy: string[];\n /** Transport of this API — 'rpc' (direct call) or 'pubsub' (delivered through a queue). */\n type?: ApiTransport;\n /**\n * The api-lib project that OWNS this contract. For a contract nothing in-repo implements, this\n * is the external library the calls leave the repo through (`lib-firestore`, `lib-gmail`), which\n * is what the runtime viz labels its terminal external nodes with.\n */\n owner?: string;\n /**\n * Set when the contract carries an `@externalSystem <kind> [label]` JSDoc tag — the vendor seam\n * declaring WHAT it is a seam to, so the viz can draw firestore as a database rather than as the\n * same grey box as every other external. Absent on everything else.\n */\n externalSystem?: ExternalSystemDeclaration;\n}\n\nexport interface RuntimeEdge {\n from: string;\n to: string;\n via: string[];\n /**\n * Transport of this edge. 'rpc' → a direct call arrow. 'pubsub' → the producer enqueues and the\n * consumer is delivered later, so the runtime viz draws it as producer → QUEUE → consumer.\n * Edges are split by transport, so every edge is a single kind.\n */\n type?: ApiTransport;\n /**\n * `\"ApiClassName.methodName\"` — the queue this edge flows through. Present iff `type` is\n * 'pubsub'. Queues are per METHOD, not per service pair, because that is the unit Cloud Tasks\n * (and Terraform) actually create, so two services exchanging three queued methods are three\n * queues rather than one arrow.\n */\n queue?: string;\n /**\n * True when the CALLING project carries a `cutLegacyCycle:<target>` nx tag naming this edge's\n * target — an admission that this hop closes a REAL cycle that is being tolerated as legacy\n * debt, not a claim that it is harmless. The edge is still drawn (dashed, labelled \"legacy\n * cycle\"); it is only excluded from leveling and cycle detection. Absent on every other edge.\n */\n cutLegacyCycle?: boolean;\n}\n\n/**\n * One Cloud Tasks queue: the async seam between a producer and a consumer, at METHOD granularity.\n *\n * Only exact method declarations may populate producer facts. Contract-only client wiring\n * produces contract-level edges without a method queue, regardless of the endpoint table.\n */\nexport interface RuntimeQueue {\n api: string;\n method: string;\n /** `@Queue(...)` override, else `${Api}-${method}` — the name Terraform must match 1:1. */\n queueName: string;\n producedBy: string[];\n consumedBy: string[];\n}\n\n/**\n * An endpoint driven by something that is NOT an in-repo caller — a clock or an outside system.\n * These never appear as runtime EDGES (there is no in-repo `from`), which is exactly why they were\n * invisible until now: a nightly sweep and a GCP push subscription are real runtime entry points\n * with real Terraform behind them, and the graph showed neither.\n */\nexport interface RuntimeTrigger {\n /** 'cron' → a scheduler fires it; 'external' → a system outside this repo posts to it. */\n kind: 'cron' | 'external';\n api: string;\n method: string;\n /** The service that SERVES the endpoint (the arrow's head). */\n service: string;\n /** Present for 'cron': the Cloud Scheduler job / queue name Terraform must match. */\n queueName?: string;\n /**\n * Present for 'external': WHO outside this repo posts to it, declared as\n * `@Endpoint(POST, p, WRITE, EXTERNAL, { calledBy: 'twilio' })`.\n *\n * The SAME `(kind,label)` an OUTBOUND {@link RuntimeExternalSystem} carries, so an inbound\n * `saas twilio` and an outbound `saas twilio` share a node identity and converge on ONE box —\n * `label` is the identity here too, not display text.\n *\n * Optional only for graphs generated BEFORE the caller was required; generation now fails rather\n * than emitting an `external` method without one.\n */\n caller?: ExternalSystemDeclaration;\n}\n\nexport interface RuntimeUnresolved {\n service: string;\n api: string;\n}\n\n/**\n * A system outside this repo that a service TALKS TO, drawn with a shape that says what it is\n * (a database as a cylinder, a bucket as a folder) instead of the one grey box every external\n * used to collapse into.\n *\n * Two declaration sites feed this, 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 decorator cannot go on a TS\n * `interface`, and these seams are interfaces, so JSDoc is the only marker that fits in place.\n * - **Unwrapped** — the service opens the connection itself with no contract to mark (a `pg.Pool`,\n * a TypeORM `DataSource`), so the declaration is an `external:<kind>:<identity>` nx tag on that\n * project's project.json.\n *\n * `label` is the node IDENTITY, not just display text: two services declaring `postgres` converge on\n * ONE node with two arrows into it, rather than drawing a database each.\n */\nexport interface RuntimeExternalSystem {\n kind: ExternalSystemKind;\n /** Display name AND node identity — declarations sharing a label share a node. */\n label: string;\n /** Services with a direct arrow to it. No transitive fan-out: a tag speaks only for its project. */\n usedBy: string[];\n /** Contracts flowing over it. Empty for the unwrapped (tag-declared) case — there are none. */\n apis: string[];\n}\n\n/**\n * One caller's calls into a {@link RuntimeRemoteService}, one entry per (caller, transport): `via`\n * lists the contracts it calls there.\n */\nexport interface RuntimeRemoteCall {\n from: string;\n via: string[];\n type: ApiTransport;\n}\n\n/**\n * A service OUTSIDE this workspace (another repo, a VM, partner endpoints) declared in the reviewed\n * `remoteServices` table of dependencies.json (#1203). It is a SERVICE — it implements our contracts —\n * so it is drawn as a service node, distinct from a vendor external system. Render-only, like the\n * external systems: it is never leveled or cycle-checked, because its own calls are not in this repo.\n */\nexport interface RuntimeRemoteService {\n /** Display text from the reviewed declaration; the key is the identity. */\n label?: string;\n /** The contracts it is declared to implement, as `Api (transport)`, in reviewed order. */\n implements: string[];\n /** Workspace services that target it, sorted by caller then transport. */\n calledBy: RuntimeRemoteCall[];\n}\n\nexport interface RuntimeGraph {\n services: Record<string, RuntimeService>;\n apis: Record<string, RuntimeApi>;\n runtimeEdges: RuntimeEdge[];\n unresolvedUses: RuntimeUnresolved[];\n /** `\"Api.method\"` -> the queue between its producers and its consumers. */\n queues: Record<string, RuntimeQueue>;\n /** Clock- and outside-driven entry points, sorted for determinism. */\n triggers: RuntimeTrigger[];\n /**\n * Declared external systems, keyed by identity. Optional so every graph written before this\n * existed still parses — an absent map means \"nothing declared\", which renders exactly as before.\n */\n externalSystems?: Record<string, RuntimeExternalSystem>;\n /**\n * Reviewed out-of-workspace services, keyed by service name (#1203). Absent when none are\n * declared, so every graph written before this existed parses and renders exactly as before.\n */\n remoteServices?: Record<string, RuntimeRemoteService>;\n}\n"]}
|
|
1
|
+
{"version":3,"file":"runtime-graph-model.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph-model.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;AASH,MAAa,YAAY;IACrB,KAAK,GAAG,CAAC,CAAC;IACV,YAAY,GAAG,EAAE,CAAC;CACrB;AAHD,oCAGC;AAkGD;;;;;GAKG;AACH,MAAa,gBAAiB,SAAQ,YAAY;IAC9C,SAAS,GAAG,KAAK,CAAC;IAClB,UAAU,GAAa,EAAE,CAAC;IAC1B,UAAU,GAAa,EAAE,CAAC;CAC7B;AAJD,4CAIC","sourcesContent":["/**\n * Runtime Graph model\n *\n * The serialization DTOs for architecture/runtime-dependencies.json. Split out of runtime-graph.ts,\n * which owns the DERIVATION (and had grown past the file-size limit), so the committed data shape\n * can be read on its own — it is what every consumer of the file, and any future Terraform\n * cross-check, actually programs against.\n *\n * Interfaces rather than classes: these are parsed straight out of JSON with `JSON.parse`, so a\n * class would only ever be a shape assertion over a plain object, never a constructed instance.\n */\n\nimport type {\n ApiRef,\n ApiTransport,\n ExternalSystemDeclaration,\n ExternalSystemKind,\n} from './api-usage/api-relations';\n\nexport class RuntimeLevel {\n level = 0;\n levelBecause = '';\n}\n\nexport interface RuntimeService extends RuntimeLevel {\n /**\n * The project's declared nx role — 'server' or 'client' (a library is never a node). Carried so\n * the viz can LABEL the node with what it actually is: it used to infer the role from\n * `implements.length > 0`, which labelled every relation-less server \"client\". Optional so a\n * runtime-dependencies.json written before this field existed still parses and still renders\n * exactly as it did (the visualizer falls back to the old inference).\n */\n role?: string;\n /**\n * The name clients address this service by (`new ClientConfig('helper-fsdb')`), declared in its\n * project.json. Absent for a service nothing calls by name (e.g. a browser app).\n */\n serviceName?: string;\n /**\n * The service(s) this node's clients call when the call site carries no literal `ClientConfig`,\n * declared in its project.json (metadata.webpieces.callsService). A single name, or an\n * `{ apiClassName: serviceName }` map. Absent when the node declares no target. Mirrors\n * GraphEntry.callsService; it is the CALLING-side counterpart of `serviceName`.\n */\n callsService?: string | Record<string, string>;\n implements: string[];\n /**\n * apiClassName -> the LIBRARY project whose apiRelations declared that implements, for the apis\n * this service serves through an embedded library rather than its own source (e.g. a shared\n * route-registration lib). Answers \"who implements WarmupApi, and where did that come from?\",\n * which previously required walking the dependsOn closure by hand.\n */\n implementsVia?: Record<string, string>;\n uses: string[];\n dependsOn: string[];\n /** Reviewed binding provenance and runtime conditions, retained for compact detail views. */\n wiringUses?: ApiRef[];\n wiringImplements?: ApiRef[];\n /**\n * When false, this service is hidden from the rendered runtime graph (its\n * node AND every edge touching it are omitted from the HTML/DOT). It stays\n * in runtime-dependencies.json so the data view is complete. Absent means\n * drawn (the default). Mirrors GraphEntry.drawOnGraph from the `drawOnGraph:`\n * nx tag.\n */\n drawOnGraph?: boolean;\n /**\n * The products this service belongs to (#1179), sorted: exactly its project's declared\n * `product:<name>` tags (#1216). Never derived from a runtime call or a queue; a call into another\n * product's service shows as the runtime edge, not as a product. Absent for a service whose\n * project is untagged. See graph-products.ts.\n */\n products?: string[];\n}\n\nexport interface RuntimeApi {\n implementedBy: string[];\n usedBy: string[];\n /** Transport of this API — 'rpc' (direct call) or 'pubsub' (delivered through a queue). */\n type?: ApiTransport;\n /**\n * The api-lib project that OWNS this contract. For a contract nothing in-repo implements, this\n * is the external library the calls leave the repo through (`lib-firestore`, `lib-gmail`), which\n * is what the runtime viz labels its terminal external nodes with.\n */\n owner?: string;\n /**\n * Set when the contract carries an `@externalSystem <kind> [label]` JSDoc tag — the vendor seam\n * declaring WHAT it is a seam to, so the viz can draw firestore as a database rather than as the\n * same grey box as every other external. Absent on everything else.\n */\n externalSystem?: ExternalSystemDeclaration;\n}\n\nexport interface RuntimeEdge {\n from: string;\n to: string;\n via: string[];\n /**\n * Transport of this edge. 'rpc' → a direct call arrow. 'pubsub' → the producer enqueues and the\n * consumer is delivered later, so the runtime viz draws it as producer → QUEUE → consumer.\n * Edges are split by transport, so every edge is a single kind.\n */\n type?: ApiTransport;\n /**\n * `\"ApiClassName.methodName\"` — the queue this edge flows through. Present iff `type` is\n * 'pubsub'. Queues are per METHOD, not per service pair, because that is the unit Cloud Tasks\n * (and Terraform) actually create, so two services exchanging three queued methods are three\n * queues rather than one arrow.\n */\n queue?: string;\n /**\n * True when the CALLING project carries a `cutLegacyCycle:<target>` nx tag naming this edge's\n * target — an admission that this hop closes a REAL cycle that is being tolerated as legacy\n * debt, not a claim that it is harmless. The edge is still drawn (dashed, labelled \"legacy\n * cycle\"); it is only excluded from leveling and cycle detection. Absent on every other edge.\n */\n cutLegacyCycle?: boolean;\n}\n\n/**\n * One Cloud Tasks queue: the async seam between a producer and a consumer, at METHOD granularity.\n *\n * Only exact method declarations may populate producer facts. Contract-only client wiring\n * produces contract-level edges without a method queue, regardless of the endpoint table.\n */\nexport class RuntimeQueueFlow extends RuntimeLevel {\n selfQueue = false;\n producedBy: string[] = [];\n consumedBy: string[] = [];\n}\n\nexport interface RuntimeQueue extends RuntimeQueueFlow {\n api: string;\n method: string;\n /** `@Queue(...)` override, else `${Api}-${method}` — the name Terraform must match 1:1. */\n queueName: string;\n}\n\n/**\n * An endpoint driven by something that is NOT an in-repo caller — a clock or an outside system.\n * These never appear as runtime EDGES (there is no in-repo `from`), which is exactly why they were\n * invisible until now: a nightly sweep and a GCP push subscription are real runtime entry points\n * with real Terraform behind them, and the graph showed neither.\n */\nexport interface RuntimeTrigger extends RuntimeLevel {\n /** 'cron' → a scheduler fires it; 'external' → a system outside this repo posts to it. */\n kind: 'cron' | 'external';\n api: string;\n method: string;\n /** The service that SERVES the endpoint (the arrow's head). */\n service: string;\n /** Present for 'cron': the Cloud Scheduler job / queue name Terraform must match. */\n queueName?: string;\n /**\n * Present for 'external': WHO outside this repo posts to it, declared as\n * `@Endpoint(POST, p, WRITE, EXTERNAL, { calledBy: 'twilio' })`.\n *\n * The caller is drawn separately from an outbound system with the same label: callers\n * start the flow on the top row, while outbound systems are sinks on L0.\n *\n * Optional only for graphs generated BEFORE the caller was required; generation now fails rather\n * than emitting an `external` method without one.\n */\n caller?: ExternalSystemDeclaration;\n}\n\nexport interface RuntimeUnresolved extends RuntimeLevel {\n service: string;\n api: string;\n}\n\n/**\n * A system outside this repo that a service TALKS TO, drawn with a shape that says what it is\n * (a database as a cylinder, a bucket as a folder) instead of the one grey box every external\n * used to collapse into.\n *\n * Two declaration sites feed this, 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 decorator cannot go on a TS\n * `interface`, and these seams are interfaces, so JSDoc is the only marker that fits in place.\n * - **Unwrapped** — the service opens the connection itself with no contract to mark (a `pg.Pool`,\n * a TypeORM `DataSource`), so the declaration is an `external:<kind>:<identity>` nx tag on that\n * project's project.json.\n *\n * `label` is the node IDENTITY, not just display text: two services declaring `postgres` converge on\n * ONE node with two arrows into it, rather than drawing a database each.\n */\nexport interface RuntimeExternalSystem extends RuntimeLevel {\n kind: ExternalSystemKind;\n /** Display name AND node identity — declarations sharing a label share a node. */\n label: string;\n /** Services with a direct arrow to it. No transitive fan-out: a tag speaks only for its project. */\n usedBy: string[];\n /** Contracts flowing over it. Empty for the unwrapped (tag-declared) case — there are none. */\n apis: string[];\n}\n\n/**\n * One caller's calls into a {@link RuntimeRemoteService}, one entry per (caller, transport): `via`\n * lists the contracts it calls there.\n */\nexport interface RuntimeRemoteCall {\n from: string;\n via: string[];\n type: ApiTransport;\n}\n\n/**\n * A service OUTSIDE this workspace (another repo, a VM, partner endpoints) declared in the reviewed\n * `remoteServices` table of dependencies.json (#1203). It is a SERVICE — it implements our contracts —\n * so it is drawn as a service node, distinct from a vendor external system. Its level is zero:\n * outbound calls from outside this workspace are not part of this graph.\n */\nexport interface RuntimeRemoteService extends RuntimeLevel {\n /** Display text from the reviewed declaration; the key is the identity. */\n label?: string;\n /** The contracts it is declared to implement, as `Api (transport)`, in reviewed order. */\n implements: string[];\n /** Workspace services that target it, sorted by caller then transport. */\n calledBy: RuntimeRemoteCall[];\n}\n\nexport interface RuntimeGraph {\n maxLevel: number;\n /** Contract-only pubsub flows; these do not assert any method-level producers. */\n contractQueues: Record<string, RuntimeQueueFlow>;\n services: Record<string, RuntimeService>;\n apis: Record<string, RuntimeApi>;\n runtimeEdges: RuntimeEdge[];\n unresolvedUses: RuntimeUnresolved[];\n /** `\"Api.method\"` -> the queue between its producers and its consumers. */\n queues: Record<string, RuntimeQueue>;\n /** Clock- and outside-driven entry points, sorted for determinism. */\n triggers: RuntimeTrigger[];\n /**\n * Declared external systems, keyed by identity. Optional so every graph written before this\n * existed still parses — an absent map means \"nothing declared\", which renders exactly as before.\n */\n externalSystems?: Record<string, RuntimeExternalSystem>;\n /**\n * Reviewed out-of-workspace services, keyed by service name (#1203). Absent when none are\n * declared, so every graph written before this existed parses and renders exactly as before.\n */\n remoteServices?: Record<string, RuntimeRemoteService>;\n}\n"]}
|
|
@@ -94,4 +94,4 @@ export declare function deriveRuntimeGraph(projects: EnhancedGraph, hiddenProjec
|
|
|
94
94
|
* file that records its own doubts would just get committed and stop being read.
|
|
95
95
|
*/
|
|
96
96
|
export declare function deriveRuntimeGraphReport(projects: EnhancedGraph, hiddenProjects?: Set<string>, apiContracts?: ApiContracts, externalSystems?: ExternalSystemDecls, remoteServices?: RemoteServices): RuntimeGraphReport;
|
|
97
|
-
export { runtimeAdjacency } from './runtime-
|
|
97
|
+
export { runtimeAdjacency } from './runtime-node-levels';
|
package/src/lib/runtime-graph.js
CHANGED
|
@@ -39,6 +39,7 @@ const external_systems_1 = require("./api-usage/external-systems");
|
|
|
39
39
|
const graph_products_1 = require("./graph-products");
|
|
40
40
|
const runtime_remote_calls_1 = require("./runtime-remote-calls");
|
|
41
41
|
const runtime_graph_levels_1 = require("./runtime-graph-levels");
|
|
42
|
+
const runtime_node_levels_1 = require("./runtime-node-levels");
|
|
42
43
|
// Persistence lives in runtime-graph-io.ts; re-exported for the same reason as the model types.
|
|
43
44
|
var runtime_graph_io_1 = require("./runtime-graph-io");
|
|
44
45
|
Object.defineProperty(exports, "DEFAULT_RUNTIME_GRAPH_PATH", { enumerable: true, get: function () { return runtime_graph_io_1.DEFAULT_RUNTIME_GRAPH_PATH; } });
|
|
@@ -172,6 +173,8 @@ class RuntimeGraphDeriver {
|
|
|
172
173
|
for (const api of Array.from(apis.keys()).sort())
|
|
173
174
|
apisObj[api] = apis.get(api);
|
|
174
175
|
const graph = {
|
|
176
|
+
maxLevel: 0,
|
|
177
|
+
contractQueues: {},
|
|
175
178
|
services,
|
|
176
179
|
apis: apisObj,
|
|
177
180
|
runtimeEdges: edgeResult.edges,
|
|
@@ -188,6 +191,7 @@ class RuntimeGraphDeriver {
|
|
|
188
191
|
const systems = (0, external_systems_1.resolveExternalSystems)(this.externalSystemDecls, services);
|
|
189
192
|
(0, external_systems_1.attachExternalSystems)(graph, systems);
|
|
190
193
|
this.remoteCalls.attach(graph);
|
|
194
|
+
new runtime_node_levels_1.RuntimeNodeLevels().apply(graph);
|
|
191
195
|
return new RuntimeGraphReport(graph, this.warnings, this.problems, [...this.autoHidden].sort());
|
|
192
196
|
}
|
|
193
197
|
/**
|
|
@@ -205,6 +209,8 @@ class RuntimeGraphDeriver {
|
|
|
205
209
|
if (method.kind !== 'cron' && method.kind !== 'external')
|
|
206
210
|
continue;
|
|
207
211
|
const trigger = {
|
|
212
|
+
level: 0,
|
|
213
|
+
levelBecause: '',
|
|
208
214
|
kind: method.kind,
|
|
209
215
|
api: ref.api,
|
|
210
216
|
method: method.name,
|
|
@@ -369,7 +375,12 @@ class RuntimeGraphDeriver {
|
|
|
369
375
|
if (implementers.length === 0) {
|
|
370
376
|
// Nobody in-repo serves it. For a vendor contract that is the ANSWER, not a gap:
|
|
371
377
|
// the call leaves the repo, and the viz terminates it at a dashed vendor node.
|
|
372
|
-
unresolved.push({
|
|
378
|
+
unresolved.push({
|
|
379
|
+
level: 0,
|
|
380
|
+
levelBecause: 'outbound sink',
|
|
381
|
+
service: decl.name,
|
|
382
|
+
api: ref.api,
|
|
383
|
+
});
|
|
373
384
|
continue;
|
|
374
385
|
}
|
|
375
386
|
for (const target of this.targetsFor(decl.name, ref, implementers)) {
|
|
@@ -507,6 +518,7 @@ class RuntimeGraphDeriver {
|
|
|
507
518
|
// the committed JSON stays clean AND deterministic without conditional assembly.
|
|
508
519
|
const service = {
|
|
509
520
|
level: 0,
|
|
521
|
+
levelBecause: '',
|
|
510
522
|
role: this.projects[decl.name]?.role, // labels a relation-less node for what it IS
|
|
511
523
|
serviceName: this.projects[decl.name]?.serviceName,
|
|
512
524
|
callsService: this.projects[decl.name]?.callsService,
|
|
@@ -532,9 +544,6 @@ class RuntimeGraphDeriver {
|
|
|
532
544
|
this.autoHidden.push(decl.name);
|
|
533
545
|
}
|
|
534
546
|
}
|
|
535
|
-
const levels = (0, runtime_graph_levels_1.assignLevels)((0, runtime_graph_levels_1.adjacencyFromEdges)(Object.keys(services), edges));
|
|
536
|
-
for (const name of Object.keys(services))
|
|
537
|
-
services[name].level = levels[name] ?? 0;
|
|
538
547
|
return services;
|
|
539
548
|
}
|
|
540
549
|
}
|
|
@@ -560,6 +569,6 @@ function deriveRuntimeGraphReport(projects, hiddenProjects = new Set(), apiContr
|
|
|
560
569
|
}
|
|
561
570
|
// Levels + adjacency live in runtime-graph-levels.ts; re-exported for the same reason the model types
|
|
562
571
|
// and the io helpers above are — one obvious place to import the runtime graph from.
|
|
563
|
-
var
|
|
564
|
-
Object.defineProperty(exports, "runtimeAdjacency", { enumerable: true, get: function () { return
|
|
572
|
+
var runtime_node_levels_2 = require("./runtime-node-levels");
|
|
573
|
+
Object.defineProperty(exports, "runtimeAdjacency", { enumerable: true, get: function () { return runtime_node_levels_2.runtimeAdjacency; } });
|
|
565
574
|
//# sourceMappingURL=runtime-graph.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runtime-graph.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Runtime Graph\n *\n * Derives the runtime microservice graph SOLELY from architecture/dependencies.json\n * (the single source of truth): each project's `apiRelations` carry which api\n * classes it implements/uses and their transport (rpc | pubsub). Both\n * `architecture:generate` and `architecture:validate-runtime-architecture` call\n * the SAME `deriveRuntimeGraph`, so the committed graph and the validated graph\n * can never diverge.\n *\n * The runtime edge Z -> X (Z depends on X at runtime) is INFERRED: Z `uses` api\n * Y and X `implements` api Y. This edge does not exist in the compile-time\n * dependencies.json (both Z and X only compile-depend on the api library Y).\n *\n * WHICH X is decided by the call site, not by \"everyone who implements Y\", in\n * priority order: (1) a literal at the call site —\n * `createRpcClient(Y, new ClientConfig('helper-fsdb'))` — kept as\n * `ApiRef.targetService`; else (2) the calling project's declared `callsService`\n * (project.json metadata.webpieces.callsService), the symmetric half of\n * `serviceName` for the shared-library case where the client is built once from a\n * config field so no literal can sit at the call site; else (3) fan-out. A named\n * target from (1) or (2) is matched against each node's DECLARED `serviceName`.\n * Fanning an edge out to every implementer is catastrophic for a company-wide\n * contract registered in a shared library — it manufactures calls that cannot\n * happen, and cycles that do not exist. When a target cannot be resolved the old\n * fan-out still happens, but a warning names the call site (see\n * RuntimeGraphReport.warnings): a wrong-but-green graph is worse than a failing\n * one, so it must never degrade silently.\n */\n\nimport { sortGraphTopologically } from './graph-sorter';\nimport type { EnhancedGraph, GraphEntry } from './graph-sorter';\nimport type {\n ApiContracts,\n ApiMethodMeta,\n ApiRef,\n ApiTransport,\n ExternalSystemDecls,\n} from './api-usage/api-relations';\nimport { sortApiRefs } from './api-usage/api-relations';\nimport { RelationSink } from './runtime-graph-decls';\nimport type { ScanDecl } from './runtime-graph-decls';\nimport { dedupApiRefs, sortedRecord, sortUnresolved } from './runtime-graph-sorters';\nimport { attachExternalSystems, resolveExternalSystems } from './api-usage/external-systems';\nimport { RuntimeProductMembership } from './graph-products';\nimport { RemoteCallCollector } from './runtime-remote-calls';\nimport type { RemoteServices } from './remote-services';\nimport type {\n RuntimeApi,\n RuntimeEdge,\n RuntimeExternalSystem,\n RuntimeGraph,\n RuntimeQueue,\n RuntimeService,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\nimport { toError } from '../toError';\n\n// The committed data shape lives in runtime-graph-model.ts; re-exported here so every existing\n// `from './runtime-graph'` import keeps working and there is still one obvious place to import from.\nexport type {\n RuntimeApi,\n RuntimeEdge,\n RuntimeExternalSystem,\n RuntimeGraph,\n RuntimeQueue,\n RuntimeRemoteCall,\n RuntimeRemoteService,\n RuntimeService,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\nimport { adjacencyFromEdges, applyCycleCuts, assignLevels } from './runtime-graph-levels';\n\n// Persistence lives in runtime-graph-io.ts; re-exported for the same reason as the model types.\nexport {\n DEFAULT_RUNTIME_GRAPH_PATH,\n saveRuntimeGraph,\n runtimeGraphFileExists,\n loadRuntimeGraph,\n serializeRuntimeGraph,\n} from './runtime-graph-io';\n\ninterface EdgeResult {\n edges: RuntimeEdge[];\n unresolved: RuntimeUnresolved[];\n queues: Record<string, RuntimeQueue>;\n}\n\n/**\n * The derived graph PLUS everything the derivation had to guess at. `warnings` is deliberately not\n * part of RuntimeGraph: it is not committed data, it is the report that stops a guessed edge from\n * passing for a derived one. Executors print it.\n */\nexport class RuntimeGraphReport {\n constructor(\n public readonly graph: RuntimeGraph,\n /** Human-readable lines naming every call site whose target the graph could not pin down. */\n public readonly warnings: string[],\n /**\n * Call sites that name a target the repo does not contain. Unlike a warning these FAIL the\n * build: the contract IS served in-repo, so the name is a typo or a stale rename, and the\n * only reason it stayed invisible is that the graph quietly fanned the edge out instead.\n * (A call to a service outside the repo never reaches here — nothing in-repo implements its\n * contract, so it is `unresolvedUses`.)\n */\n public readonly problems: string[] = [],\n /**\n * role:server nodes omitted from the DRAWING because they declare no webpieces runtime\n * package anywhere in their library closure and serve/call nothing in-repo. Neither a\n * warning nor a problem — this is intended behavior, and shouting on every clean run is how\n * warnings stop being read. Deliberately NOT persisted: a graph file that records its own\n * omissions gets committed and stops being read. Executors print it instead, so the\n * omission is always visible and never silent.\n */\n public readonly autoHidden: string[] = [],\n ) {}\n}\n\n/**\n * Derives the runtime microservice graph from architecture/dependencies.json `apiRelations`:\n * implementers × users per API, split by transport. An rpc edge is a direct call; a pubsub edge flows\n * through a queue (drawn producer → queue → consumer by the visualizer).\n *\n * Nodes are the runnable apps only — role:server / role:client. A library is NEVER a node; its\n * implements/uses (a shared client-factory or route-registration lib) are attributed to the\n * servers/clients that embed it, walked transitively over dependsOn (see collectDecls).\n */\nclass RuntimeGraphDeriver {\n /** apiClassName -> the api-lib project that owns the contract (from the apiRelations key). */\n private readonly apiOwners = new Map<string, string>();\n /** Addressable name -> the runtime node answering to it; how a targeted call resolves. */\n private readonly nodeByServiceName = new Map<string, string>();\n private readonly warnings: string[] = [];\n private readonly problems: string[] = [];\n /** role:server nodes hidden by isNonParticipantServer, in the order buildServices met them. */\n private readonly autoHidden: string[] = [];\n /** Uses aimed at a reviewed out-of-workspace service (#1203); built once the names are claimed. */\n private readonly remoteCalls: RemoteCallCollector;\n\n /**\n * False when NO project in the graph carries `webpiecesRuntime`, i.e. the file was written\n * before the field existed. Auto-hiding is then off ENTIRELY, so an old dependencies.json\n * renders exactly as it did — absence of the field must never be read as \"declares none\".\n * Any repo regenerated after this change has at least one webpieces project, so the guard\n * cannot wrongly suppress the feature.\n */\n private readonly markersKnown: boolean;\n\n constructor(\n private readonly projects: EnhancedGraph,\n /** Project names tagged drawOnGraph:false — kept in the JSON but flagged so the viz omits them. */\n private readonly hiddenProjects: Set<string>,\n /**\n * The per-contract method tables, read from the generated `architecture/apis/<Api>.json`\n * files (ApiContractFiles). Pubsub edges remain at the contract level, independently\n * of method metadata: an endpoint table does not declare which methods a client uses.\n */\n private readonly apiContracts: ApiContracts = {},\n /**\n * Declared external systems from dependencies.json, keyed by identity. Empty means nothing\n * was declared, which renders exactly as it did before declarations existed.\n */\n private readonly externalSystemDecls: ExternalSystemDecls = {},\n /** Reviewed out-of-workspace services from dependencies.json `remoteServices` (#1203). */\n remoteServices: RemoteServices = {},\n ) {\n this.markersKnown = Object.values(projects).some(\n (entry: GraphEntry) => entry.webpiecesRuntime !== undefined,\n );\n // A node ALWAYS answers to its own module name, so a repo whose deployed names match its\n // project names needs no declaration at all — and no alias can ever redirect 'ai-chat' away\n // from the ai-chat module. Module names are therefore claimed FIRST and are unshadowable.\n for (const name of Object.keys(projects).sort()) {\n if (this.isNode(name)) this.nodeByServiceName.set(name, name);\n }\n // `serviceName` then adds the alias a repo needs when its deployed name is NOT its module\n // name (helper-svr serving 'helper-portal'). Colliding with a module is a misconfiguration,\n // not a precedence question: the alias is silently unreachable, so say so.\n for (const name of Object.keys(projects).sort()) {\n const declared = projects[name].serviceName;\n if (!this.isNode(name) || declared === undefined) continue;\n const claimant = this.nodeByServiceName.get(declared);\n if (claimant === undefined) {\n this.nodeByServiceName.set(declared, name);\n } else if (claimant !== name) {\n this.problems.push(\n `${name} declares serviceName '${declared}', but that is already the module name of ` +\n `${claimant} — the alias can never be reached. Rename one of them.`,\n );\n }\n }\n this.remoteCalls = new RemoteCallCollector(\n remoteServices,\n (name: string): boolean => this.nodeByServiceName.has(name),\n this.problems,\n );\n }\n\n assemble(): RuntimeGraphReport {\n const decls = this.collectDecls();\n const apis = this.buildApis(decls);\n const edgeResult = this.buildEdges(decls, apis);\n // Cuts are stamped BEFORE buildServices, which is where leveling reads the edges.\n this.problems.push(\n ...applyCycleCuts(edgeResult.edges, this.projects, this.nodeByServiceName),\n );\n const services = this.buildServices(decls, edgeResult.edges);\n const apisObj: Record<string, RuntimeApi> = {};\n for (const api of Array.from(apis.keys()).sort()) apisObj[api] = apis.get(api)!;\n const graph: RuntimeGraph = {\n services,\n apis: apisObj,\n runtimeEdges: edgeResult.edges,\n unresolvedUses: edgeResult.unresolved,\n queues: edgeResult.queues,\n triggers: this.buildTriggers(decls),\n };\n // #1179/#1216: each service's products are exactly its project's declared product tags.\n new RuntimeProductMembership().apply(graph, this.projects);\n // A destination whose address arrives at RUNTIME is declared on its CONTRACT\n // (`@externalSystem runtime partner-webhooks`), so it needs no branch of its own here: it is\n // resolved by the same call every other declared system is, converges on one node when two\n // services deliver over the same contract, and is watched by the same drift check.\n const systems = resolveExternalSystems(this.externalSystemDecls, services);\n attachExternalSystems(graph, systems);\n this.remoteCalls.attach(graph);\n return new RuntimeGraphReport(\n graph,\n this.warnings,\n this.problems,\n [...this.autoHidden].sort(),\n );\n }\n\n /**\n * The clock- and outside-driven entry points: for every contract a node IMPLEMENTS, each method\n * declared `cron` or `external` becomes a trigger pointing AT that node.\n *\n * Driven off `implements` rather than `uses` on purpose — these have no in-repo caller at all,\n * which is why they never produced an edge and stayed invisible.\n */\n private buildTriggers(decls: ScanDecl[]): RuntimeTrigger[] {\n const triggers: RuntimeTrigger[] = [];\n for (const decl of decls) {\n for (const ref of decl.implementsApis) {\n for (const method of this.methodsOf(ref.api)) {\n if (method.kind !== 'cron' && method.kind !== 'external') continue;\n const trigger: RuntimeTrigger = {\n kind: method.kind,\n api: ref.api,\n method: method.name,\n service: decl.name,\n };\n if (method.kind === 'cron') trigger.queueName = method.queueName;\n // ONLY for 'external' (a clock needs no vendor name), and only when declared —\n // a graph committed before callers were required has none and must still render.\n if (method.kind === 'external' && method.caller !== undefined)\n trigger.caller = method.caller;\n triggers.push(trigger);\n }\n }\n }\n return triggers.sort(\n (a: RuntimeTrigger, b: RuntimeTrigger) =>\n a.kind.localeCompare(b.kind) ||\n a.api.localeCompare(b.api) ||\n a.method.localeCompare(b.method) ||\n a.service.localeCompare(b.service),\n );\n }\n\n /** The committed method table for a contract, or [] when it declares none (e.g. a vendor seam). */\n private methodsOf(api: string): ApiMethodMeta[] {\n return this.apiContracts[api]?.methods ?? [];\n }\n\n /**\n * One ScanDecl per RUNTIME NODE (role:server / role:client). A node's effective\n * relations are its OWN apiRelations PLUS those of every library in its transitive\n * dependsOn closure: a lib that calls createRpcClient/addRoutes runs inside the\n * server/client that embeds it, so the relation is attributed to that node — the\n * lib is never a runtime node. Recursion stops at other nodes (each server/client\n * owns its own relations). dependsOn is transitively reduced, but reduction\n * preserves reachability, so the recursive walk still reaches every lib a node\n * embeds through libraries.\n */\n private collectDecls(): ScanDecl[] {\n const decls: ScanDecl[] = [];\n for (const name of Object.keys(this.projects).sort()) {\n if (!this.isNode(name)) continue;\n const sink = new RelationSink(name);\n this.collectEffectiveRelations(name, sink, new Set<string>([name]));\n // EVERY node gets a decl, including one with zero relations. Gating this on \"has at\n // least one implements/uses\" silently DELETED any deployed server whose work arrives\n // from outside the repo (a pull subscription): the diagram looked complete while missing\n // a running service — confidently wrong, not visibly incomplete. isNode() is the whole\n // test; drawOnGraph:false is the ONE opt-out, and such a node renders as an isolated box.\n decls.push({\n name,\n implementsApis: dedupApiRefs(sortApiRefs(sink.implementsApis)),\n usesApis: dedupApiRefs(sortApiRefs(sink.usesApis)),\n implementsVia: sink.implementsVia,\n markerVia: sink.markerVia,\n });\n }\n return decls;\n }\n\n /** True when a project is a runtime node — a server or client app (never a library). */\n private isNode(name: string): boolean {\n const role = this.projects[name]?.role;\n return role === 'server' || role === 'client';\n }\n\n /**\n * True for a role:server that speaks NO webpieces runtime package anywhere in its library\n * closure AND serves/calls no in-repo contract — a legacy Express/NestJS service sitting in the\n * same monorepo. This graph is built entirely out of webpieces contracts, so such a service can\n * only ever draw as a disconnected box: it has no edges by construction and never will.\n *\n * It stays a NODE and stays in runtime-dependencies.json (drawOnGraph:false), which is the whole\n * difference between hiding it and the silent deletion #542 removed — the data view still shows\n * a deployed role:server, only the picture drops it. Removing it from isNode() instead would\n * also drop it from nodeByServiceName, so a targeted ClientConfig('orders-manager') from a drawn\n * service would stop resolving and decay into an unresolved use: a graph that got quietly WORSE\n * while looking cleaner. The COMPILE-TIME graph (dependencies.html) still draws these projects;\n * they genuinely exist, and only the runtime drawing claims to show webpieces services.\n *\n * role:client is deliberately never auto-hidden: a browser app can legitimately declare no\n * marker at all, and nothing checks clients for presence the way checkServersPresent checks\n * servers, so there is no equivalent failure to prevent.\n */\n private isNonParticipantServer(decl: ScanDecl): boolean {\n if (!this.markersKnown) return false;\n if (this.projects[decl.name]?.role !== 'server') return false;\n if (decl.markerVia.size > 0) return false;\n // Belt-and-braces: serving or calling an in-repo contract IS participation, whatever the\n // package list says. This is what keeps a mechanism we did not anticipate from being blanked.\n return decl.implementsApis.length === 0 && decl.usesApis.length === 0;\n }\n\n /**\n * Accumulate `name`'s own api relations, then recurse through its LIBRARY deps\n * (skipping other nodes, which own their own relations). `visited` guards against\n * re-walking a lib reachable by more than one path (and any cycle).\n */\n private collectEffectiveRelations(\n name: string,\n sink: RelationSink,\n visited: Set<string>,\n includeRelations: boolean = true,\n ): void {\n const entry = this.projects[name];\n if (entry === undefined) return;\n sink.addMarkers(entry.webpiecesRuntime, name);\n const relations = entry.apiRelations;\n if (includeRelations && relations !== undefined) {\n for (const owner of Object.keys(relations).sort()) {\n for (const ref of relations[owner].implements) {\n this.apiOwners.set(ref.api, owner);\n sink.addImplements(ref, ref.declaredVia ?? name);\n }\n for (const ref of relations[owner].uses) {\n this.apiOwners.set(ref.api, owner);\n sink.addUses(ref);\n }\n }\n }\n for (const dep of entry.dependsOn) {\n if (visited.has(dep)) continue;\n visited.add(dep);\n if (this.isNode(dep)) continue; // another server/client owns its own relations\n this.collectEffectiveRelations(\n dep,\n sink,\n visited,\n includeRelations && entry.runtimeComposition !== true,\n );\n }\n }\n\n /** apiClassName -> { implementedBy, usedBy, type }. */\n private buildApis(decls: ScanDecl[]): Map<string, RuntimeApi> {\n const apis = new Map<string, RuntimeApi>();\n const ensure = (api: string, type: ApiTransport): RuntimeApi => {\n let entry = apis.get(api);\n if (!entry) {\n entry = { implementedBy: [], usedBy: [], type };\n apis.set(api, entry);\n }\n return entry;\n };\n for (const decl of decls) {\n for (const ref of decl.implementsApis)\n ensure(ref.api, ref.type).implementedBy.push(decl.name);\n for (const ref of decl.usesApis) ensure(ref.api, ref.type).usedBy.push(decl.name);\n }\n for (const api of apis.keys()) {\n const entry = apis.get(api)!;\n entry.implementedBy.sort();\n entry.usedBy = Array.from(new Set(entry.usedBy)).sort();\n const owner = this.apiOwners.get(api);\n if (owner !== undefined) entry.owner = owner;\n }\n return apis;\n }\n\n /** Inferred edges U -> I via api A, split by transport; plus uses with no implementer. */\n private buildEdges(decls: ScanDecl[], apis: Map<string, RuntimeApi>): EdgeResult {\n const viaByKey = new Map<string, Set<string>>();\n const unresolved: RuntimeUnresolved[] = [];\n for (const decl of decls) {\n for (const ref of decl.usesApis) {\n // A named target outside this workspace (#1203) is a remote service, not an implementer\n // to resolve among the nodes and not an unresolved vendor call.\n const wanted = ref.targetService ?? this.callsServiceFor(decl.name, ref.api);\n if (this.remoteCalls.claim(decl.name, ref, wanted)) continue;\n const implementers = apis.get(ref.api)?.implementedBy ?? [];\n if (implementers.length === 0) {\n // Nobody in-repo serves it. For a vendor contract that is the ANSWER, not a gap:\n // the call leaves the repo, and the viz terminates it at a dashed vendor node.\n unresolved.push({ service: decl.name, api: ref.api });\n continue;\n }\n for (const target of this.targetsFor(decl.name, ref, implementers)) {\n // A service calling ITSELF synchronously is noise; a service ENQUEUEING to\n // itself is a real, common topology (deferring its own work), and dropping it\n // was hiding the single most interesting thing about a queue.\n if (target === decl.name && ref.type !== 'pubsub') continue;\n const key = `${decl.name} ${target} ${ref.type}`;\n if (!viaByKey.has(key)) viaByKey.set(key, new Set());\n viaByKey.get(key)!.add(ref.api);\n }\n }\n }\n return {\n edges: this.edgesFromKeys(viaByKey),\n unresolved: sortUnresolved(unresolved),\n // Client wiring declares contracts, never the methods a producer enqueues.\n queues: {},\n };\n }\n\n /**\n * WHICH implementers this one `uses` reaches. Resolution order (most specific wins):\n * 1. a literal `ClientConfig` at the call site (`ref.targetService`) — names ONE node;\n * 2. else the calling project's declared `callsService` (project.json) — for the shared-library\n * case where the literal cannot sit at the call site, it lives one indirection away in the app;\n * 3. else the historical fan-out — the only safe superset — recording WHY, so a fanned-out (i.e.\n * possibly fictional) edge can never pass for a derived one.\n * A named target (1 or 2) that resolves to no module, or to a module that does not serve the\n * contract, is a PROBLEM (fails the build), never a silent drop.\n */\n private targetsFor(user: string, ref: ApiRef, implementers: string[]): string[] {\n const literal = ref.targetService;\n if (literal !== undefined)\n return this.resolveNamedTarget(user, ref, implementers, literal, 'literal');\n\n const declared = this.callsServiceFor(user, ref.api);\n if (declared !== undefined)\n return this.resolveNamedTarget(user, ref, implementers, declared, 'callsService');\n\n return this.untargetedFanOut(user, ref, implementers);\n }\n\n /**\n * The declared `callsService` target for this node's use of `api`, or undefined when none applies.\n * A string declaration aims every untargeted use at one service; a map aims per api-class.\n */\n private callsServiceFor(user: string, api: string): string | undefined {\n const declared = this.projects[user]?.callsService;\n if (declared === undefined) return undefined;\n if (typeof declared === 'string') return declared;\n return declared[api];\n }\n\n /**\n * Resolve a NAMED target (from a call-site literal or a project-level `callsService`) to the one\n * module that serves it. A name no module answers to, or a module that does not serve the\n * contract, fails the build — the wording names which declaration to fix, `source` deciding it.\n */\n private resolveNamedTarget(\n user: string,\n ref: ApiRef,\n implementers: string[],\n wanted: string,\n source: 'literal' | 'callsService',\n ): string[] {\n const node = this.nodeByServiceName.get(wanted);\n const served = `${implementers.length} module(s) serve this contract (${implementers.join(', ')})`;\n if (node === undefined) {\n this.problems.push(\n source === 'callsService'\n ? `${user} declares metadata.webpieces.callsService '${wanted}' in its project.json, but NO ` +\n `module answers to that name. ${served}. Point callsService at a module name, or declare ` +\n `metadata.webpieces.serviceName: '${wanted}' on the module that serves \"${ref.api}\".`\n : `${user} calls \"${ref.api}\" at service '${wanted}', but NO module answers to that name. ` +\n `${served}. Either use the module name, or declare metadata.webpieces.serviceName: ` +\n `'${wanted}' on the module that serves it (and translate any environment prefix in ` +\n `ClientRegistry.setDeriver, not here).`,\n );\n return implementers;\n }\n if (!implementers.includes(node)) {\n const via =\n source === 'callsService'\n ? `${user} declares callsService '${wanted}' (module ${node})`\n : `${user} calls \"${ref.api}\" at service '${wanted}' (module ${node})`;\n this.problems.push(\n `${via}, but ${node} does NOT serve \"${ref.api}\" — ${implementers.join(', ')} do. At runtime that ` +\n `call has nothing to answer it.`,\n );\n return implementers;\n }\n return [node];\n }\n\n /**\n * A use with no literal client config. One implementer is unambiguous, so it stays silent; more\n * than one means every edge but one is fiction — exactly the failure this mechanism exists to\n * stop — so it is reported even though the graph still (conservatively) draws them all.\n */\n private untargetedFanOut(user: string, ref: ApiRef, implementers: string[]): string[] {\n if (implementers.length > 1) {\n this.warnings.push(\n `${user} uses \"${ref.api}\" with no literal client config, and ${implementers.length} services ` +\n `implement it (${implementers.join(', ')}) — an edge is drawn to EVERY one, so all but one ` +\n `are fiction. Name the target: createRpcClient(${ref.api}, new ClientConfig('<serviceName>', ClientRole.SERVER)); ` +\n `or, when the client is built in a shared library (no literal can sit at the call site), ` +\n `declare metadata.webpieces.callsService: '<serviceName>' on ${user}'s project.json.`,\n );\n }\n return implementers;\n }\n\n /**\n * Rebuild edges from their `\"from to type queue\"` keys. The 4th part is the queue (\"Api.method\")\n * and is empty for every non-queued edge; it is part of the KEY so two queued methods between\n * the same pair stay two edges instead of collapsing into one.\n */\n private edgesFromKeys(viaByKey: Map<string, Set<string>>): RuntimeEdge[] {\n const edges: RuntimeEdge[] = [];\n for (const key of viaByKey.keys()) {\n const parts = key.split(' ');\n const edge: RuntimeEdge = {\n from: parts[0],\n to: parts[1],\n via: Array.from(viaByKey.get(key)!).sort(),\n type: parts[2] as ApiTransport,\n };\n if (parts[3] !== undefined && parts[3] !== '') edge.queue = parts[3];\n edges.push(edge);\n }\n edges.sort(\n (a: RuntimeEdge, b: RuntimeEdge) =>\n a.from.localeCompare(b.from) ||\n a.to.localeCompare(b.to) ||\n (a.type ?? '').localeCompare(b.type ?? '') ||\n (a.queue ?? '').localeCompare(b.queue ?? ''),\n );\n return edges;\n }\n\n private buildServices(decls: ScanDecl[], edges: RuntimeEdge[]): Record<string, RuntimeService> {\n const services: Record<string, RuntimeService> = {};\n for (const decl of decls) {\n // A queued hop depends on the QUEUE, not on the peer: the producer hands off and returns,\n // so naming the consumer here would assert a coupling that does not exist (and would make\n // a service that defers work to itself look self-dependent).\n const dependsOn = Array.from(\n new Set(\n edges\n .filter((e: RuntimeEdge) => e.from === decl.name)\n .flatMap((e: RuntimeEdge) =>\n e.type === 'pubsub'\n ? e.via.map((api) => `queue:${e.queue ?? api}`)\n : [e.to],\n ),\n ),\n ).sort();\n // Keys are written in this order; an undefined value is omitted by JSON.stringify, so\n // the committed JSON stays clean AND deterministic without conditional assembly.\n const service: RuntimeService = {\n level: 0,\n role: this.projects[decl.name]?.role, // labels a relation-less node for what it IS\n serviceName: this.projects[decl.name]?.serviceName,\n callsService: this.projects[decl.name]?.callsService,\n implements: decl.implementsApis.map((r: ApiRef) => r.api),\n implementsVia:\n decl.implementsVia.size > 0 ? sortedRecord(decl.implementsVia) : undefined,\n // One api used against two services is two refs but ONE api in this list.\n uses: Array.from(new Set(decl.usesApis.map((r: ApiRef) => r.api))),\n dependsOn,\n wiringUses: decl.usesApis.some((ref) => ref.declaredVia !== undefined)\n ? decl.usesApis\n : undefined,\n wiringImplements: decl.implementsApis.some((ref) => ref.declaredVia !== undefined)\n ? decl.implementsApis\n : undefined,\n };\n services[decl.name] = service;\n // The explicit tag wins and is NOT reported as auto-hidden — somebody asked for it.\n if (this.hiddenProjects.has(decl.name)) {\n service.drawOnGraph = false;\n } else if (this.isNonParticipantServer(decl)) {\n service.drawOnGraph = false;\n this.autoHidden.push(decl.name);\n }\n }\n const levels = assignLevels(adjacencyFromEdges(Object.keys(services), edges));\n for (const name of Object.keys(services)) services[name].level = levels[name] ?? 0;\n return services;\n }\n}\n\n/**\n * Derive the runtime graph from dependencies.json's project `apiRelations` — the\n * single source of truth shared by generate + validate. `hiddenProjects`\n * (drawOnGraph:false, defaults to none) are kept in the graph but flagged so the\n * runtime visualizer omits their nodes + edges.\n */\n// webpieces-disable no-function-outside-class -- module entry point for the runtime graph derivation\nexport function deriveRuntimeGraph(\n projects: EnhancedGraph,\n hiddenProjects: Set<string> = new Set<string>(),\n apiContracts: ApiContracts = {},\n): RuntimeGraph {\n return deriveRuntimeGraphReport(projects, hiddenProjects, apiContracts).graph;\n}\n\n/**\n * The same derivation, plus the warnings it produced (every edge it had to GUESS at). Executors use\n * this form and print the warnings; `deriveRuntimeGraph` is the convenience form for callers that\n * only want the data. The warnings are deliberately kept OUT of runtime-dependencies.json — a graph\n * file that records its own doubts would just get committed and stop being read.\n */\n// webpieces-disable no-function-outside-class -- module entry point for the runtime graph derivation\nexport function deriveRuntimeGraphReport(\n projects: EnhancedGraph,\n hiddenProjects: Set<string> = new Set<string>(),\n apiContracts: ApiContracts = {},\n externalSystems: ExternalSystemDecls = {},\n remoteServices: RemoteServices = {},\n): RuntimeGraphReport {\n return new RuntimeGraphDeriver(\n projects,\n hiddenProjects,\n apiContracts,\n externalSystems,\n remoteServices,\n ).assemble();\n}\n\n// Levels + adjacency live in runtime-graph-levels.ts; re-exported for the same reason the model types\n// and the io helpers above are — one obvious place to import the runtime graph from.\nexport { runtimeAdjacency } from './runtime-graph-levels';\n"]}
|
|
1
|
+
{"version":3,"file":"runtime-graph.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-graph.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Runtime Graph\n *\n * Derives the runtime microservice graph SOLELY from architecture/dependencies.json\n * (the single source of truth): each project's `apiRelations` carry which api\n * classes it implements/uses and their transport (rpc | pubsub). Both\n * `architecture:generate` and `architecture:validate-runtime-architecture` call\n * the SAME `deriveRuntimeGraph`, so the committed graph and the validated graph\n * can never diverge.\n *\n * The runtime edge Z -> X (Z depends on X at runtime) is INFERRED: Z `uses` api\n * Y and X `implements` api Y. This edge does not exist in the compile-time\n * dependencies.json (both Z and X only compile-depend on the api library Y).\n *\n * WHICH X is decided by the call site, not by \"everyone who implements Y\", in\n * priority order: (1) a literal at the call site —\n * `createRpcClient(Y, new ClientConfig('helper-fsdb'))` — kept as\n * `ApiRef.targetService`; else (2) the calling project's declared `callsService`\n * (project.json metadata.webpieces.callsService), the symmetric half of\n * `serviceName` for the shared-library case where the client is built once from a\n * config field so no literal can sit at the call site; else (3) fan-out. A named\n * target from (1) or (2) is matched against each node's DECLARED `serviceName`.\n * Fanning an edge out to every implementer is catastrophic for a company-wide\n * contract registered in a shared library — it manufactures calls that cannot\n * happen, and cycles that do not exist. When a target cannot be resolved the old\n * fan-out still happens, but a warning names the call site (see\n * RuntimeGraphReport.warnings): a wrong-but-green graph is worse than a failing\n * one, so it must never degrade silently.\n */\n\nimport { sortGraphTopologically } from './graph-sorter';\nimport type { EnhancedGraph, GraphEntry } from './graph-sorter';\nimport type {\n ApiContracts,\n ApiMethodMeta,\n ApiRef,\n ApiTransport,\n ExternalSystemDecls,\n} from './api-usage/api-relations';\nimport { sortApiRefs } from './api-usage/api-relations';\nimport { RelationSink } from './runtime-graph-decls';\nimport type { ScanDecl } from './runtime-graph-decls';\nimport { dedupApiRefs, sortedRecord, sortUnresolved } from './runtime-graph-sorters';\nimport { attachExternalSystems, resolveExternalSystems } from './api-usage/external-systems';\nimport { RuntimeProductMembership } from './graph-products';\nimport { RemoteCallCollector } from './runtime-remote-calls';\nimport type { RemoteServices } from './remote-services';\nimport type {\n RuntimeApi,\n RuntimeEdge,\n RuntimeExternalSystem,\n RuntimeGraph,\n RuntimeQueue,\n RuntimeService,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\nimport { toError } from '../toError';\n\n// The committed data shape lives in runtime-graph-model.ts; re-exported here so every existing\n// `from './runtime-graph'` import keeps working and there is still one obvious place to import from.\nexport type {\n RuntimeApi,\n RuntimeEdge,\n RuntimeExternalSystem,\n RuntimeGraph,\n RuntimeQueue,\n RuntimeRemoteCall,\n RuntimeRemoteService,\n RuntimeService,\n RuntimeTrigger,\n RuntimeUnresolved,\n} from './runtime-graph-model';\nimport { applyCycleCuts } from './runtime-graph-levels';\nimport { RuntimeNodeLevels } from './runtime-node-levels';\n\n// Persistence lives in runtime-graph-io.ts; re-exported for the same reason as the model types.\nexport {\n DEFAULT_RUNTIME_GRAPH_PATH,\n saveRuntimeGraph,\n runtimeGraphFileExists,\n loadRuntimeGraph,\n serializeRuntimeGraph,\n} from './runtime-graph-io';\n\ninterface EdgeResult {\n edges: RuntimeEdge[];\n unresolved: RuntimeUnresolved[];\n queues: Record<string, RuntimeQueue>;\n}\n\n/**\n * The derived graph PLUS everything the derivation had to guess at. `warnings` is deliberately not\n * part of RuntimeGraph: it is not committed data, it is the report that stops a guessed edge from\n * passing for a derived one. Executors print it.\n */\nexport class RuntimeGraphReport {\n constructor(\n public readonly graph: RuntimeGraph,\n /** Human-readable lines naming every call site whose target the graph could not pin down. */\n public readonly warnings: string[],\n /**\n * Call sites that name a target the repo does not contain. Unlike a warning these FAIL the\n * build: the contract IS served in-repo, so the name is a typo or a stale rename, and the\n * only reason it stayed invisible is that the graph quietly fanned the edge out instead.\n * (A call to a service outside the repo never reaches here — nothing in-repo implements its\n * contract, so it is `unresolvedUses`.)\n */\n public readonly problems: string[] = [],\n /**\n * role:server nodes omitted from the DRAWING because they declare no webpieces runtime\n * package anywhere in their library closure and serve/call nothing in-repo. Neither a\n * warning nor a problem — this is intended behavior, and shouting on every clean run is how\n * warnings stop being read. Deliberately NOT persisted: a graph file that records its own\n * omissions gets committed and stops being read. Executors print it instead, so the\n * omission is always visible and never silent.\n */\n public readonly autoHidden: string[] = [],\n ) {}\n}\n\n/**\n * Derives the runtime microservice graph from architecture/dependencies.json `apiRelations`:\n * implementers × users per API, split by transport. An rpc edge is a direct call; a pubsub edge flows\n * through a queue (drawn producer → queue → consumer by the visualizer).\n *\n * Nodes are the runnable apps only — role:server / role:client. A library is NEVER a node; its\n * implements/uses (a shared client-factory or route-registration lib) are attributed to the\n * servers/clients that embed it, walked transitively over dependsOn (see collectDecls).\n */\nclass RuntimeGraphDeriver {\n /** apiClassName -> the api-lib project that owns the contract (from the apiRelations key). */\n private readonly apiOwners = new Map<string, string>();\n /** Addressable name -> the runtime node answering to it; how a targeted call resolves. */\n private readonly nodeByServiceName = new Map<string, string>();\n private readonly warnings: string[] = [];\n private readonly problems: string[] = [];\n /** role:server nodes hidden by isNonParticipantServer, in the order buildServices met them. */\n private readonly autoHidden: string[] = [];\n /** Uses aimed at a reviewed out-of-workspace service (#1203); built once the names are claimed. */\n private readonly remoteCalls: RemoteCallCollector;\n\n /**\n * False when NO project in the graph carries `webpiecesRuntime`, i.e. the file was written\n * before the field existed. Auto-hiding is then off ENTIRELY, so an old dependencies.json\n * renders exactly as it did — absence of the field must never be read as \"declares none\".\n * Any repo regenerated after this change has at least one webpieces project, so the guard\n * cannot wrongly suppress the feature.\n */\n private readonly markersKnown: boolean;\n\n constructor(\n private readonly projects: EnhancedGraph,\n /** Project names tagged drawOnGraph:false — kept in the JSON but flagged so the viz omits them. */\n private readonly hiddenProjects: Set<string>,\n /**\n * The per-contract method tables, read from the generated `architecture/apis/<Api>.json`\n * files (ApiContractFiles). Pubsub edges remain at the contract level, independently\n * of method metadata: an endpoint table does not declare which methods a client uses.\n */\n private readonly apiContracts: ApiContracts = {},\n /**\n * Declared external systems from dependencies.json, keyed by identity. Empty means nothing\n * was declared, which renders exactly as it did before declarations existed.\n */\n private readonly externalSystemDecls: ExternalSystemDecls = {},\n /** Reviewed out-of-workspace services from dependencies.json `remoteServices` (#1203). */\n remoteServices: RemoteServices = {},\n ) {\n this.markersKnown = Object.values(projects).some(\n (entry: GraphEntry) => entry.webpiecesRuntime !== undefined,\n );\n // A node ALWAYS answers to its own module name, so a repo whose deployed names match its\n // project names needs no declaration at all — and no alias can ever redirect 'ai-chat' away\n // from the ai-chat module. Module names are therefore claimed FIRST and are unshadowable.\n for (const name of Object.keys(projects).sort()) {\n if (this.isNode(name)) this.nodeByServiceName.set(name, name);\n }\n // `serviceName` then adds the alias a repo needs when its deployed name is NOT its module\n // name (helper-svr serving 'helper-portal'). Colliding with a module is a misconfiguration,\n // not a precedence question: the alias is silently unreachable, so say so.\n for (const name of Object.keys(projects).sort()) {\n const declared = projects[name].serviceName;\n if (!this.isNode(name) || declared === undefined) continue;\n const claimant = this.nodeByServiceName.get(declared);\n if (claimant === undefined) {\n this.nodeByServiceName.set(declared, name);\n } else if (claimant !== name) {\n this.problems.push(\n `${name} declares serviceName '${declared}', but that is already the module name of ` +\n `${claimant} — the alias can never be reached. Rename one of them.`,\n );\n }\n }\n this.remoteCalls = new RemoteCallCollector(\n remoteServices,\n (name: string): boolean => this.nodeByServiceName.has(name),\n this.problems,\n );\n }\n\n assemble(): RuntimeGraphReport {\n const decls = this.collectDecls();\n const apis = this.buildApis(decls);\n const edgeResult = this.buildEdges(decls, apis);\n // Cuts are stamped BEFORE buildServices, which is where leveling reads the edges.\n this.problems.push(\n ...applyCycleCuts(edgeResult.edges, this.projects, this.nodeByServiceName),\n );\n const services = this.buildServices(decls, edgeResult.edges);\n const apisObj: Record<string, RuntimeApi> = {};\n for (const api of Array.from(apis.keys()).sort()) apisObj[api] = apis.get(api)!;\n const graph: RuntimeGraph = {\n maxLevel: 0,\n contractQueues: {},\n services,\n apis: apisObj,\n runtimeEdges: edgeResult.edges,\n unresolvedUses: edgeResult.unresolved,\n queues: edgeResult.queues,\n triggers: this.buildTriggers(decls),\n };\n // #1179/#1216: each service's products are exactly its project's declared product tags.\n new RuntimeProductMembership().apply(graph, this.projects);\n // A destination whose address arrives at RUNTIME is declared on its CONTRACT\n // (`@externalSystem runtime partner-webhooks`), so it needs no branch of its own here: it is\n // resolved by the same call every other declared system is, converges on one node when two\n // services deliver over the same contract, and is watched by the same drift check.\n const systems = resolveExternalSystems(this.externalSystemDecls, services);\n attachExternalSystems(graph, systems);\n this.remoteCalls.attach(graph);\n new RuntimeNodeLevels().apply(graph);\n return new RuntimeGraphReport(\n graph,\n this.warnings,\n this.problems,\n [...this.autoHidden].sort(),\n );\n }\n\n /**\n * The clock- and outside-driven entry points: for every contract a node IMPLEMENTS, each method\n * declared `cron` or `external` becomes a trigger pointing AT that node.\n *\n * Driven off `implements` rather than `uses` on purpose — these have no in-repo caller at all,\n * which is why they never produced an edge and stayed invisible.\n */\n private buildTriggers(decls: ScanDecl[]): RuntimeTrigger[] {\n const triggers: RuntimeTrigger[] = [];\n for (const decl of decls) {\n for (const ref of decl.implementsApis) {\n for (const method of this.methodsOf(ref.api)) {\n if (method.kind !== 'cron' && method.kind !== 'external') continue;\n const trigger: RuntimeTrigger = {\n level: 0,\n levelBecause: '',\n kind: method.kind,\n api: ref.api,\n method: method.name,\n service: decl.name,\n };\n if (method.kind === 'cron') trigger.queueName = method.queueName;\n // ONLY for 'external' (a clock needs no vendor name), and only when declared —\n // a graph committed before callers were required has none and must still render.\n if (method.kind === 'external' && method.caller !== undefined)\n trigger.caller = method.caller;\n triggers.push(trigger);\n }\n }\n }\n return triggers.sort(\n (a: RuntimeTrigger, b: RuntimeTrigger) =>\n a.kind.localeCompare(b.kind) ||\n a.api.localeCompare(b.api) ||\n a.method.localeCompare(b.method) ||\n a.service.localeCompare(b.service),\n );\n }\n\n /** The committed method table for a contract, or [] when it declares none (e.g. a vendor seam). */\n private methodsOf(api: string): ApiMethodMeta[] {\n return this.apiContracts[api]?.methods ?? [];\n }\n\n /**\n * One ScanDecl per RUNTIME NODE (role:server / role:client). A node's effective\n * relations are its OWN apiRelations PLUS those of every library in its transitive\n * dependsOn closure: a lib that calls createRpcClient/addRoutes runs inside the\n * server/client that embeds it, so the relation is attributed to that node — the\n * lib is never a runtime node. Recursion stops at other nodes (each server/client\n * owns its own relations). dependsOn is transitively reduced, but reduction\n * preserves reachability, so the recursive walk still reaches every lib a node\n * embeds through libraries.\n */\n private collectDecls(): ScanDecl[] {\n const decls: ScanDecl[] = [];\n for (const name of Object.keys(this.projects).sort()) {\n if (!this.isNode(name)) continue;\n const sink = new RelationSink(name);\n this.collectEffectiveRelations(name, sink, new Set<string>([name]));\n // EVERY node gets a decl, including one with zero relations. Gating this on \"has at\n // least one implements/uses\" silently DELETED any deployed server whose work arrives\n // from outside the repo (a pull subscription): the diagram looked complete while missing\n // a running service — confidently wrong, not visibly incomplete. isNode() is the whole\n // test; drawOnGraph:false is the ONE opt-out, and such a node renders as an isolated box.\n decls.push({\n name,\n implementsApis: dedupApiRefs(sortApiRefs(sink.implementsApis)),\n usesApis: dedupApiRefs(sortApiRefs(sink.usesApis)),\n implementsVia: sink.implementsVia,\n markerVia: sink.markerVia,\n });\n }\n return decls;\n }\n\n /** True when a project is a runtime node — a server or client app (never a library). */\n private isNode(name: string): boolean {\n const role = this.projects[name]?.role;\n return role === 'server' || role === 'client';\n }\n\n /**\n * True for a role:server that speaks NO webpieces runtime package anywhere in its library\n * closure AND serves/calls no in-repo contract — a legacy Express/NestJS service sitting in the\n * same monorepo. This graph is built entirely out of webpieces contracts, so such a service can\n * only ever draw as a disconnected box: it has no edges by construction and never will.\n *\n * It stays a NODE and stays in runtime-dependencies.json (drawOnGraph:false), which is the whole\n * difference between hiding it and the silent deletion #542 removed — the data view still shows\n * a deployed role:server, only the picture drops it. Removing it from isNode() instead would\n * also drop it from nodeByServiceName, so a targeted ClientConfig('orders-manager') from a drawn\n * service would stop resolving and decay into an unresolved use: a graph that got quietly WORSE\n * while looking cleaner. The COMPILE-TIME graph (dependencies.html) still draws these projects;\n * they genuinely exist, and only the runtime drawing claims to show webpieces services.\n *\n * role:client is deliberately never auto-hidden: a browser app can legitimately declare no\n * marker at all, and nothing checks clients for presence the way checkServersPresent checks\n * servers, so there is no equivalent failure to prevent.\n */\n private isNonParticipantServer(decl: ScanDecl): boolean {\n if (!this.markersKnown) return false;\n if (this.projects[decl.name]?.role !== 'server') return false;\n if (decl.markerVia.size > 0) return false;\n // Belt-and-braces: serving or calling an in-repo contract IS participation, whatever the\n // package list says. This is what keeps a mechanism we did not anticipate from being blanked.\n return decl.implementsApis.length === 0 && decl.usesApis.length === 0;\n }\n\n /**\n * Accumulate `name`'s own api relations, then recurse through its LIBRARY deps\n * (skipping other nodes, which own their own relations). `visited` guards against\n * re-walking a lib reachable by more than one path (and any cycle).\n */\n private collectEffectiveRelations(\n name: string,\n sink: RelationSink,\n visited: Set<string>,\n includeRelations: boolean = true,\n ): void {\n const entry = this.projects[name];\n if (entry === undefined) return;\n sink.addMarkers(entry.webpiecesRuntime, name);\n const relations = entry.apiRelations;\n if (includeRelations && relations !== undefined) {\n for (const owner of Object.keys(relations).sort()) {\n for (const ref of relations[owner].implements) {\n this.apiOwners.set(ref.api, owner);\n sink.addImplements(ref, ref.declaredVia ?? name);\n }\n for (const ref of relations[owner].uses) {\n this.apiOwners.set(ref.api, owner);\n sink.addUses(ref);\n }\n }\n }\n for (const dep of entry.dependsOn) {\n if (visited.has(dep)) continue;\n visited.add(dep);\n if (this.isNode(dep)) continue; // another server/client owns its own relations\n this.collectEffectiveRelations(\n dep,\n sink,\n visited,\n includeRelations && entry.runtimeComposition !== true,\n );\n }\n }\n\n /** apiClassName -> { implementedBy, usedBy, type }. */\n private buildApis(decls: ScanDecl[]): Map<string, RuntimeApi> {\n const apis = new Map<string, RuntimeApi>();\n const ensure = (api: string, type: ApiTransport): RuntimeApi => {\n let entry = apis.get(api);\n if (!entry) {\n entry = { implementedBy: [], usedBy: [], type };\n apis.set(api, entry);\n }\n return entry;\n };\n for (const decl of decls) {\n for (const ref of decl.implementsApis)\n ensure(ref.api, ref.type).implementedBy.push(decl.name);\n for (const ref of decl.usesApis) ensure(ref.api, ref.type).usedBy.push(decl.name);\n }\n for (const api of apis.keys()) {\n const entry = apis.get(api)!;\n entry.implementedBy.sort();\n entry.usedBy = Array.from(new Set(entry.usedBy)).sort();\n const owner = this.apiOwners.get(api);\n if (owner !== undefined) entry.owner = owner;\n }\n return apis;\n }\n\n /** Inferred edges U -> I via api A, split by transport; plus uses with no implementer. */\n private buildEdges(decls: ScanDecl[], apis: Map<string, RuntimeApi>): EdgeResult {\n const viaByKey = new Map<string, Set<string>>();\n const unresolved: RuntimeUnresolved[] = [];\n for (const decl of decls) {\n for (const ref of decl.usesApis) {\n // A named target outside this workspace (#1203) is a remote service, not an implementer\n // to resolve among the nodes and not an unresolved vendor call.\n const wanted = ref.targetService ?? this.callsServiceFor(decl.name, ref.api);\n if (this.remoteCalls.claim(decl.name, ref, wanted)) continue;\n const implementers = apis.get(ref.api)?.implementedBy ?? [];\n if (implementers.length === 0) {\n // Nobody in-repo serves it. For a vendor contract that is the ANSWER, not a gap:\n // the call leaves the repo, and the viz terminates it at a dashed vendor node.\n unresolved.push({\n level: 0,\n levelBecause: 'outbound sink',\n service: decl.name,\n api: ref.api,\n });\n continue;\n }\n for (const target of this.targetsFor(decl.name, ref, implementers)) {\n // A service calling ITSELF synchronously is noise; a service ENQUEUEING to\n // itself is a real, common topology (deferring its own work), and dropping it\n // was hiding the single most interesting thing about a queue.\n if (target === decl.name && ref.type !== 'pubsub') continue;\n const key = `${decl.name} ${target} ${ref.type}`;\n if (!viaByKey.has(key)) viaByKey.set(key, new Set());\n viaByKey.get(key)!.add(ref.api);\n }\n }\n }\n return {\n edges: this.edgesFromKeys(viaByKey),\n unresolved: sortUnresolved(unresolved),\n // Client wiring declares contracts, never the methods a producer enqueues.\n queues: {},\n };\n }\n\n /**\n * WHICH implementers this one `uses` reaches. Resolution order (most specific wins):\n * 1. a literal `ClientConfig` at the call site (`ref.targetService`) — names ONE node;\n * 2. else the calling project's declared `callsService` (project.json) — for the shared-library\n * case where the literal cannot sit at the call site, it lives one indirection away in the app;\n * 3. else the historical fan-out — the only safe superset — recording WHY, so a fanned-out (i.e.\n * possibly fictional) edge can never pass for a derived one.\n * A named target (1 or 2) that resolves to no module, or to a module that does not serve the\n * contract, is a PROBLEM (fails the build), never a silent drop.\n */\n private targetsFor(user: string, ref: ApiRef, implementers: string[]): string[] {\n const literal = ref.targetService;\n if (literal !== undefined)\n return this.resolveNamedTarget(user, ref, implementers, literal, 'literal');\n\n const declared = this.callsServiceFor(user, ref.api);\n if (declared !== undefined)\n return this.resolveNamedTarget(user, ref, implementers, declared, 'callsService');\n\n return this.untargetedFanOut(user, ref, implementers);\n }\n\n /**\n * The declared `callsService` target for this node's use of `api`, or undefined when none applies.\n * A string declaration aims every untargeted use at one service; a map aims per api-class.\n */\n private callsServiceFor(user: string, api: string): string | undefined {\n const declared = this.projects[user]?.callsService;\n if (declared === undefined) return undefined;\n if (typeof declared === 'string') return declared;\n return declared[api];\n }\n\n /**\n * Resolve a NAMED target (from a call-site literal or a project-level `callsService`) to the one\n * module that serves it. A name no module answers to, or a module that does not serve the\n * contract, fails the build — the wording names which declaration to fix, `source` deciding it.\n */\n private resolveNamedTarget(\n user: string,\n ref: ApiRef,\n implementers: string[],\n wanted: string,\n source: 'literal' | 'callsService',\n ): string[] {\n const node = this.nodeByServiceName.get(wanted);\n const served = `${implementers.length} module(s) serve this contract (${implementers.join(', ')})`;\n if (node === undefined) {\n this.problems.push(\n source === 'callsService'\n ? `${user} declares metadata.webpieces.callsService '${wanted}' in its project.json, but NO ` +\n `module answers to that name. ${served}. Point callsService at a module name, or declare ` +\n `metadata.webpieces.serviceName: '${wanted}' on the module that serves \"${ref.api}\".`\n : `${user} calls \"${ref.api}\" at service '${wanted}', but NO module answers to that name. ` +\n `${served}. Either use the module name, or declare metadata.webpieces.serviceName: ` +\n `'${wanted}' on the module that serves it (and translate any environment prefix in ` +\n `ClientRegistry.setDeriver, not here).`,\n );\n return implementers;\n }\n if (!implementers.includes(node)) {\n const via =\n source === 'callsService'\n ? `${user} declares callsService '${wanted}' (module ${node})`\n : `${user} calls \"${ref.api}\" at service '${wanted}' (module ${node})`;\n this.problems.push(\n `${via}, but ${node} does NOT serve \"${ref.api}\" — ${implementers.join(', ')} do. At runtime that ` +\n `call has nothing to answer it.`,\n );\n return implementers;\n }\n return [node];\n }\n\n /**\n * A use with no literal client config. One implementer is unambiguous, so it stays silent; more\n * than one means every edge but one is fiction — exactly the failure this mechanism exists to\n * stop — so it is reported even though the graph still (conservatively) draws them all.\n */\n private untargetedFanOut(user: string, ref: ApiRef, implementers: string[]): string[] {\n if (implementers.length > 1) {\n this.warnings.push(\n `${user} uses \"${ref.api}\" with no literal client config, and ${implementers.length} services ` +\n `implement it (${implementers.join(', ')}) — an edge is drawn to EVERY one, so all but one ` +\n `are fiction. Name the target: createRpcClient(${ref.api}, new ClientConfig('<serviceName>', ClientRole.SERVER)); ` +\n `or, when the client is built in a shared library (no literal can sit at the call site), ` +\n `declare metadata.webpieces.callsService: '<serviceName>' on ${user}'s project.json.`,\n );\n }\n return implementers;\n }\n\n /**\n * Rebuild edges from their `\"from to type queue\"` keys. The 4th part is the queue (\"Api.method\")\n * and is empty for every non-queued edge; it is part of the KEY so two queued methods between\n * the same pair stay two edges instead of collapsing into one.\n */\n private edgesFromKeys(viaByKey: Map<string, Set<string>>): RuntimeEdge[] {\n const edges: RuntimeEdge[] = [];\n for (const key of viaByKey.keys()) {\n const parts = key.split(' ');\n const edge: RuntimeEdge = {\n from: parts[0],\n to: parts[1],\n via: Array.from(viaByKey.get(key)!).sort(),\n type: parts[2] as ApiTransport,\n };\n if (parts[3] !== undefined && parts[3] !== '') edge.queue = parts[3];\n edges.push(edge);\n }\n edges.sort(\n (a: RuntimeEdge, b: RuntimeEdge) =>\n a.from.localeCompare(b.from) ||\n a.to.localeCompare(b.to) ||\n (a.type ?? '').localeCompare(b.type ?? '') ||\n (a.queue ?? '').localeCompare(b.queue ?? ''),\n );\n return edges;\n }\n\n private buildServices(decls: ScanDecl[], edges: RuntimeEdge[]): Record<string, RuntimeService> {\n const services: Record<string, RuntimeService> = {};\n for (const decl of decls) {\n // A queued hop depends on the QUEUE, not on the peer: the producer hands off and returns,\n // so naming the consumer here would assert a coupling that does not exist (and would make\n // a service that defers work to itself look self-dependent).\n const dependsOn = Array.from(\n new Set(\n edges\n .filter((e: RuntimeEdge) => e.from === decl.name)\n .flatMap((e: RuntimeEdge) =>\n e.type === 'pubsub'\n ? e.via.map((api) => `queue:${e.queue ?? api}`)\n : [e.to],\n ),\n ),\n ).sort();\n // Keys are written in this order; an undefined value is omitted by JSON.stringify, so\n // the committed JSON stays clean AND deterministic without conditional assembly.\n const service: RuntimeService = {\n level: 0,\n levelBecause: '',\n role: this.projects[decl.name]?.role, // labels a relation-less node for what it IS\n serviceName: this.projects[decl.name]?.serviceName,\n callsService: this.projects[decl.name]?.callsService,\n implements: decl.implementsApis.map((r: ApiRef) => r.api),\n implementsVia:\n decl.implementsVia.size > 0 ? sortedRecord(decl.implementsVia) : undefined,\n // One api used against two services is two refs but ONE api in this list.\n uses: Array.from(new Set(decl.usesApis.map((r: ApiRef) => r.api))),\n dependsOn,\n wiringUses: decl.usesApis.some((ref) => ref.declaredVia !== undefined)\n ? decl.usesApis\n : undefined,\n wiringImplements: decl.implementsApis.some((ref) => ref.declaredVia !== undefined)\n ? decl.implementsApis\n : undefined,\n };\n services[decl.name] = service;\n // The explicit tag wins and is NOT reported as auto-hidden — somebody asked for it.\n if (this.hiddenProjects.has(decl.name)) {\n service.drawOnGraph = false;\n } else if (this.isNonParticipantServer(decl)) {\n service.drawOnGraph = false;\n this.autoHidden.push(decl.name);\n }\n }\n return services;\n }\n}\n\n/**\n * Derive the runtime graph from dependencies.json's project `apiRelations` — the\n * single source of truth shared by generate + validate. `hiddenProjects`\n * (drawOnGraph:false, defaults to none) are kept in the graph but flagged so the\n * runtime visualizer omits their nodes + edges.\n */\n// webpieces-disable no-function-outside-class -- module entry point for the runtime graph derivation\nexport function deriveRuntimeGraph(\n projects: EnhancedGraph,\n hiddenProjects: Set<string> = new Set<string>(),\n apiContracts: ApiContracts = {},\n): RuntimeGraph {\n return deriveRuntimeGraphReport(projects, hiddenProjects, apiContracts).graph;\n}\n\n/**\n * The same derivation, plus the warnings it produced (every edge it had to GUESS at). Executors use\n * this form and print the warnings; `deriveRuntimeGraph` is the convenience form for callers that\n * only want the data. The warnings are deliberately kept OUT of runtime-dependencies.json — a graph\n * file that records its own doubts would just get committed and stop being read.\n */\n// webpieces-disable no-function-outside-class -- module entry point for the runtime graph derivation\nexport function deriveRuntimeGraphReport(\n projects: EnhancedGraph,\n hiddenProjects: Set<string> = new Set<string>(),\n apiContracts: ApiContracts = {},\n externalSystems: ExternalSystemDecls = {},\n remoteServices: RemoteServices = {},\n): RuntimeGraphReport {\n return new RuntimeGraphDeriver(\n projects,\n hiddenProjects,\n apiContracts,\n externalSystems,\n remoteServices,\n ).assemble();\n}\n\n// Levels + adjacency live in runtime-graph-levels.ts; re-exported for the same reason the model types\n// and the io helpers above are — one obvious place to import the runtime graph from.\nexport { runtimeAdjacency } from './runtime-node-levels';\n"]}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RuntimeGraph } from './runtime-graph-model';
|
|
2
|
+
/** Generator-owned layering. Renderers consume these numbers without deriving topology. */
|
|
3
|
+
export declare class RuntimeNodeLevels {
|
|
4
|
+
apply(graph: RuntimeGraph): void;
|
|
5
|
+
/** All runtime dependencies except self-producers and explicitly admitted legacy RPC cuts. */
|
|
6
|
+
adjacency(graph: RuntimeGraph): Record<string, string[]>;
|
|
7
|
+
private collectContracts;
|
|
8
|
+
private queues;
|
|
9
|
+
private queueLevel;
|
|
10
|
+
private sink;
|
|
11
|
+
}
|
|
12
|
+
/** Complete generated runtime dependencies, including foreign queues and outbound sinks. */
|
|
13
|
+
export declare function runtimeAdjacency(graph: RuntimeGraph): Record<string, string[]>;
|