@webpieces/nx-webpieces-rules 0.4.795 → 0.4.796

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.
@@ -1 +1 @@
1
- {"version":3,"file":"api-ast.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/api-ast.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * API contract AST accessors\n *\n * The pure, stateless half of the api scan: given a TypeScript node, what contract / endpoint /\n * injected type does it describe? Split out of api-scanner.ts, which owns the STATEFUL walk (project\n * programs, the source index, relation accumulation) and had grown past the file-size limit.\n *\n * Everything here is parser-level on purpose. Decorators must be read exactly as written, and a\n * plain parse cannot be diverted to a decorator-erased `.d.ts` by module resolution — the bug\n * api-scanner's source pre-pass exists to guard against.\n */\n\nimport * as ts from 'typescript';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { classDecorators, decoratorName } from '../di-graph/bindings';\nimport {\n ApiClassInfo,\n ApiMethodMeta,\n ApiParameterMeta,\n ContractHttpMethod,\n ApiTransport,\n EmptiedApiContract,\n EndpointKind,\n ExternalSystemDeclaration,\n isExternalSystemKind,\n NonLiteralDecoratorArg,\n UndeclaredExternalCaller,\n UnresolvedEndpointPath,\n} from './api-relations';\n\n/** Legal `@Endpoint(path, kind)` values; anything else is a source error, not a kind we invent. */\nconst ENDPOINT_KINDS: readonly EndpointKind[] = ['rpc', 'cloudtasks', 'cron', 'external'];\n\n/**\n * Name suffix that marks an exported type in an `externalApiPaths` project as a vendor CONTRACT\n * (`GmailApi`, `StorageApi`) rather than one of the DTOs, configs or clients sitting beside it.\n * The same convention the in-repo contracts already follow, applied where no decorator can be read.\n */\nconst EXTERNAL_CONTRACT_SUFFIX = 'Api';\n\n/** JSDoc tag a vendor contract uses to declare WHAT it is a seam to: `@externalSystem database Firestore`. */\nconst EXTERNAL_SYSTEM_TAG = 'externalSystem';\n\n/**\n * Client-config class-name suffix whose FIRST constructor argument is the target service name —\n * `ClientConfig('helper-fsdb')` (rpc) and `TaskClientConfig('helper-fsdb')` (pubsub) both take\n * `svcName` first, and a consumer's own `XxxClientConfig` follows the same shape.\n */\nconst CLIENT_CONFIG_SUFFIX = 'ClientConfig';\n\n/**\n * The module-scope `const NAME = '<string literal>'` bindings of ONE source file.\n *\n * A contract that hoists its route to a constant (`@ApiPath(WHATSAPP_API_PATH)`) is good practice —\n * it lets a sibling contract and its callers share the symbol — but a decorator argument is read as\n * TEXT here, with no checker to constant-fold it. Without this table such an argument resolved to\n * nothing: the class lost its basePath, and a class whose every @Endpoint path was a constant\n * resolved to zero methods and was dropped from the graph entirely.\n *\n * Deliberately SAME-MODULE only. Following an import would mean resolving modules, which is exactly\n * what the source pre-pass avoids (it can be diverted to a decorator-erased `.d.ts`). A cross-module\n * constant is therefore still unresolvable — and is REPORTED rather than silently dropped, see\n * DecoratorArgDiagnostics.\n */\nexport class ModuleStringConstants {\n constructor(private readonly byName: Map<string, string>) {}\n\n lookup(name: string): string | null {\n return this.byName.get(name) ?? null;\n }\n}\n\n/** Parsed constants per source file — every class in a file shares one table. */\nconst CONSTANTS_BY_FILE = new WeakMap<ts.SourceFile, ModuleStringConstants>();\n\n/** The module-scope string constants of `sourceFile`, parsed once per file. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function stringConstantsOf(sourceFile: ts.SourceFile): ModuleStringConstants {\n const cached = CONSTANTS_BY_FILE.get(sourceFile);\n if (cached !== undefined) return cached;\n const byName = new Map<string, string>();\n for (const statement of sourceFile.statements) {\n if (!ts.isVariableStatement(statement)) continue;\n if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0) continue;\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name)) continue;\n const text = stringValueOf(declaration.initializer);\n if (text !== null) byName.set(declaration.name.text, text);\n }\n }\n const constants = new ModuleStringConstants(byName);\n CONSTANTS_BY_FILE.set(sourceFile, constants);\n return constants;\n}\n\n/** The string an initializer denotes, unwrapping `as const` / parentheses, else null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function stringValueOf(expr: ts.Expression | undefined): string | null {\n if (expr === undefined) return null;\n if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) return expr.text;\n if (ts.isAsExpression(expr) || ts.isParenthesizedExpression(expr))\n return stringValueOf(expr.expression);\n return null;\n}\n\n/**\n * ONE decorator argument that had to be a string, and what came of it.\n *\n * `value` is the string when it was a literal or resolved through a same-module constant.\n * `unresolvedName` is the argument as written (`WHATSAPP_API_PATH`) when it is present but could not\n * be reduced — the case that must be reported, never silently dropped. Both are null when the\n * argument is simply absent.\n */\nexport class DecoratorArgValue {\n constructor(\n public readonly value: string | null,\n public readonly unresolvedName: string | null,\n ) {}\n}\n\n/** Read one decorator argument as a string, resolving same-module constants. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function decoratorArgValue(\n expr: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): DecoratorArgValue {\n if (expr === undefined) return new DecoratorArgValue(null, null);\n const literal = stringValueOf(expr);\n if (literal !== null) return new DecoratorArgValue(literal, null);\n if (ts.isIdentifier(expr)) {\n const resolved = constants.lookup(expr.text);\n if (resolved !== null) return new DecoratorArgValue(resolved, null);\n return new DecoratorArgValue(null, expr.text);\n }\n return new DecoratorArgValue(null, expr.getText());\n}\n\n/**\n * Collects everything this parser-only pass had to drop: decorator arguments it could not reduce to\n * a string, plus the two of those that are FATAL rather than merely lossy.\n *\n * A same-module constant now resolves, but a cross-module one (`import { PATH } from './paths'`)\n * genuinely cannot — the source pre-pass has no checker by design. That gap used to be invisible:\n * the contract simply came out with no basePath, or with fewer methods, or not at all. Recording it\n * turns a silent drop into a named one, pointing at the exact file, line and identifier.\n *\n * Three sinks, because the consequences differ. `record` is the warning stream (a @Queue name falls\n * back to a derived one, so the graph is degraded, not wrong). `recordUnresolvedPath` and\n * `recordEmptiedContract` are collected so generation can FAIL — one aggregated error naming every\n * offender, because an author fixing five constants wants all five in one run.\n */\nexport class DecoratorArgDiagnostics {\n private readonly found: NonLiteralDecoratorArg[] = [];\n private readonly unresolvedPaths: UnresolvedEndpointPath[] = [];\n private readonly emptied: EmptiedApiContract[] = [];\n private readonly undeclaredCallers: UndeclaredExternalCaller[] = [];\n\n constructor(private readonly workspaceRoot: string) {}\n\n /** Record `argument` (as written) as unresolvable at `node`'s location. */\n record(\n api: string,\n decorator: string,\n method: string | null,\n argument: string,\n node: ts.Node,\n ): void {\n this.found.push(\n new NonLiteralDecoratorArg(api, decorator, method, argument, this.locate(node)),\n );\n }\n\n /** Record an `@Endpoint` whose path argument is unreadable — fatal, see UnresolvedEndpointPathError. */\n recordUnresolvedPath(api: string, method: string, argument: string, node: ts.Node): void {\n this.unresolvedPaths.push(\n new UnresolvedEndpointPath(api, method, argument, this.locate(node)),\n );\n }\n\n /** Record a class that declared `declared` `@Endpoint` methods and kept none of them. */\n recordEmptiedContract(api: string, declared: number, node: ts.Node): void {\n this.emptied.push(new EmptiedApiContract(api, declared, this.locate(node)));\n }\n\n /** Record an `external` `@Endpoint` whose caller is unreadable — fatal, see UndeclaredExternalCallerError. */\n recordUndeclaredCaller(api: string, method: string, argument: string, node: ts.Node): void {\n this.undeclaredCallers.push(\n new UndeclaredExternalCaller(api, method, argument, this.locate(node)),\n );\n }\n\n all(): NonLiteralDecoratorArg[] {\n return this.found;\n }\n\n unresolvedEndpointPaths(): UnresolvedEndpointPath[] {\n return this.unresolvedPaths;\n }\n\n emptiedContracts(): EmptiedApiContract[] {\n return this.emptied;\n }\n\n undeclaredExternalCallers(): UndeclaredExternalCaller[] {\n return this.undeclaredCallers;\n }\n\n private locate(node: ts.Node): string {\n const sourceFile = node.getSourceFile();\n const position = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n return `${path.relative(this.workspaceRoot, sourceFile.fileName)}:${position.line + 1}`;\n }\n}\n\n/** {api, owner: `project`, type} when `cls` is an `abstract class` carrying `@ApiPath`, else null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function apiClassInfoFrom(\n cls: ts.ClassDeclaration,\n project: string,\n diagnostics: DecoratorArgDiagnostics | null = null,\n): ApiClassInfo | null {\n if (!isAbstractClass(cls) || !hasClassDecorator(cls, 'ApiPath') || !cls.name) return null;\n const api = cls.name.text;\n const constants = stringConstantsOf(cls.getSourceFile());\n const info: ApiClassInfo = {\n api,\n owner: project,\n type: apiTransport(cls),\n methods: endpointMethodsOf(cls, api, constants, diagnostics),\n };\n const basePath = decoratorStringArg(cls, 'ApiPath', constants, diagnostics, api);\n if (basePath !== null) info.basePath = basePath;\n return info;\n}\n\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function apiTransport(cls: ts.ClassDeclaration): ApiTransport {\n return hasClassDecorator(cls, 'PubSub') ? 'pubsub' : 'rpc';\n}\n\n/** The @Endpoint kinds that are actually DELIVERED through a named queue or schedule. */\nconst QUEUED_KINDS: readonly EndpointKind[] = ['cloudtasks', 'cron'];\n\n/**\n * Every `@Endpoint(path, kind)` method on a contract class, in declaration order.\n *\n * `kind` is a REQUIRED argument of the decorator, so a missing/non-literal second argument means the\n * source does not compile (or is mid-edit) — we skip the method rather than defaulting it. Defaulting\n * would put an undeclared cron or webhook into the graph as an ordinary rpc call, which is precisely\n * the blindness the required argument exists to remove.\n *\n * `path` is NOT skippable. It may be a same-module constant; an argument that is present but still\n * cannot be reduced is recorded on `diagnostics` as an UnresolvedEndpointPath, which FAILS generation\n * later. Upstream components need the URL — a client computes its request as `basePath + path` — so\n * dropping the method here shipped a contract missing routing information, and a class whose every\n * path was a constant lost every method and disappeared from the graph entirely.\n *\n * A class that declared endpoints and kept NONE of them is recorded too: `buildApiContracts` skips\n * zero-method classes, which is the door a gutted contract used to leave through unannounced.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function endpointMethodsOf(\n cls: ts.ClassDeclaration,\n api: string,\n constants: ModuleStringConstants = new ModuleStringConstants(new Map<string, string>()),\n diagnostics: DecoratorArgDiagnostics | null = null,\n): ApiMethodMeta[] {\n const methods: ApiMethodMeta[] = [];\n let declared = 0;\n for (const member of cls.members) {\n if (!ts.isMethodDeclaration(member) || !ts.isIdentifier(member.name)) continue;\n const endpoint = memberDecorator(member, 'Endpoint');\n if (endpoint === null) continue;\n declared++;\n const name = member.name.text;\n const args = decoratorArgs(endpoint);\n const pathArg = decoratorArgValue(args[0], constants);\n const kindArg = decoratorArgValue(args[1], constants);\n reportUnresolved(diagnostics, api, 'Endpoint', name, pathArg, endpoint);\n reportUnresolved(diagnostics, api, 'Endpoint', name, kindArg, endpoint);\n if (diagnostics !== null && pathArg.unresolvedName !== null) {\n diagnostics.recordUnresolvedPath(api, name, pathArg.unresolvedName, endpoint);\n }\n const kind = kindArg.value;\n if (\n pathArg.value === null ||\n kind === null ||\n !ENDPOINT_KINDS.includes(kind as EndpointKind)\n )\n continue;\n const httpMethod = httpMethodOf(args[2], constants, diagnostics, api, name, endpoint);\n const method: ApiMethodMeta = {\n name,\n path: pathArg.value,\n kind: kind as EndpointKind,\n httpMethod,\n };\n const parameters = httpParametersOf(\n member,\n httpMethod,\n constants,\n diagnostics,\n api,\n name,\n );\n if (parameters.length > 0) method.parameters = parameters;\n if (endpointResponseTypeOf(args[2], constants) === 'full') method.responseType = 'full';\n // Only a queued or scheduled endpoint HAS a queue. Naming one for a synchronous rpc invited a\n // tool to read `methods.map(m => m.queueName)` as a provisioning list and create queues that\n // nothing will ever deliver to.\n if (QUEUED_KINDS.includes(method.kind)) {\n method.queueName = queueNameOf(member, api, name, constants, diagnostics);\n }\n // Only an `external` endpoint HAS an outside caller, mirroring the queue rule above. A\n // caller recorded on an rpc method would be a fact about nothing, and would put a vendor\n // box on the graph beside an endpoint no vendor calls.\n if (method.kind === 'external') {\n const caller = externalCallerOf(args[2], constants);\n if (caller.declaration !== null) method.caller = caller.declaration;\n else if (diagnostics !== null)\n diagnostics.recordUndeclaredCaller(api, name, caller.problem!, endpoint);\n }\n methods.push(method);\n }\n // Declared endpoints, kept none: the class is about to be skipped as \"zero methods\" and would\n // leave no trace. Never legitimate — a routeless contract declares no @Endpoint at all.\n if (diagnostics !== null && declared > 0 && methods.length === 0) {\n diagnostics.recordEmptiedContract(api, declared, cls);\n }\n return methods;\n}\n\n/** `@Endpoint(..., { httpMethod: 'GET' })`, defaulting to the runtime's POST default. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function httpMethodOf(\n options: ts.Expression | undefined,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n method: string,\n node: ts.Node,\n): ContractHttpMethod {\n if (options === undefined || !ts.isObjectLiteralExpression(options)) return 'POST';\n const declared = objectPropertyValue(options, 'httpMethod', constants);\n reportUnresolved(diagnostics, api, 'Endpoint.httpMethod', method, declared, node);\n return declared.value === 'GET' ? 'GET' : 'POST';\n}\n\n/** Only the non-default full-response marker needs an architecture field. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function endpointResponseTypeOf(\n options: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): 'body' | 'full' {\n if (options === undefined || !ts.isObjectLiteralExpression(options)) return 'body';\n return objectPropertyValue(options, 'responseType', constants).value === 'full'\n ? 'full'\n : 'body';\n}\n\n/** Explicit `@PathParam` / `@QueryParam` mappings in source declaration order. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function httpParametersOf(\n member: ts.MethodDeclaration,\n httpMethod: ContractHttpMethod,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n method: string,\n): ApiParameterMeta[] {\n const parameters: ApiParameterMeta[] = [];\n member.parameters.forEach((parameter: ts.ParameterDeclaration, index: number) => {\n let mapped = false;\n for (const source of ['path', 'query'] as const) {\n const decoratorNameWanted = source === 'path' ? 'PathParam' : 'QueryParam';\n const decorator = decoratorOn(parameter, decoratorNameWanted);\n if (decorator === null) continue;\n mapped = true;\n const wireName = decoratorArgValue(decoratorArgs(decorator)[0], constants);\n reportUnresolved(diagnostics, api, decoratorNameWanted, method, wireName, decorator);\n if (wireName.value !== null) {\n parameters.push({ index, source, wireName: wireName.value });\n }\n }\n if (!mapped && httpMethod === 'POST') parameters.push({ index, source: 'body' });\n });\n return parameters;\n}\n\n/** Default `callerKind` when an `external` endpoint declares `calledBy` alone — mirrors core-util. */\nconst DEFAULT_CALLER_KIND = 'saas';\n\n/**\n * The outcome of reading `@Endpoint(path, 'external', { calledBy, callerKind })`'s third argument:\n * either the resolved declaration, or the reason it could not be resolved (never both).\n */\nexport class ExternalCallerRead {\n constructor(\n public readonly declaration: ExternalSystemDeclaration | null,\n /** What was wrong, as written, for the diagnostic. Null exactly when `declaration` is set. */\n public readonly problem: string | null,\n ) {}\n}\n\n/**\n * Read the declared caller out of the @Endpoint OPTIONS OBJECT LITERAL — `args[2]`, not a positional\n * argument, because that is where `formPost` already lives and one options bag beats two.\n *\n * Everything unreadable is a PROBLEM, never a default: an unknown `callerKind` draws the wrong shape\n * (which teaches the reader something false), and a missing `calledBy` puts us back at a box that can\n * only name our own contract. The kind default applies ONLY to the case the API deliberately allows —\n * `calledBy` present, `callerKind` absent.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function externalCallerOf(\n arg: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): ExternalCallerRead {\n if (arg === undefined) return new ExternalCallerRead(null, '<no options argument>');\n if (!ts.isObjectLiteralExpression(arg)) return new ExternalCallerRead(null, arg.getText());\n const calledBy = objectPropertyValue(arg, 'calledBy', constants);\n if (calledBy.value === null || calledBy.value === '') {\n return new ExternalCallerRead(null, calledBy.unresolvedName ?? '<no calledBy>');\n }\n const callerKind = objectPropertyValue(arg, 'callerKind', constants);\n if (callerKind.value === null && callerKind.unresolvedName !== null) {\n return new ExternalCallerRead(null, `callerKind: ${callerKind.unresolvedName}`);\n }\n const kind = callerKind.value ?? DEFAULT_CALLER_KIND;\n if (!isExternalSystemKind(kind)) return new ExternalCallerRead(null, `callerKind: '${kind}'`);\n return new ExternalCallerRead({ kind, label: calledBy.value }, null);\n}\n\n/** One property of an object literal, read as a string through the same constant folding as an argument. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function objectPropertyValue(\n literal: ts.ObjectLiteralExpression,\n name: string,\n constants: ModuleStringConstants,\n): DecoratorArgValue {\n for (const property of literal.properties) {\n if (!ts.isPropertyAssignment(property) || property.name === undefined) continue;\n const key =\n ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)\n ? property.name.text\n : null;\n if (key !== name) continue;\n return decoratorArgValue(property.initializer, constants);\n }\n return new DecoratorArgValue(null, null);\n}\n\n/** `@Queue('...')` override when present and resolvable, else the derived `${Api}-${method}`. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function queueNameOf(\n member: ts.MethodDeclaration,\n api: string,\n name: string,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n): string {\n const override = memberDecorator(member, 'Queue');\n if (override === null) return `${api}-${name}`;\n const queueArg = decoratorArgValue(decoratorArgs(override)[0], constants);\n reportUnresolved(diagnostics, api, 'Queue', name, queueArg, override);\n return queueArg.value ?? `${api}-${name}`;\n}\n\n/** Record an argument that is present but unresolvable; a resolved or absent one is silent. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function reportUnresolved(\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n decorator: string,\n method: string | null,\n arg: DecoratorArgValue,\n node: ts.Node,\n): void {\n if (diagnostics === null || arg.unresolvedName === null) return;\n diagnostics.record(api, decorator, method, arg.unresolvedName, node);\n}\n\n/** The arguments of a decorator's call expression, or [] when it is a bare `@Foo` reference. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function decoratorArgs(\n decorator: ts.Decorator,\n): ts.NodeArray<ts.Expression> | ts.Expression[] {\n return ts.isCallExpression(decorator.expression) ? decorator.expression.arguments : [];\n}\n\n/** The named decorator on a class member, or null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function memberDecorator(member: ts.ClassElement, name: string): ts.Decorator | null {\n const decorators = ts.getDecorators(member as ts.HasDecorators) ?? [];\n return decorators.find((d: ts.Decorator) => decoratorName(d) === name) ?? null;\n}\n\n/** The named decorator on any decorator-capable AST node (notably a method parameter). */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching memberDecorator\nexport function decoratorOn(node: ts.HasDecorators, name: string): ts.Decorator | null {\n const decorators = ts.getDecorators(node) ?? [];\n return decorators.find((decorator: ts.Decorator) => decoratorName(decorator) === name) ?? null;\n}\n\n/**\n * The first argument of a class decorator as a string (`@ApiPath('/x')`, `@ApiPath(X_PATH)`), else\n * null. A same-module constant resolves; anything else is recorded on `diagnostics`.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function decoratorStringArg(\n cls: ts.ClassDeclaration,\n name: string,\n constants: ModuleStringConstants = new ModuleStringConstants(new Map<string, string>()),\n diagnostics: DecoratorArgDiagnostics | null = null,\n api: string = name,\n): string | null {\n const decorator = classDecorators(cls).find((d: ts.Decorator) => decoratorName(d) === name);\n if (decorator === undefined) return null;\n const arg = decoratorArgValue(decoratorArgs(decorator)[0], constants);\n reportUnresolved(diagnostics, api, name, null, arg, decorator);\n return arg.value;\n}\n\n/** The constructor's parameters, or [] when the class declares no constructor. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function constructorParamsOf(cls: ts.ClassDeclaration): readonly ts.ParameterDeclaration[] {\n for (const member of cls.members) {\n if (ts.isConstructorDeclaration(member)) return member.parameters;\n }\n return [];\n}\n\n/**\n * The bare name of a type reference (`GmailApi`, or `gmail.GmailApi` -> `GmailApi`), else null.\n * Generic wrappers are deliberately NOT unwrapped: `Provider<GmailApi>` hands out the contract\n * lazily, which is still a use, but it is not the shape any of these seams take today and guessing\n * at type arguments would start matching things that merely mention a contract.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function typeReferenceName(type: ts.TypeNode | undefined): string | null {\n if (type === undefined || !ts.isTypeReferenceNode(type)) return null;\n const name = type.typeName;\n if (ts.isIdentifier(name)) return name.text;\n return ts.isQualifiedName(name) && ts.isIdentifier(name.right) ? name.right.text : null;\n}\n\n/** Every type name in the class's `implements` clause — the contracts this class IS, not ones it calls. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function implementedTypeNames(cls: ts.ClassDeclaration): Set<string> {\n const names = new Set<string>();\n for (const clause of cls.heritageClauses ?? []) {\n if (clause.token !== ts.SyntaxKind.ImplementsKeyword) continue;\n for (const type of clause.types) {\n if (ts.isIdentifier(type.expression)) names.add(type.expression.text);\n }\n }\n return names;\n}\n\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function isAbstractClass(cls: ts.ClassDeclaration): boolean {\n return (ts.getModifiers(cls) ?? []).some(\n (m: ts.Modifier) => m.kind === ts.SyntaxKind.AbstractKeyword,\n );\n}\n\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function hasClassDecorator(cls: ts.ClassDeclaration, name: string): boolean {\n return classDecorators(cls).some((d: ts.Decorator) => decoratorName(d) === name);\n}\n\n/**\n * The service a client-factory call aims at, from its config argument:\n * `createRpcClient(WarmupApi, new ClientConfig('helper-fsdb'))` → `'helper-fsdb'`.\n *\n * Only a `new <Xxx>ClientConfig('<string literal>')` yields a name. A variable, a template string\n * or a computed expression yields null — the target is genuinely unknown at scan time, and the\n * runtime graph must fall back to fan-out (loudly) rather than guess.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function targetServiceOf(call: ts.CallExpression): string | null {\n if (call.arguments.length < 2) return null;\n const config = call.arguments[1];\n if (!ts.isNewExpression(config) || !ts.isIdentifier(config.expression)) return null;\n if (!config.expression.text.endsWith(CLIENT_CONFIG_SUFFIX)) return null;\n const first = config.arguments?.[0];\n if (first === undefined || !ts.isStringLiteral(first)) return null;\n return first.text.length > 0 ? first.text : null;\n}\n\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function calleeMethodName(call: ts.CallExpression): string | null {\n const callee = call.expression;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n if (ts.isIdentifier(callee)) return callee.text;\n return null;\n}\n\n// webpieces-disable no-function-outside-class -- pure path predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function isTestFile(fileName: string): boolean {\n return (\n fileName.includes('/__tests__/') ||\n fileName.includes('.spec.') ||\n fileName.includes('.test.')\n );\n}\n\n/** {api, owner, type:'rpc'|'pubsub'} for an in-repo contract class, else null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function apiClassInfoFromNode(\n node: ts.Node,\n project: string,\n diagnostics: DecoratorArgDiagnostics | null = null,\n): ApiClassInfo | null {\n return ts.isClassDeclaration(node) ? apiClassInfoFrom(node, project, diagnostics) : null;\n}\n\n/**\n * {api, owner, type:'external'} for a VENDOR contract, else null.\n *\n * A vendor contract cannot be detected the way an in-repo one is. It carries no @ApiPath (there is\n * no route — the call leaves through a vendor SDK), and it is usually a plain `interface` bound to a\n * Symbol token, which is not even a class. So inside a project the workspace has DECLARED external\n * (`runtime-architecture.externalApiPaths`) the signal is structural instead: an exported\n * `interface`/`abstract class` whose name ends in `Api`. That deliberately picks up `GmailApi` and\n * `StorageApi` while leaving their DTOs, `*Config` types and `*Client` implementations alone.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function externalApiInfoFrom(node: ts.Node, project: string): ApiClassInfo | null {\n const named =\n ts.isInterfaceDeclaration(node) || (ts.isClassDeclaration(node) && isAbstractClass(node));\n if (!named || !node.name || !isExported(node)) return null;\n const api = node.name.text;\n if (!api.endsWith(EXTERNAL_CONTRACT_SUFFIX)) return null;\n const externalSystem = externalSystemTagFrom(node, api);\n return externalSystem === null\n ? { api, owner: project, type: 'external', methods: [] }\n : { api, owner: project, type: 'external', methods: [], externalSystem };\n}\n\n/**\n * The `@externalSystem <kind> [label]` JSDoc tag on a vendor contract, or null when absent.\n *\n * JSDoc rather than a decorator is not a style choice: these seams are TS `interface`s, and TS has\n * no interface decorators. Without the tag the contract still renders — as the generic dashed box it\n * always was — so this is purely additive and nothing needs migrating.\n *\n * The label defaults to the contract name minus its `Api` suffix (`FirestoreAdminApi` →\n * `FirestoreAdmin`), because the label is the node IDENTITY: two contracts that mean the same system\n * must be given the SAME explicit label to converge on one node.\n *\n * An unrecognised kind is ignored rather than defaulted. Silently drawing a `@externalSystem\n * databse` typo as a generic box is recoverable; drawing it as the wrong shape teaches the reader\n * something false about the architecture.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function externalSystemTagFrom(\n node: ts.Node,\n api: string,\n): ExternalSystemDeclaration | null {\n for (const tag of ts.getJSDocTags(node)) {\n if (tag.tagName.text !== EXTERNAL_SYSTEM_TAG) continue;\n const comment = typeof tag.comment === 'string' ? tag.comment : '';\n const parts = comment\n .trim()\n .split(/\\s+/)\n .filter((part: string) => part !== '');\n if (parts.length === 0) continue;\n const kind = parts[0].toLowerCase();\n if (!isExternalSystemKind(kind)) continue;\n const label = parts.slice(1).join(' ').trim();\n return { kind, label: label === '' ? api.replace(/Api$/, '') : label };\n }\n return null;\n}\n\n/** True when the declaration carries an `export` modifier. */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function isExported(node: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {\n return (ts.getModifiers(node) ?? []).some(\n (m: ts.Modifier) => m.kind === ts.SyntaxKind.ExportKeyword,\n );\n}\n\n// webpieces-disable no-function-outside-class -- recursive fs walker, matching the AST-helper style here\nexport function collectTsFiles(dir: string): string[] {\n const out: string[] = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== 'node_modules') out.push(...collectTsFiles(full));\n } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {\n out.push(full);\n }\n }\n return out;\n}\n"]}
1
+ {"version":3,"file":"api-ast.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/api-ast.ts"],"names":[],"mappings":"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","sourcesContent":["/** Parser-only API contract AST accessors; deliberately avoids module resolution and erased d.ts. */\n\nimport * as ts from 'typescript';\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport { classDecorators, decoratorName } from '../di-graph/bindings';\nimport {\n ApiClassInfo,\n ApiMethodMeta,\n ApiParameterMeta,\n ContractHttpMethod,\n ApiTransport,\n EmptiedApiContract,\n EndpointKind,\n EndpointOperation,\n ExternalSystemDeclaration,\n isExternalSystemKind,\n NonLiteralDecoratorArg,\n UndeclaredExternalCaller,\n UndeclaredEndpointOperation,\n UnresolvedEndpointPath,\n} from './api-relations';\n\n/** Legal enum-backed `@Endpoint` symbols; tooling reads source without importing application code. */\nconst ENDPOINT_KINDS: readonly EndpointKind[] = ['rpc', 'cloudtasks', 'cron', 'external'];\nconst ENDPOINT_OPERATIONS: readonly EndpointOperation[] = ['read', 'write-idempotent', 'write'];\nconst ENDPOINT_SYMBOLS: Readonly<Record<string, string>> = {\n GET: 'GET',\n POST: 'POST',\n READ: 'read',\n WRITE_IDEMPOTENT: 'write-idempotent',\n WRITE: 'write',\n RPC: 'rpc',\n CLOUDTASKS: 'cloudtasks',\n CRON: 'cron',\n EXTERNAL: 'external',\n};\n\n/**\n * Name suffix that marks an exported type in an `externalApiPaths` project as a vendor CONTRACT\n * (`GmailApi`, `StorageApi`) rather than one of the DTOs, configs or clients sitting beside it.\n * The same convention the in-repo contracts already follow, applied where no decorator can be read.\n */\nconst EXTERNAL_CONTRACT_SUFFIX = 'Api';\n\n/** JSDoc tag a vendor contract uses to declare WHAT it is a seam to: `@externalSystem database Firestore`. */\nconst EXTERNAL_SYSTEM_TAG = 'externalSystem';\n\n/**\n * Client-config class-name suffix whose FIRST constructor argument is the target service name —\n * `ClientConfig('helper-fsdb')` (rpc) and `TaskClientConfig('helper-fsdb')` (pubsub) both take\n * `svcName` first, and a consumer's own `XxxClientConfig` follows the same shape.\n */\nconst CLIENT_CONFIG_SUFFIX = 'ClientConfig';\n\n/** Same-module string constants that parser-only decorator scanning can safely resolve. */\nexport class ModuleStringConstants {\n constructor(private readonly byName: Map<string, string>) {}\n\n lookup(name: string): string | null {\n return this.byName.get(name) ?? null;\n }\n}\n\n/** Parsed constants per source file — every class in a file shares one table. */\nconst CONSTANTS_BY_FILE = new WeakMap<ts.SourceFile, ModuleStringConstants>();\n\n/** The module-scope string constants of `sourceFile`, parsed once per file. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function stringConstantsOf(sourceFile: ts.SourceFile): ModuleStringConstants {\n const cached = CONSTANTS_BY_FILE.get(sourceFile);\n if (cached !== undefined) return cached;\n const byName = new Map<string, string>();\n for (const statement of sourceFile.statements) {\n if (!ts.isVariableStatement(statement)) continue;\n if ((statement.declarationList.flags & ts.NodeFlags.Const) === 0) continue;\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name)) continue;\n const text = stringValueOf(declaration.initializer);\n if (text !== null) byName.set(declaration.name.text, text);\n }\n }\n const constants = new ModuleStringConstants(byName);\n CONSTANTS_BY_FILE.set(sourceFile, constants);\n return constants;\n}\n\n/** The string an initializer denotes, unwrapping `as const` / parentheses, else null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function stringValueOf(expr: ts.Expression | undefined): string | null {\n if (expr === undefined) return null;\n if (ts.isStringLiteral(expr) || ts.isNoSubstitutionTemplateLiteral(expr)) return expr.text;\n if (ts.isAsExpression(expr) || ts.isParenthesizedExpression(expr))\n return stringValueOf(expr.expression);\n return null;\n}\n\n/** A resolved decorator string, or the source spelling that could not be reduced. */\nexport class DecoratorArgValue {\n constructor(\n public readonly value: string | null,\n public readonly unresolvedName: string | null,\n ) {}\n}\n\n/** Read one decorator argument as a string, resolving same-module constants. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function decoratorArgValue(\n expr: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): DecoratorArgValue {\n if (expr === undefined) return new DecoratorArgValue(null, null);\n const literal = stringValueOf(expr);\n if (literal !== null) return new DecoratorArgValue(literal, null);\n if (ts.isIdentifier(expr)) {\n const resolved = constants.lookup(expr.text);\n if (resolved !== null) return new DecoratorArgValue(resolved, null);\n return new DecoratorArgValue(null, expr.text);\n }\n return new DecoratorArgValue(null, expr.getText());\n}\n\n/** Resolve the enum-backed constants intentionally allowed in @Endpoint positional arguments. */\n// webpieces-disable no-function-outside-class -- pure AST accessor for decorator source\nfunction endpointSymbolArgValue(\n expr: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): DecoratorArgValue {\n if (expr && ts.isIdentifier(expr)) {\n const builtin = ENDPOINT_SYMBOLS[expr.text];\n if (builtin !== undefined) return new DecoratorArgValue(builtin, null);\n }\n return decoratorArgValue(expr, constants);\n}\n\n/** Aggregates parser-only scan losses so generation can fail loudly instead of dropping routes. */\nexport class DecoratorArgDiagnostics {\n private readonly found: NonLiteralDecoratorArg[] = [];\n private readonly unresolvedPaths: UnresolvedEndpointPath[] = [];\n private readonly emptied: EmptiedApiContract[] = [];\n private readonly undeclaredCallers: UndeclaredExternalCaller[] = [];\n private readonly undeclaredOperations: UndeclaredEndpointOperation[] = [];\n\n constructor(private readonly workspaceRoot: string) {}\n\n /** Record `argument` (as written) as unresolvable at `node`'s location. */\n record(\n api: string,\n decorator: string,\n method: string | null,\n argument: string,\n node: ts.Node,\n ): void {\n this.found.push(\n new NonLiteralDecoratorArg(api, decorator, method, argument, this.locate(node)),\n );\n }\n\n /** Record an `@Endpoint` whose path argument is unreadable — fatal, see UnresolvedEndpointPathError. */\n recordUnresolvedPath(api: string, method: string, argument: string, node: ts.Node): void {\n this.unresolvedPaths.push(\n new UnresolvedEndpointPath(api, method, argument, this.locate(node)),\n );\n }\n\n /** Record a class that declared `declared` `@Endpoint` methods and kept none of them. */\n recordEmptiedContract(api: string, declared: number, node: ts.Node): void {\n this.emptied.push(new EmptiedApiContract(api, declared, this.locate(node)));\n }\n\n /** Record an `external` `@Endpoint` whose caller is unreadable — fatal, see UndeclaredExternalCallerError. */\n recordUndeclaredCaller(api: string, method: string, argument: string, node: ts.Node): void {\n this.undeclaredCallers.push(\n new UndeclaredExternalCaller(api, method, argument, this.locate(node)),\n );\n }\n\n /** Record an `@Endpoint` whose required operation is missing or unreadable. */\n recordUndeclaredOperation(api: string, method: string, argument: string, node: ts.Node): void {\n this.undeclaredOperations.push(\n new UndeclaredEndpointOperation(api, method, argument, this.locate(node)),\n );\n }\n\n all(): NonLiteralDecoratorArg[] {\n return this.found;\n }\n\n unresolvedEndpointPaths(): UnresolvedEndpointPath[] {\n return this.unresolvedPaths;\n }\n\n emptiedContracts(): EmptiedApiContract[] {\n return this.emptied;\n }\n\n undeclaredExternalCallers(): UndeclaredExternalCaller[] {\n return this.undeclaredCallers;\n }\n\n undeclaredEndpointOperations(): UndeclaredEndpointOperation[] {\n return this.undeclaredOperations;\n }\n\n private locate(node: ts.Node): string {\n const sourceFile = node.getSourceFile();\n const position = sourceFile.getLineAndCharacterOfPosition(node.getStart());\n return `${path.relative(this.workspaceRoot, sourceFile.fileName)}:${position.line + 1}`;\n }\n}\n\n/** {api, owner: `project`, type} when `cls` is an `abstract class` carrying `@ApiPath`, else null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function apiClassInfoFrom(\n cls: ts.ClassDeclaration,\n project: string,\n diagnostics: DecoratorArgDiagnostics | null = null,\n): ApiClassInfo | null {\n if (!isAbstractClass(cls) || !hasClassDecorator(cls, 'ApiPath') || !cls.name) return null;\n const api = cls.name.text;\n const constants = stringConstantsOf(cls.getSourceFile());\n const info: ApiClassInfo = {\n api,\n owner: project,\n type: apiTransport(cls),\n methods: endpointMethodsOf(cls, api, constants, diagnostics),\n };\n const basePath = decoratorStringArg(cls, 'ApiPath', constants, diagnostics, api);\n if (basePath !== null) info.basePath = basePath;\n return info;\n}\n\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function apiTransport(cls: ts.ClassDeclaration): ApiTransport {\n return hasClassDecorator(cls, 'PubSub') ? 'pubsub' : 'rpc';\n}\n\n/** The @Endpoint kinds that are actually DELIVERED through a named queue or schedule. */\nconst QUEUED_KINDS: readonly EndpointKind[] = ['cloudtasks', 'cron'];\n\n/** Reads required `(method, path, operation, kind, options?)` endpoint declarations in order. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function endpointMethodsOf(\n cls: ts.ClassDeclaration,\n api: string,\n constants: ModuleStringConstants = new ModuleStringConstants(new Map<string, string>()),\n diagnostics: DecoratorArgDiagnostics | null = null,\n): ApiMethodMeta[] {\n const methods: ApiMethodMeta[] = [];\n let declared = 0;\n for (const member of cls.members) {\n if (!ts.isMethodDeclaration(member) || !ts.isIdentifier(member.name)) continue;\n const endpoint = memberDecorator(member, 'Endpoint');\n if (endpoint === null) continue;\n declared++;\n const name = member.name.text;\n const args = decoratorArgs(endpoint);\n const methodArg = endpointSymbolArgValue(args[0], constants);\n const pathArg = decoratorArgValue(args[1], constants);\n const operationArg = endpointSymbolArgValue(args[2], constants);\n const kindArg = endpointSymbolArgValue(args[3], constants);\n reportUnresolved(diagnostics, api, 'Endpoint', name, methodArg, endpoint);\n reportUnresolved(diagnostics, api, 'Endpoint', name, pathArg, endpoint);\n reportUnresolved(diagnostics, api, 'Endpoint', name, operationArg, endpoint);\n reportUnresolved(diagnostics, api, 'Endpoint', name, kindArg, endpoint);\n if (diagnostics !== null && pathArg.unresolvedName !== null) {\n diagnostics.recordUnresolvedPath(api, name, pathArg.unresolvedName, endpoint);\n }\n const httpMethod = methodArg.value;\n const operation = endpointOperationOf(args[2], constants, diagnostics, api, name, endpoint);\n const kind = kindArg.value;\n if (\n (httpMethod !== 'GET' && httpMethod !== 'POST') ||\n pathArg.value === null ||\n operation === null ||\n kind === null ||\n !ENDPOINT_KINDS.includes(kind as EndpointKind)\n )\n continue;\n const method: ApiMethodMeta = {\n name,\n path: pathArg.value,\n kind: kind as EndpointKind,\n operation,\n httpMethod,\n };\n const parameters = httpParametersOf(member, httpMethod, constants, diagnostics, api, name);\n if (parameters.length > 0) method.parameters = parameters;\n if (endpointResponseTypeOf(args[4], constants) === 'full') method.responseType = 'full';\n if (QUEUED_KINDS.includes(method.kind)) {\n method.queueName = queueNameOf(member, api, name, constants, diagnostics);\n }\n if (method.kind === 'external') {\n const caller = externalCallerOf(args[4], constants);\n if (caller.declaration !== null) method.caller = caller.declaration;\n else if (diagnostics !== null)\n diagnostics.recordUndeclaredCaller(api, name, caller.problem!, endpoint);\n }\n methods.push(method);\n }\n if (diagnostics !== null && declared > 0 && methods.length === 0) {\n diagnostics.recordEmptiedContract(api, declared, cls);\n }\n return methods;\n}\n\n/** Required side-effect declaration; no inference from GET/POST is permitted. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function endpointOperationOf(\n operationArgument: ts.Expression | undefined,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n method: string,\n node: ts.Node,\n): EndpointOperation | null {\n const declared = endpointSymbolArgValue(operationArgument, constants);\n if (declared.value === null) {\n diagnostics?.recordUndeclaredOperation(\n api,\n method,\n declared.unresolvedName ?? '<missing>',\n node,\n );\n return null;\n }\n reportUnresolved(diagnostics, api, 'Endpoint.operation', method, declared, node);\n if (\n declared.value !== null &&\n ENDPOINT_OPERATIONS.includes(declared.value as EndpointOperation)\n ) {\n return declared.value as EndpointOperation;\n }\n diagnostics?.recordUndeclaredOperation(\n api,\n method,\n declared.unresolvedName ?? declared.value ?? '<missing>',\n node,\n );\n return null;\n}\n\n/** Required first positional HTTP method. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function httpMethodOf(\n methodArgument: ts.Expression | undefined,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n method: string,\n node: ts.Node,\n): ContractHttpMethod | null {\n const declared = endpointSymbolArgValue(methodArgument, constants);\n reportUnresolved(diagnostics, api, 'Endpoint.httpMethod', method, declared, node);\n if (declared.value === 'GET' || declared.value === 'POST') return declared.value;\n return null;\n}\n\n/** Only the non-default full-response marker needs an architecture field. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function endpointResponseTypeOf(\n options: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): 'body' | 'full' {\n if (options === undefined || !ts.isObjectLiteralExpression(options)) return 'body';\n return objectPropertyValue(options, 'responseType', constants).value === 'full'\n ? 'full'\n : 'body';\n}\n\n/** Explicit `@PathParam` / `@QueryParam` mappings in source declaration order. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers\nexport function httpParametersOf(\n member: ts.MethodDeclaration,\n httpMethod: ContractHttpMethod,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n method: string,\n): ApiParameterMeta[] {\n const parameters: ApiParameterMeta[] = [];\n member.parameters.forEach((parameter: ts.ParameterDeclaration, index: number) => {\n let mapped = false;\n for (const source of ['path', 'query'] as const) {\n const decoratorNameWanted = source === 'path' ? 'PathParam' : 'QueryParam';\n const decorator = decoratorOn(parameter, decoratorNameWanted);\n if (decorator === null) continue;\n mapped = true;\n const wireName = decoratorArgValue(decoratorArgs(decorator)[0], constants);\n reportUnresolved(diagnostics, api, decoratorNameWanted, method, wireName, decorator);\n if (wireName.value !== null) {\n parameters.push({ index, source, wireName: wireName.value });\n }\n }\n if (!mapped && httpMethod === 'POST') parameters.push({ index, source: 'body' });\n });\n return parameters;\n}\n\n/** Default `callerKind` when an `external` endpoint declares `calledBy` alone — mirrors core-util. */\nconst DEFAULT_CALLER_KIND = 'saas';\n\n/** Resolved EXTERNAL caller declaration, or the source problem that prevented resolution. */\nexport class ExternalCallerRead {\n constructor(\n public readonly declaration: ExternalSystemDeclaration | null,\n /** What was wrong, as written, for the diagnostic. Null exactly when `declaration` is set. */\n public readonly problem: string | null,\n ) {}\n}\n\n/** Reads `calledBy` and optional `callerKind` from the fifth positional options argument. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function externalCallerOf(\n arg: ts.Expression | undefined,\n constants: ModuleStringConstants,\n): ExternalCallerRead {\n if (arg === undefined) return new ExternalCallerRead(null, '<no options argument>');\n if (!ts.isObjectLiteralExpression(arg)) return new ExternalCallerRead(null, arg.getText());\n const calledBy = objectPropertyValue(arg, 'calledBy', constants);\n if (calledBy.value === null || calledBy.value === '') {\n return new ExternalCallerRead(null, calledBy.unresolvedName ?? '<no calledBy>');\n }\n const callerKind = objectPropertyValue(arg, 'callerKind', constants);\n if (callerKind.value === null && callerKind.unresolvedName !== null) {\n return new ExternalCallerRead(null, `callerKind: ${callerKind.unresolvedName}`);\n }\n const kind = callerKind.value ?? DEFAULT_CALLER_KIND;\n if (!isExternalSystemKind(kind)) return new ExternalCallerRead(null, `callerKind: '${kind}'`);\n return new ExternalCallerRead({ kind, label: calledBy.value }, null);\n}\n\n/** One property of an object literal, read as a string through the same constant folding as an argument. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function objectPropertyValue(\n literal: ts.ObjectLiteralExpression,\n name: string,\n constants: ModuleStringConstants,\n): DecoratorArgValue {\n for (const property of literal.properties) {\n if (!ts.isPropertyAssignment(property) || property.name === undefined) continue;\n const key =\n ts.isIdentifier(property.name) || ts.isStringLiteral(property.name)\n ? property.name.text\n : null;\n if (key !== name) continue;\n return decoratorArgValue(property.initializer, constants);\n }\n return new DecoratorArgValue(null, null);\n}\n\n/** `@Queue('...')` override when present and resolvable, else the derived `${Api}-${method}`. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function queueNameOf(\n member: ts.MethodDeclaration,\n api: string,\n name: string,\n constants: ModuleStringConstants,\n diagnostics: DecoratorArgDiagnostics | null,\n): string {\n const override = memberDecorator(member, 'Queue');\n if (override === null) return `${api}-${name}`;\n const queueArg = decoratorArgValue(decoratorArgs(override)[0], constants);\n reportUnresolved(diagnostics, api, 'Queue', name, queueArg, override);\n return queueArg.value ?? `${api}-${name}`;\n}\n\n/** Record an argument that is present but unresolvable; a resolved or absent one is silent. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function reportUnresolved(\n diagnostics: DecoratorArgDiagnostics | null,\n api: string,\n decorator: string,\n method: string | null,\n arg: DecoratorArgValue,\n node: ts.Node,\n): void {\n if (diagnostics === null || arg.unresolvedName === null) return;\n diagnostics.record(api, decorator, method, arg.unresolvedName, node);\n}\n\n/** The arguments of a decorator's call expression, or [] when it is a bare `@Foo` reference. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function decoratorArgs(\n decorator: ts.Decorator,\n): ts.NodeArray<ts.Expression> | ts.Expression[] {\n return ts.isCallExpression(decorator.expression) ? decorator.expression.arguments : [];\n}\n\n/** The named decorator on a class member, or null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function memberDecorator(member: ts.ClassElement, name: string): ts.Decorator | null {\n const decorators = ts.getDecorators(member as ts.HasDecorators) ?? [];\n return decorators.find((d: ts.Decorator) => decoratorName(d) === name) ?? null;\n}\n\n/** The named decorator on any decorator-capable AST node (notably a method parameter). */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching memberDecorator\nexport function decoratorOn(node: ts.HasDecorators, name: string): ts.Decorator | null {\n const decorators = ts.getDecorators(node) ?? [];\n return decorators.find((decorator: ts.Decorator) => decoratorName(decorator) === name) ?? null;\n}\n\n/**\n * The first argument of a class decorator as a string (`@ApiPath('/x')`, `@ApiPath(X_PATH)`), else\n * null. A same-module constant resolves; anything else is recorded on `diagnostics`.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function decoratorStringArg(\n cls: ts.ClassDeclaration,\n name: string,\n constants: ModuleStringConstants = new ModuleStringConstants(new Map<string, string>()),\n diagnostics: DecoratorArgDiagnostics | null = null,\n api: string = name,\n): string | null {\n const decorator = classDecorators(cls).find((d: ts.Decorator) => decoratorName(d) === name);\n if (decorator === undefined) return null;\n const arg = decoratorArgValue(decoratorArgs(decorator)[0], constants);\n reportUnresolved(diagnostics, api, name, null, arg, decorator);\n return arg.value;\n}\n\n/** The constructor's parameters, or [] when the class declares no constructor. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function constructorParamsOf(cls: ts.ClassDeclaration): readonly ts.ParameterDeclaration[] {\n for (const member of cls.members) {\n if (ts.isConstructorDeclaration(member)) return member.parameters;\n }\n return [];\n}\n\n/**\n * The bare name of a type reference (`GmailApi`, or `gmail.GmailApi` -> `GmailApi`), else null.\n * Generic wrappers are deliberately NOT unwrapped: `Provider<GmailApi>` hands out the contract\n * lazily, which is still a use, but it is not the shape any of these seams take today and guessing\n * at type arguments would start matching things that merely mention a contract.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function typeReferenceName(type: ts.TypeNode | undefined): string | null {\n if (type === undefined || !ts.isTypeReferenceNode(type)) return null;\n const name = type.typeName;\n if (ts.isIdentifier(name)) return name.text;\n return ts.isQualifiedName(name) && ts.isIdentifier(name.right) ? name.right.text : null;\n}\n\n/** Every type name in the class's `implements` clause — the contracts this class IS, not ones it calls. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function implementedTypeNames(cls: ts.ClassDeclaration): Set<string> {\n const names = new Set<string>();\n for (const clause of cls.heritageClauses ?? []) {\n if (clause.token !== ts.SyntaxKind.ImplementsKeyword) continue;\n for (const type of clause.types) {\n if (ts.isIdentifier(type.expression)) names.add(type.expression.text);\n }\n }\n return names;\n}\n\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function isAbstractClass(cls: ts.ClassDeclaration): boolean {\n return (ts.getModifiers(cls) ?? []).some(\n (m: ts.Modifier) => m.kind === ts.SyntaxKind.AbstractKeyword,\n );\n}\n\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function hasClassDecorator(cls: ts.ClassDeclaration, name: string): boolean {\n return classDecorators(cls).some((d: ts.Decorator) => decoratorName(d) === name);\n}\n\n/**\n * The service a client-factory call aims at, from its config argument:\n * `createRpcClient(WarmupApi, new ClientConfig('helper-fsdb'))` → `'helper-fsdb'`.\n *\n * Only a `new <Xxx>ClientConfig('<string literal>')` yields a name. A variable, a template string\n * or a computed expression yields null — the target is genuinely unknown at scan time, and the\n * runtime graph must fall back to fan-out (loudly) rather than guess.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function targetServiceOf(call: ts.CallExpression): string | null {\n if (call.arguments.length < 2) return null;\n const config = call.arguments[1];\n if (!ts.isNewExpression(config) || !ts.isIdentifier(config.expression)) return null;\n if (!config.expression.text.endsWith(CLIENT_CONFIG_SUFFIX)) return null;\n const first = config.arguments?.[0];\n if (first === undefined || !ts.isStringLiteral(first)) return null;\n return first.text.length > 0 ? first.text : null;\n}\n\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function calleeMethodName(call: ts.CallExpression): string | null {\n const callee = call.expression;\n if (ts.isPropertyAccessExpression(callee)) return callee.name.text;\n if (ts.isIdentifier(callee)) return callee.text;\n return null;\n}\n\n// webpieces-disable no-function-outside-class -- pure path predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function isTestFile(fileName: string): boolean {\n return (\n fileName.includes('/__tests__/') ||\n fileName.includes('.spec.') ||\n fileName.includes('.test.')\n );\n}\n\n/** {api, owner, type:'rpc'|'pubsub'} for an in-repo contract class, else null. */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function apiClassInfoFromNode(\n node: ts.Node,\n project: string,\n diagnostics: DecoratorArgDiagnostics | null = null,\n): ApiClassInfo | null {\n return ts.isClassDeclaration(node) ? apiClassInfoFrom(node, project, diagnostics) : null;\n}\n\n/**\n * {api, owner, type:'external'} for a VENDOR contract, else null.\n *\n * A vendor contract cannot be detected the way an in-repo one is. It carries no @ApiPath (there is\n * no route — the call leaves through a vendor SDK), and it is usually a plain `interface` bound to a\n * Symbol token, which is not even a class. So inside a project the workspace has DECLARED external\n * (`runtime-architecture.externalApiPaths`) the signal is structural instead: an exported\n * `interface`/`abstract class` whose name ends in `Api`. That deliberately picks up `GmailApi` and\n * `StorageApi` while leaving their DTOs, `*Config` types and `*Client` implementations alone.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function externalApiInfoFrom(node: ts.Node, project: string): ApiClassInfo | null {\n const named =\n ts.isInterfaceDeclaration(node) || (ts.isClassDeclaration(node) && isAbstractClass(node));\n if (!named || !node.name || !isExported(node)) return null;\n const api = node.name.text;\n if (!api.endsWith(EXTERNAL_CONTRACT_SUFFIX)) return null;\n const externalSystem = externalSystemTagFrom(node, api);\n return externalSystem === null\n ? { api, owner: project, type: 'external', methods: [] }\n : { api, owner: project, type: 'external', methods: [], externalSystem };\n}\n\n/**\n * The `@externalSystem <kind> [label]` JSDoc tag on a vendor contract, or null when absent.\n *\n * JSDoc rather than a decorator is not a style choice: these seams are TS `interface`s, and TS has\n * no interface decorators. Without the tag the contract still renders — as the generic dashed box it\n * always was — so this is purely additive and nothing needs migrating.\n *\n * The label defaults to the contract name minus its `Api` suffix (`FirestoreAdminApi` →\n * `FirestoreAdmin`), because the label is the node IDENTITY: two contracts that mean the same system\n * must be given the SAME explicit label to converge on one node.\n *\n * An unrecognised kind is ignored rather than defaulted. Silently drawing a `@externalSystem\n * databse` typo as a generic box is recoverable; drawing it as the wrong shape teaches the reader\n * something false about the architecture.\n */\n// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts\nexport function externalSystemTagFrom(\n node: ts.Node,\n api: string,\n): ExternalSystemDeclaration | null {\n for (const tag of ts.getJSDocTags(node)) {\n if (tag.tagName.text !== EXTERNAL_SYSTEM_TAG) continue;\n const comment = typeof tag.comment === 'string' ? tag.comment : '';\n const parts = comment\n .trim()\n .split(/\\s+/)\n .filter((part: string) => part !== '');\n if (parts.length === 0) continue;\n const kind = parts[0].toLowerCase();\n if (!isExternalSystemKind(kind)) continue;\n const label = parts.slice(1).join(' ').trim();\n return { kind, label: label === '' ? api.replace(/Api$/, '') : label };\n }\n return null;\n}\n\n/** True when the declaration carries an `export` modifier. */\n// webpieces-disable no-function-outside-class -- pure AST predicate, matching the sibling helpers in di-graph/bindings.ts\nexport function isExported(node: ts.InterfaceDeclaration | ts.ClassDeclaration): boolean {\n return (ts.getModifiers(node) ?? []).some(\n (m: ts.Modifier) => m.kind === ts.SyntaxKind.ExportKeyword,\n );\n}\n\n// webpieces-disable no-function-outside-class -- recursive fs walker, matching the AST-helper style here\nexport function collectTsFiles(dir: string): string[] {\n const out: string[] = [];\n for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {\n const full = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n if (entry.name !== 'node_modules') out.push(...collectTsFiles(full));\n } else if (entry.name.endsWith('.ts') && !entry.name.endsWith('.d.ts')) {\n out.push(full);\n }\n }\n return out;\n}\n"]}
@@ -1,7 +1,7 @@
1
1
  /**
2
- * The three ways `buildApiContracts` refuses to emit a green, wrong api contract table.
2
+ * The ways `buildApiContracts` refuses to emit a green, wrong api contract table.
3
3
  *
4
- * All three share one rule: an entry that is PRESENT but incomplete is worse than an absent one.
4
+ * They share one rule: an entry that is PRESENT but incomplete is worse than an absent one.
5
5
  * Every other entry in the table is complete, so a consumer has no reason to suspect the one that
6
6
  * lost a field — it just computes a confidently wrong URL, or draws a service with no queues.
7
7
  *
@@ -10,7 +10,12 @@
10
10
  *
11
11
  * Split out of api-scanner.ts, which owns the scan itself and is at its file-size limit.
12
12
  */
