@webpieces/nx-webpieces-rules 0.4.905 → 0.4.906

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/nx-webpieces-rules",
3
- "version": "0.4.905",
3
+ "version": "0.4.906",
4
4
  "description": "Nx-specific webpieces validation rules, graph tooling, executors, and inference plugin.",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -20,12 +20,12 @@
20
20
  "README.md"
21
21
  ],
22
22
  "dependencies": {
23
- "@webpieces/api-doc-model": "0.4.905",
24
- "@webpieces/core-util": "0.4.905",
25
- "@webpieces/eslint-rules": "0.4.905",
26
- "@webpieces/rules-config": "0.4.905",
23
+ "@webpieces/api-doc-model": "0.4.906",
24
+ "@webpieces/core-util": "0.4.906",
25
+ "@webpieces/eslint-rules": "0.4.906",
26
+ "@webpieces/rules-config": "0.4.906",
27
27
  "madge": "8.0.0",
28
- "@webpieces/rules-sdk": "0.4.905"
28
+ "@webpieces/rules-sdk": "0.4.906"
29
29
  },
30
30
  "peerDependencies": {
31
31
  "@nx/devkit": ">=18.0.0"
@@ -41,7 +41,7 @@
41
41
  "access": "public"
42
42
  },
43
43
  "devDependencies": {
44
- "@webpieces/tooling-testkit": "0.4.905"
44
+ "@webpieces/tooling-testkit": "0.4.906"
45
45
  },
46
46
  "types": "./src/index.d.ts",
