@webpieces/nx-webpieces-rules 0.4.528 → 0.4.530
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +6 -6
- package/src/lib/api-usage/api-ast.d.ts +29 -1
- package/src/lib/api-usage/api-ast.js +79 -1
- package/src/lib/api-usage/api-ast.js.map +1 -1
- package/src/lib/api-usage/api-contract-errors.d.ts +15 -1
- package/src/lib/api-usage/api-contract-errors.js +30 -1
- package/src/lib/api-usage/api-contract-errors.js.map +1 -1
- package/src/lib/api-usage/api-relations.d.ts +42 -0
- package/src/lib/api-usage/api-relations.js +31 -1
- package/src/lib/api-usage/api-relations.js.map +1 -1
- package/src/lib/api-usage/api-scanner.d.ts +8 -2
- package/src/lib/api-usage/api-scanner.js +8 -1
- package/src/lib/api-usage/api-scanner.js.map +1 -1
- package/src/lib/runtime-graph-model.d.ts +12 -0
- package/src/lib/runtime-graph-model.js.map +1 -1
- package/src/lib/runtime-graph-sorters.d.ts +26 -0
- package/src/lib/runtime-graph-sorters.js +66 -0
- package/src/lib/runtime-graph-sorters.js.map +1 -0
- package/src/lib/runtime-graph.js +12 -51
- package/src/lib/runtime-graph.js.map +1 -1
- package/src/lib/runtime-visualizer.client.js +22 -0
- package/src/lib/runtime-visualizer.d.ts +10 -4
- package/src/lib/runtime-visualizer.js +206 -174
- package/src/lib/runtime-visualizer.js.map +1 -1
- package/src/lib/runtime-viz-theme.d.ts +61 -0
- package/src/lib/runtime-viz-theme.js +137 -0
- package/src/lib/runtime-viz-theme.js.map +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webpieces/nx-webpieces-rules",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.530",
|
|
4
4
|
"description": "Nx-specific webpieces validation rules and graph tooling. Bundles all @webpieces rule packages with Nx graph validators and an inference plugin.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
"bin/**/*"
|
|
23
23
|
],
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@webpieces/ai-hook-rules": "0.4.
|
|
26
|
-
"@webpieces/code-rules": "0.4.
|
|
27
|
-
"@webpieces/eslint-rules": "0.4.
|
|
28
|
-
"@webpieces/pr-gate": "0.4.
|
|
29
|
-
"@webpieces/rules-config": "0.4.
|
|
25
|
+
"@webpieces/ai-hook-rules": "0.4.530",
|
|
26
|
+
"@webpieces/code-rules": "0.4.530",
|
|
27
|
+
"@webpieces/eslint-rules": "0.4.530",
|
|
28
|
+
"@webpieces/pr-gate": "0.4.530",
|
|
29
|
+
"@webpieces/rules-config": "0.4.530",
|
|
30
30
|
"madge": "8.0.0"
|
|
31
31
|
},
|
|
32
32
|
"peerDependencies": {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* api-scanner's source pre-pass exists to guard against.
|
|
11
11
|
*/
|
|
12
12
|
import * as ts from 'typescript';
|
|
13
|
-
import { ApiClassInfo, ApiMethodMeta, ApiTransport, EmptiedApiContract, ExternalSystemDeclaration, NonLiteralDecoratorArg, UnresolvedEndpointPath } from './api-relations';
|
|
13
|
+
import { ApiClassInfo, ApiMethodMeta, ApiTransport, EmptiedApiContract, ExternalSystemDeclaration, NonLiteralDecoratorArg, UndeclaredExternalCaller, UnresolvedEndpointPath } from './api-relations';
|
|
14
14
|
/**
|
|
15
15
|
* The module-scope `const NAME = '<string literal>'` bindings of ONE source file.
|
|
16
16
|
*
|
|
@@ -68,6 +68,7 @@ export declare class DecoratorArgDiagnostics {
|
|
|
68
68
|
private readonly found;
|
|
69
69
|
private readonly unresolvedPaths;
|
|
70
70
|
private readonly emptied;
|
|
71
|
+
private readonly undeclaredCallers;
|
|
71
72
|
constructor(workspaceRoot: string);
|
|
72
73
|
/** Record `argument` (as written) as unresolvable at `node`'s location. */
|
|
73
74
|
record(api: string, decorator: string, method: string | null, argument: string, node: ts.Node): void;
|
|
@@ -75,9 +76,12 @@ export declare class DecoratorArgDiagnostics {
|
|
|
75
76
|
recordUnresolvedPath(api: string, method: string, argument: string, node: ts.Node): void;
|
|
76
77
|
/** Record a class that declared `declared` `@Endpoint` methods and kept none of them. */
|
|
77
78
|
recordEmptiedContract(api: string, declared: number, node: ts.Node): void;
|
|
79
|
+
/** Record an `external` `@Endpoint` whose caller is unreadable — fatal, see UndeclaredExternalCallerError. */
|
|
80
|
+
recordUndeclaredCaller(api: string, method: string, argument: string, node: ts.Node): void;
|
|
78
81
|
all(): NonLiteralDecoratorArg[];
|
|
79
82
|
unresolvedEndpointPaths(): UnresolvedEndpointPath[];
|
|
80
83
|
emptiedContracts(): EmptiedApiContract[];
|
|
84
|
+
undeclaredExternalCallers(): UndeclaredExternalCaller[];
|
|
81
85
|
private locate;
|
|
82
86
|
}
|
|
83
87
|
/** {api, owner: `project`, type} when `cls` is an `abstract class` carrying `@ApiPath`, else null. */
|
|
@@ -101,6 +105,30 @@ export declare function apiTransport(cls: ts.ClassDeclaration): ApiTransport;
|
|
|
101
105
|
* zero-method classes, which is the door a gutted contract used to leave through unannounced.
|
|
102
106
|
*/
|
|
103
107
|
export declare function endpointMethodsOf(cls: ts.ClassDeclaration, api: string, constants?: ModuleStringConstants, diagnostics?: DecoratorArgDiagnostics | null): ApiMethodMeta[];
|
|
108
|
+
/**
|
|
109
|
+
* The outcome of reading `@Endpoint(path, 'external', { calledBy, callerKind })`'s third argument:
|
|
110
|
+
* either the resolved declaration, or the reason it could not be resolved (never both).
|
|
111
|
+
*/
|
|
112
|
+
export declare class ExternalCallerRead {
|
|
113
|
+
readonly declaration: ExternalSystemDeclaration | null;
|
|
114
|
+
/** What was wrong, as written, for the diagnostic. Null exactly when `declaration` is set. */
|
|
115
|
+
readonly problem: string | null;
|
|
116
|
+
constructor(declaration: ExternalSystemDeclaration | null,
|
|
117
|
+
/** What was wrong, as written, for the diagnostic. Null exactly when `declaration` is set. */
|
|
118
|
+
problem: string | null);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Read the declared caller out of the @Endpoint OPTIONS OBJECT LITERAL — `args[2]`, not a positional
|
|
122
|
+
* argument, because that is where `formPost` already lives and one options bag beats two.
|
|
123
|
+
*
|
|
124
|
+
* Everything unreadable is a PROBLEM, never a default: an unknown `callerKind` draws the wrong shape
|
|
125
|
+
* (which teaches the reader something false), and a missing `calledBy` puts us back at a box that can
|
|
126
|
+
* only name our own contract. The kind default applies ONLY to the case the API deliberately allows —
|
|
127
|
+
* `calledBy` present, `callerKind` absent.
|
|
128
|
+
*/
|
|
129
|
+
export declare function externalCallerOf(arg: ts.Expression | undefined, constants: ModuleStringConstants): ExternalCallerRead;
|
|
130
|
+
/** One property of an object literal, read as a string through the same constant folding as an argument. */
|
|
131
|
+
export declare function objectPropertyValue(literal: ts.ObjectLiteralExpression, name: string, constants: ModuleStringConstants): DecoratorArgValue;
|
|
104
132
|
/** `@Queue('...')` override when present and resolvable, else the derived `${Api}-${method}`. */
|
|
105
133
|
export declare function queueNameOf(member: ts.MethodDeclaration, api: string, name: string, constants: ModuleStringConstants, diagnostics: DecoratorArgDiagnostics | null): string;
|
|
106
134
|
/** Record an argument that is present but unresolvable; a resolved or absent one is silent. */
|
|
@@ -11,13 +11,15 @@
|
|
|
11
11
|
* api-scanner's source pre-pass exists to guard against.
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.DecoratorArgDiagnostics = exports.DecoratorArgValue = exports.ModuleStringConstants = void 0;
|
|
14
|
+
exports.ExternalCallerRead = exports.DecoratorArgDiagnostics = exports.DecoratorArgValue = exports.ModuleStringConstants = void 0;
|
|
15
15
|
exports.stringConstantsOf = stringConstantsOf;
|
|
16
16
|
exports.stringValueOf = stringValueOf;
|
|
17
17
|
exports.decoratorArgValue = decoratorArgValue;
|
|
18
18
|
exports.apiClassInfoFrom = apiClassInfoFrom;
|
|
19
19
|
exports.apiTransport = apiTransport;
|
|
20
20
|
exports.endpointMethodsOf = endpointMethodsOf;
|
|
21
|
+
exports.externalCallerOf = externalCallerOf;
|
|
22
|
+
exports.objectPropertyValue = objectPropertyValue;
|
|
21
23
|
exports.queueNameOf = queueNameOf;
|
|
22
24
|
exports.reportUnresolved = reportUnresolved;
|
|
23
25
|
exports.decoratorArgs = decoratorArgs;
|
|
@@ -171,6 +173,7 @@ class DecoratorArgDiagnostics {
|
|
|
171
173
|
found = [];
|
|
172
174
|
unresolvedPaths = [];
|
|
173
175
|
emptied = [];
|
|
176
|
+
undeclaredCallers = [];
|
|
174
177
|
constructor(workspaceRoot) {
|
|
175
178
|
this.workspaceRoot = workspaceRoot;
|
|
176
179
|
}
|
|
@@ -186,6 +189,10 @@ class DecoratorArgDiagnostics {
|
|
|
186
189
|
recordEmptiedContract(api, declared, node) {
|
|
187
190
|
this.emptied.push(new api_relations_1.EmptiedApiContract(api, declared, this.locate(node)));
|
|
188
191
|
}
|
|
192
|
+
/** Record an `external` `@Endpoint` whose caller is unreadable — fatal, see UndeclaredExternalCallerError. */
|
|
193
|
+
recordUndeclaredCaller(api, method, argument, node) {
|
|
194
|
+
this.undeclaredCallers.push(new api_relations_1.UndeclaredExternalCaller(api, method, argument, this.locate(node)));
|
|
195
|
+
}
|
|
189
196
|
all() {
|
|
190
197
|
return this.found;
|
|
191
198
|
}
|
|
@@ -195,6 +202,9 @@ class DecoratorArgDiagnostics {
|
|
|
195
202
|
emptiedContracts() {
|
|
196
203
|
return this.emptied;
|
|
197
204
|
}
|
|
205
|
+
undeclaredExternalCallers() {
|
|
206
|
+
return this.undeclaredCallers;
|
|
207
|
+
}
|
|
198
208
|
locate(node) {
|
|
199
209
|
const sourceFile = node.getSourceFile();
|
|
200
210
|
const position = sourceFile.getLineAndCharacterOfPosition(node.getStart());
|
|
@@ -273,6 +283,16 @@ function endpointMethodsOf(cls, api, constants = new ModuleStringConstants(new M
|
|
|
273
283
|
if (QUEUED_KINDS.includes(method.kind)) {
|
|
274
284
|
method.queueName = queueNameOf(member, api, name, constants, diagnostics);
|
|
275
285
|
}
|
|
286
|
+
// Only an `external` endpoint HAS an outside caller, mirroring the queue rule above. A
|
|
287
|
+
// caller recorded on an rpc method would be a fact about nothing, and would put a vendor
|
|
288
|
+
// box on the graph beside an endpoint no vendor calls.
|
|
289
|
+
if (method.kind === 'external') {
|
|
290
|
+
const caller = externalCallerOf(args[2], constants);
|
|
291
|
+
if (caller.declaration !== null)
|
|
292
|
+
method.caller = caller.declaration;
|
|
293
|
+
else if (diagnostics !== null)
|
|
294
|
+
diagnostics.recordUndeclaredCaller(api, name, caller.problem, endpoint);
|
|
295
|
+
}
|
|
276
296
|
methods.push(method);
|
|
277
297
|
}
|
|
278
298
|
// Declared endpoints, kept none: the class is about to be skipped as "zero methods" and would
|
|
@@ -282,6 +302,64 @@ function endpointMethodsOf(cls, api, constants = new ModuleStringConstants(new M
|
|
|
282
302
|
}
|
|
283
303
|
return methods;
|
|
284
304
|
}
|
|
305
|
+
/** Default `callerKind` when an `external` endpoint declares `calledBy` alone — mirrors core-util. */
|
|
306
|
+
const DEFAULT_CALLER_KIND = 'saas';
|
|
307
|
+
/**
|
|
308
|
+
* The outcome of reading `@Endpoint(path, 'external', { calledBy, callerKind })`'s third argument:
|
|
309
|
+
* either the resolved declaration, or the reason it could not be resolved (never both).
|
|
310
|
+
*/
|
|
311
|
+
class ExternalCallerRead {
|
|
312
|
+
declaration;
|
|
313
|
+
problem;
|
|
314
|
+
constructor(declaration,
|
|
315
|
+
/** What was wrong, as written, for the diagnostic. Null exactly when `declaration` is set. */
|
|
316
|
+
problem) {
|
|
317
|
+
this.declaration = declaration;
|
|
318
|
+
this.problem = problem;
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
exports.ExternalCallerRead = ExternalCallerRead;
|
|
322
|
+
/**
|
|
323
|
+
* Read the declared caller out of the @Endpoint OPTIONS OBJECT LITERAL — `args[2]`, not a positional
|
|
324
|
+
* argument, because that is where `formPost` already lives and one options bag beats two.
|
|
325
|
+
*
|
|
326
|
+
* Everything unreadable is a PROBLEM, never a default: an unknown `callerKind` draws the wrong shape
|
|
327
|
+
* (which teaches the reader something false), and a missing `calledBy` puts us back at a box that can
|
|
328
|
+
* only name our own contract. The kind default applies ONLY to the case the API deliberately allows —
|
|
329
|
+
* `calledBy` present, `callerKind` absent.
|
|
330
|
+
*/
|
|
331
|
+
// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts
|
|
332
|
+
function externalCallerOf(arg, constants) {
|
|
333
|
+
if (arg === undefined)
|
|
334
|
+
return new ExternalCallerRead(null, '<no options argument>');
|
|
335
|
+
if (!ts.isObjectLiteralExpression(arg))
|
|
336
|
+
return new ExternalCallerRead(null, arg.getText());
|
|
337
|
+
const calledBy = objectPropertyValue(arg, 'calledBy', constants);
|
|
338
|
+
if (calledBy.value === null || calledBy.value === '') {
|
|
339
|
+
return new ExternalCallerRead(null, calledBy.unresolvedName ?? '<no calledBy>');
|
|
340
|
+
}
|
|
341
|
+
const callerKind = objectPropertyValue(arg, 'callerKind', constants);
|
|
342
|
+
if (callerKind.value === null && callerKind.unresolvedName !== null) {
|
|
343
|
+
return new ExternalCallerRead(null, `callerKind: ${callerKind.unresolvedName}`);
|
|
344
|
+
}
|
|
345
|
+
const kind = callerKind.value ?? DEFAULT_CALLER_KIND;
|
|
346
|
+
if (!(0, api_relations_1.isExternalSystemKind)(kind))
|
|
347
|
+
return new ExternalCallerRead(null, `callerKind: '${kind}'`);
|
|
348
|
+
return new ExternalCallerRead({ kind, label: calledBy.value }, null);
|
|
349
|
+
}
|
|
350
|
+
/** One property of an object literal, read as a string through the same constant folding as an argument. */
|
|
351
|
+
// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts
|
|
352
|
+
function objectPropertyValue(literal, name, constants) {
|
|
353
|
+
for (const property of literal.properties) {
|
|
354
|
+
if (!ts.isPropertyAssignment(property) || property.name === undefined)
|
|
355
|
+
continue;
|
|
356
|
+
const key = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) ? property.name.text : null;
|
|
357
|
+
if (key !== name)
|
|
358
|
+
continue;
|
|
359
|
+
return decoratorArgValue(property.initializer, constants);
|
|
360
|
+
}
|
|
361
|
+
return new DecoratorArgValue(null, null);
|
|
362
|
+
}
|
|
285
363
|
/** `@Queue('...')` override when present and resolvable, else the derived `${Api}-${method}`. */
|
|
286
364
|
// webpieces-disable no-function-outside-class -- pure AST accessor, matching the sibling helpers in di-graph/bindings.ts
|
|
287
365
|
function queueNameOf(member, api, name, constants, diagnostics) {
|
|
@@ -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 ApiTransport,\n EmptiedApiContract,\n EndpointKind,\n ExternalSystemDeclaration,\n isExternalSystemKind,\n NonLiteralDecoratorArg,\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)) 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\n constructor(private readonly workspaceRoot: string) {}\n\n /** Record `argument` (as written) as unresolvable at `node`'s location. */\n record(api: string, decorator: string, method: string | null, argument: string, node: ts.Node): void {\n this.found.push(new NonLiteralDecoratorArg(api, decorator, method, argument, this.locate(node)));\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(new UnresolvedEndpointPath(api, method, argument, this.locate(node)));\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 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 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 (pathArg.value === null || kind === null || !ENDPOINT_KINDS.includes(kind as EndpointKind)) continue;\n const method: ApiMethodMeta = { name, path: pathArg.value, kind: kind as EndpointKind };\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 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/** `@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(decorator: ts.Decorator): 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/**\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((m: ts.Modifier) => m.kind === ts.SyntaxKind.AbstractKeyword);\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\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 = 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(node: ts.Node, api: string): 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.trim().split(/\\s+/).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((m: ts.Modifier) => m.kind === ts.SyntaxKind.ExportKeyword);\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":";AAAA;;;;;;;;;;GAUG;;;AAkEH,8CAgBC;AAID,sCAKC;AAmBD,8CAaC;AAqED,4CAiBC;AAGD,oCAEC;AAuBD,8CA+CC;AA2BD,4CAiBC;AAID,kDAYC;AAID,kCAYC;AAID,4CAUC;AAID,sCAEC;AAID,0CAGC;AAOD,gDAYC;AAID,kDAKC;AASD,8CAKC;AAID,oDASC;AAGD,0CAEC;AAGD,8CAEC;AAWD,0CAQC;AAGD,4CAKC;AAGD,gCAMC;AAKD,oDAMC;AAaD,kDASC;AAkBD,sDAYC;AAID,gCAEC;AAGD,wCAWC;;AAzjBD,uDAAiC;AACjC,+CAAyB;AACzB,mDAA6B;AAC7B,mDAAsE;AACtE,mDAWyB;AAEzB,mGAAmG;AACnG,MAAM,cAAc,GAA4B,CAAC,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;AAE1F;;;;GAIG;AACH,MAAM,wBAAwB,GAAG,KAAK,CAAC;AAEvC,8GAA8G;AAC9G,MAAM,mBAAmB,GAAG,gBAAgB,CAAC;AAE7C;;;;GAIG;AACH,MAAM,oBAAoB,GAAG,cAAc,CAAC;AAE5C;;;;;;;;;;;;;GAaG;AACH,MAAa,qBAAqB;IACD;IAA7B,YAA6B,MAA2B;QAA3B,WAAM,GAAN,MAAM,CAAqB;IAAG,CAAC;IAE5D,MAAM,CAAC,IAAY;QACf,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC;IACzC,CAAC;CACJ;AAND,sDAMC;AAED,iFAAiF;AACjF,MAAM,iBAAiB,GAAG,IAAI,OAAO,EAAwC,CAAC;AAE9E,+EAA+E;AAC/E,yHAAyH;AACzH,SAAgB,iBAAiB,CAAC,UAAyB;IACvD,MAAM,MAAM,GAAG,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;IACjD,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,EAAkB,CAAC;IACzC,KAAK,MAAM,SAAS,IAAI,UAAU,CAAC,UAAU,EAAE,CAAC;QAC5C,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,SAAS,CAAC;YAAE,SAAS;QACjD,IAAI,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC;YAAE,SAAS;QAC3E,KAAK,MAAM,WAAW,IAAI,SAAS,CAAC,eAAe,CAAC,YAAY,EAAE,CAAC;YAC/D,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,CAAC,IAAI,CAAC;gBAAE,SAAS;YACjD,MAAM,IAAI,GAAG,aAAa,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;YACpD,IAAI,IAAI,KAAK,IAAI;gBAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;QAC/D,CAAC;IACL,CAAC;IACD,MAAM,SAAS,GAAG,IAAI,qBAAqB,CAAC,MAAM,CAAC,CAAC;IACpD,iBAAiB,CAAC,GAAG,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;IAC7C,OAAO,SAAS,CAAC;AACrB,CAAC;AAED,yFAAyF;AACzF,yHAAyH;AACzH,SAAgB,aAAa,CAAC,IAA+B;IACzD,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACpC,IAAI,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,+BAA+B,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC3F,IAAI,EAAE,CAAC,cAAc,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,yBAAyB,CAAC,IAAI,CAAC;QAAE,OAAO,aAAa,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzG,OAAO,IAAI,CAAC;AAChB,CAAC;AAED;;;;;;;GAOG;AACH,MAAa,iBAAiB;IAEN;IACA;IAFpB,YACoB,KAAoB,EACpB,cAA6B;QAD7B,UAAK,GAAL,KAAK,CAAe;QACpB,mBAAc,GAAd,cAAc,CAAe;IAC9C,CAAC;CACP;AALD,8CAKC;AAED,gFAAgF;AAChF,yHAAyH;AACzH,SAAgB,iBAAiB,CAC7B,IAA+B,EAC/B,SAAgC;IAEhC,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,CAAC,CAAC;IACpC,IAAI,OAAO,KAAK,IAAI;QAAE,OAAO,IAAI,iBAAiB,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;IAClE,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACxB,MAAM,QAAQ,GAAG,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC7C,IAAI,QAAQ,KAAK,IAAI;YAAE,OAAO,IAAI,iBAAiB,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QACpE,OAAO,IAAI,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;IAClD,CAAC;IACD,OAAO,IAAI,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC;AACvD,CAAC;AAED;;;;;;;;;;;;;GAaG;AACH,MAAa,uBAAuB;IAMH;IALZ,KAAK,GAA6B,EAAE,CAAC;IACrC,eAAe,GAA6B,EAAE,CAAC;IAC/C,OAAO,GAAyB,EAAE,CAAC;IACnC,iBAAiB,GAA+B,EAAE,CAAC;IAEpE,YAA6B,aAAqB;QAArB,kBAAa,GAAb,aAAa,CAAQ;IAAG,CAAC;IAEtD,2EAA2E;IAC3E,MAAM,CAAC,GAAW,EAAE,SAAiB,EAAE,MAAqB,EAAE,QAAgB,EAAE,IAAa;QACzF,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,sCAAsB,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACrG,CAAC;IAED,wGAAwG;IACxG,oBAAoB,CAAC,GAAW,EAAE,MAAc,EAAE,QAAgB,EAAE,IAAa;QAC7E,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,sCAAsB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpG,CAAC;IAED,yFAAyF;IACzF,qBAAqB,CAAC,GAAW,EAAE,QAAgB,EAAE,IAAa;QAC9D,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,kCAAkB,CAAC,GAAG,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChF,CAAC;IAED,8GAA8G;IAC9G,sBAAsB,CAAC,GAAW,EAAE,MAAc,EAAE,QAAgB,EAAE,IAAa;QAC/E,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,wCAAwB,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACxG,CAAC;IAED,GAAG;QACC,OAAO,IAAI,CAAC,KAAK,CAAC;IACtB,CAAC;IAED,uBAAuB;QACnB,OAAO,IAAI,CAAC,eAAe,CAAC;IAChC,CAAC;IAED,gBAAgB;QACZ,OAAO,IAAI,CAAC,OAAO,CAAC;IACxB,CAAC;IAED,yBAAyB;QACrB,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAClC,CAAC;IAEO,MAAM,CAAC,IAAa;QACxB,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACxC,MAAM,QAAQ,GAAG,UAAU,CAAC,6BAA6B,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;QAC3E,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;IAC5F,CAAC;CACJ;AAjDD,0DAiDC;AAED,sGAAsG;AACtG,yHAAyH;AACzH,SAAgB,gBAAgB,CAC5B,GAAwB,EACxB,OAAe,EACf,cAA8C,IAAI;IAElD,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,GAAG,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,OAAO,IAAI,CAAC;IAC1F,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC;IAC1B,MAAM,SAAS,GAAG,iBAAiB,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC;IACzD,MAAM,IAAI,GAAiB;QACvB,GAAG;QACH,KAAK,EAAE,OAAO;QACd,IAAI,EAAE,YAAY,CAAC,GAAG,CAAC;QACvB,OAAO,EAAE,iBAAiB,CAAC,GAAG,EAAE,GAAG,EAAE,SAAS,EAAE,WAAW,CAAC;KAC/D,CAAC;IACF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,GAAG,EAAE,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,CAAC,CAAC;IACjF,IAAI,QAAQ,KAAK,IAAI;QAAE,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAChD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,0HAA0H;AAC1H,SAAgB,YAAY,CAAC,GAAwB;IACjD,OAAO,iBAAiB,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/D,CAAC;AAED,yFAAyF;AACzF,MAAM,YAAY,GAA4B,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC;AAErE;;;;;;;;;;;;;;;;GAgBG;AACH,yHAAyH;AACzH,SAAgB,iBAAiB,CAC7B,GAAwB,EACxB,GAAW,EACX,YAAmC,IAAI,qBAAqB,CAAC,IAAI,GAAG,EAAkB,CAAC,EACvF,cAA8C,IAAI;IAElD,MAAM,OAAO,GAAoB,EAAE,CAAC;IACpC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC;YAAE,SAAS;QAC/E,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;QACrD,IAAI,QAAQ,KAAK,IAAI;YAAE,SAAS;QAChC,QAAQ,EAAE,CAAC;QACX,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;QAC9B,MAAM,IAAI,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACtD,MAAM,OAAO,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;QACtD,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACxE,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,UAAU,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QACxE,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;YAC1D,WAAW,CAAC,oBAAoB,CAAC,GAAG,EAAE,IAAI,EAAE,OAAO,CAAC,cAAc,EAAE,QAAQ,CAAC,CAAC;QAClF,CAAC;QACD,MAAM,IAAI,GAAG,OAAO,CAAC,KAAK,CAAC;QAC3B,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,IAAI,KAAK,IAAI,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,IAAoB,CAAC;YAAE,SAAS;QACxG,MAAM,MAAM,GAAkB,EAAE,IAAI,EAAE,IAAI,EAAE,OAAO,CAAC,KAAK,EAAE,IAAI,EAAE,IAAoB,EAAE,CAAC;QACxF,8FAA8F;QAC9F,6FAA6F;QAC7F,gCAAgC;QAChC,IAAI,YAAY,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC;YACrC,MAAM,CAAC,SAAS,GAAG,WAAW,CAAC,MAAM,EAAE,GAAG,EAAE,IAAI,EAAE,SAAS,EAAE,WAAW,CAAC,CAAC;QAC9E,CAAC;QACD,uFAAuF;QACvF,yFAAyF;QACzF,uDAAuD;QACvD,IAAI,MAAM,CAAC,IAAI,KAAK,UAAU,EAAE,CAAC;YAC7B,MAAM,MAAM,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;YACpD,IAAI,MAAM,CAAC,WAAW,KAAK,IAAI;gBAAE,MAAM,CAAC,MAAM,GAAG,MAAM,CAAC,WAAW,CAAC;iBAC/D,IAAI,WAAW,KAAK,IAAI;gBAAE,WAAW,CAAC,sBAAsB,CAAC,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,OAAQ,EAAE,QAAQ,CAAC,CAAC;QAC5G,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACzB,CAAC;IACD,8FAA8F;IAC9F,wFAAwF;IACxF,IAAI,WAAW,KAAK,IAAI,IAAI,QAAQ,GAAG,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC/D,WAAW,CAAC,qBAAqB,CAAC,GAAG,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;IAC1D,CAAC;IACD,OAAO,OAAO,CAAC;AACnB,CAAC;AAED,sGAAsG;AACtG,MAAM,mBAAmB,GAAG,MAAM,CAAC;AAEnC;;;GAGG;AACH,MAAa,kBAAkB;IAEP;IAEA;IAHpB,YACoB,WAA6C;IAC7D,8FAA8F;IAC9E,OAAsB;QAFtB,gBAAW,GAAX,WAAW,CAAkC;QAE7C,YAAO,GAAP,OAAO,CAAe;IACvC,CAAC;CACP;AAND,gDAMC;AAED;;;;;;;;GAQG;AACH,yHAAyH;AACzH,SAAgB,gBAAgB,CAC5B,GAA8B,EAC9B,SAAgC;IAEhC,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,IAAI,kBAAkB,CAAC,IAAI,EAAE,uBAAuB,CAAC,CAAC;IACpF,IAAI,CAAC,EAAE,CAAC,yBAAyB,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,kBAAkB,CAAC,IAAI,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;IAC3F,MAAM,QAAQ,GAAG,mBAAmB,CAAC,GAAG,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACjE,IAAI,QAAQ,CAAC,KAAK,KAAK,IAAI,IAAI,QAAQ,CAAC,KAAK,KAAK,EAAE,EAAE,CAAC;QACnD,OAAO,IAAI,kBAAkB,CAAC,IAAI,EAAE,QAAQ,CAAC,cAAc,IAAI,eAAe,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,UAAU,GAAG,mBAAmB,CAAC,GAAG,EAAE,YAAY,EAAE,SAAS,CAAC,CAAC;IACrE,IAAI,UAAU,CAAC,KAAK,KAAK,IAAI,IAAI,UAAU,CAAC,cAAc,KAAK,IAAI,EAAE,CAAC;QAClE,OAAO,IAAI,kBAAkB,CAAC,IAAI,EAAE,eAAe,UAAU,CAAC,cAAc,EAAE,CAAC,CAAC;IACpF,CAAC;IACD,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,IAAI,mBAAmB,CAAC;IACrD,IAAI,CAAC,IAAA,oCAAoB,EAAC,IAAI,CAAC;QAAE,OAAO,IAAI,kBAAkB,CAAC,IAAI,EAAE,gBAAgB,IAAI,GAAG,CAAC,CAAC;IAC9F,OAAO,IAAI,kBAAkB,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,CAAC,KAAK,EAAE,EAAE,IAAI,CAAC,CAAC;AACzE,CAAC;AAED,4GAA4G;AAC5G,yHAAyH;AACzH,SAAgB,mBAAmB,CAC/B,OAAmC,EACnC,IAAY,EACZ,SAAgC;IAEhC,KAAK,MAAM,QAAQ,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,CAAC,EAAE,CAAC,oBAAoB,CAAC,QAAQ,CAAC,IAAI,QAAQ,CAAC,IAAI,KAAK,SAAS;YAAE,SAAS;QAChF,MAAM,GAAG,GAAG,EAAE,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;QAC5G,IAAI,GAAG,KAAK,IAAI;YAAE,SAAS;QAC3B,OAAO,iBAAiB,CAAC,QAAQ,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,IAAI,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;AAC7C,CAAC;AAED,iGAAiG;AACjG,yHAAyH;AACzH,SAAgB,WAAW,CACvB,MAA4B,EAC5B,GAAW,EACX,IAAY,EACZ,SAAgC,EAChC,WAA2C;IAE3C,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClD,IAAI,QAAQ,KAAK,IAAI;QAAE,OAAO,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAC/C,MAAM,QAAQ,GAAG,iBAAiB,CAAC,aAAa,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IAC1E,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IACtE,OAAO,QAAQ,CAAC,KAAK,IAAI,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;AAC9C,CAAC;AAED,+FAA+F;AAC/F,yHAAyH;AACzH,SAAgB,gBAAgB,CAC5B,WAA2C,EAC3C,GAAW,EACX,SAAiB,EACjB,MAAqB,EACrB,GAAsB,EACtB,IAAa;IAEb,IAAI,WAAW,KAAK,IAAI,IAAI,GAAG,CAAC,cAAc,KAAK,IAAI;QAAE,OAAO;IAChE,WAAW,CAAC,MAAM,CAAC,GAAG,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,CAAC,cAAc,EAAE,IAAI,CAAC,CAAC;AACzE,CAAC;AAED,gGAAgG;AAChG,yHAAyH;AACzH,SAAgB,aAAa,CAAC,SAAuB;IACjD,OAAO,EAAE,CAAC,gBAAgB,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;AAC3F,CAAC;AAED,sDAAsD;AACtD,yHAAyH;AACzH,SAAgB,eAAe,CAAC,MAAuB,EAAE,IAAY;IACjE,MAAM,UAAU,GAAG,EAAE,CAAC,aAAa,CAAC,MAA0B,CAAC,IAAI,EAAE,CAAC;IACtE,OAAO,UAAU,CAAC,IAAI,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,IAAA,wBAAa,EAAC,CAAC,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AACnF,CAAC;AAED;;;GAGG;AACH,yHAAyH;AACzH,SAAgB,kBAAkB,CAC9B,GAAwB,EACxB,IAAY,EACZ,YAAmC,IAAI,qBAAqB,CAAC,IAAI,GAAG,EAAkB,CAAC,EACvF,cAA8C,IAAI,EAClD,MAAc,IAAI;IAElB,MAAM,SAAS,GAAG,IAAA,0BAAe,EAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,IAAA,wBAAa,EAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;IAC5F,IAAI,SAAS,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACzC,MAAM,GAAG,GAAG,iBAAiB,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC;IACtE,gBAAgB,CAAC,WAAW,EAAE,GAAG,EAAE,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,SAAS,CAAC,CAAC;IAC/D,OAAO,GAAG,CAAC,KAAK,CAAC;AACrB,CAAC;AAED,kFAAkF;AAClF,yHAAyH;AACzH,SAAgB,mBAAmB,CAAC,GAAwB;IACxD,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,OAAO,EAAE,CAAC;QAC/B,IAAI,EAAE,CAAC,wBAAwB,CAAC,MAAM,CAAC;YAAE,OAAO,MAAM,CAAC,UAAU,CAAC;IACtE,CAAC;IACD,OAAO,EAAE,CAAC;AACd,CAAC;AAED;;;;;GAKG;AACH,yHAAyH;AACzH,SAAgB,iBAAiB,CAAC,IAA6B;IAC3D,IAAI,IAAI,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACrE,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC;IAC3B,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC,IAAI,CAAC;IAC5C,OAAO,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AAC5F,CAAC;AAED,2GAA2G;AAC3G,yHAAyH;AACzH,SAAgB,oBAAoB,CAAC,GAAwB;IACzD,MAAM,KAAK,GAAG,IAAI,GAAG,EAAU,CAAC;IAChC,KAAK,MAAM,MAAM,IAAI,GAAG,CAAC,eAAe,IAAI,EAAE,EAAE,CAAC;QAC7C,IAAI,MAAM,CAAC,KAAK,KAAK,EAAE,CAAC,UAAU,CAAC,iBAAiB;YAAE,SAAS;QAC/D,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;YAC9B,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;gBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC1E,CAAC;IACL,CAAC;IACD,OAAO,KAAK,CAAC;AACjB,CAAC;AAED,0HAA0H;AAC1H,SAAgB,eAAe,CAAC,GAAwB;IACpD,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,eAAe,CAAC,CAAC;AAC3G,CAAC;AAED,0HAA0H;AAC1H,SAAgB,iBAAiB,CAAC,GAAwB,EAAE,IAAY;IACpE,OAAO,IAAA,0BAAe,EAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAe,EAAE,EAAE,CAAC,IAAA,wBAAa,EAAC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC;AACrF,CAAC;AAED;;;;;;;GAOG;AACH,yHAAyH;AACzH,SAAgB,eAAe,CAAC,IAAuB;IACnD,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACpF,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,oBAAoB,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;IACpC,IAAI,KAAK,KAAK,SAAS,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnE,OAAO,KAAK,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC;AACrD,CAAC;AAED,yHAAyH;AACzH,SAAgB,gBAAgB,CAAC,IAAuB;IACpD,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IAC/B,IAAI,EAAE,CAAC,0BAA0B,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC;IACnE,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC,IAAI,CAAC;IAChD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,2HAA2H;AAC3H,SAAgB,UAAU,CAAC,QAAgB;IACvC,OAAO,CACH,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC;QAChC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC3B,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAC9B,CAAC;AACN,CAAC;AAGD,kFAAkF;AAClF,yHAAyH;AACzH,SAAgB,oBAAoB,CAChC,IAAa,EACb,OAAe,EACf,cAA8C,IAAI;IAElD,OAAO,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;AAC7F,CAAC;AAED;;;;;;;;;GASG;AACH,yHAAyH;AACzH,SAAgB,mBAAmB,CAAC,IAAa,EAAE,OAAe;IAC9D,MAAM,KAAK,GAAG,EAAE,CAAC,sBAAsB,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,kBAAkB,CAAC,IAAI,CAAC,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC;IACxG,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3D,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;IAC3B,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,wBAAwB,CAAC;QAAE,OAAO,IAAI,CAAC;IACzD,MAAM,cAAc,GAAG,qBAAqB,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;IACxD,OAAO,cAAc,KAAK,IAAI;QAC1B,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,EAAE;QACxD,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,OAAO,EAAE,EAAE,EAAE,cAAc,EAAE,CAAC;AACjF,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,yHAAyH;AACzH,SAAgB,qBAAqB,CAAC,IAAa,EAAE,GAAW;IAC5D,KAAK,MAAM,GAAG,IAAI,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;QACtC,IAAI,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,mBAAmB;YAAE,SAAS;QACvD,MAAM,OAAO,GAAG,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QACnE,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,IAAI,KAAK,EAAE,CAAC,CAAC;QAChF,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;YAAE,SAAS;QACjC,MAAM,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;QACpC,IAAI,CAAC,IAAA,oCAAoB,EAAC,IAAI,CAAC;YAAE,SAAS;QAC1C,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QAC9C,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC;AAED,8DAA8D;AAC9D,0HAA0H;AAC1H,SAAgB,UAAU,CAAC,IAAmD;IAC1E,OAAO,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,CAAc,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,EAAE,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;AAC1G,CAAC;AAED,yGAAyG;AACzG,SAAgB,cAAc,CAAC,GAAW;IACtC,MAAM,GAAG,GAAa,EAAE,CAAC;IACzB,KAAK,MAAM,KAAK,IAAI,EAAE,CAAC,WAAW,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,EAAE,CAAC;QAC/D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QACxC,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACtB,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;gBAAE,GAAG,CAAC,IAAI,CAAC,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC,CAAC;QACzE,CAAC;aAAM,IAAI,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACrE,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnB,CAAC;IACL,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC","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 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)) 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(api: string, decorator: string, method: string | null, argument: string, node: ts.Node): void {\n this.found.push(new NonLiteralDecoratorArg(api, decorator, method, argument, this.locate(node)));\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(new UnresolvedEndpointPath(api, method, argument, this.locate(node)));\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(new UndeclaredExternalCaller(api, method, argument, this.locate(node)));\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 (pathArg.value === null || kind === null || !ENDPOINT_KINDS.includes(kind as EndpointKind)) continue;\n const method: ApiMethodMeta = { name, path: pathArg.value, kind: kind as EndpointKind };\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) 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/** 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 = ts.isIdentifier(property.name) || ts.isStringLiteral(property.name) ? property.name.text : 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(decorator: ts.Decorator): 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/**\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((m: ts.Modifier) => m.kind === ts.SyntaxKind.AbstractKeyword);\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\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 = 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(node: ts.Node, api: string): 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.trim().split(/\\s+/).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((m: ts.Modifier) => m.kind === ts.SyntaxKind.ExportKeyword);\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"]}
|
|
@@ -10,7 +10,7 @@
|
|
|
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, UnresolvedEndpointPath } from './api-relations';
|
|
13
|
+
import { EmptiedApiContract, UndeclaredExternalCaller, UnresolvedEndpointPath } from './api-relations';
|
|
14
14
|
/**
|
|
15
15
|
* A routed contract whose `@ApiPath` argument the scan could not read. Fatal on purpose: shipping the
|
|
16
16
|
* entry without its basePath is what made `/whatsapp/test` render as `/test` in a downstream runbook.
|
|
@@ -30,6 +30,20 @@ export declare class UnresolvedEndpointPathError extends Error {
|
|
|
30
30
|
readonly paths: readonly UnresolvedEndpointPath[];
|
|
31
31
|
constructor(paths: readonly UnresolvedEndpointPath[]);
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* `external` endpoints whose CALLER the scan could not read. Fatal, like the two above, because the
|
|
35
|
+
* alternative is a diagram that lies by omission: the inbound box exists solely to name the system
|
|
36
|
+
* calling us from outside, and with nothing to name it falls back to restating our own contract
|
|
37
|
+
* name — which the reader already sees on the service box the arrow points at.
|
|
38
|
+
*
|
|
39
|
+
* `@Endpoint`'s TS overloads make `calledBy` a compile error to omit, so a scan reaching here saw a
|
|
40
|
+
* JS caller, an `as any`, a cross-module constant this parser-only pass cannot fold, or a
|
|
41
|
+
* `callerKind` that is not one of the declared kinds.
|
|
42
|
+
*/
|
|
43
|
+
export declare class UndeclaredExternalCallerError extends Error {
|
|
44
|
+
readonly callers: readonly UndeclaredExternalCaller[];
|
|
45
|
+
constructor(callers: readonly UndeclaredExternalCaller[]);
|
|
46
|
+
}
|
|
33
47
|
/**
|
|
34
48
|
* Contract classes that declared `@Endpoint` methods and kept none of them. Fatal because the
|
|
35
49
|
* alternative is the silent drop the api scan exists to close: buildApiContracts legitimately skips
|
|
@@ -12,7 +12,7 @@
|
|
|
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.UnresolvedEndpointPathError = exports.MissingBasePathError = void 0;
|
|
15
|
+
exports.EmptiedApiContractError = exports.UndeclaredExternalCallerError = exports.UnresolvedEndpointPathError = exports.MissingBasePathError = void 0;
|
|
16
16
|
/**
|
|
17
17
|
* A routed contract whose `@ApiPath` argument the scan could not read. Fatal on purpose: shipping the
|
|
18
18
|
* entry without its basePath is what made `/whatsapp/test` render as `/test` in a downstream runbook.
|
|
@@ -56,6 +56,35 @@ class UnresolvedEndpointPathError extends Error {
|
|
|
56
56
|
}
|
|
57
57
|
}
|
|
58
58
|
exports.UnresolvedEndpointPathError = UnresolvedEndpointPathError;
|
|
59
|
+
/**
|
|
60
|
+
* `external` endpoints whose CALLER the scan could not read. Fatal, like the two above, because the
|
|
61
|
+
* alternative is a diagram that lies by omission: the inbound box exists solely to name the system
|
|
62
|
+
* calling us from outside, and with nothing to name it falls back to restating our own contract
|
|
63
|
+
* name — which the reader already sees on the service box the arrow points at.
|
|
64
|
+
*
|
|
65
|
+
* `@Endpoint`'s TS overloads make `calledBy` a compile error to omit, so a scan reaching here saw a
|
|
66
|
+
* JS caller, an `as any`, a cross-module constant this parser-only pass cannot fold, or a
|
|
67
|
+
* `callerKind` that is not one of the declared kinds.
|
|
68
|
+
*/
|
|
69
|
+
class UndeclaredExternalCallerError extends Error {
|
|
70
|
+
callers;
|
|
71
|
+
constructor(callers) {
|
|
72
|
+
super(`${callers.length} 'external' @Endpoint(s) do not declare WHO calls them:\n` +
|
|
73
|
+
callers
|
|
74
|
+
.map((c) => ` • ${c.api}.${c.method} — ${c.argument} at ${c.at}`)
|
|
75
|
+
.join('\n') +
|
|
76
|
+
`\n An 'external' endpoint is driven by a system OUTSIDE this repo, and the runtime\n` +
|
|
77
|
+
` architecture graph draws that system as an inbound box. Name it:\n` +
|
|
78
|
+
` @Endpoint('/hook', 'external', { calledBy: 'twilio' })\n` +
|
|
79
|
+
` Add callerKind for anything that is not a vendor SaaS — database | cache | queue |\n` +
|
|
80
|
+
` storage | saas | system — e.g. a GCP Pub/Sub push subscription:\n` +
|
|
81
|
+
` @Endpoint('/push', 'external', { calledBy: 'pubsub-push', callerKind: 'system' })\n` +
|
|
82
|
+
` Use a string LITERAL or a SAME-module const: this scan is parser-only by design.`);
|
|
83
|
+
this.callers = callers;
|
|
84
|
+
this.name = 'UndeclaredExternalCallerError';
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
exports.UndeclaredExternalCallerError = UndeclaredExternalCallerError;
|
|
59
88
|
/**
|
|
60
89
|
* Contract classes that declared `@Endpoint` methods and kept none of them. Fatal because the
|
|
61
90
|
* alternative is the silent drop the api scan exists to close: buildApiContracts legitimately skips
|
|
@@ -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,wFAAwF;YACxF,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,oFAAoF;YACpF,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;;;;;;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,wFAAwF;YACxF,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 `apiContracts` 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, 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 apiContracts — 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 apiContracts — 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 * 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 apiContracts, 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;;;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,wFAAwF;YACxF,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,oFAAoF;YACpF,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,wFAAwF;YACxF,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 `apiContracts` 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 apiContracts — 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 apiContracts — 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 apiContracts, 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"]}
|
|
@@ -132,6 +132,20 @@ export interface ApiMethodMeta {
|
|
|
132
132
|
* provisioned as a queue.
|
|
133
133
|
*/
|
|
134
134
|
queueName?: string;
|
|
135
|
+
/**
|
|
136
|
+
* WHO outside this repo drives this endpoint, from `@Endpoint(p, 'external', { calledBy })`.
|
|
137
|
+
*
|
|
138
|
+
* ONLY on an `external` method, the same way `queueName` is only on the kinds that HAVE a queue.
|
|
139
|
+
*
|
|
140
|
+
* Deliberately the SAME {@link ExternalSystemDeclaration} the OUTBOUND `@externalSystem` tag
|
|
141
|
+
* resolves to, not a parallel inbound-only type: an inbound `saas twilio` and an outbound
|
|
142
|
+
* `saas twilio` are the same vendor, so sharing the type makes them share an IDENTITY and
|
|
143
|
+
* converge on ONE node instead of drawing twilio twice facing opposite directions.
|
|
144
|
+
*
|
|
145
|
+
* Optional in the TYPE only for graphs generated before the caller was required — generation
|
|
146
|
+
* FAILS on an `external` method whose caller cannot be read (UndeclaredExternalCallerError).
|
|
147
|
+
*/
|
|
148
|
+
caller?: ExternalSystemDeclaration;
|
|
135
149
|
}
|
|
136
150
|
/**
|
|
137
151
|
* A discovered API contract class: its name, the api-lib project that owns it, its transport, and
|
|
@@ -245,6 +259,34 @@ export declare class UnresolvedEndpointPath {
|
|
|
245
259
|
/** `path/to/file.ts:LINE`, workspace-relative. */
|
|
246
260
|
at: string);
|
|
247
261
|
}
|
|
262
|
+
/**
|
|
263
|
+
* ONE `external` `@Endpoint` whose CALLER could not be read from the source.
|
|
264
|
+
*
|
|
265
|
+
* Fatal for the same reason {@link UnresolvedEndpointPath} is. The inbound box exists to say who is
|
|
266
|
+
* calling us from outside; with no caller it can only restate our own contract name, which is the
|
|
267
|
+
* exact bug this diagnostic exists to make impossible to reintroduce. `@Endpoint`'s TS overloads
|
|
268
|
+
* already require `calledBy`, so anything reaching here is a JS caller, an `as any`, a cross-module
|
|
269
|
+
* constant the parser-only scan cannot fold, or an unknown `callerKind`.
|
|
270
|
+
*/
|
|
271
|
+
export declare class UndeclaredExternalCaller {
|
|
272
|
+
/** The contract class the method is declared on. */
|
|
273
|
+
readonly api: string;
|
|
274
|
+
/** The method name. */
|
|
275
|
+
readonly method: string;
|
|
276
|
+
/** What was wrong, as written — `<missing>`, `SOME_CONST`, `callerKind: 'vendor'`. */
|
|
277
|
+
readonly argument: string;
|
|
278
|
+
/** `path/to/file.ts:LINE`, workspace-relative. */
|
|
279
|
+
readonly at: string;
|
|
280
|
+
constructor(
|
|
281
|
+
/** The contract class the method is declared on. */
|
|
282
|
+
api: string,
|
|
283
|
+
/** The method name. */
|
|
284
|
+
method: string,
|
|
285
|
+
/** What was wrong, as written — `<missing>`, `SOME_CONST`, `callerKind: 'vendor'`. */
|
|
286
|
+
argument: string,
|
|
287
|
+
/** `path/to/file.ts:LINE`, workspace-relative. */
|
|
288
|
+
at: string);
|
|
289
|
+
}
|
|
248
290
|
/**
|
|
249
291
|
* A contract class that DECLARED `@Endpoint` methods and kept none of them.
|
|
250
292
|
*
|