13
- import { EmptiedApiContract, UndeclaredExternalCaller, UnresolvedEndpointPath } from './api-relations';
13
+ import { EmptiedApiContract, UndeclaredEndpointOperation, UndeclaredExternalCaller, UnresolvedEndpointPath } from './api-relations';
14
+ /** Endpoints missing the operation declaration that drives retry safety and MCP annotations. */
15
+ export declare class UndeclaredEndpointOperationError extends Error {
16
+ readonly endpoints: readonly UndeclaredEndpointOperation[];
17
+ constructor(endpoints: readonly UndeclaredEndpointOperation[]);
18
+ }
14
19
  /**
15
20
  * A routed contract whose `@ApiPath` argument the scan could not read. Fatal on purpose: shipping the
16
21
  * entry without its basePath is what made `/whatsapp/test` render as `/test` in a downstream runbook.
@@ -1,8 +1,8 @@
1
1
  "use strict";
2
2
  /**
3
- * The three ways `buildApiContracts` refuses to emit a green, wrong api contract table.
3
+ * The ways `buildApiContracts` refuses to emit a green, wrong api contract table.
4
4
  *
5
- * All three share one rule: an entry that is PRESENT but incomplete is worse than an absent one.
5
+ * They share one rule: an entry that is PRESENT but incomplete is worse than an absent one.
6
6
  * Every other entry in the table is complete, so a consumer has no reason to suspect the one that
7
7
  * lost a field — it just computes a confidently wrong URL, or draws a service with no queues.
8
8
  *
@@ -12,7 +12,23 @@
12
12
  * Split out of api-scanner.ts, which owns the scan itself and is at its file-size limit.
13
13
  */
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.EmptiedApiContractError = exports.UndeclaredExternalCallerError = exports.UnresolvedEndpointPathError = exports.MissingBasePathError = void 0;
15
+ exports.EmptiedApiContractError = exports.UndeclaredExternalCallerError = exports.UnresolvedEndpointPathError = exports.MissingBasePathError = exports.UndeclaredEndpointOperationError = void 0;
16
+ /** Endpoints missing the operation declaration that drives retry safety and MCP annotations. */
17
+ class UndeclaredEndpointOperationError extends Error {
18
+ endpoints;
19
+ constructor(endpoints) {
20
+ super(`${endpoints.length} @Endpoint(s) do not declare a readable operation:\n` +
21
+ endpoints
22
+ .map((e) => ` • ${e.api}.${e.method} — ${e.argument} at ${e.at}`)
23
+ .join('\n') +
24
+ `\n operation is REQUIRED because it controls retry safety and generated MCP hints.\n` +
25
+ ` Pass exactly one of READ, WRITE_IDEMPOTENT, or WRITE as the third argument.\n` +
26
+ ` Do not infer operation semantics from the HTTP verb.`);
27
+ this.endpoints = endpoints;
28
+ this.name = 'UndeclaredEndpointOperationError';
29
+ }
30
+ }
31
+ exports.UndeclaredEndpointOperationError = UndeclaredEndpointOperationError;
16
32
  /**
17
33
  * A routed contract whose `@ApiPath` argument the scan could not read. Fatal on purpose: shipping the
18
34
  * entry without its basePath is what made `/whatsapp/test` render as `/test` in a downstream runbook.
@@ -75,10 +91,10 @@ class UndeclaredExternalCallerError extends Error {
75
91
  .join('\n') +
76
92
  `\n An 'external' endpoint is driven by a system OUTSIDE this repo, and the runtime\n` +
77
93
  ` architecture graph draws that system as an inbound box. Name it:\n` +
78
- ` @Endpoint('/hook', 'external', { calledBy: 'twilio' })\n` +
94
+ ` @Endpoint(POST, '/hook', WRITE, EXTERNAL, { calledBy: 'twilio' })\n` +
79
95
  ` Add callerKind for anything that is not a vendor SaaS — database | cache | queue |\n` +
80
96
  ` storage | saas | system — e.g. a GCP Pub/Sub push subscription:\n` +
81
- ` @Endpoint('/push', 'external', { calledBy: 'pubsub-push', callerKind: 'system' })\n` +
97
+ ` @Endpoint(POST, '/push', WRITE, EXTERNAL, { calledBy: 'pubsub-push', callerKind: 'system' })\n` +
82
98
  ` Use a string LITERAL or a SAME-module const: this scan is parser-only by design.`);
83
99
  this.callers = callers;
84
100
  this.name = 'UndeclaredExternalCallerError';
@@ -101,9 +117,9 @@ class EmptiedApiContractError extends Error {
101
117
  .join('\n') +
102
118
  `\n A contract with zero usable methods is DROPPED from the api contracts, so the class,\n` +
103
119
  ` its queues and its triggers disappear from the architecture graph with no error.\n` +
104
- ` Both @Endpoint arguments must be readable: the path as a string literal or a\n` +
105
- ` SAME-module const, and the kind as a literal 'rpc' | 'cloudtasks' | 'cron' |\n` +
106
- ` 'external'. Fix the arguments above, or remove the @Endpoint decorators if the\n` +
120
+ ` Every @Endpoint argument must be readable: the path as a string literal or a\n` +
121
+ ` SAME-module const, the kind as 'rpc' | 'cloudtasks' | 'cron' | 'external', and\n` +
122
+ ` the options must declare operation. Fix the arguments above, or remove the decorators if the\n` +
107
123
  ` class is genuinely not routed.`);
108
124
  this.contracts = contracts;
109
125
  this.name = 'EmptiedApiContractError';
@@ -1 +1 @@
1
- {"version":3,"file":"api-contract-errors.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/api-contract-errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AAIH;;;GAGG;AACH,MAAa,oBAAqB,SAAQ,KAAK;IACf;IAA5B,YAA4B,SAA4B;QACpD,KAAK,CACD,GAAG,SAAS,CAAC,MAAM,8EAA8E;YAC7F,SAAS,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACtD,8FAA8F;YAC9F,0FAA0F;YAC1F,qEAAqE,CAC5E,CAAC;QAPsB,cAAS,GAAT,SAAS,CAAmB;QAQpD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACvC,CAAC;CACJ;AAXD,oDAWC;AAED;;;;;;GAMG;AACH,MAAa,2BAA4B,SAAQ,KAAK;IACtB;IAA5B,YAA4B,KAAwC;QAChE,KAAK,CACD,GAAG,KAAK,CAAC,MAAM,qDAAqD;YAChE,KAAK;iBACA,GAAG,CACA,CAAC,CAAyB,EAAE,EAAE,CAC1B,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,gBAAgB,CAAC,CAAC,QAAQ,aAAa,CAAC,CAAC,EAAE,EAAE,CAC/E;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,0FAA0F;YAC1F,wFAAwF;YACxF,kFAAkF;YAClF,wFAAwF;YACxF,sFAAsF;YACtF,yFAAyF;YACzF,6BAA6B,CACpC,CAAC;QAhBsB,UAAK,GAAL,KAAK,CAAmC;QAiBhE,IAAI,CAAC,IAAI,GAAG,6BAA6B,CAAC;IAC9C,CAAC;CACJ;AApBD,kEAoBC;AAED;;;;;;;;;GASG;AACH,MAAa,6BAA8B,SAAQ,KAAK;IACxB;IAA5B,YAA4B,OAA4C;QACpE,KAAK,CACD,GAAG,OAAO,CAAC,MAAM,2DAA2D;YACxE,OAAO;iBACF,GAAG,CACA,CAAC,CAA2B,EAAE,EAAE,CAC5B,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,EAAE,EAAE,CAC/D;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,wFAAwF;YACxF,uEAAuE;YACvE,+DAA+D;YAC/D,yFAAyF;YACzF,sEAAsE;YACtE,0FAA0F;YAC1F,qFAAqF,CAC5F,CAAC;QAhBsB,YAAO,GAAP,OAAO,CAAqC;QAiBpE,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAChD,CAAC;CACJ;AApBD,sEAoBC;AAED;;;;;;GAMG;AACH,MAAa,uBAAwB,SAAQ,KAAK;IAClB;IAA5B,YAA4B,SAAwC;QAChE,KAAK,CACD,GAAG,SAAS,CAAC,MAAM,4EAA4E;YAC3F,SAAS;iBACJ,GAAG,CACA,CAAC,CAAqB,EAAE,EAAE,CACtB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,QAAQ,+CAA+C,CAAC,CAAC,EAAE,EAAE,CAC3F;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,6FAA6F;YAC7F,uFAAuF;YACvF,mFAAmF;YACnF,mFAAmF;YACnF,qFAAqF;YACrF,mCAAmC,CAC1C,CAAC;QAfsB,cAAS,GAAT,SAAS,CAA+B;QAgBhE,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IAC1C,CAAC;CACJ;AAnBD,0DAmBC","sourcesContent":["/**\n * The three ways `buildApiContracts` refuses to emit a green, wrong api contract table.\n *\n * All three share one rule: an entry that is PRESENT but incomplete is worse than an absent one.\n * Every other entry in the table is complete, so a consumer has no reason to suspect the one that\n * lost a field — it just computes a confidently wrong URL, or draws a service with no queues.\n *\n * Each aggregates EVERY offender into one message rather than throwing on the first: an author who\n * moved a constants module broke five decorators at once and wants all five named in one run.\n *\n * Split out of api-scanner.ts, which owns the scan itself and is at its file-size limit.\n */\n\nimport { EmptiedApiContract, UndeclaredExternalCaller, UnresolvedEndpointPath } from './api-relations';\n\n/**\n * A routed contract whose `@ApiPath` argument the scan could not read. Fatal on purpose: shipping the\n * entry without its basePath is what made `/whatsapp/test` render as `/test` in a downstream runbook.\n */\nexport class MissingBasePathError extends Error {\n constructor(public readonly contracts: readonly string[]) {\n super(\n `${contracts.length} API contract(s) have @Endpoint methods but no readable @ApiPath basePath:\\n` +\n contracts.map((c: string) => ` • ${c}`).join('\\n') +\n `\\n basePath is REQUIRED in every api contract — an entry without it makes every consumer\\n` +\n ` compute basePath + path as just path, silently. Inline the @ApiPath string literal,\\n` +\n ` or move the constant into the same module as the contract class.`,\n );\n this.name = 'MissingBasePathError';\n }\n}\n\n/**\n * `@Endpoint` paths the scan could not read. Fatal for the same reason MissingBasePathError is: the\n * two arguments are the two halves of ONE url. An http client builds its request as\n * `basePath + path`, so a contract shipped without a method's path is missing routing information,\n * and the consumer computes a confidently wrong URL. Skipping the method instead was worse still —\n * a class whose every path was an unreadable constant lost every method and vanished from the graph.\n */\nexport class UnresolvedEndpointPathError extends Error {\n constructor(public readonly paths: readonly UnresolvedEndpointPath[]) {\n super(\n `${paths.length} @Endpoint path(s) could not be read as a string:\\n` +\n paths\n .map(\n (p: UnresolvedEndpointPath) =>\n ` • ${p.api}.${p.method} — @Endpoint(${p.argument}, ...) at ${p.at}`,\n )\n .join('\\n') +\n `\\n path is REQUIRED in every api contract — every consumer builds its request URL as\\n` +\n ` basePath + path, so an unreadable path is MISSING ROUTING, not cosmetic metadata,\\n` +\n ` and a class whose every path is unreadable drops out of the graph entirely.\\n` +\n ` Inline the @Endpoint string literal, or move the constant into the SAME module as\\n` +\n ` the contract class — a same-module const IS resolved, one imported from another\\n` +\n ` module is NOT (this scan is parser-only by design: module resolution can land on a\\n` +\n ` decorator-erased .d.ts).`,\n );\n this.name = 'UnresolvedEndpointPathError';\n }\n}\n\n/**\n * `external` endpoints whose CALLER the scan could not read. Fatal, like the two above, because the\n * alternative is a diagram that lies by omission: the inbound box exists solely to name the system\n * calling us from outside, and with nothing to name it falls back to restating our own contract\n * name — which the reader already sees on the service box the arrow points at.\n *\n * `@Endpoint`'s TS overloads make `calledBy` a compile error to omit, so a scan reaching here saw a\n * JS caller, an `as any`, a cross-module constant this parser-only pass cannot fold, or a\n * `callerKind` that is not one of the declared kinds.\n */\nexport class UndeclaredExternalCallerError extends Error {\n constructor(public readonly callers: readonly UndeclaredExternalCaller[]) {\n super(\n `${callers.length} 'external' @Endpoint(s) do not declare WHO calls them:\\n` +\n callers\n .map(\n (c: UndeclaredExternalCaller) =>\n ` • ${c.api}.${c.method} — ${c.argument} at ${c.at}`,\n )\n .join('\\n') +\n `\\n An 'external' endpoint is driven by a system OUTSIDE this repo, and the runtime\\n` +\n ` architecture graph draws that system as an inbound box. Name it:\\n` +\n ` @Endpoint('/hook', 'external', { calledBy: 'twilio' })\\n` +\n ` Add callerKind for anything that is not a vendor SaaS — database | cache | queue |\\n` +\n ` storage | saas | system — e.g. a GCP Pub/Sub push subscription:\\n` +\n ` @Endpoint('/push', 'external', { calledBy: 'pubsub-push', callerKind: 'system' })\\n` +\n ` Use a string LITERAL or a SAME-module const: this scan is parser-only by design.`,\n );\n this.name = 'UndeclaredExternalCallerError';\n }\n}\n\n/**\n * Contract classes that declared `@Endpoint` methods and kept none of them. Fatal because the\n * alternative is the silent drop the api scan exists to close: buildApiContracts legitimately skips\n * a zero-method class (a vendor seam has no routes), and a class gutted by unreadable decorator\n * arguments used the very same exit — which is how a service lost two real Cloud Tasks queues and an\n * inbound webhook without a single line of output.\n */\nexport class EmptiedApiContractError extends Error {\n constructor(public readonly contracts: readonly EmptiedApiContract[]) {\n super(\n `${contracts.length} API contract class(es) declare @Endpoint methods but kept NONE of them:\\n` +\n contracts\n .map(\n (c: EmptiedApiContract) =>\n ` • ${c.api} — ${c.declared} @Endpoint method(s) declared, 0 usable, at ${c.at}`,\n )\n .join('\\n') +\n `\\n A contract with zero usable methods is DROPPED from the api contracts, so the class,\\n` +\n ` its queues and its triggers disappear from the architecture graph with no error.\\n` +\n ` Both @Endpoint arguments must be readable: the path as a string literal or a\\n` +\n ` SAME-module const, and the kind as a literal 'rpc' | 'cloudtasks' | 'cron' |\\n` +\n ` 'external'. Fix the arguments above, or remove the @Endpoint decorators if the\\n` +\n ` class is genuinely not routed.`,\n );\n this.name = 'EmptiedApiContractError';\n }\n}\n"]}
1
+ {"version":3,"file":"api-contract-errors.js","sourceRoot":"","sources":["../../../../../../../packages/tooling/nx-webpieces-rules/src/lib/api-usage/api-contract-errors.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;GAWG;;;AASH,gGAAgG;AAChG,MAAa,gCAAiC,SAAQ,KAAK;IAC3B;IAA5B,YAA4B,SAAiD;QACzE,KAAK,CACD,GAAG,SAAS,CAAC,MAAM,sDAAsD;YACrE,SAAS;iBACJ,GAAG,CACA,CAAC,CAA8B,EAAE,EAAE,CAC/B,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,EAAE,EAAE,CAC/D;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,wFAAwF;YACxF,kFAAkF;YAClF,yDAAyD,CAChE,CAAC;QAZsB,cAAS,GAAT,SAAS,CAAwC;QAazE,IAAI,CAAC,IAAI,GAAG,kCAAkC,CAAC;IACnD,CAAC;CACJ;AAhBD,4EAgBC;AAED;;;GAGG;AACH,MAAa,oBAAqB,SAAQ,KAAK;IACf;IAA5B,YAA4B,SAA4B;QACpD,KAAK,CACD,GAAG,SAAS,CAAC,MAAM,8EAA8E;YAC7F,SAAS,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC;YACtD,8FAA8F;YAC9F,0FAA0F;YAC1F,qEAAqE,CAC5E,CAAC;QAPsB,cAAS,GAAT,SAAS,CAAmB;QAQpD,IAAI,CAAC,IAAI,GAAG,sBAAsB,CAAC;IACvC,CAAC;CACJ;AAXD,oDAWC;AAED;;;;;;GAMG;AACH,MAAa,2BAA4B,SAAQ,KAAK;IACtB;IAA5B,YAA4B,KAAwC;QAChE,KAAK,CACD,GAAG,KAAK,CAAC,MAAM,qDAAqD;YAChE,KAAK;iBACA,GAAG,CACA,CAAC,CAAyB,EAAE,EAAE,CAC1B,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,gBAAgB,CAAC,CAAC,QAAQ,aAAa,CAAC,CAAC,EAAE,EAAE,CAC/E;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,0FAA0F;YAC1F,wFAAwF;YACxF,kFAAkF;YAClF,wFAAwF;YACxF,sFAAsF;YACtF,yFAAyF;YACzF,6BAA6B,CACpC,CAAC;QAhBsB,UAAK,GAAL,KAAK,CAAmC;QAiBhE,IAAI,CAAC,IAAI,GAAG,6BAA6B,CAAC;IAC9C,CAAC;CACJ;AApBD,kEAoBC;AAED;;;;;;;;;GASG;AACH,MAAa,6BAA8B,SAAQ,KAAK;IACxB;IAA5B,YAA4B,OAA4C;QACpE,KAAK,CACD,GAAG,OAAO,CAAC,MAAM,2DAA2D;YACxE,OAAO;iBACF,GAAG,CACA,CAAC,CAA2B,EAAE,EAAE,CAC5B,UAAU,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,MAAM,MAAM,CAAC,CAAC,QAAQ,OAAO,CAAC,CAAC,EAAE,EAAE,CAC/D;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,wFAAwF;YACxF,uEAAuE;YACvE,0EAA0E;YAC1E,yFAAyF;YACzF,sEAAsE;YACtE,qGAAqG;YACrG,qFAAqF,CAC5F,CAAC;QAhBsB,YAAO,GAAP,OAAO,CAAqC;QAiBpE,IAAI,CAAC,IAAI,GAAG,+BAA+B,CAAC;IAChD,CAAC;CACJ;AApBD,sEAoBC;AAED;;;;;;GAMG;AACH,MAAa,uBAAwB,SAAQ,KAAK;IAClB;IAA5B,YAA4B,SAAwC;QAChE,KAAK,CACD,GAAG,SAAS,CAAC,MAAM,4EAA4E;YAC3F,SAAS;iBACJ,GAAG,CACA,CAAC,CAAqB,EAAE,EAAE,CACtB,UAAU,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,QAAQ,+CAA+C,CAAC,CAAC,EAAE,EAAE,CAC3F;iBACA,IAAI,CAAC,IAAI,CAAC;YACf,6FAA6F;YAC7F,uFAAuF;YACvF,mFAAmF;YACnF,qFAAqF;YACrF,mGAAmG;YACnG,mCAAmC,CAC1C,CAAC;QAfsB,cAAS,GAAT,SAAS,CAA+B;QAgBhE,IAAI,CAAC,IAAI,GAAG,yBAAyB,CAAC;IAC1C,CAAC;CACJ;AAnBD,0DAmBC","sourcesContent":["/**\n * The ways `buildApiContracts` refuses to emit a green, wrong api contract table.\n *\n * They share one rule: an entry that is PRESENT but incomplete is worse than an absent one.\n * Every other entry in the table is complete, so a consumer has no reason to suspect the one that\n * lost a field — it just computes a confidently wrong URL, or draws a service with no queues.\n *\n * Each aggregates EVERY offender into one message rather than throwing on the first: an author who\n * moved a constants module broke five decorators at once and wants all five named in one run.\n *\n * Split out of api-scanner.ts, which owns the scan itself and is at its file-size limit.\n */\n\nimport {\n EmptiedApiContract,\n UndeclaredEndpointOperation,\n UndeclaredExternalCaller,\n UnresolvedEndpointPath,\n} from './api-relations';\n\n/** Endpoints missing the operation declaration that drives retry safety and MCP annotations. */\nexport class UndeclaredEndpointOperationError extends Error {\n constructor(public readonly endpoints: readonly UndeclaredEndpointOperation[]) {\n super(\n `${endpoints.length} @Endpoint(s) do not declare a readable operation:\\n` +\n endpoints\n .map(\n (e: UndeclaredEndpointOperation) =>\n ` • ${e.api}.${e.method} — ${e.argument} at ${e.at}`,\n )\n .join('\\n') +\n `\\n operation is REQUIRED because it controls retry safety and generated MCP hints.\\n` +\n ` Pass exactly one of READ, WRITE_IDEMPOTENT, or WRITE as the third argument.\\n` +\n ` Do not infer operation semantics from the HTTP verb.`,\n );\n this.name = 'UndeclaredEndpointOperationError';\n }\n}\n\n/**\n * A routed contract whose `@ApiPath` argument the scan could not read. Fatal on purpose: shipping the\n * entry without its basePath is what made `/whatsapp/test` render as `/test` in a downstream runbook.\n */\nexport class MissingBasePathError extends Error {\n constructor(public readonly contracts: readonly string[]) {\n super(\n `${contracts.length} API contract(s) have @Endpoint methods but no readable @ApiPath basePath:\\n` +\n contracts.map((c: string) => ` • ${c}`).join('\\n') +\n `\\n basePath is REQUIRED in every api contract — an entry without it makes every consumer\\n` +\n ` compute basePath + path as just path, silently. Inline the @ApiPath string literal,\\n` +\n ` or move the constant into the same module as the contract class.`,\n );\n this.name = 'MissingBasePathError';\n }\n}\n\n/**\n * `@Endpoint` paths the scan could not read. Fatal for the same reason MissingBasePathError is: the\n * two arguments are the two halves of ONE url. An http client builds its request as\n * `basePath + path`, so a contract shipped without a method's path is missing routing information,\n * and the consumer computes a confidently wrong URL. Skipping the method instead was worse still —\n * a class whose every path was an unreadable constant lost every method and vanished from the graph.\n */\nexport class UnresolvedEndpointPathError extends Error {\n constructor(public readonly paths: readonly UnresolvedEndpointPath[]) {\n super(\n `${paths.length} @Endpoint path(s) could not be read as a string:\\n` +\n paths\n .map(\n (p: UnresolvedEndpointPath) =>\n ` • ${p.api}.${p.method} — @Endpoint(${p.argument}, ...) at ${p.at}`,\n )\n .join('\\n') +\n `\\n path is REQUIRED in every api contract — every consumer builds its request URL as\\n` +\n ` basePath + path, so an unreadable path is MISSING ROUTING, not cosmetic metadata,\\n` +\n ` and a class whose every path is unreadable drops out of the graph entirely.\\n` +\n ` Inline the @Endpoint string literal, or move the constant into the SAME module as\\n` +\n ` the contract class — a same-module const IS resolved, one imported from another\\n` +\n ` module is NOT (this scan is parser-only by design: module resolution can land on a\\n` +\n ` decorator-erased .d.ts).`,\n );\n this.name = 'UnresolvedEndpointPathError';\n }\n}\n\n/**\n * `external` endpoints whose CALLER the scan could not read. Fatal, like the two above, because the\n * alternative is a diagram that lies by omission: the inbound box exists solely to name the system\n * calling us from outside, and with nothing to name it falls back to restating our own contract\n * name — which the reader already sees on the service box the arrow points at.\n *\n * `@Endpoint`'s TS overloads make `calledBy` a compile error to omit, so a scan reaching here saw a\n * JS caller, an `as any`, a cross-module constant this parser-only pass cannot fold, or a\n * `callerKind` that is not one of the declared kinds.\n */\nexport class UndeclaredExternalCallerError extends Error {\n constructor(public readonly callers: readonly UndeclaredExternalCaller[]) {\n super(\n `${callers.length} 'external' @Endpoint(s) do not declare WHO calls them:\\n` +\n callers\n .map(\n (c: UndeclaredExternalCaller) =>\n ` • ${c.api}.${c.method} — ${c.argument} at ${c.at}`,\n )\n .join('\\n') +\n `\\n An 'external' endpoint is driven by a system OUTSIDE this repo, and the runtime\\n` +\n ` architecture graph draws that system as an inbound box. Name it:\\n` +\n ` @Endpoint(POST, '/hook', WRITE, EXTERNAL, { calledBy: 'twilio' })\\n` +\n ` Add callerKind for anything that is not a vendor SaaS — database | cache | queue |\\n` +\n ` storage | saas | system — e.g. a GCP Pub/Sub push subscription:\\n` +\n ` @Endpoint(POST, '/push', WRITE, EXTERNAL, { calledBy: 'pubsub-push', callerKind: 'system' })\\n` +\n ` Use a string LITERAL or a SAME-module const: this scan is parser-only by design.`,\n );\n this.name = 'UndeclaredExternalCallerError';\n }\n}\n\n/**\n * Contract classes that declared `@Endpoint` methods and kept none of them. Fatal because the\n * alternative is the silent drop the api scan exists to close: buildApiContracts legitimately skips\n * a zero-method class (a vendor seam has no routes), and a class gutted by unreadable decorator\n * arguments used the very same exit — which is how a service lost two real Cloud Tasks queues and an\n * inbound webhook without a single line of output.\n */\nexport class EmptiedApiContractError extends Error {\n constructor(public readonly contracts: readonly EmptiedApiContract[]) {\n super(\n `${contracts.length} API contract class(es) declare @Endpoint methods but kept NONE of them:\\n` +\n contracts\n .map(\n (c: EmptiedApiContract) =>\n ` • ${c.api} — ${c.declared} @Endpoint method(s) declared, 0 usable, at ${c.at}`,\n )\n .join('\\n') +\n `\\n A contract with zero usable methods is DROPPED from the api contracts, so the class,\\n` +\n ` its queues and its triggers disappear from the architecture graph with no error.\\n` +\n ` Every @Endpoint argument must be readable: the path as a string literal or a\\n` +\n ` SAME-module const, the kind as 'rpc' | 'cloudtasks' | 'cron' | 'external', and\\n` +\n ` the options must declare operation. Fix the arguments above, or remove the decorators if the\\n` +\n ` class is genuinely not routed.`,\n );\n this.name = 'EmptiedApiContractError';\n }\n}\n"]}
@@ -113,6 +113,8 @@ export type ProjectApiRelations = Record<string, ApiRelation>;
113
113
  * different @webpieces version than the tooling itself).