47
47
  "bin": {
@@ -81,17 +81,6 @@ export interface ApiRef {
81
81
  /** Qualified selected library export that contributed this relationship. */
82
82
  declaredVia?: string;
83
83
  conditional?: string;
84
- /**
85
- * ONLY on a `pubsub` uses ref. True means "this producer was attributed to EVERY cloudtasks
86
- * method of the contract, not to the methods it actually enqueues".
87
- *
88
- * A producer builds one client for the whole contract (`createPubSubClient(EmailTaskApi, cfg)`)
89
- * and enqueues through a proxy (`emailTasks.send(req)`) somewhere else entirely — often after
90
- * the client has been stored in a DI binding — so WHICH methods it enqueues is not statically
91
- * recoverable. The consumer side IS exact (addRoutes + the contract's method table). Recording
92
- * the difference keeps a producer-side queue from being read as proof that queue is used.
93
- */
94
- methodsInferred?: boolean;
95
84
  }
96
85
  /**
97
86
  * Identity of a ref for de-duplication: an api used twice against DIFFERENT services is two distinct
@@ -1 +1 @@
1
- {"version":3,"file":"api-relations.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/api-relations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API Relations model\n *\n * The typed classification of a compile-time dependency edge P -> apiLib in\n * architecture/dependencies.json. Where the flat `dependsOn` only says \"P depends\n * on apiLib\", `apiRelations[apiLib]` says WHY: which API contracts P IMPLEMENTS\n * (serves, `class Ctrl extends XxxApi`) and which it USES (calls as a client,\n * `factory.createRpcClient(XxxApi, ...)` / `createPubSubClient(...)`), each tagged\n * with its transport.\n *\n * Interfaces + object literals here mirror the sibling runtime-graph.ts model —\n * these are serialization DTOs written verbatim into the committed JSON, and\n * `implements`/`uses` are legal interface property names (they are reserved words\n * only as binding identifiers, not as member names).\n */\n\n/**\n * Transport of an API contract:\n * - `rpc` — synchronous request/response over HTTP\n * - `pubsub` — fire-and-forget, delivered later through a Cloud Tasks queue\n * - `external` — a contract for a system OUTSIDE this repo (firestore, gmail, ...). Nothing in-repo\n * implements it, so it never becomes a service→service edge; it terminates the graph\n * at a dashed vendor node. Detected from `runtime-architecture.externalApiPaths`\n * rather than from a decorator, because a vendor contract is a plain interface bound\n * to a Symbol token, not an `abstract class` carrying @ApiPath.\n */\nexport type ApiTransport = 'rpc' | 'pubsub' | 'external';\n\n/**\n * The kinds an external system can be DECLARED as. Each draws its own shape in the runtime viz, so\n * a datastore stops looking like an HTTP service.\n *\n * Lives here rather than beside the runtime graph model because that model already imports from this\n * file; putting it there and importing back would close a module cycle.\n */\nexport const EXTERNAL_SYSTEM_KINDS = [\n 'database',\n 'cache',\n 'queue',\n 'storage',\n 'saas',\n 'system',\n /**\n * A destination whose ADDRESS is supplied at runtime — a URL a partner registered, an OAuth\n * callback, a per-tenant host. Unlike every other kind it does not name one vendor: it names the\n * PLACE in our own system where somebody else's address is dialled, which is the fact a security\n * review is looking for.\n *\n * Declared like every other kind, on the CONTRACT: `@externalSystem runtime partner-webhooks`.\n * On the contract rather than at a `createRpcClient` call site deliberately — \"the far end of\n * this contract is outside our estate\" is a property of the CONTRACT, true for every caller of\n * it, so putting it there means one declaration however many services deliver over it, and\n * nothing to keep in step when a second one appears. It also means this kind rides the exact\n * same declare → resolve → draw pipeline `saas` and `database` already ride, rather than a\n * second mechanism that reads construction sites and can disagree with the first.\n */\n 'runtime',\n] as const;\n\nexport type ExternalSystemKind = (typeof EXTERNAL_SYSTEM_KINDS)[number];\n\n/** True for a string that names one of {@link EXTERNAL_SYSTEM_KINDS}. */\n// webpieces-disable no-function-outside-class -- type guard beside the type it guards, matching this file's DTO style\nexport function isExternalSystemKind(value: string): value is ExternalSystemKind {\n return (EXTERNAL_SYSTEM_KINDS as readonly string[]).includes(value);\n}\n\n/**\n * ONE declared external system, keyed in {@link ExternalSystemDecls} by its IDENTITY.\n *\n * Identity, not display text: two projects each tagged `external:database:postgres` name the same\n * `postgres` node and converge on it with one arrow apiece, instead of drawing a database each.\n *\n * The two arrays are the two declaration sites, and a system may legitimately have both — a repo can\n * wrap a datastore behind a contract in one service and open it directly in another.\n */\nexport interface ExternalSystemDecl {\n kind: ExternalSystemKind;\n label: string;\n /** Contracts declaring it with an `@externalSystem` JSDoc tag; every user of one gets an arrow. */\n apis: string[];\n /** Projects declaring it with an `external:<kind>:<identity>` nx tag; each gets its OWN arrow. */\n projects: string[];\n}\n\n/** identity -> its declaration. Serialized as the `externalSystems` key of dependencies.json. */\nexport type ExternalSystemDecls = Record<string, ExternalSystemDecl>;\n\n/**\n * How a project relates to ONE api-lib it depends on:\n * - `implements` — it serves the api (a controller extends it)\n * - `uses` — it calls the api (generates a client)\n * - `uses-implements` — it does BOTH (implements some of the api-lib's contracts,\n * uses others)\n */\nexport type ApiRelationKind = 'implements' | 'uses' | 'uses-implements';\n\n/** One API class a project implements or uses, with its transport. */\nexport interface ApiRef {\n api: string;\n type: ApiTransport;\n /**\n * ONLY on a `uses` ref: the service the call site aims at, read from the client config literal\n * (`createRpcClient(XxxApi, new ClientConfig('helper-fsdb'))` → `helper-fsdb`). It is matched\n * against a project's DECLARED `serviceName` to pick the ONE runtime edge target, instead of\n * fanning the edge out to every implementer of the api — which is catastrophically wrong for a\n * company-wide contract registered in a shared library and therefore implemented by every server.\n *\n * Absent when the config argument is not a `new <Xxx>ClientConfig('<literal>')` (a variable, a\n * computed name, ...). Absent means \"unknown target\", NOT \"no target\" — the runtime graph then\n * falls back to the old fan-out and says so out loud.\n */\n targetService?: string;\n /** Qualified selected library export that contributed this relationship. */\n declaredVia?: string;\n conditional?: string;\n /**\n * ONLY on a `pubsub` uses ref. True means \"this producer was attributed to EVERY cloudtasks\n * method of the contract, not to the methods it actually enqueues\".\n *\n * A producer builds one client for the whole contract (`createPubSubClient(EmailTaskApi, cfg)`)\n * and enqueues through a proxy (`emailTasks.send(req)`) somewhere else entirely — often after\n * the client has been stored in a DI binding — so WHICH methods it enqueues is not statically\n * recoverable. The consumer side IS exact (addRoutes + the contract's method table). Recording\n * the difference keeps a producer-side queue from being read as proof that queue is used.\n */\n methodsInferred?: boolean;\n}\n\n/**\n * Identity of a ref for de-duplication: an api used twice against DIFFERENT services is two distinct\n * relations (two distinct runtime edges), so the api name alone is not the key.\n */\n// webpieces-disable no-function-outside-class -- pure data helper for these serialization DTOs\nexport function apiRefKey(ref: ApiRef): string {\n return `${ref.api} ${ref.targetService ?? ''}`;\n}\n\n/**\n * A project's relationship to ONE api-lib it depends on. Serialized verbatim into\n * architecture/dependencies.json under `apiRelations[apiLibProjectName]`.\n */\nexport interface ApiRelation {\n kind: ApiRelationKind;\n implements: ApiRef[];\n uses: ApiRef[];\n}\n\n/** apiLibProjectName -> relation. Attached to a GraphEntry as `apiRelations`. */\nexport type ProjectApiRelations = Record<string, ApiRelation>;\n\n/**\n * What triggers ONE endpoint, mirroring core-util's `EndpointKind`. Duplicated as a string union\n * rather than imported: nx-webpieces-rules is build tooling and must not take a runtime dependency\n * on the framework it inspects (it reads decorators as TEXT, from projects that may be on a\n * different @webpieces version than the tooling itself).\n */\nexport type EndpointKind = 'rpc' | 'cloudtasks' | 'cron' | 'external';\n\n/** Side-effect semantics copied from the required third `@Endpoint` argument. */\nexport type EndpointOperation = 'read' | 'write-idempotent' | 'write';\n\n/** HTTP verbs understood by the generated contract runtime. */\nexport type ContractHttpMethod = 'GET' | 'POST';\n\n/** One path/query/body mapping emitted into architecture/dependencies.json. */\nexport interface ApiParameterMeta {\n index: number;\n source: 'path' | 'query' | 'body';\n /** Path/query wire name; a JSON/form body occupies the whole entity and has no key. */\n wireName?: string;\n}\n\n/**\n * One method on an API contract, as written in source: what triggers it, where it is mounted, and\n * (for a queued method) which Cloud Tasks queue delivers it.\n */\nexport interface ApiMethodMeta {\n name: string;\n /** The @Endpoint path, relative to the class's @ApiPath basePath. */\n path: string;\n kind: EndpointKind;\n /** Retry/idempotency contract; independent of the HTTP verb. */\n operation: EndpointOperation;\n /** Required in newly scanned source; optional only when reading older committed graph data. */\n httpMethod?: ContractHttpMethod;\n /** Explicit parameter mappings; absent only when the method has none. */\n parameters?: ApiParameterMeta[];\n /** Present when callers receive the transport-neutral full response. */\n responseType?: 'full';\n /**\n * `@Queue(...)` override, else `${ApiClassName}-${methodName}`.\n *\n * ONLY on a `cloudtasks` or `cron` method — those are the kinds actually delivered through a\n * named queue or schedule, and Terraform matches on this string. A synchronous `rpc` (or an\n * inbound `external`) endpoint has no queue and needs none; emitting a plausible-looking name for\n * one put every synchronous endpoint one naive `methods.map(m => m.queueName)` away from being\n * provisioned as a queue.\n */\n queueName?: string;\n /**\n * WHO outside this repo drives this endpoint, from `@Endpoint(POST, p, WRITE, EXTERNAL, { calledBy })`.\n *\n * ONLY on an `external` method, the same way `queueName` is only on the kinds that HAVE a queue.\n *\n * Deliberately the SAME {@link ExternalSystemDeclaration} the OUTBOUND `@externalSystem` tag\n * resolves to, not a parallel inbound-only type: an inbound `saas twilio` and an outbound\n * `saas twilio` are the same vendor, so sharing the type makes them share an IDENTITY and\n * converge on ONE node instead of drawing twilio twice facing opposite directions.\n *\n * Optional in the TYPE only for graphs generated before the caller was required — generation\n * FAILS on an `external` method whose caller cannot be read (UndeclaredExternalCallerError).\n */\n caller?: ExternalSystemDeclaration;\n}\n\n/**\n * A discovered API contract class: its name, the api-lib project that owns it, its transport, and\n * its per-method trigger table.\n */\nexport interface ApiClassInfo {\n api: string;\n owner: string;\n type: ApiTransport;\n /** The class's @ApiPath basePath; absent for an external (vendor) contract, which has no route. */\n basePath?: string;\n /**\n * Every @Endpoint method, in declaration order. Empty for an external contract (a vendor\n * interface has no endpoints — it is called through a vendor SDK, not mounted).\n */\n methods: ApiMethodMeta[];\n /**\n * Set when the contract carries an `@externalSystem <kind> [label]` JSDoc tag — a vendor seam\n * declaring WHAT it is a seam to. JSDoc rather than a decorator because these seams are plain TS\n * `interface`s, which cannot carry one.\n */\n externalSystem?: ExternalSystemDeclaration;\n}\n\n/** The `(kind, label)` pair a single declaration resolves to. */\nexport interface ExternalSystemDeclaration {\n kind: ExternalSystemKind;\n label: string;\n}\n\n/**\n * The committed, per-contract view written to `architecture/apis/<ApiName>.json` (one file per API;\n * dependencies.json only links to it under `apiContractFiles` — see ApiContractFiles).\n *\n * The runtime graph is derived from the committed files so generate and validate can never\n * diverge — which means anything the runtime graph needs must be COMMITTED, not re-scanned.\n * Per-method trigger kinds and queue names are exactly that: without this table the derivation\n * cannot tell a queued endpoint from a cron sweep, and cannot name the queue between two services.\n */\nexport interface ApiContract {\n owner: string;\n /** 'rpc' | 'pubsub' for an in-repo contract, 'external' for a vendor seam. */\n apiKind: ApiTransport;\n /**\n * REQUIRED. Every routed contract carries `@ApiPath`, so every entry in this table must carry the\n * base path its methods hang off. Optional was worse than absent: a consumer joining\n * `basePath + path` for the ONE entry that lost it computed `/test` where the real route was\n * `/whatsapp/test`, and had no reason to suspect it — every other entry had the field. Generation\n * now FAILS instead of shipping an entry that computes a confidently wrong URL.\n */\n basePath: string;\n methods: ApiMethodMeta[];\n}\n\n/** apiClassName -> its committed contract. Serialized as one `architecture/apis/<ApiName>.json` each. */\nexport type ApiContracts = Record<string, ApiContract>;\n\n/**\n * An `addRoutes`/`createRpcClient`/`createPubSubClient` first argument that resolved to an\n * abstract class in a DECLARATION file which owns no indexed contract. Unambiguously a broken\n * scan (a real api-lib whose source we never indexed), never a \"this isn't an API\" argument —\n * so it is reported loudly instead of collapsing into a silent `return null`.\n */\nexport class UnresolvedApiCall {\n constructor(\n /** The project whose source makes the call. */\n public readonly project: string,\n /** The contract class name as written at the call site. */\n public readonly api: string,\n /** `path/to/file.ts:LINE` of the call site, workspace-relative. */\n public readonly at: string,\n /** The declaration file the checker resolved to (where decorators are erased). */\n public readonly declaredIn: string,\n ) {}\n}\n\n/**\n * ONE decorator argument the scan saw but could not reduce to a string — `@ApiPath(SOME_CONST)`\n * where SOME_CONST is imported from another module, a computed expression, an enum member, ...\n *\n * Recorded rather than dropped. Before this existed, an unresolvable argument cost the contract its\n * basePath, or a method, or (when EVERY method's path was one) the whole class — with nothing\n * printed anywhere. Same-module constants now resolve, so what remains here is the genuinely\n * unresolvable, which the author can fix by inlining the literal or moving the constant in-module.\n */\nexport class NonLiteralDecoratorArg {\n constructor(\n /** The contract class the argument was written on. */\n public readonly api: string,\n /** `ApiPath` | `Endpoint` | `Queue`. */\n public readonly decorator: string,\n /** The method name for a member decorator, null for a class decorator. */\n public readonly method: string | null,\n /** The argument exactly as written, e.g. `WHATSAPP_API_PATH`. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/**\n * ONE `@Endpoint(path, kind)` whose PATH argument was present but could not be reduced to a string.\n *\n * Split out of NonLiteralDecoratorArg (which stays a warning, covering @Queue and the rest) because\n * this one is FATAL. Upstream components need the URL: an http client builds its request as\n * `basePath + path`, so an unreadable path is missing ROUTING, not missing metadata — the same\n * reasoning that already makes basePath required. Skipping the method instead used to delete it, and\n * a class whose every path was a constant lost every method and vanished from the contract table with\n * nothing printed anywhere.\n */\nexport class UnresolvedEndpointPath {\n constructor(\n /** The contract class the method is declared on. */\n public readonly api: string,\n /** The method name. */\n public readonly method: string,\n /** The path argument exactly as written, e.g. `PROCESS_PATH`. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/**\n * ONE `external` `@Endpoint` whose CALLER could not be read from the source.\n *\n * Fatal for the same reason {@link UnresolvedEndpointPath} is. The inbound box exists to say who is\n * calling us from outside; with no caller it can only restate our own contract name, which is the\n * exact bug this diagnostic exists to make impossible to reintroduce. `@Endpoint`'s TS overloads\n * already require `calledBy`, so anything reaching here is a JS caller, an `as any`, a cross-module\n * constant the parser-only scan cannot fold, or an unknown `callerKind`.\n */\nexport class UndeclaredExternalCaller {\n constructor(\n /** The contract class the method is declared on. */\n public readonly api: string,\n /** The method name. */\n public readonly method: string,\n /** What was wrong, as written — `<missing>`, `SOME_CONST`, `callerKind: 'vendor'`. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/** ONE `@Endpoint` whose required side-effect declaration could not be read from source. */\nexport class UndeclaredEndpointOperation {\n constructor(\n /** The contract class the method is declared on. */\n public readonly api: string,\n /** The method name. */\n public readonly method: string,\n /** What was wrong, as written — `<missing>`, a constant name, or an invalid literal. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/**\n * A contract class that DECLARED `@Endpoint` methods and kept none of them.\n *\n * The backstop for the mechanism that hid an entire service: `buildApiContracts` skips a zero-method\n * class (correctly — a vendor seam has no routes), so a class gutted by unreadable decorator\n * arguments left through the same door as a legitimately routeless one. A class that declared\n * endpoints and produced none is never legitimate, so it is named instead.\n */\nexport class EmptiedApiContract {\n constructor(\n /** The contract class name. */\n public readonly api: string,\n /** How many `@Endpoint` decorators were written on it. */\n public readonly declared: number,\n /** `path/to/file.ts:LINE` of the class, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/** Derive the relation kind from the (possibly empty) implements/uses ref lists. */\n// webpieces-disable no-function-outside-class -- pure data helper for these serialization DTOs\nexport function deriveApiRelationKind(\n implementsRefs: ApiRef[],\n usesRefs: ApiRef[],\n): ApiRelationKind {\n if (implementsRefs.length > 0 && usesRefs.length > 0) return 'uses-implements';\n if (implementsRefs.length > 0) return 'implements';\n return 'uses';\n}\n\n/**\n * Stable-sort a ref list by api name, then by target service, so the committed JSON is\n * deterministic even when one api is used against two different services.\n */\n// webpieces-disable no-function-outside-class -- pure data helper for these serialization DTOs\nexport function sortApiRefs(refs: ApiRef[]): ApiRef[] {\n return [...refs].sort(\n (a: ApiRef, b: ApiRef) =>\n a.api.localeCompare(b.api) ||\n (a.targetService ?? '').localeCompare(b.targetService ?? ''),\n );\n}\n"]}
1
+ {"version":3,"file":"api-relations.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/api-relations.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API Relations model\n *\n * The typed classification of a compile-time dependency edge P -> apiLib in\n * architecture/dependencies.json. Where the flat `dependsOn` only says \"P depends\n * on apiLib\", `apiRelations[apiLib]` says WHY: which API contracts P IMPLEMENTS\n * (serves, `class Ctrl extends XxxApi`) and which it USES (calls as a client,\n * `factory.createRpcClient(XxxApi, ...)` / `createPubSubClient(...)`), each tagged\n * with its transport.\n *\n * Interfaces + object literals here mirror the sibling runtime-graph.ts model —\n * these are serialization DTOs written verbatim into the committed JSON, and\n * `implements`/`uses` are legal interface property names (they are reserved words\n * only as binding identifiers, not as member names).\n */\n\n/**\n * Transport of an API contract:\n * - `rpc` — synchronous request/response over HTTP\n * - `pubsub` — fire-and-forget, delivered later through a Cloud Tasks queue\n * - `external` — a contract for a system OUTSIDE this repo (firestore, gmail, ...). Nothing in-repo\n * implements it, so it never becomes a service→service edge; it terminates the graph\n * at a dashed vendor node. Detected from `runtime-architecture.externalApiPaths`\n * rather than from a decorator, because a vendor contract is a plain interface bound\n * to a Symbol token, not an `abstract class` carrying @ApiPath.\n */\nexport type ApiTransport = 'rpc' | 'pubsub' | 'external';\n\n/**\n * The kinds an external system can be DECLARED as. Each draws its own shape in the runtime viz, so\n * a datastore stops looking like an HTTP service.\n *\n * Lives here rather than beside the runtime graph model because that model already imports from this\n * file; putting it there and importing back would close a module cycle.\n */\nexport const EXTERNAL_SYSTEM_KINDS = [\n 'database',\n 'cache',\n 'queue',\n 'storage',\n 'saas',\n 'system',\n /**\n * A destination whose ADDRESS is supplied at runtime — a URL a partner registered, an OAuth\n * callback, a per-tenant host. Unlike every other kind it does not name one vendor: it names the\n * PLACE in our own system where somebody else's address is dialled, which is the fact a security\n * review is looking for.\n *\n * Declared like every other kind, on the CONTRACT: `@externalSystem runtime partner-webhooks`.\n * On the contract rather than at a `createRpcClient` call site deliberately — \"the far end of\n * this contract is outside our estate\" is a property of the CONTRACT, true for every caller of\n * it, so putting it there means one declaration however many services deliver over it, and\n * nothing to keep in step when a second one appears. It also means this kind rides the exact\n * same declare → resolve → draw pipeline `saas` and `database` already ride, rather than a\n * second mechanism that reads construction sites and can disagree with the first.\n */\n 'runtime',\n] as const;\n\nexport type ExternalSystemKind = (typeof EXTERNAL_SYSTEM_KINDS)[number];\n\n/** True for a string that names one of {@link EXTERNAL_SYSTEM_KINDS}. */\n// webpieces-disable no-function-outside-class -- type guard beside the type it guards, matching this file's DTO style\nexport function isExternalSystemKind(value: string): value is ExternalSystemKind {\n return (EXTERNAL_SYSTEM_KINDS as readonly string[]).includes(value);\n}\n\n/**\n * ONE declared external system, keyed in {@link ExternalSystemDecls} by its IDENTITY.\n *\n * Identity, not display text: two projects each tagged `external:database:postgres` name the same\n * `postgres` node and converge on it with one arrow apiece, instead of drawing a database each.\n *\n * The two arrays are the two declaration sites, and a system may legitimately have both — a repo can\n * wrap a datastore behind a contract in one service and open it directly in another.\n */\nexport interface ExternalSystemDecl {\n kind: ExternalSystemKind;\n label: string;\n /** Contracts declaring it with an `@externalSystem` JSDoc tag; every user of one gets an arrow. */\n apis: string[];\n /** Projects declaring it with an `external:<kind>:<identity>` nx tag; each gets its OWN arrow. */\n projects: string[];\n}\n\n/** identity -> its declaration. Serialized as the `externalSystems` key of dependencies.json. */\nexport type ExternalSystemDecls = Record<string, ExternalSystemDecl>;\n\n/**\n * How a project relates to ONE api-lib it depends on:\n * - `implements` — it serves the api (a controller extends it)\n * - `uses` — it calls the api (generates a client)\n * - `uses-implements` — it does BOTH (implements some of the api-lib's contracts,\n * uses others)\n */\nexport type ApiRelationKind = 'implements' | 'uses' | 'uses-implements';\n\n/** One API class a project implements or uses, with its transport. */\nexport interface ApiRef {\n api: string;\n type: ApiTransport;\n /**\n * ONLY on a `uses` ref: the service the call site aims at, read from the client config literal\n * (`createRpcClient(XxxApi, new ClientConfig('helper-fsdb'))` → `helper-fsdb`). It is matched\n * against a project's DECLARED `serviceName` to pick the ONE runtime edge target, instead of\n * fanning the edge out to every implementer of the api — which is catastrophically wrong for a\n * company-wide contract registered in a shared library and therefore implemented by every server.\n *\n * Absent when the config argument is not a `new <Xxx>ClientConfig('<literal>')` (a variable, a\n * computed name, ...). Absent means \"unknown target\", NOT \"no target\" — the runtime graph then\n * falls back to the old fan-out and says so out loud.\n */\n targetService?: string;\n /** Qualified selected library export that contributed this relationship. */\n declaredVia?: string;\n conditional?: string;\n}\n\n/**\n * Identity of a ref for de-duplication: an api used twice against DIFFERENT services is two distinct\n * relations (two distinct runtime edges), so the api name alone is not the key.\n */\n// webpieces-disable no-function-outside-class -- pure data helper for these serialization DTOs\nexport function apiRefKey(ref: ApiRef): string {\n return `${ref.api} ${ref.targetService ?? ''}`;\n}\n\n/**\n * A project's relationship to ONE api-lib it depends on. Serialized verbatim into\n * architecture/dependencies.json under `apiRelations[apiLibProjectName]`.\n */\nexport interface ApiRelation {\n kind: ApiRelationKind;\n implements: ApiRef[];\n uses: ApiRef[];\n}\n\n/** apiLibProjectName -> relation. Attached to a GraphEntry as `apiRelations`. */\nexport type ProjectApiRelations = Record<string, ApiRelation>;\n\n/**\n * What triggers ONE endpoint, mirroring core-util's `EndpointKind`. Duplicated as a string union\n * rather than imported: nx-webpieces-rules is build tooling and must not take a runtime dependency\n * on the framework it inspects (it reads decorators as TEXT, from projects that may be on a\n * different @webpieces version than the tooling itself).\n */\nexport type EndpointKind = 'rpc' | 'cloudtasks' | 'cron' | 'external';\n\n/** Side-effect semantics copied from the required third `@Endpoint` argument. */\nexport type EndpointOperation = 'read' | 'write-idempotent' | 'write';\n\n/** HTTP verbs understood by the generated contract runtime. */\nexport type ContractHttpMethod = 'GET' | 'POST';\n\n/** One path/query/body mapping emitted into architecture/dependencies.json. */\nexport interface ApiParameterMeta {\n index: number;\n source: 'path' | 'query' | 'body';\n /** Path/query wire name; a JSON/form body occupies the whole entity and has no key. */\n wireName?: string;\n}\n\n/**\n * One method on an API contract, as written in source: what triggers it, where it is mounted, and\n * (for a queued method) which Cloud Tasks queue delivers it.\n */\nexport interface ApiMethodMeta {\n name: string;\n /** The @Endpoint path, relative to the class's @ApiPath basePath. */\n path: string;\n kind: EndpointKind;\n /** Retry/idempotency contract; independent of the HTTP verb. */\n operation: EndpointOperation;\n /** Required in newly scanned source; optional only when reading older committed graph data. */\n httpMethod?: ContractHttpMethod;\n /** Explicit parameter mappings; absent only when the method has none. */\n parameters?: ApiParameterMeta[];\n /** Present when callers receive the transport-neutral full response. */\n responseType?: 'full';\n /**\n * `@Queue(...)` override, else `${ApiClassName}-${methodName}`.\n *\n * ONLY on a `cloudtasks` or `cron` method — those are the kinds actually delivered through a\n * named queue or schedule, and Terraform matches on this string. A synchronous `rpc` (or an\n * inbound `external`) endpoint has no queue and needs none; emitting a plausible-looking name for\n * one put every synchronous endpoint one naive `methods.map(m => m.queueName)` away from being\n * provisioned as a queue.\n */\n queueName?: string;\n /**\n * WHO outside this repo drives this endpoint, from `@Endpoint(POST, p, WRITE, EXTERNAL, { calledBy })`.\n *\n * ONLY on an `external` method, the same way `queueName` is only on the kinds that HAVE a queue.\n *\n * Deliberately the SAME {@link ExternalSystemDeclaration} the OUTBOUND `@externalSystem` tag\n * resolves to, not a parallel inbound-only type: an inbound `saas twilio` and an outbound\n * `saas twilio` are the same vendor, so sharing the type makes them share an IDENTITY and\n * converge on ONE node instead of drawing twilio twice facing opposite directions.\n *\n * Optional in the TYPE only for graphs generated before the caller was required — generation\n * FAILS on an `external` method whose caller cannot be read (UndeclaredExternalCallerError).\n */\n caller?: ExternalSystemDeclaration;\n}\n\n/**\n * A discovered API contract class: its name, the api-lib project that owns it, its transport, and\n * its per-method trigger table.\n */\nexport interface ApiClassInfo {\n api: string;\n owner: string;\n type: ApiTransport;\n /** The class's @ApiPath basePath; absent for an external (vendor) contract, which has no route. */\n basePath?: string;\n /**\n * Every @Endpoint method, in declaration order. Empty for an external contract (a vendor\n * interface has no endpoints — it is called through a vendor SDK, not mounted).\n */\n methods: ApiMethodMeta[];\n /**\n * Set when the contract carries an `@externalSystem <kind> [label]` JSDoc tag — a vendor seam\n * declaring WHAT it is a seam to. JSDoc rather than a decorator because these seams are plain TS\n * `interface`s, which cannot carry one.\n */\n externalSystem?: ExternalSystemDeclaration;\n}\n\n/** The `(kind, label)` pair a single declaration resolves to. */\nexport interface ExternalSystemDeclaration {\n kind: ExternalSystemKind;\n label: string;\n}\n\n/**\n * The committed, per-contract view written to `architecture/apis/<ApiName>.json` (one file per API;\n * dependencies.json only links to it under `apiContractFiles` — see ApiContractFiles).\n *\n * The runtime graph is derived from the committed files so generate and validate can never\n * diverge — which means anything the runtime graph needs must be COMMITTED, not re-scanned.\n * Per-method trigger kinds and queue names are exactly that: without this table the derivation\n * cannot tell a queued endpoint from a cron sweep, and cannot name the queue between two services.\n */\nexport interface ApiContract {\n owner: string;\n /** 'rpc' | 'pubsub' for an in-repo contract, 'external' for a vendor seam. */\n apiKind: ApiTransport;\n /**\n * REQUIRED. Every routed contract carries `@ApiPath`, so every entry in this table must carry the\n * base path its methods hang off. Optional was worse than absent: a consumer joining\n * `basePath + path` for the ONE entry that lost it computed `/test` where the real route was\n * `/whatsapp/test`, and had no reason to suspect it — every other entry had the field. Generation\n * now FAILS instead of shipping an entry that computes a confidently wrong URL.\n */\n basePath: string;\n methods: ApiMethodMeta[];\n}\n\n/** apiClassName -> its committed contract. Serialized as one `architecture/apis/<ApiName>.json` each. */\nexport type ApiContracts = Record<string, ApiContract>;\n\n/**\n * An `addRoutes`/`createRpcClient`/`createPubSubClient` first argument that resolved to an\n * abstract class in a DECLARATION file which owns no indexed contract. Unambiguously a broken\n * scan (a real api-lib whose source we never indexed), never a \"this isn't an API\" argument —\n * so it is reported loudly instead of collapsing into a silent `return null`.\n */\nexport class UnresolvedApiCall {\n constructor(\n /** The project whose source makes the call. */\n public readonly project: string,\n /** The contract class name as written at the call site. */\n public readonly api: string,\n /** `path/to/file.ts:LINE` of the call site, workspace-relative. */\n public readonly at: string,\n /** The declaration file the checker resolved to (where decorators are erased). */\n public readonly declaredIn: string,\n ) {}\n}\n\n/**\n * ONE decorator argument the scan saw but could not reduce to a string — `@ApiPath(SOME_CONST)`\n * where SOME_CONST is imported from another module, a computed expression, an enum member, ...\n *\n * Recorded rather than dropped. Before this existed, an unresolvable argument cost the contract its\n * basePath, or a method, or (when EVERY method's path was one) the whole class — with nothing\n * printed anywhere. Same-module constants now resolve, so what remains here is the genuinely\n * unresolvable, which the author can fix by inlining the literal or moving the constant in-module.\n */\nexport class NonLiteralDecoratorArg {\n constructor(\n /** The contract class the argument was written on. */\n public readonly api: string,\n /** `ApiPath` | `Endpoint` | `Queue`. */\n public readonly decorator: string,\n /** The method name for a member decorator, null for a class decorator. */\n public readonly method: string | null,\n /** The argument exactly as written, e.g. `WHATSAPP_API_PATH`. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/**\n * ONE `@Endpoint(path, kind)` whose PATH argument was present but could not be reduced to a string.\n *\n * Split out of NonLiteralDecoratorArg (which stays a warning, covering @Queue and the rest) because\n * this one is FATAL. Upstream components need the URL: an http client builds its request as\n * `basePath + path`, so an unreadable path is missing ROUTING, not missing metadata — the same\n * reasoning that already makes basePath required. Skipping the method instead used to delete it, and\n * a class whose every path was a constant lost every method and vanished from the contract table with\n * nothing printed anywhere.\n */\nexport class UnresolvedEndpointPath {\n constructor(\n /** The contract class the method is declared on. */\n public readonly api: string,\n /** The method name. */\n public readonly method: string,\n /** The path argument exactly as written, e.g. `PROCESS_PATH`. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/**\n * ONE `external` `@Endpoint` whose CALLER could not be read from the source.\n *\n * Fatal for the same reason {@link UnresolvedEndpointPath} is. The inbound box exists to say who is\n * calling us from outside; with no caller it can only restate our own contract name, which is the\n * exact bug this diagnostic exists to make impossible to reintroduce. `@Endpoint`'s TS overloads\n * already require `calledBy`, so anything reaching here is a JS caller, an `as any`, a cross-module\n * constant the parser-only scan cannot fold, or an unknown `callerKind`.\n */\nexport class UndeclaredExternalCaller {\n constructor(\n /** The contract class the method is declared on. */\n public readonly api: string,\n /** The method name. */\n public readonly method: string,\n /** What was wrong, as written — `<missing>`, `SOME_CONST`, `callerKind: 'vendor'`. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/** ONE `@Endpoint` whose required side-effect declaration could not be read from source. */\nexport class UndeclaredEndpointOperation {\n constructor(\n /** The contract class the method is declared on. */\n public readonly api: string,\n /** The method name. */\n public readonly method: string,\n /** What was wrong, as written — `<missing>`, a constant name, or an invalid literal. */\n public readonly argument: string,\n /** `path/to/file.ts:LINE`, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/**\n * A contract class that DECLARED `@Endpoint` methods and kept none of them.\n *\n * The backstop for the mechanism that hid an entire service: `buildApiContracts` skips a zero-method\n * class (correctly — a vendor seam has no routes), so a class gutted by unreadable decorator\n * arguments left through the same door as a legitimately routeless one. A class that declared\n * endpoints and produced none is never legitimate, so it is named instead.\n */\nexport class EmptiedApiContract {\n constructor(\n /** The contract class name. */\n public readonly api: string,\n /** How many `@Endpoint` decorators were written on it. */\n public readonly declared: number,\n /** `path/to/file.ts:LINE` of the class, workspace-relative. */\n public readonly at: string,\n ) {}\n}\n\n/** Derive the relation kind from the (possibly empty) implements/uses ref lists. */\n// webpieces-disable no-function-outside-class -- pure data helper for these serialization DTOs\nexport function deriveApiRelationKind(\n implementsRefs: ApiRef[],\n usesRefs: ApiRef[],\n): ApiRelationKind {\n if (implementsRefs.length > 0 && usesRefs.length > 0) return 'uses-implements';\n if (implementsRefs.length > 0) return 'implements';\n return 'uses';\n}\n\n/**\n * Stable-sort a ref list by api name, then by target service, so the committed JSON is\n * deterministic even when one api is used against two different services.\n */\n// webpieces-disable no-function-outside-class -- pure data helper for these serialization DTOs\nexport function sortApiRefs(refs: ApiRef[]): ApiRef[] {\n return [...refs].sort(\n (a: ApiRef, b: ApiRef) =>\n a.api.localeCompare(b.api) ||\n (a.targetService ?? '').localeCompare(b.targetService ?? ''),\n );\n}\n"]}
@@ -10,6 +10,7 @@ export declare class RuntimeDetails {
10
10
  readonly nodes: Record<string, RuntimeNodeDetails>;
11
11
  readonly edges: Record<string, string[]>;
12
12
  constructor(graph: RuntimeGraph, showExternalNodes: boolean, model?: GraphRenderModel | null);
13
+ private wiringUses;
13
14
  /** A reviewed remote service's node details: the contracts its declaration says it implements. */
14
15
  private remoteNodes;
15
16
  /**
@@ -19,5 +20,6 @@ export declare class RuntimeDetails {
19
20
  private remoteCalls;
20
21
  /** Bind detail facts to renderer-owned identities, including grouped queue hops. */
21
22
  private renderEdges;
23
+ private conditions;
22
24
  script(): string;
23
25
  }