114
114
  */
115
115
  export type EndpointKind = 'rpc' | 'cloudtasks' | 'cron' | 'external';
116
+ /** Side-effect semantics copied from the required third `@Endpoint` argument. */
117
+ export type EndpointOperation = 'read' | 'write-idempotent' | 'write';
116
118
  /** HTTP verbs understood by the generated contract runtime. */
117
119
  export type ContractHttpMethod = 'GET' | 'POST';
118
120
  /** One path/query/body mapping emitted into architecture/dependencies.json. */
@@ -131,7 +133,9 @@ export interface ApiMethodMeta {
131
133
  /** The @Endpoint path, relative to the class's @ApiPath basePath. */
132
134
  path: string;
133
135
  kind: EndpointKind;
134
- /** The actual incoming/outgoing verb; POST when @Endpoint omits httpMethod. */
136
+ /** Retry/idempotency contract; independent of the HTTP verb. */
137
+ operation: EndpointOperation;
138
+ /** Required in newly scanned source; optional only when reading older committed graph data. */
135
139
  httpMethod?: ContractHttpMethod;
136
140
  /** Explicit parameter mappings; absent only when the method has none. */
137
141
  parameters?: ApiParameterMeta[];
@@ -148,7 +152,7 @@ export interface ApiMethodMeta {
148
152
  */
149
153
  queueName?: string;
150
154
  /**
151
- * WHO outside this repo drives this endpoint, from `@Endpoint(p, 'external', { calledBy })`.
155
+ * WHO outside this repo drives this endpoint, from `@Endpoint(POST, p, WRITE, EXTERNAL, { calledBy })`.
152
156
  *
153
157
  * ONLY on an `external` method, the same way `queueName` is only on the kinds that HAVE a queue.
154
158
  *
@@ -303,6 +307,26 @@ export declare class UndeclaredExternalCaller {
303
307
  /** `path/to/file.ts:LINE`, workspace-relative. */
304
308
  at: string);
305
309
  }
310
+ /** ONE `@Endpoint` whose required side-effect declaration could not be read from source. */
311
+ export declare class UndeclaredEndpointOperation {
312
+ /** The contract class the method is declared on. */
313
+ readonly api: string;
314
+ /** The method name. */
315
+ readonly method: string;
316
+ /** What was wrong, as written — `<missing>`, a constant name, or an invalid literal. */
317
+ readonly argument: string;
318
+ /** `path/to/file.ts:LINE`, workspace-relative. */
319
+ readonly at: string;
320
+ constructor(
321
+ /** The contract class the method is declared on. */
322
+ api: string,
323
+ /** The method name. */
324
+ method: string,
325
+ /** What was wrong, as written — `<missing>`, a constant name, or an invalid literal. */
326
+ argument: string,
327
+ /** `path/to/file.ts:LINE`, workspace-relative. */
328
+ at: string);
329
+ }
306
330
  /**
307
331
  * A contract class that DECLARED `@Endpoint` methods and kept none of them.
308
332
  *
@@ -15,7 +15,7 @@
15
15
  * only as binding identifiers, not as member names).
16
16
  */
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
- exports.EmptiedApiContract = exports.UndeclaredExternalCaller = exports.UnresolvedEndpointPath = exports.NonLiteralDecoratorArg = exports.EXTERNAL_SYSTEM_KINDS = void 0;
18
+ exports.EmptiedApiContract = exports.UndeclaredEndpointOperation = exports.UndeclaredExternalCaller = exports.UnresolvedEndpointPath = exports.NonLiteralDecoratorArg = exports.EXTERNAL_SYSTEM_KINDS = void 0;
19
19
  exports.isExternalSystemKind = isExternalSystemKind;