@@ -45,13 +45,7 @@ class RuntimeDetails {
45
45
  implemented.push(`${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} via ${ref.declaredVia ?? name}` +
46
46
  (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`));
47
47
  }
48
- for (const ref of service.wiringUses ?? []) {
49
- used.push(`${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} → ${ref.targetService ?? 'unknown external target'} via ${ref.declaredVia ?? name}` +
50
- (ref.conditional === undefined
51
- ? ''
52
- : ` conditional on ${ref.conditional}`) +
53
- (ref.methodsInferred ? ' — queue methods inferred from contract' : ''));
54
- }
48
+ used.push(...this.wiringUses(graph, name));
55
49
  for (const [identity, system] of Object.entries(graph.externalSystems ?? {})) {
56
50
  if (!system.usedBy.includes(name))
57
51
  continue;
@@ -71,6 +65,10 @@ class RuntimeDetails {
71
65
  if (model !== null)
72
66
  this.renderEdges(graph, model);
73
67
  }
68
+ wiringUses(graph, name) {
69
+ return (graph.services[name].wiringUses ?? []).map((ref) => `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} → ${ref.targetService ?? 'unknown external target'} (${ref.type})` +
70
+ (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`));
71
+ }
74
72
  /** A reviewed remote service's node details: the contracts its declaration says it implements. */
75
73
  remoteNodes(graph) {
76
74
  for (const [name, remote] of Object.entries(graph.remoteServices ?? {}))
@@ -111,33 +109,24 @@ class RuntimeDetails {
111
109
  for (const call of via) {
112
110
  for (const api of call.via) {
113
111
  entries.push(`${graph.apis[api]?.owner ?? '?'}#${api} → ${call.to} (${call.type ?? 'rpc'})` +
114
- (call.queue === undefined ? '' : ` via ${call.queue}`));
115
- for (const ref of graph.services[call.from]?.wiringUses ?? []) {
116
- if (ref.api !== api ||
117
- (ref.targetService !== undefined && ref.targetService !== call.to))
118
- continue;
119
- entries.push(`${ref.api} via ${ref.declaredVia ?? call.from}` +
120
- (ref.conditional === undefined
121
- ? ''
122
- : ` conditional on ${ref.conditional}`) +
123
- (ref.methodsInferred
124
- ? ' — queue methods inferred from contract'
125
- : ''));
126
- }
112
+ (call.queue === undefined ? '' : ` via ${call.queue}`) +
113
+ this.conditions(graph, call, api));
127
114
  }
128
115
  }
129
- for (const ref of graph.services[service]?.wiringUses ?? []) {
130
- if (!entries.some((entry) => entry.includes(`#${ref.api} →`)))
131
- continue;
132
- entries.push(`${ref.api} via ${ref.declaredVia ?? service}` +
133
- (ref.conditional === undefined
134
- ? ''
135
- : ` conditional on ${ref.conditional}`) +
136
- (ref.methodsInferred ? ' — queue methods inferred from contract' : ''));
137
- }
138
116
  this.edges[identity] = [...new Set(entries)];
139
117
  }
140
118
  }
119
+ conditions(graph, call, api) {
120
+ const conditions = (graph.services[call.from]?.wiringUses ?? [])
121
+ .filter((ref) => ref.api === api &&
122
+ ref.type === call.type &&
123
+ (ref.targetService === undefined || ref.targetService === call.to))
124
+ .map((ref) => ref.conditional)
125
+ .filter((condition) => condition !== undefined);
126
+ return conditions.length === 0
127
+ ? ''
128
+ : ` conditional on ${[...new Set(conditions)].join(' or ')}`;
129
+ }
141
130
  script() {
142
131
  const encoded = JSON.stringify(this).replace(/</g, '\\u003c');
143
132
  return `new (${RuntimeDetailsController.toString()})(${encoded});`;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-details.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-details.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GraphRenderModel } from './graph-render-model';\nimport type { RuntimeGraph, RuntimeRemoteCall } from './runtime-graph-model';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\n\nexport class RuntimeNodeDetails {\n constructor(\n public readonly implemented: string[],\n public readonly used: string[],\n ) {}\n}\n\n/** View data stays complete even when external destinations are hidden from DOT. */\nexport class RuntimeDetails {\n readonly nodes: Record<string, RuntimeNodeDetails> = {};\n readonly edges: Record<string, string[]> = {};\n\n constructor(\n graph: RuntimeGraph,\n showExternalNodes: boolean,\n model: GraphRenderModel | null = null,\n ) {\n for (const [name, service] of Object.entries(graph.services)) {\n const implemented = service.implements.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api}` +\n (service.implementsVia?.[api] === undefined\n ? ''\n : ` via ${service.implementsVia[api]}`),\n );\n const used: string[] = [];\n for (const edge of graph.runtimeEdges.filter((edge) => edge.from === name)) {\n const entries = edge.via.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${edge.to} (${edge.type ?? 'rpc'})` +\n (edge.queue === undefined ? '' : ` via ${edge.queue}`),\n );\n if (service.wiringUses === undefined) used.push(...entries);\n if (edge.type !== 'pubsub') this.edges[`${edge.from}->${edge.to}`] = entries;\n }\n const remoteApis = this.remoteCalls(graph, name, service.wiringUses === undefined ? used : []);\n for (const api of service.wiringUses === undefined ? service.uses : []) {\n if (graph.runtimeEdges.some((edge) => edge.from === name && edge.via.includes(api)))\n continue;\n if (remoteApis.has(api)) continue;\n const contract = graph.apis[api];\n const external =\n contract?.type === 'external' || contract?.implementedBy.length === 0;\n used.push(\n `${contract?.owner ?? '?'}#${api} → ${external ? (contract?.externalSystem?.label ?? contract?.owner ?? 'unknown external') : 'unknown / inferred target'}` +\n ` (${contract?.type ?? 'rpc'})${external && !showExternalNodes ? ' — destination hidden' : ''}`,\n );\n }\n for (const ref of service.wiringImplements ?? []) {\n implemented.push(\n `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} via ${ref.declaredVia ?? name}` +\n (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`),\n );\n }\n for (const ref of service.wiringUses ?? []) {\n used.push(\n `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} → ${ref.targetService ?? 'unknown external target'} via ${ref.declaredVia ?? name}` +\n (ref.conditional === undefined\n ? ''\n : ` conditional on ${ref.conditional}`) +\n (ref.methodsInferred ? ' — queue methods inferred from contract' : ''),\n );\n }\n for (const [identity, system] of Object.entries(graph.externalSystems ?? {})) {\n if (!system.usedBy.includes(name)) continue;\n this.edges[`${name}->system__${identity.replace(/[^a-zA-Z0-9_]/g, '_')}`] =\n system.apis.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${system.label} (${system.kind})`,\n );\n }\n for (const use of graph.unresolvedUses.filter((use) => use.service === name)) {\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n const owner = graph.apis[use.api]?.owner ?? use.api;\n const key = `${name}->external__${owner}`;\n (this.edges[key] ??= []).push(`${owner}#${use.api} → ${owner} (external)`);\n }\n this.nodes[name] = new RuntimeNodeDetails(implemented, [...new Set(used)]);\n }\n this.remoteNodes(graph);\n if (model !== null) this.renderEdges(graph, model);\n }\n\n /** A reviewed remote service's node details: the contracts its declaration says it implements. */\n private remoteNodes(graph: RuntimeGraph): void {\n for (const [name, remote] of Object.entries(graph.remoteServices ?? {}))\n this.nodes[new RemoteServiceNodeId().of(name)] = new RuntimeNodeDetails(\n remote.implements.map((api: string) => `${api} — declared in remoteServices.${name}`),\n [],\n );\n }\n\n /**\n * The edge details for `name`'s calls into reviewed remote services (#1203), also appended to\n * `used`; returns the contracts covered so they are not reported as an unknown target.\n */\n private remoteCalls(graph: RuntimeGraph, name: string, used: string[]): Set<string> {\n const covered = new Set<string>();\n for (const [remoteName, remote] of Object.entries(graph.remoteServices ?? {}))\n for (const call of remote.calledBy.filter((entry: RuntimeRemoteCall) => entry.from === name)) {\n const entries = call.via.map(\n (api: string) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${remoteName} (remote service, ${call.type})`,\n );\n for (const api of call.via) covered.add(api);\n used.push(...entries);\n const key = `${name}->${new RemoteServiceNodeId().of(remoteName)}`;\n this.edges[key] = [...(this.edges[key] ?? []), ...entries];\n }\n return covered;\n }\n\n /** Bind detail facts to renderer-owned identities, including grouped queue hops. */\n private renderEdges(graph: RuntimeGraph, model: GraphRenderModel): void {\n for (const edge of model.edges) {\n const identity = `${edge.from}->${edge.to}`;\n const service = graph.services[edge.from] === undefined ? edge.to : edge.from;\n const queue = model.nodes.find(\n (node) => node.id === (edge.from.startsWith('queue__') ? edge.from : edge.to),\n );\n const via = graph.runtimeEdges.filter((call) => {\n if (call.type !== 'pubsub') return call.from === edge.from && call.to === edge.to;\n if (call.from !== service && call.to !== service) return false;\n return (\n queue !== undefined &&\n queue.queueKeys.includes(call.queue ?? `${call.from}->${call.to}`)\n );\n });\n const entries = [...(this.edges[identity] ?? [])];\n for (const call of via) {\n for (const api of call.via) {\n entries.push(\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${call.to} (${call.type ?? 'rpc'})` +\n (call.queue === undefined ? '' : ` via ${call.queue}`),\n );\n for (const ref of graph.services[call.from]?.wiringUses ?? []) {\n if (\n ref.api !== api ||\n (ref.targetService !== undefined && ref.targetService !== call.to)\n )\n continue;\n entries.push(\n `${ref.api} via ${ref.declaredVia ?? call.from}` +\n (ref.conditional === undefined\n ? ''\n : ` conditional on ${ref.conditional}`) +\n (ref.methodsInferred\n ? ' — queue methods inferred from contract'\n : ''),\n );\n }\n }\n }\n for (const ref of graph.services[service]?.wiringUses ?? []) {\n if (!entries.some((entry) => entry.includes(`#${ref.api} →`))) continue;\n entries.push(\n `${ref.api} via ${ref.declaredVia ?? service}` +\n (ref.conditional === undefined\n ? ''\n : ` conditional on ${ref.conditional}`) +\n (ref.methodsInferred ? ' — queue methods inferred from contract' : ''),\n );\n }\n this.edges[identity] = [...new Set(entries)];\n }\n }\n\n script(): string {\n const encoded = JSON.stringify(this).replace(/</g, '\\\\u003c');\n return `new (${RuntimeDetailsController.toString()})(${encoded});`;\n }\n}\n\n/** Explicit click controller; redraws rebind edge controls and dismiss stale anchors. */\nclass RuntimeDetailsController {\n private readonly host = document.getElementById('graph');\n private readonly panel = document.createElement('div');\n private anchor: SVGElement | null = null;\n private identity = '';\n private implemented = false;\n\n constructor(private readonly data: RuntimeDetails) {\n if (this.host === null) return;\n this.panel.className = 'wp-graph-details';\n this.panel.hidden = true;\n this.panel.setAttribute('role', 'dialog');\n this.panel.setAttribute('aria-label', 'API relationships');\n document.body.append(this.panel);\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n document.addEventListener('wp-runtime-implements', (event) => {\n const detail = (event as CustomEvent<RuntimeImplementsPayload>).detail;\n this.open(\n detail.node,\n this.data.nodes[detail.name]?.implemented ?? [],\n detail.name,\n true,\n );\n });\n document.addEventListener('keydown', (event) => {\n if (event.key === 'Escape' && !this.panel.hidden) {\n event.stopPropagation();\n this.close();\n }\n });\n document.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n !this.panel.contains(event.target) &&\n !this.anchor?.contains(event.target)\n )\n this.close();\n });\n document.addEventListener('wp-graph-transform', () => this.position());\n this.host.addEventListener('scroll', () => this.position());\n window.addEventListener('resize', () => this.position());\n }\n\n private position(): void {\n if (this.anchor === null || this.panel.hidden) return;\n const rect = this.anchor.getBoundingClientRect();\n this.panel.style.left = `${Math.max(12, Math.min(rect.left, innerWidth - this.panel.offsetWidth - 12))}px`;\n this.panel.style.top = `${Math.max(12, Math.min(rect.bottom + 6, innerHeight - this.panel.offsetHeight - 12))}px`;\n }\n\n private close(): void {\n if (this.panel.hidden) return;\n const old = this.anchor;\n this.panel.hidden = true;\n this.anchor = null;\n if (old?.isConnected) old.focus();\n }\n\n private open(\n element: SVGElement,\n entries: string[],\n identity: string,\n implemented: boolean,\n ): void {\n this.anchor = element;\n this.identity = identity;\n this.implemented = implemented;\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = 'Close';\n close.onclick = () => this.close();\n const list = document.createElement('ul');\n for (const entry of entries.length === 0\n ? ['No implemented contracts recorded in this saved snapshot.']\n : entries) {\n const item = document.createElement('li');\n item.textContent = entry;\n list.append(item);\n }\n this.panel.replaceChildren(close, list);\n this.panel.hidden = false;\n this.position();\n close.focus({ preventScroll: true });\n }\n\n private attach(): void {\n for (const group of Array.from(this.host?.querySelectorAll<SVGGElement>('g.edge') ?? [])) {\n const identity = group.querySelector('title')?.textContent ?? '';\n const entries = this.data.edges[identity];\n if (entries === undefined || entries.length === 0) continue;\n const element = group.querySelector('text');\n if (element !== null && element.dataset['wpDetails'] !== 'true')\n this.attachElement(element, entries, identity);\n }\n if (this.panel.hidden || this.anchor?.isConnected) return;\n const groups = Array.from(\n this.host?.querySelectorAll<SVGGElement>(this.implemented ? 'g.node' : 'g.edge') ?? [],\n );\n const group = groups.find(\n (node) => node.querySelector('title')?.textContent === this.identity,\n );\n const replacement = this.implemented ? group : group?.querySelector('text');\n if (replacement) {\n this.anchor = replacement;\n this.position();\n } else this.close();\n }\n\n private attachElement(element: SVGTextElement, entries: string[], identity: string): void {\n element.dataset['wpDetails'] = 'true';\n element.classList.add('wp-api-detail');\n element.setAttribute('tabindex', '0');\n element.setAttribute('role', 'button');\n element.setAttribute('aria-label', `Uses: ${identity}`);\n element.setAttribute('aria-haspopup', 'dialog');\n element.addEventListener('click', (event) => {\n event.stopPropagation();\n this.open(element, entries, identity, false);\n });\n element.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n this.open(element, entries, identity, false);\n }\n });\n }\n}\n"]}
1
+ {"version":3,"file":"runtime-details.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-details.ts"],"names":[],"mappings":"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","sourcesContent":["import type { GraphRenderModel } from './graph-render-model';\nimport type { RuntimeEdge, RuntimeGraph, RuntimeRemoteCall } from './runtime-graph-model';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\n\nexport class RuntimeNodeDetails {\n constructor(\n public readonly implemented: string[],\n public readonly used: string[],\n ) {}\n}\n\n/** View data stays complete even when external destinations are hidden from DOT. */\nexport class RuntimeDetails {\n readonly nodes: Record<string, RuntimeNodeDetails> = {};\n readonly edges: Record<string, string[]> = {};\n\n constructor(\n graph: RuntimeGraph,\n showExternalNodes: boolean,\n model: GraphRenderModel | null = null,\n ) {\n for (const [name, service] of Object.entries(graph.services)) {\n const implemented = service.implements.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api}` +\n (service.implementsVia?.[api] === undefined\n ? ''\n : ` via ${service.implementsVia[api]}`),\n );\n const used: string[] = [];\n for (const edge of graph.runtimeEdges.filter((edge) => edge.from === name)) {\n const entries = edge.via.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${edge.to} (${edge.type ?? 'rpc'})` +\n (edge.queue === undefined ? '' : ` via ${edge.queue}`),\n );\n if (service.wiringUses === undefined) used.push(...entries);\n if (edge.type !== 'pubsub') this.edges[`${edge.from}->${edge.to}`] = entries;\n }\n const remoteApis = this.remoteCalls(\n graph,\n name,\n service.wiringUses === undefined ? used : [],\n );\n for (const api of service.wiringUses === undefined ? service.uses : []) {\n if (graph.runtimeEdges.some((edge) => edge.from === name && edge.via.includes(api)))\n continue;\n if (remoteApis.has(api)) continue;\n const contract = graph.apis[api];\n const external =\n contract?.type === 'external' || contract?.implementedBy.length === 0;\n used.push(\n `${contract?.owner ?? '?'}#${api} → ${external ? (contract?.externalSystem?.label ?? contract?.owner ?? 'unknown external') : 'unknown / inferred target'}` +\n ` (${contract?.type ?? 'rpc'})${external && !showExternalNodes ? ' — destination hidden' : ''}`,\n );\n }\n for (const ref of service.wiringImplements ?? []) {\n implemented.push(\n `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} via ${ref.declaredVia ?? name}` +\n (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`),\n );\n }\n used.push(...this.wiringUses(graph, name));\n for (const [identity, system] of Object.entries(graph.externalSystems ?? {})) {\n if (!system.usedBy.includes(name)) continue;\n this.edges[`${name}->system__${identity.replace(/[^a-zA-Z0-9_]/g, '_')}`] =\n system.apis.map(\n (api) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${system.label} (${system.kind})`,\n );\n }\n for (const use of graph.unresolvedUses.filter((use) => use.service === name)) {\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n const owner = graph.apis[use.api]?.owner ?? use.api;\n const key = `${name}->external__${owner}`;\n (this.edges[key] ??= []).push(`${owner}#${use.api} → ${owner} (external)`);\n }\n this.nodes[name] = new RuntimeNodeDetails(implemented, [...new Set(used)]);\n }\n this.remoteNodes(graph);\n if (model !== null) this.renderEdges(graph, model);\n }\n\n private wiringUses(graph: RuntimeGraph, name: string): string[] {\n return (graph.services[name].wiringUses ?? []).map(\n (ref) =>\n `${graph.apis[ref.api]?.owner ?? '?'}#${ref.api} → ${ref.targetService ?? 'unknown external target'} (${ref.type})` +\n (ref.conditional === undefined ? '' : ` conditional on ${ref.conditional}`),\n );\n }\n\n /** A reviewed remote service's node details: the contracts its declaration says it implements. */\n private remoteNodes(graph: RuntimeGraph): void {\n for (const [name, remote] of Object.entries(graph.remoteServices ?? {}))\n this.nodes[new RemoteServiceNodeId().of(name)] = new RuntimeNodeDetails(\n remote.implements.map(\n (api: string) => `${api} — declared in remoteServices.${name}`,\n ),\n [],\n );\n }\n\n /**\n * The edge details for `name`'s calls into reviewed remote services (#1203), also appended to\n * `used`; returns the contracts covered so they are not reported as an unknown target.\n */\n private remoteCalls(graph: RuntimeGraph, name: string, used: string[]): Set<string> {\n const covered = new Set<string>();\n for (const [remoteName, remote] of Object.entries(graph.remoteServices ?? {}))\n for (const call of remote.calledBy.filter(\n (entry: RuntimeRemoteCall) => entry.from === name,\n )) {\n const entries = call.via.map(\n (api: string) =>\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${remoteName} (remote service, ${call.type})`,\n );\n for (const api of call.via) covered.add(api);\n used.push(...entries);\n const key = `${name}->${new RemoteServiceNodeId().of(remoteName)}`;\n this.edges[key] = [...(this.edges[key] ?? []), ...entries];\n }\n return covered;\n }\n\n /** Bind detail facts to renderer-owned identities, including grouped queue hops. */\n private renderEdges(graph: RuntimeGraph, model: GraphRenderModel): void {\n for (const edge of model.edges) {\n const identity = `${edge.from}->${edge.to}`;\n const service = graph.services[edge.from] === undefined ? edge.to : edge.from;\n const queue = model.nodes.find(\n (node) => node.id === (edge.from.startsWith('queue__') ? edge.from : edge.to),\n );\n const via = graph.runtimeEdges.filter((call) => {\n if (call.type !== 'pubsub') return call.from === edge.from && call.to === edge.to;\n if (call.from !== service && call.to !== service) return false;\n return (\n queue !== undefined &&\n queue.queueKeys.includes(call.queue ?? `${call.from}->${call.to}`)\n );\n });\n const entries = [...(this.edges[identity] ?? [])];\n for (const call of via) {\n for (const api of call.via) {\n entries.push(\n `${graph.apis[api]?.owner ?? '?'}#${api} → ${call.to} (${call.type ?? 'rpc'})` +\n (call.queue === undefined ? '' : ` via ${call.queue}`) +\n this.conditions(graph, call, api),\n );\n }\n }\n this.edges[identity] = [...new Set(entries)];\n }\n }\n\n private conditions(graph: RuntimeGraph, call: RuntimeEdge, api: string): string {\n const conditions = (graph.services[call.from]?.wiringUses ?? [])\n .filter(\n (ref) =>\n ref.api === api &&\n ref.type === call.type &&\n (ref.targetService === undefined || ref.targetService === call.to),\n )\n .map((ref) => ref.conditional)\n .filter((condition) => condition !== undefined);\n return conditions.length === 0\n ? ''\n : ` conditional on ${[...new Set(conditions)].join(' or ')}`;\n }\n\n script(): string {\n const encoded = JSON.stringify(this).replace(/</g, '\\\\u003c');\n return `new (${RuntimeDetailsController.toString()})(${encoded});`;\n }\n}\n\n/** Explicit click controller; redraws rebind edge controls and dismiss stale anchors. */\nclass RuntimeDetailsController {\n private readonly host = document.getElementById('graph');\n private readonly panel = document.createElement('div');\n private anchor: SVGElement | null = null;\n private identity = '';\n private implemented = false;\n\n constructor(private readonly data: RuntimeDetails) {\n if (this.host === null) return;\n this.panel.className = 'wp-graph-details';\n this.panel.hidden = true;\n this.panel.setAttribute('role', 'dialog');\n this.panel.setAttribute('aria-label', 'API relationships');\n document.body.append(this.panel);\n new MutationObserver(() => this.attach()).observe(this.host, { childList: true });\n this.attach();\n document.addEventListener('wp-runtime-implements', (event) => {\n const detail = (event as CustomEvent<RuntimeImplementsPayload>).detail;\n this.open(\n detail.node,\n this.data.nodes[detail.name]?.implemented ?? [],\n detail.name,\n true,\n );\n });\n document.addEventListener('keydown', (event) => {\n if (event.key === 'Escape' && !this.panel.hidden) {\n event.stopPropagation();\n this.close();\n }\n });\n document.addEventListener('click', (event) => {\n if (\n event.target instanceof Node &&\n !this.panel.contains(event.target) &&\n !this.anchor?.contains(event.target)\n )\n this.close();\n });\n document.addEventListener('wp-graph-transform', () => this.position());\n this.host.addEventListener('scroll', () => this.position());\n window.addEventListener('resize', () => this.position());\n }\n\n private position(): void {\n if (this.anchor === null || this.panel.hidden) return;\n const rect = this.anchor.getBoundingClientRect();\n this.panel.style.left = `${Math.max(12, Math.min(rect.left, innerWidth - this.panel.offsetWidth - 12))}px`;\n this.panel.style.top = `${Math.max(12, Math.min(rect.bottom + 6, innerHeight - this.panel.offsetHeight - 12))}px`;\n }\n\n private close(): void {\n if (this.panel.hidden) return;\n const old = this.anchor;\n this.panel.hidden = true;\n this.anchor = null;\n if (old?.isConnected) old.focus();\n }\n\n private open(\n element: SVGElement,\n entries: string[],\n identity: string,\n implemented: boolean,\n ): void {\n this.anchor = element;\n this.identity = identity;\n this.implemented = implemented;\n const close = document.createElement('button');\n close.type = 'button';\n close.textContent = 'Close';\n close.onclick = () => this.close();\n const list = document.createElement('ul');\n for (const entry of entries.length === 0\n ? ['No implemented contracts recorded in this saved snapshot.']\n : entries) {\n const item = document.createElement('li');\n item.textContent = entry;\n list.append(item);\n }\n this.panel.replaceChildren(close, list);\n this.panel.hidden = false;\n this.position();\n close.focus({ preventScroll: true });\n }\n\n private attach(): void {\n for (const group of Array.from(this.host?.querySelectorAll<SVGGElement>('g.edge') ?? [])) {\n const identity = group.querySelector('title')?.textContent ?? '';\n const entries = this.data.edges[identity];\n if (entries === undefined || entries.length === 0) continue;\n const element = group.querySelector('text');\n if (element !== null && element.dataset['wpDetails'] !== 'true')\n this.attachElement(element, entries, identity);\n }\n if (this.panel.hidden || this.anchor?.isConnected) return;\n const groups = Array.from(\n this.host?.querySelectorAll<SVGGElement>(this.implemented ? 'g.node' : 'g.edge') ?? [],\n );\n const group = groups.find(\n (node) => node.querySelector('title')?.textContent === this.identity,\n );\n const replacement = this.implemented ? group : group?.querySelector('text');\n if (replacement) {\n this.anchor = replacement;\n this.position();\n } else this.close();\n }\n\n private attachElement(element: SVGTextElement, entries: string[], identity: string): void {\n element.dataset['wpDetails'] = 'true';\n element.classList.add('wp-api-detail');\n element.setAttribute('tabindex', '0');\n element.setAttribute('role', 'button');\n element.setAttribute('aria-label', `Uses: ${identity}`);\n element.setAttribute('aria-haspopup', 'dialog');\n element.addEventListener('click', (event) => {\n event.stopPropagation();\n this.open(element, entries, identity, false);\n });\n element.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' || event.key === ' ') {\n event.preventDefault();\n event.stopPropagation();\n this.open(element, entries, identity, false);\n }\n });\n }\n}\n"]}
@@ -107,10 +107,8 @@ export interface RuntimeEdge {
107
107
  /**
108
108
  * One Cloud Tasks queue: the async seam between a producer and a consumer, at METHOD granularity.
109
109
  *
110
- * `producedBy` and `consumedBy` are deliberately not symmetric in confidence — see
111
- * {@link ApiRef.methodsInferred}. The consumer is derived from `addRoutes` plus the contract's
112
- * method table and is exact; the producer is attributed to every queued method of the contract it
113
- * built a client for, because which methods it enqueues is not statically recoverable.
110
+ * Only exact method declarations may populate producer facts. Contract-only client wiring
111
+ * produces contract-level edges without a method queue, regardless of the endpoint table.
114
112
  */
115
113
  export interface RuntimeQueue {
116
114
  api: string;
@@ -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 * `producedBy` and `consumedBy` are deliberately not symmetric in confidence — see\n * {@link ApiRef.methodsInferred}. The consumer is derived from `addRoutes` plus the contract's\n * method table and is exact; the producer is attributed to every queued method of the contract it\n * built a client for, because which methods it enqueues is not statically recoverable.\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","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"]}
@@ -120,8 +120,8 @@ class RuntimeGraphDeriver {
120
120
  hiddenProjects,
121
121
  /**
122
122
  * The per-contract method tables, read from the generated `architecture/apis/<Api>.json`
123
- * files (ApiContractFiles). A contract with no entry — no file, or no endpoints — makes every
124
- * pubsub edge through it fall back to one unnamed queue per service pair instead of failing.
123
+ * files (ApiContractFiles). Pubsub edges remain at the contract level, independently
124
+ * of method metadata: an endpoint table does not declare which methods a client uses.
125
125
  */
126
126
  apiContracts = {},
127
127
  /**
@@ -358,7 +358,6 @@ class RuntimeGraphDeriver {
358
358
  buildEdges(decls, apis) {
359
359
  const viaByKey = new Map();
360
360
  const unresolved = [];
361
- const queues = new Map();
362
361
  for (const decl of decls) {
363
362
  for (const ref of decl.usesApis) {
364
363
  // A named target outside this workspace (#1203) is a remote service, not an implementer
@@ -379,10 +378,6 @@ class RuntimeGraphDeriver {
379
378
  // was hiding the single most interesting thing about a queue.
380
379
  if (target === decl.name && ref.type !== 'pubsub')
381
380
  continue;
382
- if (ref.type === 'pubsub') {
383
- this.addQueuedEdges(decl.name, target, ref.api, viaByKey, queues);
384
- continue;
385
- }
386
381
  const key = `${decl.name} ${target} ${ref.type}`;
387
382
  if (!viaByKey.has(key))
388
383
  viaByKey.set(key, new Set());
@@ -393,53 +388,10 @@ class RuntimeGraphDeriver {
393
388
  return {
394
389
  edges: this.edgesFromKeys(viaByKey),
395
390
  unresolved: (0, runtime_graph_sorters_1.sortUnresolved)(unresolved),
396
- queues: (0, runtime_graph_sorters_1.sortedQueues)(queues),
391
+ // Client wiring declares contracts, never the methods a producer enqueues.
392
+ queues: {},
397
393
  };
398
394
  }
399
- /**
400
- * One edge PER QUEUED METHOD of `api`, plus the queue each flows through.
401
- *
402
- * Per method rather than per service pair because that is the unit Cloud Tasks and Terraform
403
- * create: two services exchanging three queued methods run three independently-configured,
404
- * independently-backed-up queues, and collapsing them into one arrow hides which one is stuck.
405
- *
406
- * A contract with no method table (no `architecture/apis/<Api>.json` for it)
407
- * degrades to a single unnamed queue for the pair — the old behavior — rather than vanishing.
408
- */
409
- addQueuedEdges(from, to, api, viaByKey, queues) {
410
- const queued = this.methodsOf(api).filter((m) => m.kind === 'cloudtasks');
411
- if (queued.length === 0) {
412
- const key = `${from} ${to} pubsub `;
413
- if (!viaByKey.has(key))
414
- viaByKey.set(key, new Set());
415
- viaByKey.get(key).add(api);
416
- return;
417
- }
418
- for (const method of queued) {
419
- const queueKey = `${api}.${method.name}`;
420
- const key = `${from} ${to} pubsub ${queueKey}`;
421
- if (!viaByKey.has(key))
422
- viaByKey.set(key, new Set());
423
- viaByKey.get(key).add(api);
424
- let queue = queues.get(queueKey);
425
- if (queue === undefined) {
426
- queue = {
427
- api,
428
- method: method.name,
429
- // Every cloudtasks method carries a queueName; the fallback covers a
430
- // dependencies.json written before queueName became kind-specific.
431
- queueName: method.queueName ?? `${api}-${method.name}`,
432
- producedBy: [],
433
- consumedBy: [],
434
- };
435
- queues.set(queueKey, queue);
436
- }
437
- if (!queue.producedBy.includes(from))
438
- queue.producedBy.push(from);
439
- if (!queue.consumedBy.includes(to))
440
- queue.consumedBy.push(to);
441
- }
442
- }
443
395
  /**
444
396
  * WHICH implementers this one `uses` reaches. Resolution order (most specific wins):
445
397
  * 1. a literal `ClientConfig` at the call site (`ref.targetService`) — names ONE node;
@@ -548,7 +500,9 @@ class RuntimeGraphDeriver {
548
500
  // a service that defers work to itself look self-dependent).
549
501
  const dependsOn = Array.from(new Set(edges
550
502
  .filter((e) => e.from === decl.name)
551
- .map((e) => e.queue === undefined ? e.to : `queue:${e.queue}`))).sort();
503
+ .flatMap((e) => e.type === 'pubsub'
504
+ ? e.via.map((api) => `queue:${e.queue ?? api}`)
505
+ : [e.to]))).sort();
552
506
  // Keys are written in this order; an undefined value is omitted by JSON.stringify, so
553
507
  // the committed JSON stays clean AND deterministic without conditional assembly.
554
508
  const service = {
@@ -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, sortedQueues, 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). A contract with no entry — no file, or no endpoints — makes every\n * pubsub edge through it fall back to one unnamed queue per service pair instead of failing.\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 const queues = new Map<string, RuntimeQueue>();\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 if (ref.type === 'pubsub') {\n this.addQueuedEdges(decl.name, target, ref.api, viaByKey, queues);\n continue;\n }\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 queues: sortedQueues(queues),\n };\n }\n\n /**\n * One edge PER QUEUED METHOD of `api`, plus the queue each flows through.\n *\n * Per method rather than per service pair because that is the unit Cloud Tasks and Terraform\n * create: two services exchanging three queued methods run three independently-configured,\n * independently-backed-up queues, and collapsing them into one arrow hides which one is stuck.\n *\n * A contract with no method table (no `architecture/apis/<Api>.json` for it)\n * degrades to a single unnamed queue for the pair — the old behavior — rather than vanishing.\n */\n private addQueuedEdges(\n from: string,\n to: string,\n api: string,\n viaByKey: Map<string, Set<string>>,\n queues: Map<string, RuntimeQueue>,\n ): void {\n const queued = this.methodsOf(api).filter((m: ApiMethodMeta) => m.kind === 'cloudtasks');\n if (queued.length === 0) {\n const key = `${from} ${to} pubsub `;\n if (!viaByKey.has(key)) viaByKey.set(key, new Set());\n viaByKey.get(key)!.add(api);\n return;\n }\n for (const method of queued) {\n const queueKey = `${api}.${method.name}`;\n const key = `${from} ${to} pubsub ${queueKey}`;\n if (!viaByKey.has(key)) viaByKey.set(key, new Set());\n viaByKey.get(key)!.add(api);\n\n let queue = queues.get(queueKey);\n if (queue === undefined) {\n queue = {\n api,\n method: method.name,\n // Every cloudtasks method carries a queueName; the fallback covers a\n // dependencies.json written before queueName became kind-specific.\n queueName: method.queueName ?? `${api}-${method.name}`,\n producedBy: [],\n consumedBy: [],\n };\n queues.set(queueKey, queue);\n }\n if (!queue.producedBy.includes(from)) queue.producedBy.push(from);\n if (!queue.consumedBy.includes(to)) queue.consumedBy.push(to);\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 .map((e: RuntimeEdge) =>\n e.queue === undefined ? e.to : `queue:${e.queue}`,\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 { 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"]}
@@ -209,8 +209,8 @@ function queuesDot(graph, hidden, model) {
209
209
  return '';
210
210
  let dot = '\n // Queued hops. Each LINE in a box is one Cloud Tasks queue; queues of one contract\n' +
211
211
  ' // sharing the same producers AND consumers are drawn in a single box.\n';
212
- // A contract with no method table (no architecture/apis/<Api>.json for it) has no
213
- // per-method queue at all, so it keeps the historical unnamed per-pair box.
212
+ // Contract-only client wiring has no method queue, even with a complete endpoint table.
213
+ // Show only the declared contracts in a per-pair box.
214
214
  const byQueue = new Map();
215
215
  for (const edge of queued) {
216
216
  const from = edge.from;
@@ -1 +1 @@
1
- {"version":3,"file":"runtime-visualizer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-visualizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { LevelBandLayout } from './graph-level-bands';\n/**\n * Runtime Visualizer\n *\n * Renders the runtime microservice graph (services + inferred Z -> X edges,\n * each labeled with the api(s) they flow over) to DOT + interactive HTML in\n * tmp/webpieces/runtime-architecture.{dot,html}.\n *\n * Each service node names the contracts it IMPLEMENTS. That list used to be\n * collapsed into a server/client boolean and thrown away — leaving an api that a\n * server serves but nothing in-repo calls completely invisible, and making a\n * correct api design look like a detection failure. What a service USES is NOT\n * listed: every use already draws an outgoing arrow labeled with the same\n * contract, so repeating it in the box only made every box wider.\n *\n * Shape says what a node IS and line style says what a call IS. Solid = rpc: the\n * request follows the arrow and the response flows back. Dashed = event: it flows\n * in the arrow's direction and returns once it is queued. A queue is a sideways\n * cylinder, a datastore an upright one. A queue box lists ONE LINE PER QUEUE: the\n * unit underneath is still the METHOD (what Terraform creates, and what\n * runtime-dependencies.json records), but queues of one contract sharing the same\n * producers and consumers are drawn together instead of as adjacent near-identical\n * boxes — every queue is still named, only the node count drops.\n *\n * Calls that leave the repo (a contract NOTHING in-repo implements — firestore,\n * gmail, ...) are drawn as terminal nodes, so the vendor systems that actually\n * page you at 3am stop being missing from the picture. One that DECLARES what it\n * is, via an `@externalSystem` JSDoc tag or an `external:` nx tag, gets the shape\n * of the thing it is; the rest stay generic dashed boxes. They are RENDER-ONLY:\n * derivation, levels and cycle detection never see them.\n *\n * The same is true in the other direction for endpoints nothing in-repo CALLS: a\n * `cron` method hangs off a clock and an `external` method off a box naming the\n * CALLER that posts to it (`twilio`), in the same id space as the outbound\n * systems, so a vendor we both call and are called by is ONE box. Those are the\n * entry points that wake a service up at 3am, and a graph built only from in-repo\n * callers cannot show them at all.\n *\n * EVERY node on the rendered page is clickable and opens the SHARED floating menu\n * (graph-node-menu.ts — one implementation, also used by architecture/dependencies.html\n * and every project's design.html). Lock/Unlock pins separate complete incoming/outgoing chains, with hover adding a temporary chain. There is no \"View Design\"\n * item here — a node is a running service, queue, datastore or third-party system, not\n * an nx project, so no design.html exists to point at and the item is absent rather than\n * dead. Filter Unconnected follows separate incoming/outgoing chains and compacts the rendered\n * topology; Turn off Filter restores the full picture. Lock remains independent of Filter.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n RuntimeGraph,\n RuntimeEdge,\n RuntimeQueue,\n RuntimeService,\n RuntimeTrigger,\n} from './runtime-graph';\nimport { dotValue, recordValue, assertValidDot } from './dot-syntax';\nimport { GraphRenderModel } from './graph-render-model';\nimport { RuntimeViewerAdapter } from './runtime-viewer-adapter';\nimport { RuntimeHtmlPage } from './runtime-html-page';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\nimport {\n QUEUE_FILL,\n QUEUE_SHAPE,\n QUEUE_LABEL_PREFIX,\n QUEUE_CLASS,\n EXTERNAL_SHAPES,\n EXTERNAL_FILLS,\n EXTERNAL_FILL,\n EXTERNAL_BORDER,\n CRON_FILL,\n CRON_BORDER,\n CUT_CYCLE_EDGE_ATTRS,\n REMOTE_SERVICE_FILL,\n REMOTE_SERVICE_BORDER,\n} from './runtime-viz-theme';\n\n/** Apis per line inside a node label — beyond this the box grows wider than it is readable. */\nconst APIS_PER_LABEL_LINE = 3;\n\n/** Separator for the (service, external-library) grouping key; illegal in both project names. */\nconst PAIR_SEP = '|';\n\nimport { RuntimeVizOptions } from './runtime-viz-options';\nexport { RuntimeVizOptions } from './runtime-viz-options';\n\n/**\n * The DISPLAY name of a project/service: scope stripped. LABELS ONLY.\n *\n * Never an identity. `@scope/public-api` and `public-api` are two distinct projects that strip to\n * the same string, and keying nodes or edges on that fuses them into one box (and draws the edge\n * between them as a self-loop). See {@link serviceNodeId} and lib/graph-names.ts.\n */\nfunction getShortName(name: string): string {\n return name.includes('/') ? name.split('/').pop()! : name;\n}\n\n/** The DOT identity of a runtime service: its project key, escaped — never the display name. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction serviceNodeId(name: string): string {\n return dotValue(name);\n}\n\n/** Chunk a list into `\\n`-separated label lines of at most APIS_PER_LABEL_LINE entries. */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction labelList(entries: string[]): string {\n const lines: string[] = [];\n const safe = entries.map((entry: string) => dotValue(entry));\n for (let i = 0; i < safe.length; i += APIS_PER_LABEL_LINE) {\n lines.push(safe.slice(i, i + APIS_PER_LABEL_LINE).join(', '));\n }\n return lines.join('\\\\n');\n}\n\n/**\n * DOT for ONE NON-QUEUED runtime edge: a direct labeled SOLID arrow (producer calls consumer, the\n * response comes back). Queued (pubsub) hops are drawn by {@link queuesDot} instead, because they\n * are merged across methods and therefore cannot be emitted one edge at a time.\n *\n * Dashed is the graph's one line-level distinction, and it now says one of two things: an event that\n * returns as soon as it is queued, or (in red, labelled \"legacy cycle\") an edge a `cutLegacyCycle:`\n * tag holds out of leveling. A cut edge is still DRAWN — erasing the arrow would erase exactly the\n * debt the tag records — it is only drawn as the debt it is. See lib/cut-legacy-cycle-resolver.ts.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction edgeDot(edge: RuntimeEdge): string {\n const from = serviceNodeId(edge.from);\n const to = serviceNodeId(edge.to);\n const viaRaw =\n edge.via.length > 2\n ? `Uses (${edge.via.length}) ▾`\n : edge.via.map((v: string) => getShortName(v)).join(', ');\n const cut = edge.cutLegacyCycle === true;\n const label = cut ? `${dotValue(viaRaw)}\\\\nlegacy cycle` : dotValue(viaRaw);\n return ` \"${from}\" -> \"${to}\" [label=\"${label}\"${cut ? CUT_CYCLE_EDGE_ATTRS : ''}];\\n`;\n}\n\n/** The VISIBLE producers and consumers of one queue, gathered from the edges that survived hiding. */\nclass QueueEndpoints {\n readonly producers = new Set<string>();\n readonly consumers = new Set<string>();\n}\n\n/**\n * One drawn queue BOX: the queue keys (`Api.method`) it shows, and the endpoints they all share.\n * Several keys land in one box only when they agree on all three of contract, producers and\n * consumers — see {@link queuesDot}.\n */\nclass QueueGroup {\n constructor(\n public readonly members: string[],\n public readonly producers: string[],\n public readonly consumers: string[],\n ) {}\n}\n\n/**\n * The label line for ONE queue inside a box: `Api.method`, and the Terraform queue name ONLY when it\n * is not the derived `${Api}-${method}`.\n *\n * Printing a derived name was pure restatement — the line above it already said `Api.method`, so\n * every queue box carried a second line that added nothing and doubled its height. A `@Queue(...)`\n * OVERRIDE is the opposite: that string appears nowhere else on the graph, and it is the one string\n * Terraform must match, so it stays.\n */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction queueLine(key: string, queue: RuntimeQueue | undefined): string {\n const line = recordValue(key);\n if (queue === undefined) return line;\n if (queue.queueName === `${queue.api}-${queue.method}`) return line;\n return `${line}\\\\nqueue: ${recordValue(queue.queueName)}`;\n}\n\n/** The node statement + enqueue/deliver arrows for one queue box. Service identities remain raw; only emitted DOT is escaped. */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queueBoxDot(\n id: string,\n body: string,\n producers: string[],\n consumers: string[],\n model: GraphRenderModel,\n contractCount: number,\n queueKeys: string[],\n): string {\n // Record-mode label: the text must clear recordValue(), and QUEUE_LABEL_PREFIX supplies the\n // empty leading field that draws the cylinder's end cap. Drop it and the node silently\n // degrades to a plain box.\n let dot = model.node(\n id,\n ` \"${id}\" [shape=${QUEUE_SHAPE}, style=\"filled\", fillcolor=\"${QUEUE_FILL}\", ` +\n `class=\"${QUEUE_CLASS}\", label=\"${QUEUE_LABEL_PREFIX}${body}\"];\\n`,\n [],\n queueKeys,\n );\n for (const producer of producers)\n dot += model.edge(\n producer,\n id,\n ` \"${serviceNodeId(producer)}\" -> \"${id}\" [label=\"Uses (${contractCount}) ▾\\\\nenqueue\", style=dashed];\\n`,\n );\n for (const consumer of consumers)\n dot += model.edge(\n id,\n consumer,\n ` \"${id}\" -> \"${serviceNodeId(consumer)}\" [style=dashed];\\n`,\n );\n return dot;\n}\n\n/**\n * Group the queued hops into boxes. The key is (contract, producer SET, consumer SET) — NOT the\n * (from,to) pair of some edge.\n *\n * A queue node converges EVERY producer and consumer of its method onto one box, so keying by a\n * service PAIR would re-split a two-producer queue into two boxes and invent topology nobody wrote.\n * Only queues of the same contract whose producer and consumer sets are IDENTICAL may share a box;\n * anything else stays separate, because merging it would assert a routing that does not exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction groupQueues(byQueue: Map<string, QueueEndpoints>, graph: RuntimeGraph): QueueGroup[] {\n const groups = new Map<string, QueueGroup>();\n // Sorted keys → the first member of every group, and therefore its node id, is deterministic.\n for (const key of [...byQueue.keys()].sort()) {\n const ends = byQueue.get(key)!;\n const producers = [...ends.producers].sort();\n const consumers = [...ends.consumers].sort();\n const api = graph.queues[key]?.api ?? key.split('.')[0];\n const mergeKey = [api, producers.join(','), consumers.join(',')].join(PAIR_SEP);\n const existing = groups.get(mergeKey);\n if (existing === undefined)\n groups.set(mergeKey, new QueueGroup([key], producers, consumers));\n else existing.members.push(key);\n }\n return [...groups.values()];\n}\n\n/**\n * The queued half of the graph: producer → QUEUE → consumer, drawn with a sideways-cylinder node and\n * DASHED arrows (an event returns as soon as it is queued).\n *\n * One BOX may list several queues, one per line. The unit Cloud Tasks and Terraform create is still\n * the METHOD — that is what runtime-dependencies.json records and this render never changes it — but\n * two methods of one contract flowing between exactly the same producers and consumers drew two\n * adjacent boxes whose only difference was the method name, and each carried a `queue:` line that\n * merely restated it. Listing them inside ONE box keeps every queue named (you can still see which\n * one is stuck) at a fraction of the node count.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queuesDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n const queued = graph.runtimeEdges.filter(\n (e: RuntimeEdge) => e.type === 'pubsub' && !hidden.has(e.from) && !hidden.has(e.to),\n );\n if (queued.length === 0) return '';\n\n let dot =\n '\\n // Queued hops. Each LINE in a box is one Cloud Tasks queue; queues of one contract\\n' +\n ' // sharing the same producers AND consumers are drawn in a single box.\\n';\n\n // A contract with no method table (no architecture/apis/<Api>.json for it) has no\n // per-method queue at all, so it keeps the historical unnamed per-pair box.\n const byQueue = new Map<string, QueueEndpoints>();\n for (const edge of queued) {\n const from = edge.from;\n const to = edge.to;\n if (edge.queue === undefined) {\n // Kept RAW: recordValue already applies dotValue, so escaping here would double it.\n const viaRaw =\n edge.via.length > 2\n ? `Uses (${edge.via.length}) ▾`\n : edge.via.map((v: string) => getShortName(v)).join(', ');\n dot += queueBoxDot(\n `queue__${dotId(edge.from)}__${dotId(edge.to)}`,\n `${recordValue(viaRaw)}\\\\nqueue`,\n [from],\n [to],\n model,\n edge.via.length,\n [`${from}->${to}`],\n );\n continue;\n }\n if (!byQueue.has(edge.queue)) byQueue.set(edge.queue, new QueueEndpoints());\n byQueue.get(edge.queue)!.producers.add(from);\n byQueue.get(edge.queue)!.consumers.add(to);\n }\n\n for (const group of groupQueues(byQueue, graph)) {\n const body = group.members\n .map((key: string) => queueLine(key, graph.queues[key]))\n .join('\\\\n');\n dot += queueBoxDot(\n `queue__${dotId(group.members[0])}`,\n body,\n group.producers,\n group.consumers,\n model,\n 1,\n group.members,\n );\n }\n return dot;\n}\n\n/** A DOT-safe node-id fragment: anything but letters, digits and `_` becomes `_`. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction dotId(raw: string): string {\n return raw.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/**\n * ONE external-system node statement: the shape/fill of what the system IS, its label, and a\n * parenthesised subtitle saying which way it faces.\n *\n * Shared by the OUTBOUND systems ({@link externalSystemsDot}) and the INBOUND callers\n * ({@link triggerDot}) on purpose. They emit into the SAME `system__<identity>` id space, so a\n * vendor this repo both calls and is called by is one box with arrows in both directions — and a\n * copy-pasted attribute string would have let the two drift until they stopped being one box.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemNodeDot(\n identity: string,\n kind: string,\n label: string,\n subtitle: string,\n model: GraphRenderModel,\n): string {\n const shape = EXTERNAL_SHAPES[kind] ?? 'box';\n const fill = EXTERNAL_FILLS[kind] ?? EXTERNAL_FILL;\n // An Mrecord-shaped system needs the same empty leading field as a queue node, or it renders as\n // a plain box and silently loses the sideways-cylinder read.\n const isQueue = shape === QUEUE_SHAPE;\n const prefix = isQueue ? QUEUE_LABEL_PREFIX : '';\n const text = isQueue ? recordValue(label) : dotValue(label);\n // Only a queue-kind system is marked: a database here is an UPRIGHT cylinder and must not be\n // caught by the browser-side reshaping that lays queues on their side.\n const marker = isQueue ? `class=\"${QUEUE_CLASS}\", ` : '';\n return model.node(\n `system__${dotId(identity)}`,\n ` \"system__${dotId(identity)}\" [shape=${shape}, style=\"filled\", fillcolor=\"${fill}\", ` +\n `${marker}label=\"${prefix}${text}\\\\n(${subtitle})\"];\\n`,\n );\n}\n\n/**\n * The clock and outside-system nodes for endpoints NOTHING in-repo calls.\n *\n * A cron sweep and a GCP push subscription are real runtime entry points with real Terraform behind\n * them, but they produce no runtime EDGE (there is no in-repo caller), so until now they were simply\n * absent — a server's most operationally interesting endpoint could be invisible on its own graph.\n * Both are drawn pointing INTO the service that serves them, the opposite direction from\n * {@link externalDot}'s outbound vendor calls.\n *\n * The inbound box names the CALLER (`twilio`), never the contract. Naming the contract was the whole\n * bug: it restated what the service box directly below already says, while the one fact the box\n * exists to convey — which vendor is posting to us — appeared nowhere. The contract keeps its place\n * on the EDGE label, where `WhatsAppApi.inbound` reads as \"…posts to this method\".\n *\n * Identity is the caller's label in the SAME `system__` space {@link externalSystemsDot} uses, which\n * buys three things at once: two vendors hitting one contract are two boxes, one vendor hitting three\n * methods is one box with three arrows, and a vendor this repo also CALLS is that same single box.\n * `options` is passed in for the last of those — when that outbound half will be drawn, this half\n * must not restate the node statement.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction triggerDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n options: RuntimeVizOptions,\n model: GraphRenderModel,\n): string {\n const triggers = graph.triggers.filter((t: RuntimeTrigger) => !hidden.has(t.service));\n if (triggers.length === 0) return '';\n\n // Identities externalSystemsDot is about to draw; skipped here so the node is stated ONCE.\n const drawnOutbound = new Set(\n options.showExternalNodes ? Object.keys(graph.externalSystems ?? {}) : [],\n );\n const emitted = new Set<string>();\n let dot =\n '\\n // Entry points nothing in this repo calls: a clock, or a system outside the repo.\\n';\n for (const trigger of triggers) {\n const service = serviceNodeId(trigger.service);\n const label = dotValue(`${trigger.api}.${trigger.method}`);\n if (trigger.kind === 'cron') {\n const id = `cron__${dotId(`${trigger.api}_${trigger.method}`)}`;\n const schedule = dotValue(trigger.queueName ?? `${trigger.api}-${trigger.method}`);\n dot +=\n model.node(\n id,\n ` \"${id}\" [shape=circle, style=\"filled\", fillcolor=\"${CRON_FILL}\", ` +\n `color=\"${CRON_BORDER}\", label=\"⏰\\\\ncron\"];\\n`,\n ) +\n model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\\\\n${schedule}\", color=\"${CRON_BORDER}\"];\\n`,\n );\n continue;\n }\n const caller = trigger.caller;\n // Undeclared: only possible for a graph generated BEFORE the caller was required (generation\n // now fails instead). DOTTED and question-marked, so \"we were never told who this is\" cannot\n // be mistaken for a named vendor.\n const id =\n caller === undefined\n ? `inbound__${dotId(trigger.api)}`\n : `system__${dotId(caller.label)}`;\n if (!emitted.has(id) && !(caller !== undefined && drawnOutbound.has(caller.label))) {\n emitted.add(id);\n dot +=\n caller === undefined\n ? model.node(\n id,\n ` \"${id}\" [shape=box, style=\"dotted,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(trigger.api)}\\\\n? unknown caller\"];\\n`,\n )\n : externalSystemNodeDot(\n caller.label,\n caller.kind,\n caller.label,\n 'external caller',\n model,\n );\n }\n dot += model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\", style=dashed, color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The DECLARED external systems — the ones that said what they are, so they get a shape that says\n * it: a cylinder for a database, a folder for a bucket. Everything undeclared falls through to\n * {@link externalDot}'s generic grey box, which is why adding this broke nothing existing.\n *\n * The arrows are SOLID. A call to firestore or postgres is synchronous — it returns a value — and\n * the graph's rule is that solid means \"response comes back\". Being outside the repo is carried by\n * the node's shape, never by the line style; conflating the two is what made a blocking database\n * read look like an event.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemsDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const systems = graph.externalSystems ?? {};\n const ids = Object.keys(systems).sort();\n if (ids.length === 0) return '';\n\n let dot =\n '\\n // Declared external systems — drawn with the shape of what they actually are.\\n';\n for (const id of ids) {\n const system = systems[id];\n dot += externalSystemNodeDot(\n id,\n system.kind,\n system.label,\n `external ${dotValue(system.kind)}`,\n model,\n );\n }\n for (const id of ids) {\n const system = systems[id];\n const via =\n system.apis.length === 0\n ? ''\n : ` [label=\"${system.apis.length > 2 ? `Uses (${system.apis.length}) ▾` : labelList([...system.apis].sort())}\"]`;\n for (const service of [...system.usedBy].sort()) {\n if (hidden.has(service)) continue;\n dot += model.edge(\n service,\n `system__${dotId(id)}`,\n ` \"${serviceNodeId(service)}\" -> \"system__${dotId(id)}\"${via};\\n`,\n );\n }\n }\n return dot;\n}\n\n/**\n * The dashed terminal nodes + edges for calls that LEAVE the repo. Built from `unresolvedUses` —\n * a contract used by a node and implemented by nobody in-repo — which the derivation already\n * computes and which was, until now, only ever printed as a warning.\n *\n * Grouped by the api-lib that owns the contracts, so a service reaching three firestore contracts\n * draws ONE `lib-firestore (external)` box rather than three. These are drawn, never derived: they\n * are absent from levels, cycle detection and the transitive implements attribution.\n *\n * A contract carrying an `@externalSystem` declaration is skipped here — {@link externalSystemsDot}\n * has already drawn it with a real shape, and rendering it in both places would double the node.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n // \"service|externalName\" -> the apis flowing over it.\n const apisByPair = new Map<string, string[]>();\n for (const use of graph.unresolvedUses) {\n if (hidden.has(use.service)) continue;\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n // Keyed on the OWNER's full name, never its short name: two scoped api-libs whose short\n // names match are two external systems, and fusing them draws one box for both.\n const external = graph.apis[use.api]?.owner ?? use.api;\n const key = `${use.service}${PAIR_SEP}${external}`;\n if (!apisByPair.has(key)) apisByPair.set(key, []);\n apisByPair.get(key)!.push(use.api);\n }\n if (apisByPair.size === 0) return '';\n\n let dot = '\\n // Systems outside this repo — no in-repo service implements these contracts.\\n';\n // The node ID is prefixed so an external library can never collide with a service, and built\n // from the owner's FULL name so two same-short-named owners stay two boxes; only the label\n // carries the bare name.\n const externals = new Set([...apisByPair.keys()].map((key: string) => key.split(PAIR_SEP)[1]));\n for (const external of [...externals].sort()) {\n dot += model.node(\n `external__${external}`,\n ` \"external__${dotValue(external)}\" [shape=box, style=\"dashed,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(getShortName(external))}\\\\n(external)\"];\\n`,\n );\n }\n for (const key of [...apisByPair.keys()].sort()) {\n const parts = key.split(PAIR_SEP);\n const service = parts[0];\n const external = parts[1];\n const apis = apisByPair.get(key)!.sort();\n const via = apis.length > 2 ? `Uses (${apis.length}) ▾` : labelList(apis);\n // SOLID: this is a synchronous call that returns a value. Dashed is reserved for events, and\n // \"outside the repo\" is already said by the node's dashed border.\n dot += model.edge(\n service,\n `external__${external}`,\n ` \"${serviceNodeId(service)}\" -> \"external__${dotValue(external)}\" ` +\n `[label=\"${via}\", color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The reviewed out-of-workspace services (#1203): service-shaped nodes (rounded, teal, bold outline,\n * \"(remote service)\"), with an arrow from every workspace service that targets one. An rpc call is a\n * solid arrow and a queued call a dashed \"enqueue\" arrow, exactly as between two in-repo services.\n *\n * Drawn whatever `showExternalNodes` says: these are OUR services, not vendor systems, and hiding them\n * would hide calls the reviewed graph asserts exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction remoteServicesDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n const remotes = graph.remoteServices ?? {};\n const names = Object.keys(remotes).sort();\n if (names.length === 0) return '';\n\n let dot = '\\n // Remote services — ours, but outside this workspace (reviewed remoteServices).\\n';\n for (const name of names) {\n const remote = remotes[name];\n const id = new RemoteServiceNodeId().of(name);\n // The label is display text; the service name stays visible because it is what `uses` target.\n const title =\n remote.label === undefined\n ? dotValue(name)\n : `${dotValue(remote.label)}\\\\n${dotValue(name)}`;\n dot += model.node(\n id,\n ` \"${id}\" [shape=box, style=\"filled,rounded,bold\", fillcolor=\"${REMOTE_SERVICE_FILL}\", ` +\n `color=\"${REMOTE_SERVICE_BORDER}\", label=\"${title}\\\\n(remote service)\\\\n` +\n `Implements (${remote.implements.length})\"];\\n`,\n );\n }\n for (const name of names) {\n const id = new RemoteServiceNodeId().of(name);\n for (const call of remotes[name].calledBy) {\n if (hidden.has(call.from)) continue;\n const via = call.via.length > 2 ? `Uses (${call.via.length}) ▾` : labelList(call.via);\n const label = call.type === 'pubsub' ? `${via}\\\\nenqueue` : via;\n const style = call.type === 'pubsub' ? ', style=dashed' : '';\n dot += model.edge(\n call.from,\n id,\n ` \"${serviceNodeId(call.from)}\" -> \"${id}\" [label=\"${label}\", color=\"${REMOTE_SERVICE_BORDER}\"${style}];\\n`,\n );\n }\n }\n return dot;\n}\n\n/** Build the Graphviz DOT for the runtime service graph. */\n// webpieces-disable no-function-outside-class -- module entry point, matching the sibling builders here\nexport function generateRuntimeDot(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): string {\n return generateRuntimeRenderModel(graph, title, options).fullDot;\n}\n\n/** Capture synthetic drawable nodes and edges at their original emission sites. */\n// webpieces-disable no-function-outside-class -- renderer entry point matching generateRuntimeDot\nexport function generateRuntimeRenderModel(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): GraphRenderModel {\n const model = new GraphRenderModel();\n model.viewer = 'runtime';\n const adapter = new RuntimeViewerAdapter(graph, options.projects);\n let dot = 'digraph RuntimeArchitecture {\\n';\n dot += ' rankdir=TB;\\n';\n dot += ' node [shape=box, style=\"filled,rounded\", fontname=\"Arial\"];\\n';\n dot += ' edge [fontname=\"Arial\", fontsize=12];\\n\\n';\n\n model.header = dot;\n\n // Services tagged drawOnGraph:false stay in the JSON but are omitted here —\n // both their node and any edge touching them are dropped from the render.\n const hidden = new Set(\n Object.keys(graph.services).filter(\n (name: string) => graph.services[name].drawOnGraph === false,\n ),\n );\n\n for (const name of Object.keys(graph.services)) {\n if (hidden.has(name)) continue;\n const svc = graph.services[name];\n dot += adapter.service(model, name, svc);\n }\n\n model.bands = adapter.levelBands(model);\n dot += new LevelBandLayout().dot(model.bands);\n dot += '\\n';\n\n for (const edge of graph.runtimeEdges) {\n if (hidden.has(edge.from) || hidden.has(edge.to)) continue;\n // Queued hops are merged across methods, so they cannot be emitted one edge at a time.\n if (edge.type === 'pubsub') continue;\n dot += model.edge(edge.from, edge.to, edgeDot(edge));\n }\n\n dot += queuesDot(graph, hidden, model);\n dot += remoteServicesDot(graph, hidden, model);\n dot += triggerDot(graph, hidden, options, model);\n\n if (options.showExternalNodes) {\n dot += externalSystemsDot(graph, hidden, model);\n dot += externalDot(graph, hidden, model);\n }\n\n const footerStart = dot.length;\n dot += '\\n labelloc=\"t\";\\n';\n dot += ` label=\"${dotValue(title)}\\\\n(from architecture/runtime-dependencies.json)\";\\n`;\n dot += ' fontsize=20;\\n';\n dot += '}\\n';\n // Nothing downstream parses this DOT until a human opens the page, so parse-shape is checked\n // HERE — a graph that cannot render is a generation failure, not a blank page to discover later.\n assertValidDot(dot, 'runtime-architecture.dot');\n model.footer = dot.slice(footerStart);\n model.completeEndpoints();\n // #1179: a queue, trigger or external system belongs to the products of the services it touches.\n model.attachProducts((id: string): boolean => graph.services[id] !== undefined);\n adapter.contextModes(model);\n model.fullDot = dot;\n return model;\n}\n\nexport interface RuntimeVisualizationPaths {\n dotPath: string;\n htmlPath: string;\n}\n\n/** Write the DOT + HTML renderings to tmp/webpieces/. */\nexport function writeRuntimeVisualization(\n graph: RuntimeGraph,\n workspaceRoot: string,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): RuntimeVisualizationPaths {\n const outputDir = path.join(workspaceRoot, 'tmp', 'webpieces');\n if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });\n\n const model = generateRuntimeRenderModel(graph, title, options);\n const dot = model.fullDot;\n const dotPath = path.join(outputDir, 'runtime-architecture.dot');\n fs.writeFileSync(dotPath, dot, 'utf-8');\n\n const htmlPath = path.join(outputDir, 'runtime-architecture.html');\n fs.writeFileSync(htmlPath, new RuntimeHtmlPage().render(model, title, graph, options), 'utf-8');\n\n return { dotPath, htmlPath };\n}\n"]}
1
+ {"version":3,"file":"runtime-visualizer.js","sourceRoot":"","sources":["../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-visualizer.ts"],"names":[],"mappings":"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","sourcesContent":["import { LevelBandLayout } from './graph-level-bands';\n/**\n * Runtime Visualizer\n *\n * Renders the runtime microservice graph (services + inferred Z -> X edges,\n * each labeled with the api(s) they flow over) to DOT + interactive HTML in\n * tmp/webpieces/runtime-architecture.{dot,html}.\n *\n * Each service node names the contracts it IMPLEMENTS. That list used to be\n * collapsed into a server/client boolean and thrown away — leaving an api that a\n * server serves but nothing in-repo calls completely invisible, and making a\n * correct api design look like a detection failure. What a service USES is NOT\n * listed: every use already draws an outgoing arrow labeled with the same\n * contract, so repeating it in the box only made every box wider.\n *\n * Shape says what a node IS and line style says what a call IS. Solid = rpc: the\n * request follows the arrow and the response flows back. Dashed = event: it flows\n * in the arrow's direction and returns once it is queued. A queue is a sideways\n * cylinder, a datastore an upright one. A queue box lists ONE LINE PER QUEUE: the\n * unit underneath is still the METHOD (what Terraform creates, and what\n * runtime-dependencies.json records), but queues of one contract sharing the same\n * producers and consumers are drawn together instead of as adjacent near-identical\n * boxes — every queue is still named, only the node count drops.\n *\n * Calls that leave the repo (a contract NOTHING in-repo implements — firestore,\n * gmail, ...) are drawn as terminal nodes, so the vendor systems that actually\n * page you at 3am stop being missing from the picture. One that DECLARES what it\n * is, via an `@externalSystem` JSDoc tag or an `external:` nx tag, gets the shape\n * of the thing it is; the rest stay generic dashed boxes. They are RENDER-ONLY:\n * derivation, levels and cycle detection never see them.\n *\n * The same is true in the other direction for endpoints nothing in-repo CALLS: a\n * `cron` method hangs off a clock and an `external` method off a box naming the\n * CALLER that posts to it (`twilio`), in the same id space as the outbound\n * systems, so a vendor we both call and are called by is ONE box. Those are the\n * entry points that wake a service up at 3am, and a graph built only from in-repo\n * callers cannot show them at all.\n *\n * EVERY node on the rendered page is clickable and opens the SHARED floating menu\n * (graph-node-menu.ts — one implementation, also used by architecture/dependencies.html\n * and every project's design.html). Lock/Unlock pins separate complete incoming/outgoing chains, with hover adding a temporary chain. There is no \"View Design\"\n * item here — a node is a running service, queue, datastore or third-party system, not\n * an nx project, so no design.html exists to point at and the item is absent rather than\n * dead. Filter Unconnected follows separate incoming/outgoing chains and compacts the rendered\n * topology; Turn off Filter restores the full picture. Lock remains independent of Filter.\n */\n\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport type {\n RuntimeGraph,\n RuntimeEdge,\n RuntimeQueue,\n RuntimeService,\n RuntimeTrigger,\n} from './runtime-graph';\nimport { dotValue, recordValue, assertValidDot } from './dot-syntax';\nimport { GraphRenderModel } from './graph-render-model';\nimport { RuntimeViewerAdapter } from './runtime-viewer-adapter';\nimport { RuntimeHtmlPage } from './runtime-html-page';\nimport { RemoteServiceNodeId } from './remote-service-node-id';\nimport {\n QUEUE_FILL,\n QUEUE_SHAPE,\n QUEUE_LABEL_PREFIX,\n QUEUE_CLASS,\n EXTERNAL_SHAPES,\n EXTERNAL_FILLS,\n EXTERNAL_FILL,\n EXTERNAL_BORDER,\n CRON_FILL,\n CRON_BORDER,\n CUT_CYCLE_EDGE_ATTRS,\n REMOTE_SERVICE_FILL,\n REMOTE_SERVICE_BORDER,\n} from './runtime-viz-theme';\n\n/** Apis per line inside a node label — beyond this the box grows wider than it is readable. */\nconst APIS_PER_LABEL_LINE = 3;\n\n/** Separator for the (service, external-library) grouping key; illegal in both project names. */\nconst PAIR_SEP = '|';\n\nimport { RuntimeVizOptions } from './runtime-viz-options';\nexport { RuntimeVizOptions } from './runtime-viz-options';\n\n/**\n * The DISPLAY name of a project/service: scope stripped. LABELS ONLY.\n *\n * Never an identity. `@scope/public-api` and `public-api` are two distinct projects that strip to\n * the same string, and keying nodes or edges on that fuses them into one box (and draws the edge\n * between them as a self-loop). See {@link serviceNodeId} and lib/graph-names.ts.\n */\nfunction getShortName(name: string): string {\n return name.includes('/') ? name.split('/').pop()! : name;\n}\n\n/** The DOT identity of a runtime service: its project key, escaped — never the display name. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction serviceNodeId(name: string): string {\n return dotValue(name);\n}\n\n/** Chunk a list into `\\n`-separated label lines of at most APIS_PER_LABEL_LINE entries. */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction labelList(entries: string[]): string {\n const lines: string[] = [];\n const safe = entries.map((entry: string) => dotValue(entry));\n for (let i = 0; i < safe.length; i += APIS_PER_LABEL_LINE) {\n lines.push(safe.slice(i, i + APIS_PER_LABEL_LINE).join(', '));\n }\n return lines.join('\\\\n');\n}\n\n/**\n * DOT for ONE NON-QUEUED runtime edge: a direct labeled SOLID arrow (producer calls consumer, the\n * response comes back). Queued (pubsub) hops are drawn by {@link queuesDot} instead, because they\n * are merged across methods and therefore cannot be emitted one edge at a time.\n *\n * Dashed is the graph's one line-level distinction, and it now says one of two things: an event that\n * returns as soon as it is queued, or (in red, labelled \"legacy cycle\") an edge a `cutLegacyCycle:`\n * tag holds out of leveling. A cut edge is still DRAWN — erasing the arrow would erase exactly the\n * debt the tag records — it is only drawn as the debt it is. See lib/cut-legacy-cycle-resolver.ts.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction edgeDot(edge: RuntimeEdge): string {\n const from = serviceNodeId(edge.from);\n const to = serviceNodeId(edge.to);\n const viaRaw =\n edge.via.length > 2\n ? `Uses (${edge.via.length}) ▾`\n : edge.via.map((v: string) => getShortName(v)).join(', ');\n const cut = edge.cutLegacyCycle === true;\n const label = cut ? `${dotValue(viaRaw)}\\\\nlegacy cycle` : dotValue(viaRaw);\n return ` \"${from}\" -> \"${to}\" [label=\"${label}\"${cut ? CUT_CYCLE_EDGE_ATTRS : ''}];\\n`;\n}\n\n/** The VISIBLE producers and consumers of one queue, gathered from the edges that survived hiding. */\nclass QueueEndpoints {\n readonly producers = new Set<string>();\n readonly consumers = new Set<string>();\n}\n\n/**\n * One drawn queue BOX: the queue keys (`Api.method`) it shows, and the endpoints they all share.\n * Several keys land in one box only when they agree on all three of contract, producers and\n * consumers — see {@link queuesDot}.\n */\nclass QueueGroup {\n constructor(\n public readonly members: string[],\n public readonly producers: string[],\n public readonly consumers: string[],\n ) {}\n}\n\n/**\n * The label line for ONE queue inside a box: `Api.method`, and the Terraform queue name ONLY when it\n * is not the derived `${Api}-${method}`.\n *\n * Printing a derived name was pure restatement — the line above it already said `Api.method`, so\n * every queue box carried a second line that added nothing and doubled its height. A `@Queue(...)`\n * OVERRIDE is the opposite: that string appears nowhere else on the graph, and it is the one string\n * Terraform must match, so it stays.\n */\n// webpieces-disable no-function-outside-class -- DOT label builder, matching getShortName in this file\nfunction queueLine(key: string, queue: RuntimeQueue | undefined): string {\n const line = recordValue(key);\n if (queue === undefined) return line;\n if (queue.queueName === `${queue.api}-${queue.method}`) return line;\n return `${line}\\\\nqueue: ${recordValue(queue.queueName)}`;\n}\n\n/** The node statement + enqueue/deliver arrows for one queue box. Service identities remain raw; only emitted DOT is escaped. */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queueBoxDot(\n id: string,\n body: string,\n producers: string[],\n consumers: string[],\n model: GraphRenderModel,\n contractCount: number,\n queueKeys: string[],\n): string {\n // Record-mode label: the text must clear recordValue(), and QUEUE_LABEL_PREFIX supplies the\n // empty leading field that draws the cylinder's end cap. Drop it and the node silently\n // degrades to a plain box.\n let dot = model.node(\n id,\n ` \"${id}\" [shape=${QUEUE_SHAPE}, style=\"filled\", fillcolor=\"${QUEUE_FILL}\", ` +\n `class=\"${QUEUE_CLASS}\", label=\"${QUEUE_LABEL_PREFIX}${body}\"];\\n`,\n [],\n queueKeys,\n );\n for (const producer of producers)\n dot += model.edge(\n producer,\n id,\n ` \"${serviceNodeId(producer)}\" -> \"${id}\" [label=\"Uses (${contractCount}) ▾\\\\nenqueue\", style=dashed];\\n`,\n );\n for (const consumer of consumers)\n dot += model.edge(\n id,\n consumer,\n ` \"${id}\" -> \"${serviceNodeId(consumer)}\" [style=dashed];\\n`,\n );\n return dot;\n}\n\n/**\n * Group the queued hops into boxes. The key is (contract, producer SET, consumer SET) — NOT the\n * (from,to) pair of some edge.\n *\n * A queue node converges EVERY producer and consumer of its method onto one box, so keying by a\n * service PAIR would re-split a two-producer queue into two boxes and invent topology nobody wrote.\n * Only queues of the same contract whose producer and consumer sets are IDENTICAL may share a box;\n * anything else stays separate, because merging it would assert a routing that does not exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction groupQueues(byQueue: Map<string, QueueEndpoints>, graph: RuntimeGraph): QueueGroup[] {\n const groups = new Map<string, QueueGroup>();\n // Sorted keys → the first member of every group, and therefore its node id, is deterministic.\n for (const key of [...byQueue.keys()].sort()) {\n const ends = byQueue.get(key)!;\n const producers = [...ends.producers].sort();\n const consumers = [...ends.consumers].sort();\n const api = graph.queues[key]?.api ?? key.split('.')[0];\n const mergeKey = [api, producers.join(','), consumers.join(',')].join(PAIR_SEP);\n const existing = groups.get(mergeKey);\n if (existing === undefined)\n groups.set(mergeKey, new QueueGroup([key], producers, consumers));\n else existing.members.push(key);\n }\n return [...groups.values()];\n}\n\n/**\n * The queued half of the graph: producer → QUEUE → consumer, drawn with a sideways-cylinder node and\n * DASHED arrows (an event returns as soon as it is queued).\n *\n * One BOX may list several queues, one per line. The unit Cloud Tasks and Terraform create is still\n * the METHOD — that is what runtime-dependencies.json records and this render never changes it — but\n * two methods of one contract flowing between exactly the same producers and consumers drew two\n * adjacent boxes whose only difference was the method name, and each carried a `queue:` line that\n * merely restated it. Listing them inside ONE box keeps every queue named (you can still see which\n * one is stuck) at a fraction of the node count.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction queuesDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n const queued = graph.runtimeEdges.filter(\n (e: RuntimeEdge) => e.type === 'pubsub' && !hidden.has(e.from) && !hidden.has(e.to),\n );\n if (queued.length === 0) return '';\n\n let dot =\n '\\n // Queued hops. Each LINE in a box is one Cloud Tasks queue; queues of one contract\\n' +\n ' // sharing the same producers AND consumers are drawn in a single box.\\n';\n\n // Contract-only client wiring has no method queue, even with a complete endpoint table.\n // Show only the declared contracts in a per-pair box.\n const byQueue = new Map<string, QueueEndpoints>();\n for (const edge of queued) {\n const from = edge.from;\n const to = edge.to;\n if (edge.queue === undefined) {\n // Kept RAW: recordValue already applies dotValue, so escaping here would double it.\n const viaRaw =\n edge.via.length > 2\n ? `Uses (${edge.via.length}) ▾`\n : edge.via.map((v: string) => getShortName(v)).join(', ');\n dot += queueBoxDot(\n `queue__${dotId(edge.from)}__${dotId(edge.to)}`,\n `${recordValue(viaRaw)}\\\\nqueue`,\n [from],\n [to],\n model,\n edge.via.length,\n [`${from}->${to}`],\n );\n continue;\n }\n if (!byQueue.has(edge.queue)) byQueue.set(edge.queue, new QueueEndpoints());\n byQueue.get(edge.queue)!.producers.add(from);\n byQueue.get(edge.queue)!.consumers.add(to);\n }\n\n for (const group of groupQueues(byQueue, graph)) {\n const body = group.members\n .map((key: string) => queueLine(key, graph.queues[key]))\n .join('\\\\n');\n dot += queueBoxDot(\n `queue__${dotId(group.members[0])}`,\n body,\n group.producers,\n group.consumers,\n model,\n 1,\n group.members,\n );\n }\n return dot;\n}\n\n/** A DOT-safe node-id fragment: anything but letters, digits and `_` becomes `_`. */\n// webpieces-disable no-function-outside-class -- DOT id builder, matching getShortName in this file\nfunction dotId(raw: string): string {\n return raw.replace(/[^A-Za-z0-9_]/g, '_');\n}\n\n/**\n * ONE external-system node statement: the shape/fill of what the system IS, its label, and a\n * parenthesised subtitle saying which way it faces.\n *\n * Shared by the OUTBOUND systems ({@link externalSystemsDot}) and the INBOUND callers\n * ({@link triggerDot}) on purpose. They emit into the SAME `system__<identity>` id space, so a\n * vendor this repo both calls and is called by is one box with arrows in both directions — and a\n * copy-pasted attribute string would have let the two drift until they stopped being one box.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemNodeDot(\n identity: string,\n kind: string,\n label: string,\n subtitle: string,\n model: GraphRenderModel,\n): string {\n const shape = EXTERNAL_SHAPES[kind] ?? 'box';\n const fill = EXTERNAL_FILLS[kind] ?? EXTERNAL_FILL;\n // An Mrecord-shaped system needs the same empty leading field as a queue node, or it renders as\n // a plain box and silently loses the sideways-cylinder read.\n const isQueue = shape === QUEUE_SHAPE;\n const prefix = isQueue ? QUEUE_LABEL_PREFIX : '';\n const text = isQueue ? recordValue(label) : dotValue(label);\n // Only a queue-kind system is marked: a database here is an UPRIGHT cylinder and must not be\n // caught by the browser-side reshaping that lays queues on their side.\n const marker = isQueue ? `class=\"${QUEUE_CLASS}\", ` : '';\n return model.node(\n `system__${dotId(identity)}`,\n ` \"system__${dotId(identity)}\" [shape=${shape}, style=\"filled\", fillcolor=\"${fill}\", ` +\n `${marker}label=\"${prefix}${text}\\\\n(${subtitle})\"];\\n`,\n );\n}\n\n/**\n * The clock and outside-system nodes for endpoints NOTHING in-repo calls.\n *\n * A cron sweep and a GCP push subscription are real runtime entry points with real Terraform behind\n * them, but they produce no runtime EDGE (there is no in-repo caller), so until now they were simply\n * absent — a server's most operationally interesting endpoint could be invisible on its own graph.\n * Both are drawn pointing INTO the service that serves them, the opposite direction from\n * {@link externalDot}'s outbound vendor calls.\n *\n * The inbound box names the CALLER (`twilio`), never the contract. Naming the contract was the whole\n * bug: it restated what the service box directly below already says, while the one fact the box\n * exists to convey — which vendor is posting to us — appeared nowhere. The contract keeps its place\n * on the EDGE label, where `WhatsAppApi.inbound` reads as \"…posts to this method\".\n *\n * Identity is the caller's label in the SAME `system__` space {@link externalSystemsDot} uses, which\n * buys three things at once: two vendors hitting one contract are two boxes, one vendor hitting three\n * methods is one box with three arrows, and a vendor this repo also CALLS is that same single box.\n * `options` is passed in for the last of those — when that outbound half will be drawn, this half\n * must not restate the node statement.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction triggerDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n options: RuntimeVizOptions,\n model: GraphRenderModel,\n): string {\n const triggers = graph.triggers.filter((t: RuntimeTrigger) => !hidden.has(t.service));\n if (triggers.length === 0) return '';\n\n // Identities externalSystemsDot is about to draw; skipped here so the node is stated ONCE.\n const drawnOutbound = new Set(\n options.showExternalNodes ? Object.keys(graph.externalSystems ?? {}) : [],\n );\n const emitted = new Set<string>();\n let dot =\n '\\n // Entry points nothing in this repo calls: a clock, or a system outside the repo.\\n';\n for (const trigger of triggers) {\n const service = serviceNodeId(trigger.service);\n const label = dotValue(`${trigger.api}.${trigger.method}`);\n if (trigger.kind === 'cron') {\n const id = `cron__${dotId(`${trigger.api}_${trigger.method}`)}`;\n const schedule = dotValue(trigger.queueName ?? `${trigger.api}-${trigger.method}`);\n dot +=\n model.node(\n id,\n ` \"${id}\" [shape=circle, style=\"filled\", fillcolor=\"${CRON_FILL}\", ` +\n `color=\"${CRON_BORDER}\", label=\"⏰\\\\ncron\"];\\n`,\n ) +\n model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\\\\n${schedule}\", color=\"${CRON_BORDER}\"];\\n`,\n );\n continue;\n }\n const caller = trigger.caller;\n // Undeclared: only possible for a graph generated BEFORE the caller was required (generation\n // now fails instead). DOTTED and question-marked, so \"we were never told who this is\" cannot\n // be mistaken for a named vendor.\n const id =\n caller === undefined\n ? `inbound__${dotId(trigger.api)}`\n : `system__${dotId(caller.label)}`;\n if (!emitted.has(id) && !(caller !== undefined && drawnOutbound.has(caller.label))) {\n emitted.add(id);\n dot +=\n caller === undefined\n ? model.node(\n id,\n ` \"${id}\" [shape=box, style=\"dotted,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(trigger.api)}\\\\n? unknown caller\"];\\n`,\n )\n : externalSystemNodeDot(\n caller.label,\n caller.kind,\n caller.label,\n 'external caller',\n model,\n );\n }\n dot += model.edge(\n id,\n trigger.service,\n ` \"${id}\" -> \"${service}\" [label=\"${label}\", style=dashed, color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The DECLARED external systems — the ones that said what they are, so they get a shape that says\n * it: a cylinder for a database, a folder for a bucket. Everything undeclared falls through to\n * {@link externalDot}'s generic grey box, which is why adding this broke nothing existing.\n *\n * The arrows are SOLID. A call to firestore or postgres is synchronous — it returns a value — and\n * the graph's rule is that solid means \"response comes back\". Being outside the repo is carried by\n * the node's shape, never by the line style; conflating the two is what made a blocking database\n * read look like an event.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalSystemsDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const systems = graph.externalSystems ?? {};\n const ids = Object.keys(systems).sort();\n if (ids.length === 0) return '';\n\n let dot =\n '\\n // Declared external systems — drawn with the shape of what they actually are.\\n';\n for (const id of ids) {\n const system = systems[id];\n dot += externalSystemNodeDot(\n id,\n system.kind,\n system.label,\n `external ${dotValue(system.kind)}`,\n model,\n );\n }\n for (const id of ids) {\n const system = systems[id];\n const via =\n system.apis.length === 0\n ? ''\n : ` [label=\"${system.apis.length > 2 ? `Uses (${system.apis.length}) ▾` : labelList([...system.apis].sort())}\"]`;\n for (const service of [...system.usedBy].sort()) {\n if (hidden.has(service)) continue;\n dot += model.edge(\n service,\n `system__${dotId(id)}`,\n ` \"${serviceNodeId(service)}\" -> \"system__${dotId(id)}\"${via};\\n`,\n );\n }\n }\n return dot;\n}\n\n/**\n * The dashed terminal nodes + edges for calls that LEAVE the repo. Built from `unresolvedUses` —\n * a contract used by a node and implemented by nobody in-repo — which the derivation already\n * computes and which was, until now, only ever printed as a warning.\n *\n * Grouped by the api-lib that owns the contracts, so a service reaching three firestore contracts\n * draws ONE `lib-firestore (external)` box rather than three. These are drawn, never derived: they\n * are absent from levels, cycle detection and the transitive implements attribution.\n *\n * A contract carrying an `@externalSystem` declaration is skipped here — {@link externalSystemsDot}\n * has already drawn it with a real shape, and rendering it in both places would double the node.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction externalDot(graph: RuntimeGraph, hidden: Set<string>, model: GraphRenderModel): string {\n // \"service|externalName\" -> the apis flowing over it.\n const apisByPair = new Map<string, string[]>();\n for (const use of graph.unresolvedUses) {\n if (hidden.has(use.service)) continue;\n if (graph.apis[use.api]?.externalSystem !== undefined) continue;\n // Keyed on the OWNER's full name, never its short name: two scoped api-libs whose short\n // names match are two external systems, and fusing them draws one box for both.\n const external = graph.apis[use.api]?.owner ?? use.api;\n const key = `${use.service}${PAIR_SEP}${external}`;\n if (!apisByPair.has(key)) apisByPair.set(key, []);\n apisByPair.get(key)!.push(use.api);\n }\n if (apisByPair.size === 0) return '';\n\n let dot = '\\n // Systems outside this repo — no in-repo service implements these contracts.\\n';\n // The node ID is prefixed so an external library can never collide with a service, and built\n // from the owner's FULL name so two same-short-named owners stay two boxes; only the label\n // carries the bare name.\n const externals = new Set([...apisByPair.keys()].map((key: string) => key.split(PAIR_SEP)[1]));\n for (const external of [...externals].sort()) {\n dot += model.node(\n `external__${external}`,\n ` \"external__${dotValue(external)}\" [shape=box, style=\"dashed,filled\", fillcolor=\"${EXTERNAL_FILL}\", ` +\n `color=\"${EXTERNAL_BORDER}\", label=\"${dotValue(getShortName(external))}\\\\n(external)\"];\\n`,\n );\n }\n for (const key of [...apisByPair.keys()].sort()) {\n const parts = key.split(PAIR_SEP);\n const service = parts[0];\n const external = parts[1];\n const apis = apisByPair.get(key)!.sort();\n const via = apis.length > 2 ? `Uses (${apis.length}) ▾` : labelList(apis);\n // SOLID: this is a synchronous call that returns a value. Dashed is reserved for events, and\n // \"outside the repo\" is already said by the node's dashed border.\n dot += model.edge(\n service,\n `external__${external}`,\n ` \"${serviceNodeId(service)}\" -> \"external__${dotValue(external)}\" ` +\n `[label=\"${via}\", color=\"${EXTERNAL_BORDER}\"];\\n`,\n );\n }\n return dot;\n}\n\n/**\n * The reviewed out-of-workspace services (#1203): service-shaped nodes (rounded, teal, bold outline,\n * \"(remote service)\"), with an arrow from every workspace service that targets one. An rpc call is a\n * solid arrow and a queued call a dashed \"enqueue\" arrow, exactly as between two in-repo services.\n *\n * Drawn whatever `showExternalNodes` says: these are OUR services, not vendor systems, and hiding them\n * would hide calls the reviewed graph asserts exist.\n */\n// webpieces-disable no-function-outside-class -- DOT string builder, matching getShortName in this file\nfunction remoteServicesDot(\n graph: RuntimeGraph,\n hidden: Set<string>,\n model: GraphRenderModel,\n): string {\n const remotes = graph.remoteServices ?? {};\n const names = Object.keys(remotes).sort();\n if (names.length === 0) return '';\n\n let dot =\n '\\n // Remote services — ours, but outside this workspace (reviewed remoteServices).\\n';\n for (const name of names) {\n const remote = remotes[name];\n const id = new RemoteServiceNodeId().of(name);\n // The label is display text; the service name stays visible because it is what `uses` target.\n const title =\n remote.label === undefined\n ? dotValue(name)\n : `${dotValue(remote.label)}\\\\n${dotValue(name)}`;\n dot += model.node(\n id,\n ` \"${id}\" [shape=box, style=\"filled,rounded,bold\", fillcolor=\"${REMOTE_SERVICE_FILL}\", ` +\n `color=\"${REMOTE_SERVICE_BORDER}\", label=\"${title}\\\\n(remote service)\\\\n` +\n `Implements (${remote.implements.length})\"];\\n`,\n );\n }\n for (const name of names) {\n const id = new RemoteServiceNodeId().of(name);\n for (const call of remotes[name].calledBy) {\n if (hidden.has(call.from)) continue;\n const via = call.via.length > 2 ? `Uses (${call.via.length}) ▾` : labelList(call.via);\n const label = call.type === 'pubsub' ? `${via}\\\\nenqueue` : via;\n const style = call.type === 'pubsub' ? ', style=dashed' : '';\n dot += model.edge(\n call.from,\n id,\n ` \"${serviceNodeId(call.from)}\" -> \"${id}\" [label=\"${label}\", color=\"${REMOTE_SERVICE_BORDER}\"${style}];\\n`,\n );\n }\n }\n return dot;\n}\n\n/** Build the Graphviz DOT for the runtime service graph. */\n// webpieces-disable no-function-outside-class -- module entry point, matching the sibling builders here\nexport function generateRuntimeDot(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): string {\n return generateRuntimeRenderModel(graph, title, options).fullDot;\n}\n\n/** Capture synthetic drawable nodes and edges at their original emission sites. */\n// webpieces-disable no-function-outside-class -- renderer entry point matching generateRuntimeDot\nexport function generateRuntimeRenderModel(\n graph: RuntimeGraph,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): GraphRenderModel {\n const model = new GraphRenderModel();\n model.viewer = 'runtime';\n const adapter = new RuntimeViewerAdapter(graph, options.projects);\n let dot = 'digraph RuntimeArchitecture {\\n';\n dot += ' rankdir=TB;\\n';\n dot += ' node [shape=box, style=\"filled,rounded\", fontname=\"Arial\"];\\n';\n dot += ' edge [fontname=\"Arial\", fontsize=12];\\n\\n';\n\n model.header = dot;\n\n // Services tagged drawOnGraph:false stay in the JSON but are omitted here —\n // both their node and any edge touching them are dropped from the render.\n const hidden = new Set(\n Object.keys(graph.services).filter(\n (name: string) => graph.services[name].drawOnGraph === false,\n ),\n );\n\n for (const name of Object.keys(graph.services)) {\n if (hidden.has(name)) continue;\n const svc = graph.services[name];\n dot += adapter.service(model, name, svc);\n }\n\n model.bands = adapter.levelBands(model);\n dot += new LevelBandLayout().dot(model.bands);\n dot += '\\n';\n\n for (const edge of graph.runtimeEdges) {\n if (hidden.has(edge.from) || hidden.has(edge.to)) continue;\n // Queued hops are merged across methods, so they cannot be emitted one edge at a time.\n if (edge.type === 'pubsub') continue;\n dot += model.edge(edge.from, edge.to, edgeDot(edge));\n }\n\n dot += queuesDot(graph, hidden, model);\n dot += remoteServicesDot(graph, hidden, model);\n dot += triggerDot(graph, hidden, options, model);\n\n if (options.showExternalNodes) {\n dot += externalSystemsDot(graph, hidden, model);\n dot += externalDot(graph, hidden, model);\n }\n\n const footerStart = dot.length;\n dot += '\\n labelloc=\"t\";\\n';\n dot += ` label=\"${dotValue(title)}\\\\n(from architecture/runtime-dependencies.json)\";\\n`;\n dot += ' fontsize=20;\\n';\n dot += '}\\n';\n // Nothing downstream parses this DOT until a human opens the page, so parse-shape is checked\n // HERE — a graph that cannot render is a generation failure, not a blank page to discover later.\n assertValidDot(dot, 'runtime-architecture.dot');\n model.footer = dot.slice(footerStart);\n model.completeEndpoints();\n // #1179: a queue, trigger or external system belongs to the products of the services it touches.\n model.attachProducts((id: string): boolean => graph.services[id] !== undefined);\n adapter.contextModes(model);\n model.fullDot = dot;\n return model;\n}\n\nexport interface RuntimeVisualizationPaths {\n dotPath: string;\n htmlPath: string;\n}\n\n/** Write the DOT + HTML renderings to tmp/webpieces/. */\nexport function writeRuntimeVisualization(\n graph: RuntimeGraph,\n workspaceRoot: string,\n title: string = 'WebPieces Runtime Architecture',\n options: RuntimeVizOptions = new RuntimeVizOptions(),\n): RuntimeVisualizationPaths {\n const outputDir = path.join(workspaceRoot, 'tmp', 'webpieces');\n if (!fs.existsSync(outputDir)) fs.mkdirSync(outputDir, { recursive: true });\n\n const model = generateRuntimeRenderModel(graph, title, options);\n const dot = model.fullDot;\n const dotPath = path.join(outputDir, 'runtime-architecture.dot');\n fs.writeFileSync(dotPath, dot, 'utf-8');\n\n const htmlPath = path.join(outputDir, 'runtime-architecture.html');\n fs.writeFileSync(htmlPath, new RuntimeHtmlPage().render(model, title, graph, options), 'utf-8');\n\n return { dotPath, htmlPath };\n}\n"]}
@@ -54,8 +54,6 @@ class ApprovedWiringGraph {
54
54
  ref.declaredVia = resolved.via;
55
55
  if (resolved.target?.kind === 'service')
56
56
  ref.targetService = resolved.target.service;
57
- if (resolved.transport === 'pubsub' && resolved.direction === 'uses')
58
- ref.methodsInferred = true;
59
57
  relation[resolved.direction].push(ref);
60
58
  relation.kind =
61
59
  relation.implements.length === 0
@@ -1 +1 @@
1
- {"version":3,"file":"approved-graph.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/approved-graph.ts"],"names":[],"mappings":";;;;AAAA,+CAAyB;AACzB,mDAA6B;AAC7B,0DAAgE;AAChE,mCAAkD;AAClD,2CAAqD;AAKrD,kDAAuE;AACvE,8DAAyD;AACzD,uDAA2D;AAE3D,oGAAoG;AACpG,MAAa,mBAAmB;IAC5B,MAAM,CACF,aAAqB,EACrB,KAAoB,EACpB,KAAuC,EACvC,YAAoB,iCAAkB;QAEtC,MAAM,QAAQ,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QAC5D,IAAI,QAAQ,KAAK,IAAI;YACjB,IAAI,CAAC,IAAI,CACL,iGAAiG,CACpG,CAAC;QACN,MAAM,YAAY,GAAG,IAAI,GAAG,EAA8B,CAAC;QAC3D,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;YAC1B,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;YACtF,MAAM,iBAAiB,GAAG,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,YAAY,CAAC;YAChE,IAAI,CAAC,MAAM,IAAI,iBAAiB,KAAK,SAAS;gBAC1C,IAAI,CAAC,IAAI,CACL,GAAG,IAAI,gGAAgG,CAC1G,CAAC;YACN,IAAI,CAAC,MAAM;gBAAE,SAAS;YACtB,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,IAAI,EAAE,mBAAmB,CAAC,CAAC;YACtE,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,IAAI,CAAC;gBAAE,IAAI,CAAC,IAAI,CAAC,gCAAgC,IAAI,GAAG,CAAC,CAAC;YAC7E,YAAY,CAAC,GAAG,CACZ,IAAI,EACJ,IAAI,+BAAuB,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,EAAE,IAAI,CAAC,CAC5E,CAAC;YACF,KAAK,CAAC,IAAI,CAAC,CAAC,kBAAkB,GAAG,GAAG,IAAI,CAAC,IAAI,oBAAoB,CAAC;QACtE,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,kCAAsB,CAAC,YAAY,CAAC,CAAC;QAC3D,KAAK,MAAM,OAAO,IAAI,YAAY,EAAE,CAAC;YACjC,MAAM,IAAI,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YACxB,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;YAC/B,IAAI,WAAW,CAAC,KAAK,KAAK,SAAS;gBAAE,SAAS;YAC9C,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC;YAC1B,IAAI,WAAW,CAAC,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,WAAW,KAAK,WAAW,CAAC,IAAI;gBACxE,IAAI,CAAC,IAAI,CACL,GAAG,IAAI,eAAe,WAAW,CAAC,IAAI,uCAAuC,KAAK,CAAC,WAAW,GAAG,CACpG,CAAC;YACN,MAAM,SAAS,GAAwB,EAAE,CAAC;YAC1C,KAAK,MAAM,QAAQ,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;gBAC9C,MAAM,QAAQ,GAAG,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI;oBAC1C,IAAI,EAAE,MAAM;oBACZ,UAAU,EAAE,EAAE;oBACd,IAAI,EAAE,EAAE;iBACX,CAAC;gBACF,MAAM,GAAG,GAAW,EAAE,GAAG,EAAE,QAAQ,CAAC,GAAG,EAAE,IAAI,EAAE,QAAQ,CAAC,SAAS,EAAE,CAAC;gBACpE,IAAI,QAAQ,CAAC,WAAW,KAAK,SAAS;oBAAE,GAAG,CAAC,WAAW,GAAG,QAAQ,CAAC,WAAW,CAAC;gBAC/E,GAAG,CAAC,WAAW,GAAG,QAAQ,CAAC,GAAG,CAAC;gBAC/B,IAAI,QAAQ,CAAC,MAAM,EAAE,IAAI,KAAK,SAAS;oBACnC,GAAG,CAAC,aAAa,GAAG,QAAQ,CAAC,MAAM,CAAC,OAAO,CAAC;gBAChD,IAAI,QAAQ,CAAC,SAAS,KAAK,QAAQ,IAAI,QAAQ,CAAC,SAAS,KAAK,MAAM;oBAChE,GAAG,CAAC,eAAe,GAAG,IAAI,CAAC;gBAC/B,QAAQ,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;gBACvC,QAAQ,CAAC,IAAI;oBACT,QAAQ,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;wBAC5B,CAAC,CAAC,MAAM;wBACR,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,MAAM,KAAK,CAAC;4BAC1B,CAAC,CAAC,YAAY;4BACd,CAAC,CAAC,iBAAiB,CAAC;gBAC9B,SAAS,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,QAAQ,CAAC;YACzC,CAAC;YACD,KAAK,CAAC,YAAY,GAAG,SAAS,CAAC;YAC/B,KAAK,CAAC,kBAAkB,GAAG,IAAI,CAAC;QACpC,CAAC;QACD,IAAI,CAAC,eAAe,CAAC,aAAa,EAAE,SAAS,EAAE,KAAK,CAAC,CAAC;IAC1D,CAAC;IAEO,eAAe,CAAC,aAAqB,EAAE,SAAiB,EAAE,KAAoB;QAClF,MAAM,KAAK,GAAG,IAAA,+BAAgB,EAAC,aAAa,EAAE,SAAS,CAAC,CAAC;QACzD,IAAI,KAAK,KAAK,IAAI;YACd,IAAI,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC;QAChF,MAAM,SAAS,GAAG,IAAI,qCAAgB,EAAE,CAAC,IAAI,CACzC,aAAa,EACb,SAAS,EACT,KAAK,CAAC,gBAAgB,CACzB,CAAC;QACF,IAAI,wCAAsB,EAAE,CAAC,QAAQ,CACjC,KAAK,EACL,SAAS,EACT,KAAK,CAAC,eAAe,EACrB,KAAK,CAAC,cAAc,CACvB,CAAC;IACN,CAAC;IAEO,IAAI,CAAC,OAAe;QACxB,MAAM,IAAI,4BAAa,CAAC,+BAA+B,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE;YACpF,IAAI,qBAAM,CACN,wKAAwK,EACxK,IAAI,CACP;SACJ,CAAC,CAAC;IACP,CAAC;CACJ;AA/FD,kDA+FC","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport { RuntimeDeclarationCodec } from './codec';\nimport { RuntimeWiringAssembler } from './assembler';\nimport type { RuntimeDeclaration } from './declaration';\nimport type { EnhancedGraph } from '../graph-sorter';\nimport type { ProjectInfo } from '../project-info';\nimport type { ApiRef, ProjectApiRelations } from '../api-usage/api-relations';\nimport { loadBlessedGraph, DEFAULT_GRAPH_PATH } from '../graph-loader';\nimport { ApiContractFiles } from '../api-contract-files';\nimport { ServiceTargetValidator } from './service-targets';\n\n/** Assembly consumes approvals only. Source verification is a separate per-project build target. */\nexport class ApprovedWiringGraph {\n attach(\n workspaceRoot: string,\n graph: EnhancedGraph,\n infos: ReadonlyMap<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n ): void {\n const previous = loadBlessedGraph(workspaceRoot, graphPath);\n if (previous === null)\n this.fail(\n 'Missing saved graph. Complete the runtime declaration migration before generating architecture.',\n );\n const declarations = new Map<string, RuntimeDeclaration>();\n for (const project of infos) {\n const name = project[0];\n const info = project[1];\n const tagged = info.tags.includes('webpieces') || info.tags.includes('webpieces-lib');\n const existingRelations = previous.projects[name]?.apiRelations;\n if (!tagged && existingRelations !== undefined)\n this.fail(\n `${name} has saved runtime facts but is untagged. Migrate it before using declaration-only generation.`,\n );\n if (!tagged) continue;\n const file = path.join(workspaceRoot, info.root, 'runtime-deps.json');\n if (!fs.existsSync(file)) this.fail(`Missing approved declaration ${file}.`);\n declarations.set(\n name,\n new RuntimeDeclarationCodec().decode(fs.readFileSync(file, 'utf8'), name),\n );\n graph[name].runtimeDeclaration = `${info.root}/runtime-deps.json`;\n }\n const assembler = new RuntimeWiringAssembler(declarations);\n for (const project of declarations) {\n const name = project[0];\n const declaration = project[1];\n if (declaration.entry === undefined) continue;\n const entry = graph[name];\n if (declaration.host !== undefined && entry.serviceName !== declaration.host)\n this.fail(\n `${name}: plan host ${declaration.host} disagrees with project serviceName ${entry.serviceName}.`,\n );\n const relations: ProjectApiRelations = {};\n for (const resolved of assembler.assemble(name)) {\n const relation = relations[resolved.owner] ?? {\n kind: 'uses',\n implements: [],\n uses: [],\n };\n const ref: ApiRef = { api: resolved.api, type: resolved.transport };\n if (resolved.conditional !== undefined) ref.conditional = resolved.conditional;\n ref.declaredVia = resolved.via;\n if (resolved.target?.kind === 'service')\n ref.targetService = resolved.target.service;\n if (resolved.transport === 'pubsub' && resolved.direction === 'uses')\n ref.methodsInferred = true;\n relation[resolved.direction].push(ref);\n relation.kind =\n relation.implements.length === 0\n ? 'uses'\n : relation.uses.length === 0\n ? 'implements'\n : 'uses-implements';\n relations[resolved.owner] = relation;\n }\n entry.apiRelations = relations;\n entry.runtimeComposition = true;\n }\n this.validateTargets(workspaceRoot, graphPath, graph);\n }\n\n private validateTargets(workspaceRoot: string, graphPath: string, graph: EnhancedGraph): void {\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n this.fail('Missing saved contract references for approved runtime wiring.');\n const contracts = new ApiContractFiles().load(\n workspaceRoot,\n graphPath,\n saved.apiContractFiles,\n );\n new ServiceTargetValidator().validate(\n graph,\n contracts,\n saved.externalSystems,\n saved.remoteServices,\n );\n }\n\n private fail(message: string): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(\n 'Migrate canonical src/wiring.ts, tag owners webpieces/webpieces-lib, and review runtime-deps.json candidates before generation. Saved visualizations remain available.',\n true,\n ),\n ]);\n }\n}\n"]}
1
+ {"version":3,"file":"approved-graph.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/runtime-wiring/approved-graph.ts"],"names":[],"mappings":"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","sourcesContent":["import * as fs from 'fs';\nimport * as path from 'path';\nimport { Option, RuleFailError } from '@webpieces/rules-config';\nimport { RuntimeDeclarationCodec } from './codec';\nimport { RuntimeWiringAssembler } from './assembler';\nimport type { RuntimeDeclaration } from './declaration';\nimport type { EnhancedGraph } from '../graph-sorter';\nimport type { ProjectInfo } from '../project-info';\nimport type { ApiRef, ProjectApiRelations } from '../api-usage/api-relations';\nimport { loadBlessedGraph, DEFAULT_GRAPH_PATH } from '../graph-loader';\nimport { ApiContractFiles } from '../api-contract-files';\nimport { ServiceTargetValidator } from './service-targets';\n\n/** Assembly consumes approvals only. Source verification is a separate per-project build target. */\nexport class ApprovedWiringGraph {\n attach(\n workspaceRoot: string,\n graph: EnhancedGraph,\n infos: ReadonlyMap<string, ProjectInfo>,\n graphPath: string = DEFAULT_GRAPH_PATH,\n ): void {\n const previous = loadBlessedGraph(workspaceRoot, graphPath);\n if (previous === null)\n this.fail(\n 'Missing saved graph. Complete the runtime declaration migration before generating architecture.',\n );\n const declarations = new Map<string, RuntimeDeclaration>();\n for (const project of infos) {\n const name = project[0];\n const info = project[1];\n const tagged = info.tags.includes('webpieces') || info.tags.includes('webpieces-lib');\n const existingRelations = previous.projects[name]?.apiRelations;\n if (!tagged && existingRelations !== undefined)\n this.fail(\n `${name} has saved runtime facts but is untagged. Migrate it before using declaration-only generation.`,\n );\n if (!tagged) continue;\n const file = path.join(workspaceRoot, info.root, 'runtime-deps.json');\n if (!fs.existsSync(file)) this.fail(`Missing approved declaration ${file}.`);\n declarations.set(\n name,\n new RuntimeDeclarationCodec().decode(fs.readFileSync(file, 'utf8'), name),\n );\n graph[name].runtimeDeclaration = `${info.root}/runtime-deps.json`;\n }\n const assembler = new RuntimeWiringAssembler(declarations);\n for (const project of declarations) {\n const name = project[0];\n const declaration = project[1];\n if (declaration.entry === undefined) continue;\n const entry = graph[name];\n if (declaration.host !== undefined && entry.serviceName !== declaration.host)\n this.fail(\n `${name}: plan host ${declaration.host} disagrees with project serviceName ${entry.serviceName}.`,\n );\n const relations: ProjectApiRelations = {};\n for (const resolved of assembler.assemble(name)) {\n const relation = relations[resolved.owner] ?? {\n kind: 'uses',\n implements: [],\n uses: [],\n };\n const ref: ApiRef = { api: resolved.api, type: resolved.transport };\n if (resolved.conditional !== undefined) ref.conditional = resolved.conditional;\n ref.declaredVia = resolved.via;\n if (resolved.target?.kind === 'service')\n ref.targetService = resolved.target.service;\n relation[resolved.direction].push(ref);\n relation.kind =\n relation.implements.length === 0\n ? 'uses'\n : relation.uses.length === 0\n ? 'implements'\n : 'uses-implements';\n relations[resolved.owner] = relation;\n }\n entry.apiRelations = relations;\n entry.runtimeComposition = true;\n }\n this.validateTargets(workspaceRoot, graphPath, graph);\n }\n\n private validateTargets(workspaceRoot: string, graphPath: string, graph: EnhancedGraph): void {\n const saved = loadBlessedGraph(workspaceRoot, graphPath);\n if (saved === null)\n this.fail('Missing saved contract references for approved runtime wiring.');\n const contracts = new ApiContractFiles().load(\n workspaceRoot,\n graphPath,\n saved.apiContractFiles,\n );\n new ServiceTargetValidator().validate(\n graph,\n contracts,\n saved.externalSystems,\n saved.remoteServices,\n );\n }\n\n private fail(message: string): never {\n throw new RuleFailError('validate-runtime-architecture', message, undefined, undefined, [\n new Option(\n 'Migrate canonical src/wiring.ts, tag owners webpieces/webpieces-lib, and review runtime-deps.json candidates before generation. Saved visualizations remain available.',\n true,\n ),\n ]);\n }\n}\n"]}