20
20
  exports.apiRefKey = apiRefKey;
21
21
  exports.deriveApiRelationKind = deriveApiRelationKind;
@@ -158,6 +158,28 @@ class UndeclaredExternalCaller {
158
158
  }
159
159
  }
160
160
  exports.UndeclaredExternalCaller = UndeclaredExternalCaller;
161
+ /** ONE `@Endpoint` whose required side-effect declaration could not be read from source. */
162
+ class UndeclaredEndpointOperation {
163
+ api;
164
+ method;
165
+ argument;
166
+ at;
167
+ constructor(
168
+ /** The contract class the method is declared on. */
169
+ api,
170
+ /** The method name. */
171
+ method,
172
+ /** What was wrong, as written — `<missing>`, a constant name, or an invalid literal. */
173
+ argument,
174
+ /** `path/to/file.ts:LINE`, workspace-relative. */
175
+ at) {
176
+ this.api = api;
177
+ this.method = method;
178
+ this.argument = argument;
179
+ this.at = at;
180
+ }
181
+ }
182
+ exports.UndeclaredEndpointOperation = UndeclaredEndpointOperation;
161
183
  /**
162
184
  * A contract class that DECLARED `@Endpoint` methods and kept none of them.
163
185
  *
@@ -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 /**\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/** 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 /** The actual incoming/outgoing verb; POST when @Endpoint omits httpMethod. */\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(p, '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 * 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/**\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 /**\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 * 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"]}