@webpieces/core-util 0.4.582 → 0.4.584
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 +1 -1
- package/src/http/AuthJwtCompileAssertions.d.ts +29 -0
- package/src/http/AuthJwtCompileAssertions.js +54 -0
- package/src/http/AuthJwtCompileAssertions.js.map +1 -0
- package/src/http/decorators.d.ts +78 -54
- package/src/http/decorators.js +45 -67
- package/src/http/decorators.js.map +1 -1
- package/src/index.d.ts +2 -2
- package/src/index.js +5 -6
- package/src/index.js.map +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* COMPILE-TIME assertions for `JwtRoles` — the guarantee that every broken role decision is a compile
|
|
3
|
+
* error rather than a silent over-permissioning. Each `@ts-expect-error` below FAILS THE BUILD (TS2578,
|
|
4
|
+
* "unused '@ts-expect-error' directive") if the line it guards ever starts compiling. Verified by
|
|
5
|
+
* deliberately making a guarded line valid and watching `tsc -p tsconfig.lib.json` go red.
|
|
6
|
+
*
|
|
7
|
+
* WHY THIS IS NOT A `.spec.ts` FILE. It was, and it proved nothing: tsconfig.lib.json EXCLUDES spec
|
|
8
|
+
* files, and vitest transpiles with esbuild, which strips types without checking them. So a
|
|
9
|
+
* `@ts-expect-error` in a spec is inert — the suite passes whether or not the guarded line really
|
|
10
|
+
* errors. Verified the hard way: with one guarded line deliberately made VALID, both `vitest run` and
|
|
11
|
+
* `nx run core-util:ci` stayed green. A type-level guarantee must be asserted in a file the
|
|
12
|
+
* type-checker actually compiles, which is any non-spec file under `src/`.
|
|
13
|
+
*
|
|
14
|
+
* WHY IT CALLS THE FACTORIES INSTEAD OF USING DECORATOR SYNTAX. A decorator factory is a plain
|
|
15
|
+
* function, so a direct call type-checks the argument identically — and the first cut of this file,
|
|
16
|
+
* which used `@ApiPath` + `@Endpoint` on an abstract class, made the architecture scanner classify
|
|
17
|
+
* core-util as exporting an API contract and demand a `role:api-lib` tag. A test fixture must not
|
|
18
|
+
* change what the repo believes about its own architecture.
|
|
19
|
+
*
|
|
20
|
+
* The counterpart RUNTIME behaviour (that `allRolesAllowed: true` reads back as `[]` through
|
|
21
|
+
* `rolesRequired`) is asserted in `__tests__/pubsub-and-auth-decorators.spec.ts`, where runtime
|
|
22
|
+
* assertions belong.
|
|
23
|
+
*/
|
|
24
|
+
export declare class AuthJwtCompileAssertions {
|
|
25
|
+
/** The two legitimate spellings must keep compiling; this half is asserted by the ABSENCE of error. */
|
|
26
|
+
legitimate(): void;
|
|
27
|
+
/** Every one of these must be UNWRITABLE. A directive going unused here fails the build. */
|
|
28
|
+
rejected(): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AuthJwtCompileAssertions = void 0;
|
|
4
|
+
const decorators_1 = require("./decorators");
|
|
5
|
+
/**
|
|
6
|
+
* COMPILE-TIME assertions for `JwtRoles` — the guarantee that every broken role decision is a compile
|
|
7
|
+
* error rather than a silent over-permissioning. Each `@ts-expect-error` below FAILS THE BUILD (TS2578,
|
|
8
|
+
* "unused '@ts-expect-error' directive") if the line it guards ever starts compiling. Verified by
|
|
9
|
+
* deliberately making a guarded line valid and watching `tsc -p tsconfig.lib.json` go red.
|
|
10
|
+
*
|
|
11
|
+
* WHY THIS IS NOT A `.spec.ts` FILE. It was, and it proved nothing: tsconfig.lib.json EXCLUDES spec
|
|
12
|
+
* files, and vitest transpiles with esbuild, which strips types without checking them. So a
|
|
13
|
+
* `@ts-expect-error` in a spec is inert — the suite passes whether or not the guarded line really
|
|
14
|
+
* errors. Verified the hard way: with one guarded line deliberately made VALID, both `vitest run` and
|
|
15
|
+
* `nx run core-util:ci` stayed green. A type-level guarantee must be asserted in a file the
|
|
16
|
+
* type-checker actually compiles, which is any non-spec file under `src/`.
|
|
17
|
+
*
|
|
18
|
+
* WHY IT CALLS THE FACTORIES INSTEAD OF USING DECORATOR SYNTAX. A decorator factory is a plain
|
|
19
|
+
* function, so a direct call type-checks the argument identically — and the first cut of this file,
|
|
20
|
+
* which used `@ApiPath` + `@Endpoint` on an abstract class, made the architecture scanner classify
|
|
21
|
+
* core-util as exporting an API contract and demand a `role:api-lib` tag. A test fixture must not
|
|
22
|
+
* change what the repo believes about its own architecture.
|
|
23
|
+
*
|
|
24
|
+
* The counterpart RUNTIME behaviour (that `allRolesAllowed: true` reads back as `[]` through
|
|
25
|
+
* `rolesRequired`) is asserted in `__tests__/pubsub-and-auth-decorators.spec.ts`, where runtime
|
|
26
|
+
* assertions belong.
|
|
27
|
+
*/
|
|
28
|
+
class AuthJwtCompileAssertions {
|
|
29
|
+
/** The two legitimate spellings must keep compiling; this half is asserted by the ABSENCE of error. */
|
|
30
|
+
legitimate() {
|
|
31
|
+
void (0, decorators_1.AuthJwt)({ roles: ['admin'] });
|
|
32
|
+
void (0, decorators_1.AuthJwt)({ roles: ['admin', 'editor'] });
|
|
33
|
+
void (0, decorators_1.AuthJwt)({ allRolesAllowed: true });
|
|
34
|
+
void (0, decorators_1.AuthJwt)({ allRolesAllowed: true, inOrg: true });
|
|
35
|
+
void (0, decorators_1.AuthJwt)({ roles: ['admin'], tenantScoped: true });
|
|
36
|
+
}
|
|
37
|
+
/** Every one of these must be UNWRITABLE. A directive going unused here fails the build. */
|
|
38
|
+
rejected() {
|
|
39
|
+
// @ts-expect-error pick a branch — {} may not mean "any authenticated user"
|
|
40
|
+
void (0, decorators_1.AuthJwt)({});
|
|
41
|
+
// @ts-expect-error roles needs AT LEAST ONE entry; [] was the old silent widest grant
|
|
42
|
+
void (0, decorators_1.AuthJwt)({ roles: [] });
|
|
43
|
+
// @ts-expect-error contradictory: wide AND role-gated at the same time
|
|
44
|
+
void (0, decorators_1.AuthJwt)({ allRolesAllowed: true, roles: ['admin'] });
|
|
45
|
+
// @ts-expect-error allRolesAllowed:false is a redundant SECOND spelling of role-gating
|
|
46
|
+
void (0, decorators_1.AuthJwt)({ roles: ['admin'], allRolesAllowed: false });
|
|
47
|
+
// @ts-expect-error `false` is never how you say "wide" — the other branch is for that
|
|
48
|
+
void (0, decorators_1.AuthJwt)({ allRolesAllowed: false });
|
|
49
|
+
// @ts-expect-error app-defined fields alone authorize nothing; a role decision is mandatory
|
|
50
|
+
void (0, decorators_1.AuthJwt)({ inOrg: true });
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
exports.AuthJwtCompileAssertions = AuthJwtCompileAssertions;
|
|
54
|
+
//# sourceMappingURL=AuthJwtCompileAssertions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AuthJwtCompileAssertions.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/AuthJwtCompileAssertions.ts"],"names":[],"mappings":";;;AAAA,6CAAuC;AAEvC;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,MAAa,wBAAwB;IACjC,uGAAuG;IACvG,UAAU;QACN,KAAK,IAAA,oBAAO,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QACnC,KAAK,IAAA,oBAAO,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,CAAC,CAAC;QAC7C,KAAK,IAAA,oBAAO,EAAC,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;QACxC,KAAK,IAAA,oBAAO,EAAC,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;QACrD,KAAK,IAAA,oBAAO,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3D,CAAC;IAED,4FAA4F;IAC5F,QAAQ;QACJ,4EAA4E;QAC5E,KAAK,IAAA,oBAAO,EAAC,EAAE,CAAC,CAAC;QACjB,sFAAsF;QACtF,KAAK,IAAA,oBAAO,EAAC,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;QAC5B,uEAAuE;QACvE,KAAK,IAAA,oBAAO,EAAC,EAAE,eAAe,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1D,uFAAuF;QACvF,KAAK,IAAA,oBAAO,EAAC,EAAE,KAAK,EAAE,CAAC,OAAO,CAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QAC3D,sFAAsF;QACtF,KAAK,IAAA,oBAAO,EAAC,EAAE,eAAe,EAAE,KAAK,EAAE,CAAC,CAAC;QACzC,4FAA4F;QAC5F,KAAK,IAAA,oBAAO,EAAC,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAClC,CAAC;CACJ;AAzBD,4DAyBC","sourcesContent":["import { AuthJwt } from './decorators';\n\n/**\n * COMPILE-TIME assertions for `JwtRoles` — the guarantee that every broken role decision is a compile\n * error rather than a silent over-permissioning. Each `@ts-expect-error` below FAILS THE BUILD (TS2578,\n * \"unused '@ts-expect-error' directive\") if the line it guards ever starts compiling. Verified by\n * deliberately making a guarded line valid and watching `tsc -p tsconfig.lib.json` go red.\n *\n * WHY THIS IS NOT A `.spec.ts` FILE. It was, and it proved nothing: tsconfig.lib.json EXCLUDES spec\n * files, and vitest transpiles with esbuild, which strips types without checking them. So a\n * `@ts-expect-error` in a spec is inert — the suite passes whether or not the guarded line really\n * errors. Verified the hard way: with one guarded line deliberately made VALID, both `vitest run` and\n * `nx run core-util:ci` stayed green. A type-level guarantee must be asserted in a file the\n * type-checker actually compiles, which is any non-spec file under `src/`.\n *\n * WHY IT CALLS THE FACTORIES INSTEAD OF USING DECORATOR SYNTAX. A decorator factory is a plain\n * function, so a direct call type-checks the argument identically — and the first cut of this file,\n * which used `@ApiPath` + `@Endpoint` on an abstract class, made the architecture scanner classify\n * core-util as exporting an API contract and demand a `role:api-lib` tag. A test fixture must not\n * change what the repo believes about its own architecture.\n *\n * The counterpart RUNTIME behaviour (that `allRolesAllowed: true` reads back as `[]` through\n * `rolesRequired`) is asserted in `__tests__/pubsub-and-auth-decorators.spec.ts`, where runtime\n * assertions belong.\n */\nexport class AuthJwtCompileAssertions {\n /** The two legitimate spellings must keep compiling; this half is asserted by the ABSENCE of error. */\n legitimate(): void {\n void AuthJwt({ roles: ['admin'] });\n void AuthJwt({ roles: ['admin', 'editor'] });\n void AuthJwt({ allRolesAllowed: true });\n void AuthJwt({ allRolesAllowed: true, inOrg: true });\n void AuthJwt({ roles: ['admin'], tenantScoped: true });\n }\n\n /** Every one of these must be UNWRITABLE. A directive going unused here fails the build. */\n rejected(): void {\n // @ts-expect-error pick a branch — {} may not mean \"any authenticated user\"\n void AuthJwt({});\n // @ts-expect-error roles needs AT LEAST ONE entry; [] was the old silent widest grant\n void AuthJwt({ roles: [] });\n // @ts-expect-error contradictory: wide AND role-gated at the same time\n void AuthJwt({ allRolesAllowed: true, roles: ['admin'] });\n // @ts-expect-error allRolesAllowed:false is a redundant SECOND spelling of role-gating\n void AuthJwt({ roles: ['admin'], allRolesAllowed: false });\n // @ts-expect-error `false` is never how you say \"wide\" — the other branch is for that\n void AuthJwt({ allRolesAllowed: false });\n // @ts-expect-error app-defined fields alone authorize nothing; a role decision is mandatory\n void AuthJwt({ inOrg: true });\n }\n}\n"]}
|
package/src/http/decorators.d.ts
CHANGED
|
@@ -91,25 +91,55 @@ export declare class RouteMetadata {
|
|
|
91
91
|
constructor(httpMethod: string, path: string, methodName: string, controllerClassName?: string, authMeta?: AuthMeta, apiName?: string, formPost?: boolean, mask?: MaskSpec);
|
|
92
92
|
}
|
|
93
93
|
/**
|
|
94
|
-
* The
|
|
95
|
-
*
|
|
94
|
+
* The role decision for a JWT endpoint, as a union the COMPILER enforces — one spelling per decision,
|
|
95
|
+
* every broken combination a compile error:
|
|
96
96
|
*
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
97
|
+
* ```typescript
|
|
98
|
+
* @AuthJwt({ roles: ['admin'] }) // ✅ role-gated (any-of)
|
|
99
|
+
* @AuthJwt({ allRolesAllowed: true }) // ✅ every authenticated user, said out loud
|
|
100
|
+
* @AuthJwt({}) // ❌ pick a branch
|
|
101
|
+
* @AuthJwt({ roles: [] }) // ❌ needs at least one role
|
|
102
|
+
* ```
|
|
103
|
+
*
|
|
104
|
+
* `allRolesAllowed` exists ONLY on the wide branch (the dangerous half must be a greppable token, and
|
|
105
|
+
* the narrow branch rejects it as a redundant second spelling); `roles` is a NON-EMPTY tuple so
|
|
106
|
+
* "declared roles, passed none" — the old optional `string[]`'s silent widest grant — cannot be written.
|
|
107
|
+
* All six bad cases are pinned in `AuthJwtCompileAssertions.ts` — a COMPILED file, not a spec: tsc
|
|
108
|
+
* fails the build (TS2578) if any starts compiling. A spec cannot do this (see that file's header).
|
|
109
|
+
*
|
|
110
|
+
* WHY a type rather than the runtime `throw` this replaced: `.claude/review/backwards-compatibility.md`
|
|
111
|
+
* shim shapes #4 and #5. Not restated here — three copies of one rationale is three things to drift.
|
|
102
112
|
*/
|
|
113
|
+
export type JwtRoles = {
|
|
114
|
+
allRolesAllowed: true;
|
|
115
|
+
roles?: never;
|
|
116
|
+
} | {
|
|
117
|
+
roles: readonly [string, ...string[]];
|
|
118
|
+
allRolesAllowed?: never;
|
|
119
|
+
};
|
|
103
120
|
/**
|
|
104
|
-
* JwtRequirement - the
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
121
|
+
* JwtRequirement - the {@link JwtRoles} decision PLUS any app-defined authorization fields, e.g.
|
|
122
|
+
* `@AuthJwt({ allRolesAllowed: true, inOrg: true })`. The framework authenticates (JwtHook.parseJwt)
|
|
123
|
+
* and enforces the roles any-of; the app overrides JwtHook.authorizeJwt to enforce its own fields.
|
|
124
|
+
*
|
|
125
|
+
* This was a SECOND decorator (`@Auth`) whose `roles` was optional — so `@Auth({})` reached the exact
|
|
126
|
+
* widest grant that {@link JwtRoles} exists to make un-typeable. Folding it in leaves one decorator per
|
|
127
|
+
* credential kind and closes that route by construction.
|
|
108
128
|
*/
|
|
109
|
-
export
|
|
110
|
-
roles?: string[];
|
|
129
|
+
export type JwtRequirement = JwtRoles & {
|
|
111
130
|
[field: string]: unknown;
|
|
112
|
-
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* The service-to-service / user auth mode of an endpoint. Discriminated union so a filter can
|
|
134
|
+
* `switch (mode.kind)` and get the data it needs, exhaustively.
|
|
135
|
+
*
|
|
136
|
+
* - `public` → no auth check
|
|
137
|
+
* - `jwt` → user JWT; `requirement` carries the compiler-enforced role decision
|
|
138
|
+
* ({@link JwtRoles}) plus any app-defined authorization fields
|
|
139
|
+
* - `oidc` → Google OIDC service-to-service (Cloud Tasks delivery / cross-service RPC);
|
|
140
|
+
* `callers` is the allow-list of caller SAs ('self' = this service's SA)
|
|
141
|
+
* - `shared-secret` → constant-time compare of a header against the secret bound for `secretKey`
|
|
142
|
+
*/
|
|
113
143
|
export type AuthMode = {
|
|
114
144
|
kind: 'public';
|
|
115
145
|
} | {
|
|
@@ -124,19 +154,18 @@ export type AuthMode = {
|
|
|
124
154
|
};
|
|
125
155
|
/**
|
|
126
156
|
* Auth metadata attached to a class or method via one of the auth decorators
|
|
127
|
-
* (@Public / @AuthJwt / @AuthOidc / @AuthSharedSecret)
|
|
157
|
+
* (@Public / @AuthJwt / @AuthOidc / @AuthSharedSecret) — one per credential kind.
|
|
128
158
|
*
|
|
129
|
-
* Carries a discriminated {@link AuthMode}.
|
|
130
|
-
*
|
|
131
|
-
*
|
|
159
|
+
* Carries a discriminated {@link AuthMode} and nothing else. It USED to also expose
|
|
160
|
+
* `authenticated`/`roles` getters "for back-compat with readers that only understand the user-JWT
|
|
161
|
+
* model" — deleted, because nothing read them: every reader (AuthFilter, BrowserProxyClient,
|
|
162
|
+
* ProxyClient) switches on `mode.kind`, which is the whole point of the discriminated union. A
|
|
163
|
+
* flattened view of a union is a second spelling of it, and the flattened one silently answers
|
|
164
|
+
* `authenticated: true` for oidc and shared-secret too.
|
|
132
165
|
*/
|
|
133
166
|
export declare class AuthMeta {
|
|
134
167
|
mode: AuthMode;
|
|
135
168
|
constructor(mode: AuthMode);
|
|
136
|
-
/** True for every non-public mode (jwt, oidc, shared-secret). */
|
|
137
|
-
get authenticated(): boolean;
|
|
138
|
-
/** JWT roles, or empty for non-jwt modes (back-compat convenience over the requirement). */
|
|
139
|
-
get roles(): string[];
|
|
140
169
|
}
|
|
141
170
|
/**
|
|
142
171
|
* @ApiPath(basePath) - Class decorator that marks a class as an API definition
|
|
@@ -144,7 +173,7 @@ export declare class AuthMeta {
|
|
|
144
173
|
*
|
|
145
174
|
* Usage:
|
|
146
175
|
* ```typescript
|
|
147
|
-
* @
|
|
176
|
+
* @AuthJwt({ roles: ['admin'] })
|
|
148
177
|
* @ApiPath('/api/save')
|
|
149
178
|
* abstract class SaveApi {
|
|
150
179
|
* @Endpoint('/item', 'rpc')
|
|
@@ -217,43 +246,30 @@ export declare function MaskLog(fields: Record<string, MaskMode>): MethodDecorat
|
|
|
217
246
|
* caller then logs the DTO verbatim on the plain JSON.stringify fast path).
|
|
218
247
|
*/
|
|
219
248
|
export declare function getMaskSpec(apiClass: Function, methodName: string): MaskSpec | undefined;
|
|
220
|
-
/**
|
|
221
|
-
* Authentication config passed to @Authentication() decorator.
|
|
222
|
-
*/
|
|
223
|
-
export declare class AuthenticationConfig {
|
|
224
|
-
authenticated: boolean;
|
|
225
|
-
roles?: string[];
|
|
226
|
-
constructor(authenticated: boolean, roles?: string[]);
|
|
227
|
-
}
|
|
228
|
-
/**
|
|
229
|
-
* @Authentication(config) - Class or method decorator for auth requirements.
|
|
230
|
-
*
|
|
231
|
-
* Single decorator replaces @Public/@Authenticated/@Roles:
|
|
232
|
-
* - @Authentication({authenticated: false}) → public, no auth check
|
|
233
|
-
* - @Authentication({authenticated: true}) → requires authentication
|
|
234
|
-
* - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles
|
|
235
|
-
*
|
|
236
|
-
* Class-level is required. Methods can override class-level.
|
|
237
|
-
* Throws if authenticated=false but roles are specified (contradictory).
|
|
238
|
-
*/
|
|
239
|
-
export declare function Authentication(config: AuthenticationConfig): ClassDecorator & MethodDecorator;
|
|
240
249
|
/**
|
|
241
250
|
* @Public() - endpoint requires no authentication. Class- or method-level.
|
|
242
251
|
*/
|
|
243
252
|
export declare function Public(): ClassDecorator & MethodDecorator;
|
|
244
253
|
/**
|
|
245
|
-
* @AuthJwt(
|
|
246
|
-
*
|
|
254
|
+
* @AuthJwt(requirement) - THE user-facing JWT decorator, covering the whole user-JWT axis: the
|
|
255
|
+
* compiler-enforced role decision ({@link JwtRoles}) plus app-defined fields ({@link JwtRequirement}).
|
|
256
|
+
*
|
|
257
|
+
* ```typescript
|
|
258
|
+
* @AuthJwt({ roles: ['admin', 'editor'] }) // any-of
|
|
259
|
+
* @AuthJwt({ allRolesAllowed: true, inOrg: true }) // wide + an app rule enforced by authorizeJwt
|
|
260
|
+
* ```
|
|
261
|
+
*
|
|
262
|
+
* It absorbed the former `@Auth(requirement)` — same argument, same AuthMode, so two spellings of one
|
|
263
|
+
* decision. One decorator per credential kind now: `@Public` / `@AuthJwt` / `@AuthOidc` /
|
|
264
|
+
* `@AuthSharedSecret`.
|
|
247
265
|
*/
|
|
248
|
-
export declare function AuthJwt(
|
|
266
|
+
export declare function AuthJwt(requirement: JwtRequirement): ClassDecorator & MethodDecorator;
|
|
249
267
|
/**
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
*
|
|
253
|
-
* values to JwtHook.authorizeJwt — which the app overrides to enforce its own policy. This is
|
|
254
|
-
* how clients plug in their own JWT security without touching the framework.
|
|
268
|
+
* The roles an endpoint accepts, or [] when it accepts every authenticated user. The ONE reader of
|
|
269
|
+
* the {@link JwtRoles} union, so no caller has to re-derive "does absent mean wide?" — a question
|
|
270
|
+
* whose two plausible answers is how the widest grant kept hiding behind an absent field.
|
|
255
271
|
*/
|
|
256
|
-
export declare function
|
|
272
|
+
export declare function rolesRequired(requirement: JwtRequirement): readonly string[];
|
|
257
273
|
/**
|
|
258
274
|
* @AuthOidc(...callers) - Google OIDC service-to-service auth (Cloud Tasks delivery / cross-service
|
|
259
275
|
* RPC). `callers` is an OPTIONAL app-level allow-list of caller service accounts.
|
|
@@ -325,11 +341,19 @@ export declare function getAuthMeta(apiClass: Function, methodName?: string): Au
|
|
|
325
341
|
* Convenience wrapper over getAuthMeta for callers that only want the mode.
|
|
326
342
|
*/
|
|
327
343
|
export declare function getAuthMode(apiClass: Function, methodName?: string): AuthMode | undefined;
|
|
344
|
+
/**
|
|
345
|
+
* The ONE prescription for "this endpoint declares no auth", shared by the two places that raise it
|
|
346
|
+
* (here and http-routing's ApiRoutingFactory) because they had drifted into teaching different menus.
|
|
347
|
+
* A message teaching an incomplete API is the same defect as an API with two spellings: whichever menu
|
|
348
|
+
* the caller hits becomes the API they believe exists. It leads with the ROLE-GATED member on purpose —
|
|
349
|
+
* the first thing offered should not be the widest grant.
|
|
350
|
+
*/
|
|
351
|
+
export declare const MISSING_AUTH_DECORATOR_FIX: string;
|
|
328
352
|
/**
|
|
329
353
|
* Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server
|
|
330
354
|
* (ApiRoutingFactory) and the task/rpc clients call this so a missing auth
|
|
331
355
|
* decorator is a startup error, never a silent open endpoint.
|
|
332
|
-
* @throws Error naming the first endpoint with no @
|
|
356
|
+
* @throws Error naming the first endpoint with no auth decorator, via {@link MISSING_AUTH_DECORATOR_FIX}.
|
|
333
357
|
*/
|
|
334
358
|
export declare function assertEveryEndpointHasAuthMode(apiClass: Function): void;
|
|
335
359
|
/**
|
|
@@ -388,6 +412,6 @@ export declare function assertPubSubConventions(apiClass: Function): void;
|
|
|
388
412
|
export declare function getQueueName(apiClass: Function, methodName: string): string;
|
|
389
413
|
/**
|
|
390
414
|
* Validate that a class/method doesn't have conflicting auth decorators.
|
|
391
|
-
* @throws Error if multiple
|
|
415
|
+
* @throws Error if multiple auth decorators are found on the same target.
|
|
392
416
|
*/
|
|
393
417
|
export declare function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): void;
|
package/src/http/decorators.js
CHANGED
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.ENDPOINT_KINDS_BY_API_KIND = exports.
|
|
3
|
+
exports.ENDPOINT_KINDS_BY_API_KIND = exports.MISSING_AUTH_DECORATOR_FIX = exports.AuthMeta = exports.RouteMetadata = exports.METADATA_KEYS = void 0;
|
|
4
4
|
exports.ApiPath = ApiPath;
|
|
5
5
|
exports.Endpoint = Endpoint;
|
|
6
6
|
exports.MaskLog = MaskLog;
|
|
7
7
|
exports.getMaskSpec = getMaskSpec;
|
|
8
|
-
exports.Authentication = Authentication;
|
|
9
8
|
exports.Public = Public;
|
|
10
9
|
exports.AuthJwt = AuthJwt;
|
|
11
|
-
exports.
|
|
10
|
+
exports.rolesRequired = rolesRequired;
|
|
12
11
|
exports.AuthOidc = AuthOidc;
|
|
13
12
|
exports.AuthSharedSecret = AuthSharedSecret;
|
|
14
13
|
exports.getApiPath = getApiPath;
|
|
@@ -94,25 +93,20 @@ class RouteMetadata {
|
|
|
94
93
|
exports.RouteMetadata = RouteMetadata;
|
|
95
94
|
/**
|
|
96
95
|
* Auth metadata attached to a class or method via one of the auth decorators
|
|
97
|
-
* (@Public / @AuthJwt / @AuthOidc / @AuthSharedSecret)
|
|
96
|
+
* (@Public / @AuthJwt / @AuthOidc / @AuthSharedSecret) — one per credential kind.
|
|
98
97
|
*
|
|
99
|
-
* Carries a discriminated {@link AuthMode}.
|
|
100
|
-
*
|
|
101
|
-
*
|
|
98
|
+
* Carries a discriminated {@link AuthMode} and nothing else. It USED to also expose
|
|
99
|
+
* `authenticated`/`roles` getters "for back-compat with readers that only understand the user-JWT
|
|
100
|
+
* model" — deleted, because nothing read them: every reader (AuthFilter, BrowserProxyClient,
|
|
101
|
+
* ProxyClient) switches on `mode.kind`, which is the whole point of the discriminated union. A
|
|
102
|
+
* flattened view of a union is a second spelling of it, and the flattened one silently answers
|
|
103
|
+
* `authenticated: true` for oidc and shared-secret too.
|
|
102
104
|
*/
|
|
103
105
|
class AuthMeta {
|
|
104
106
|
mode;
|
|
105
107
|
constructor(mode) {
|
|
106
108
|
this.mode = mode;
|
|
107
109
|
}
|
|
108
|
-
/** True for every non-public mode (jwt, oidc, shared-secret). */
|
|
109
|
-
get authenticated() {
|
|
110
|
-
return this.mode.kind !== 'public';
|
|
111
|
-
}
|
|
112
|
-
/** JWT roles, or empty for non-jwt modes (back-compat convenience over the requirement). */
|
|
113
|
-
get roles() {
|
|
114
|
-
return this.mode.kind === 'jwt' ? (this.mode.requirement.roles ?? []) : [];
|
|
115
|
-
}
|
|
116
110
|
}
|
|
117
111
|
exports.AuthMeta = AuthMeta;
|
|
118
112
|
/**
|
|
@@ -121,7 +115,7 @@ exports.AuthMeta = AuthMeta;
|
|
|
121
115
|
*
|
|
122
116
|
* Usage:
|
|
123
117
|
* ```typescript
|
|
124
|
-
* @
|
|
118
|
+
* @AuthJwt({ roles: ['admin'] })
|
|
125
119
|
* @ApiPath('/api/save')
|
|
126
120
|
* abstract class SaveApi {
|
|
127
121
|
* @Endpoint('/item', 'rpc')
|
|
@@ -200,40 +194,6 @@ function getMaskSpec(apiClass, methodName) {
|
|
|
200
194
|
const specs = Reflect.getMetadata(exports.METADATA_KEYS.MASK_LOG, apiClass) || {};
|
|
201
195
|
return specs[methodName];
|
|
202
196
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Authentication config passed to @Authentication() decorator.
|
|
205
|
-
*/
|
|
206
|
-
class AuthenticationConfig {
|
|
207
|
-
authenticated;
|
|
208
|
-
roles;
|
|
209
|
-
constructor(authenticated, roles) {
|
|
210
|
-
this.authenticated = authenticated;
|
|
211
|
-
this.roles = roles;
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
exports.AuthenticationConfig = AuthenticationConfig;
|
|
215
|
-
/**
|
|
216
|
-
* @Authentication(config) - Class or method decorator for auth requirements.
|
|
217
|
-
*
|
|
218
|
-
* Single decorator replaces @Public/@Authenticated/@Roles:
|
|
219
|
-
* - @Authentication({authenticated: false}) → public, no auth check
|
|
220
|
-
* - @Authentication({authenticated: true}) → requires authentication
|
|
221
|
-
* - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles
|
|
222
|
-
*
|
|
223
|
-
* Class-level is required. Methods can override class-level.
|
|
224
|
-
* Throws if authenticated=false but roles are specified (contradictory).
|
|
225
|
-
*/
|
|
226
|
-
function Authentication(config) {
|
|
227
|
-
// Validate: can't be public with roles
|
|
228
|
-
if (!config.authenticated && config.roles && config.roles.length > 0) {
|
|
229
|
-
throw new Error(`Invalid @Authentication config: authenticated=false but roles=${JSON.stringify(config.roles)}. ` +
|
|
230
|
-
`Cannot require roles on a public endpoint. Set authenticated=true or remove roles.`);
|
|
231
|
-
}
|
|
232
|
-
const mode = config.authenticated
|
|
233
|
-
? { kind: 'jwt', requirement: { roles: config.roles ?? [] } }
|
|
234
|
-
: { kind: 'public' };
|
|
235
|
-
return defineAuthMode(mode);
|
|
236
|
-
}
|
|
237
197
|
/**
|
|
238
198
|
* Shared implementation for every auth decorator: stores an {@link AuthMeta} for
|
|
239
199
|
* the given {@link AuthMode} at class- or method-level, rejecting a second auth
|
|
@@ -263,21 +223,30 @@ function Public() {
|
|
|
263
223
|
return defineAuthMode({ kind: 'public' });
|
|
264
224
|
}
|
|
265
225
|
/**
|
|
266
|
-
* @AuthJwt(
|
|
267
|
-
*
|
|
226
|
+
* @AuthJwt(requirement) - THE user-facing JWT decorator, covering the whole user-JWT axis: the
|
|
227
|
+
* compiler-enforced role decision ({@link JwtRoles}) plus app-defined fields ({@link JwtRequirement}).
|
|
228
|
+
*
|
|
229
|
+
* ```typescript
|
|
230
|
+
* @AuthJwt({ roles: ['admin', 'editor'] }) // any-of
|
|
231
|
+
* @AuthJwt({ allRolesAllowed: true, inOrg: true }) // wide + an app rule enforced by authorizeJwt
|
|
232
|
+
* ```
|
|
233
|
+
*
|
|
234
|
+
* It absorbed the former `@Auth(requirement)` — same argument, same AuthMode, so two spellings of one
|
|
235
|
+
* decision. One decorator per credential kind now: `@Public` / `@AuthJwt` / `@AuthOidc` /
|
|
236
|
+
* `@AuthSharedSecret`.
|
|
268
237
|
*/
|
|
269
|
-
function
|
|
270
|
-
|
|
238
|
+
// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope
|
|
239
|
+
function AuthJwt(requirement) {
|
|
240
|
+
return defineAuthMode({ kind: 'jwt', requirement });
|
|
271
241
|
}
|
|
272
242
|
/**
|
|
273
|
-
*
|
|
274
|
-
*
|
|
275
|
-
*
|
|
276
|
-
* values to JwtHook.authorizeJwt — which the app overrides to enforce its own policy. This is
|
|
277
|
-
* how clients plug in their own JWT security without touching the framework.
|
|
243
|
+
* The roles an endpoint accepts, or [] when it accepts every authenticated user. The ONE reader of
|
|
244
|
+
* the {@link JwtRoles} union, so no caller has to re-derive "does absent mean wide?" — a question
|
|
245
|
+
* whose two plausible answers is how the widest grant kept hiding behind an absent field.
|
|
278
246
|
*/
|
|
279
|
-
function
|
|
280
|
-
|
|
247
|
+
// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getAuthMode
|
|
248
|
+
function rolesRequired(requirement) {
|
|
249
|
+
return requirement.allRolesAllowed === true ? [] : requirement.roles;
|
|
281
250
|
}
|
|
282
251
|
/**
|
|
283
252
|
* @AuthOidc(...callers) - Google OIDC service-to-service auth (Cloud Tasks delivery / cross-service
|
|
@@ -398,11 +367,20 @@ function getAuthMeta(apiClass, methodName) {
|
|
|
398
367
|
function getAuthMode(apiClass, methodName) {
|
|
399
368
|
return getAuthMeta(apiClass, methodName)?.mode;
|
|
400
369
|
}
|
|
370
|
+
/**
|
|
371
|
+
* The ONE prescription for "this endpoint declares no auth", shared by the two places that raise it
|
|
372
|
+
* (here and http-routing's ApiRoutingFactory) because they had drifted into teaching different menus.
|
|
373
|
+
* A message teaching an incomplete API is the same defect as an API with two spellings: whichever menu
|
|
374
|
+
* the caller hits becomes the API they believe exists. It leads with the ROLE-GATED member on purpose —
|
|
375
|
+
* the first thing offered should not be the widest grant.
|
|
376
|
+
*/
|
|
377
|
+
exports.MISSING_AUTH_DECORATOR_FIX = "Add one of @AuthJwt({roles: ['admin']}) / @AuthJwt({allRolesAllowed: true}) / @Public() / " +
|
|
378
|
+
'@AuthOidc(...callers) / @AuthSharedSecret(key) to the class or method.';
|
|
401
379
|
/**
|
|
402
380
|
* Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server
|
|
403
381
|
* (ApiRoutingFactory) and the task/rpc clients call this so a missing auth
|
|
404
382
|
* decorator is a startup error, never a silent open endpoint.
|
|
405
|
-
* @throws Error naming the first endpoint with no @
|
|
383
|
+
* @throws Error naming the first endpoint with no auth decorator, via {@link MISSING_AUTH_DECORATOR_FIX}.
|
|
406
384
|
*/
|
|
407
385
|
function assertEveryEndpointHasAuthMode(apiClass) {
|
|
408
386
|
const apiName = apiClass.name || 'Unknown';
|
|
@@ -410,8 +388,7 @@ function assertEveryEndpointHasAuthMode(apiClass) {
|
|
|
410
388
|
for (const methodName of Object.keys(endpoints)) {
|
|
411
389
|
if (!getAuthMeta(apiClass, methodName)) {
|
|
412
390
|
throw new Error(`Endpoint '${methodName}' in ${apiName} has no auth decorator. ` +
|
|
413
|
-
|
|
414
|
-
`to the class or method.`);
|
|
391
|
+
exports.MISSING_AUTH_DECORATOR_FIX);
|
|
415
392
|
}
|
|
416
393
|
}
|
|
417
394
|
}
|
|
@@ -517,7 +494,7 @@ function getQueueName(apiClass, methodName) {
|
|
|
517
494
|
}
|
|
518
495
|
/**
|
|
519
496
|
* Validate that a class/method doesn't have conflicting auth decorators.
|
|
520
|
-
* @throws Error if multiple
|
|
497
|
+
* @throws Error if multiple auth decorators are found on the same target.
|
|
521
498
|
*/
|
|
522
499
|
function validateNoConflictingDecorators(apiClass, methodName) {
|
|
523
500
|
const existing = methodName
|
|
@@ -526,8 +503,9 @@ function validateNoConflictingDecorators(apiClass, methodName) {
|
|
|
526
503
|
if (existing) {
|
|
527
504
|
const targetName = apiClass.name || 'Unknown';
|
|
528
505
|
const location = methodName ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;
|
|
529
|
-
throw new Error(`Conflicting
|
|
530
|
-
`Only one @
|
|
506
|
+
throw new Error(`Conflicting auth decorator on ${location}. ` +
|
|
507
|
+
`Only one of @Public() / @AuthJwt({...}) / @AuthOidc(...) / @AuthSharedSecret(...) ` +
|
|
508
|
+
`is allowed per target.`);
|
|
531
509
|
}
|
|
532
510
|
}
|
|
533
511
|
//# sourceMappingURL=decorators.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/decorators.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { MaskSpec, MaskMode } from './LogFieldMask';\nimport { DEFAULT_CALLER_KIND, ENDPOINT_CALLER_KEY, ExternalCaller, ExternalSystemKind, getEndpointCaller } from './external-caller';\n\n/**\n * Metadata keys for storing API routing information.\n * These keys are used by both server-side (routing) and client-side (client generation).\n */\nexport const METADATA_KEYS = {\n API_PATH: 'webpieces:api-path',\n ENDPOINTS: 'webpieces:endpoints',\n AUTH_META: 'webpieces:auth-meta',\n /** 'rpc' (default, sync request/response) vs 'pubsub' (fire-and-forget cloud task). */\n API_KIND: 'webpieces:api-kind',\n /** Per-method Cloud Tasks queue-name override (set via @Queue). */\n QUEUE_OVERRIDE: 'webpieces:queue-override',\n /** Per-method @Endpoint options (e.g. formPost), parallel to ENDPOINTS. */\n ENDPOINT_OPTIONS: 'webpieces:endpoint-options',\n /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external), parallel to ENDPOINTS. */\n ENDPOINT_KIND: 'webpieces:endpoint-kind',\n /** Per-method declared external CALLER (only for kind 'external'), parallel to ENDPOINTS. */\n ENDPOINT_CALLER: ENDPOINT_CALLER_KEY,\n /** Per-method @MaskLog spec (which DTO fields the LogApiCall path masks). */\n MASK_LOG: 'webpieces:mask-log',\n};\n\n/**\n * WHAT TRIGGERS an endpoint at runtime — the single fact that decides how the runtime architecture\n * graph draws it, and which Terraform resource must exist for it to ever fire:\n *\n * - `rpc` — a caller in this repo (or a browser) calls it synchronously. A direct arrow.\n * - `cloudtasks` — a producer ENQUEUES it; Cloud Tasks delivers it later. Drawn producer → queue →\n * consumer, one queue node per METHOD (see {@link Queue}). Producer and consumer\n * being the SAME service is legal and common — the queue decouples them.\n * - `cron` — a scheduler fires it on a clock. Nothing in-repo calls it; drawn hanging off a\n * clock symbol. Backed by a Cloud Scheduler job.\n * - `external` — a system OUTSIDE this repo drives it (a GCP Pub/Sub push subscription, a Twilio\n * or Gmail webhook). Drawn as an inbound dashed arrow from that system.\n *\n * Declared PER METHOD, because one api class routinely mixes them: an admin contract can have\n * caller-driven endpoints AND a nightly cron sweep. A class-level marker cannot express that, which\n * is exactly why the graph could not tell these apart before.\n */\nexport type EndpointKind = 'rpc' | 'cloudtasks' | 'cron' | 'external';\n\n/**\n * Options for a single @Endpoint. Kept in a metadata map PARALLEL to ENDPOINTS so the existing\n * `Record<methodName, path>` shape every consumer iterates stays unchanged.\n */\nexport interface EndpointOptions {\n /**\n * Parse the request body as application/x-www-form-urlencoded (flat key→value) instead of JSON.\n * For EXTERNAL webhooks (e.g. Twilio) that post form-encoded. The request DTO must be FLAT —\n * urlencoded has no nesting (unlike JSON). Default false = JSON.\n */\n formPost?: boolean;\n}\n\n/**\n * Options for an `external` @Endpoint: everything {@link EndpointOptions} carries, PLUS a REQUIRED\n * declaration of WHO is calling. See {@link Endpoint} for why, `external-caller.ts` for identity.\n */\nexport interface ExternalEndpointOptions extends EndpointOptions {\n /** The outside system that posts here (`'twilio'`) — the graph node IDENTITY, not display text. */\n calledBy: string;\n /** What that caller IS; picks the node's shape. Defaults to `'saas'` (see DEFAULT_CALLER_KIND). */\n callerKind?: ExternalSystemKind;\n}\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by ProxyClient\n * and ApiRoutingFactory.\n */\nexport class RouteMetadata {\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n /** The API contract class name (e.g. 'SaveApi') — distinct from the controller name. */\n apiName?: string;\n /**\n * True when @Endpoint(..., { formPost: true }): the body is application/x-www-form-urlencoded\n * (flat key→value), not JSON. Rides the route metadata so the per-route body parse can branch\n * without knowing the apiClass/methodName. Default false = JSON.\n */\n readonly formPost: boolean;\n /**\n * The @MaskLog field-mask spec for this route, or undefined when the method declared none. Read\n * ONCE here at route-build time and handed to {@link LogApiCall} via ApiMethodInfo, so the per-call\n * log path pays for masking only on routes that opted in (the rest stay on plain JSON.stringify).\n */\n readonly mask?: MaskSpec;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n apiName?: string,\n formPost: boolean = false,\n mask?: MaskSpec,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n this.apiName = apiName;\n this.formPost = formPost;\n this.mask = mask;\n }\n}\n\n/**\n * The service-to-service / user auth mode of an endpoint. Discriminated union so\n * a filter can `switch (mode.kind)` and get the data it needs, exhaustively.\n *\n * - `public` → no auth check\n * - `jwt` → user-facing JWT (optionally role-gated), validated by the app AuthFilter\n * - `oidc` → Google OIDC service-to-service (Cloud Tasks delivery / cross-service RPC);\n * `callers` is the allow-list of caller service accounts ('self' = this service's SA)\n * - `shared-secret` → constant-time compare of a header against the secret bound for `secretKey`\n */\n/**\n * JwtRequirement - the endpoint's JWT authorization requirement, OPAQUE to the framework. The\n * default JwtHook.authorizeJwt enforces `roles` (any-of; empty = any authenticated user); apps\n * add their OWN fields (inOrg, tenant, feature, ...) via @Auth({...}) and override authorizeJwt to\n * enforce them. This is the pluggable seam: the framework authenticates, the app authorizes.\n */\nexport interface JwtRequirement {\n roles?: string[];\n // webpieces-disable no-any-unknown -- app-defined authorization fields (inOrg, tenant, ...)\n [field: string]: unknown;\n}\n\nexport type AuthMode =\n | { kind: 'public' }\n | { kind: 'jwt'; requirement: JwtRequirement }\n | { kind: 'oidc'; callers: string[] }\n | { kind: 'shared-secret'; secretKey: string };\n\n/**\n * Auth metadata attached to a class or method via one of the auth decorators\n * (@Public / @AuthJwt / @AuthOidc / @AuthSharedSecret) or the legacy @Authentication.\n *\n * Carries a discriminated {@link AuthMode}. The `authenticated`/`roles` getters are\n * kept for back-compat with readers that only understand the user-JWT model\n * (e.g. the example AuthFilter).\n */\nexport class AuthMeta {\n mode: AuthMode;\n\n constructor(mode: AuthMode) {\n this.mode = mode;\n }\n\n /** True for every non-public mode (jwt, oidc, shared-secret). */\n get authenticated(): boolean {\n return this.mode.kind !== 'public';\n }\n\n /** JWT roles, or empty for non-jwt modes (back-compat convenience over the requirement). */\n get roles(): string[] {\n return this.mode.kind === 'jwt' ? (this.mode.requirement.roles ?? []) : [];\n }\n}\n\n/**\n * @ApiPath(basePath) - Class decorator that marks a class as an API definition\n * and sets the base path for all endpoints.\n *\n * Usage:\n * ```typescript\n * @Authentication({authenticated: true})\n * @ApiPath('/api/save')\n * abstract class SaveApi {\n * @Endpoint('/item', 'rpc')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * }\n * ```\n */\nexport function ApiPath(basePath: string): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_PATH, basePath, target);\n\n // Initialize endpoints map if not exists\n if (!Reflect.hasMetadata(METADATA_KEYS.ENDPOINTS, target)) {\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, {}, target);\n }\n };\n}\n\n/**\n * @Endpoint(path, kind, options?) - Method decorator that registers a POST endpoint at the given\n * path and declares WHAT TRIGGERS it.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item', 'rpc')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n *\n * // enqueued by a producer, delivered later by Cloud Tasks:\n * @Endpoint('/send', 'cloudtasks')\n * send(request: SendRequest): Promise<void> { ... }\n *\n * // fired by Cloud Scheduler on a clock, called by nobody in this repo:\n * @Endpoint('/nightly', 'cron')\n * nightly(request: NightlyRequest): Promise<void> { ... }\n *\n * // EXTERNAL webhook posting application/x-www-form-urlencoded (e.g. Twilio):\n * @Endpoint('/hook', 'external', { formPost: true, calledBy: 'twilio' })\n * inbound(request: InboundRequest): Promise<InboundResponse> { ... }\n * ```\n *\n * `kind` is REQUIRED and deliberately positional: it makes every pre-existing single-argument\n * `@Endpoint('/x')` a COMPILE error rather than something a lint rule has to chase, so no endpoint\n * can slip into the runtime architecture graph with its trigger left to guesswork. See\n * {@link EndpointKind} for what each value draws and which Terraform resource backs it.\n *\n * `calledBy` is REQUIRED for `external` FOR EXACTLY THE SAME REASON, enforced by the overloads below:\n * the one box on the runtime graph whose whole job is to say who calls us from outside could only\n * restate OUR OWN contract name, because nothing in the source ever said who the caller was. This is\n * BREAKING for published consumers, intentionally — an existing `@Endpoint(p, 'external', {...})`\n * stops compiling until it names its caller. Migration is one property; see the migration note in\n * `external-caller.ts`. Non-`external` endpoints are completely unaffected.\n *\n * The path write to ENDPOINTS is UNCHANGED (every consumer iterates `[methodName, path]`); kind,\n * options and caller ride PARALLEL ENDPOINT_KIND / ENDPOINT_OPTIONS / ENDPOINT_CALLER maps.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, kind: 'external', options: ExternalEndpointOptions): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, kind: Exclude<EndpointKind, 'external'>, options?: EndpointOptions): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, kind: EndpointKind, options: EndpointOptions = {}): MethodDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey as string] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n\n const kinds: Record<string, EndpointKind> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, metadataTarget) || {};\n kinds[propertyKey as string] = kind;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_KIND, kinds, metadataTarget);\n\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, metadataTarget) || {};\n opts[propertyKey as string] = options;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, opts, metadataTarget);\n\n // ONLY for 'external', mirroring how a queue name is recorded only for the kinds that HAVE\n // a queue: a caller on an rpc endpoint would be a fact about nothing.\n const declared = options as ExternalEndpointOptions;\n if (kind !== 'external' || typeof declared.calledBy !== 'string' || declared.calledBy === '') return;\n const callers: Record<string, ExternalCaller> = Reflect.getMetadata(METADATA_KEYS.ENDPOINT_CALLER, metadataTarget) || {};\n callers[propertyKey as string] = new ExternalCaller(declared.callerKind ?? DEFAULT_CALLER_KIND, declared.calledBy);\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_CALLER, callers, metadataTarget);\n };\n}\n\n/**\n * @MaskLog(fields) - declare which fields of THIS method's request/response DTOs the\n * {@link LogApiCall} logging path must mask, so a secret riding on a DTO (an OAuth refresh token, an\n * id-token JWT) is never written to the logs in cleartext. The REAL value still travels on the wire\n * untouched — masking lives in the logging path only.\n *\n * ```typescript\n * @Endpoint('/account', 'rpc')\n * @MaskLog({ refreshToken: 'full', accessToken: 'last4', credential: 'full' })\n * getEmailAccount(request: GetEmailAccountRequest): Promise<GetEmailAccountResponse> { ... }\n * ```\n *\n * Matching is by field NAME at any depth (nested objects + array elements), so\n * `response.account.refreshToken` is masked. Declared on the SHARED api contract, so BOTH the client\n * `[API-client-*]` and server `[API-server-*]` lines mask it. The spec is read ONCE at route-build\n * time and rides {@link RouteMetadata.mask}, so an unmasked method pays nothing at call time.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function MaskLog(fields: Record<string, MaskMode>): MethodDecorator {\n const spec = new MaskSpec(fields);\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, metadataTarget) || {};\n specs[propertyKey as string] = spec;\n Reflect.defineMetadata(METADATA_KEYS.MASK_LOG, specs, metadataTarget);\n };\n}\n\n/**\n * The @MaskLog spec for one method, or undefined if the method declared none (the common case — the\n * caller then logs the DTO verbatim on the plain JSON.stringify fast path).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointOptions\nexport function getMaskSpec(apiClass: Function, methodName: string): MaskSpec | undefined {\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, apiClass) || {};\n return specs[methodName];\n}\n\n/**\n * Authentication config passed to @Authentication() decorator.\n */\nexport class AuthenticationConfig {\n authenticated: boolean;\n roles?: string[];\n\n constructor(authenticated: boolean, roles?: string[]) {\n this.authenticated = authenticated;\n this.roles = roles;\n }\n}\n\n/**\n * @Authentication(config) - Class or method decorator for auth requirements.\n *\n * Single decorator replaces @Public/@Authenticated/@Roles:\n * - @Authentication({authenticated: false}) → public, no auth check\n * - @Authentication({authenticated: true}) → requires authentication\n * - @Authentication({authenticated: true, roles: ['admin']}) → requires auth + roles\n *\n * Class-level is required. Methods can override class-level.\n * Throws if authenticated=false but roles are specified (contradictory).\n */\nexport function Authentication(config: AuthenticationConfig): ClassDecorator & MethodDecorator {\n // Validate: can't be public with roles\n if (!config.authenticated && config.roles && config.roles.length > 0) {\n throw new Error(\n `Invalid @Authentication config: authenticated=false but roles=${JSON.stringify(config.roles)}. ` +\n `Cannot require roles on a public endpoint. Set authenticated=true or remove roles.`\n );\n }\n\n const mode: AuthMode = config.authenticated\n ? { kind: 'jwt', requirement: { roles: config.roles ?? [] } }\n : { kind: 'public' };\n return defineAuthMode(mode);\n}\n\n/**\n * Shared implementation for every auth decorator: stores an {@link AuthMeta} for\n * the given {@link AuthMode} at class- or method-level, rejecting a second auth\n * decorator on the same target.\n */\nfunction defineAuthMode(mode: AuthMode): ClassDecorator & MethodDecorator {\n const authMeta = new AuthMeta(mode);\n\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey?: string | symbol, _descriptor?: PropertyDescriptor) => {\n if (propertyKey !== undefined) {\n // Method decorator\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n } else {\n // Class decorator\n validateNoConflictingDecorators(target, undefined);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, target);\n }\n };\n}\n\n/**\n * @Public() - endpoint requires no authentication. Class- or method-level.\n */\nexport function Public(): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'public' });\n}\n\n/**\n * @AuthJwt(...roles) - user-facing JWT auth, optionally role-gated. The app-level\n * AuthFilter validates the token; roles=[] means \"any authenticated user\".\n */\nexport function AuthJwt(...roles: string[]): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'jwt', requirement: { roles } });\n}\n\n/**\n * @Auth(requirement) - user-facing JWT auth with an APP-DEFINED authorization requirement beyond\n * roles, e.g. `@Auth({ inOrg: true })` or `@Auth({ roles: ['admin'], tenantScoped: true })`. The\n * framework authenticates the JWT (JwtHook.parseJwt), then hands `requirement` + the parsed\n * values to JwtHook.authorizeJwt — which the app overrides to enforce its own policy. This is\n * how clients plug in their own JWT security without touching the framework.\n */\nexport function Auth(requirement: JwtRequirement): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'jwt', requirement });\n}\n\n/**\n * @AuthOidc(...callers) - Google OIDC service-to-service auth (Cloud Tasks delivery / cross-service\n * RPC). `callers` is an OPTIONAL app-level allow-list of caller service accounts.\n *\n * NO args = TRUST THE EDGE: accept any genuine Google-signed OIDC caller, because a PRIVATE Cloud\n * Run service's edge already gates WHO via `run.invoker` IAM (managed in terraform — one source of\n * truth, no hand-synced list in code). If the service is actually PUBLIC, the verifier logs a loud\n * warning (it can't be the gate then). Pass explicit SAs (`@AuthOidc('svc-a')`) only when you want\n * an additional app-level allow-list as defense-in-depth.\n */\nexport function AuthOidc(...callers: string[]): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'oidc', callers });\n}\n\n/**\n * @AuthSharedSecret(key) - constant-time compare of an inbound header against the secret bound for\n * `key`. `key` is a LOOKUP KEY (not an env var): the server looks up its accepted {@link SharedSecrets}\n * by this key, and each client looks up the value it sends by the SAME key (see {@link Secrets}).\n * For internal callers that cannot mint OIDC tokens.\n */\nexport function AuthSharedSecret(key: string): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'shared-secret', secretKey: key });\n}\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n/**\n * Get the base path from @ApiPath decorator.\n */\nexport function getApiPath(apiClass: Function): string | undefined {\n return Reflect.getMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get all endpoints from @Endpoint decorators.\n * Returns a record of methodName -> endpoint path.\n */\nexport function getEndpoints(apiClass: Function): Record<string, string> | undefined {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass);\n}\n\n/**\n * Every method's declared trigger kind, as `methodName -> kind`. Parallel to {@link getEndpoints}.\n * Empty for a class carrying no @Endpoint at all.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKinds(apiClass: Function): Record<string, EndpointKind> {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, apiClass) || {};\n}\n\n/**\n * What triggers ONE method, or undefined when the method carries no @Endpoint.\n *\n * Defaults to nothing rather than to 'rpc': `kind` is a required argument, so a missing entry means\n * \"this is not an endpoint\", never \"an endpoint that forgot to say\". Silently defaulting here would\n * put an undeclared cron or webhook back into the graph as a normal rpc call — the exact blindness\n * the required argument exists to remove.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKind(apiClass: Function, methodName: string): EndpointKind | undefined {\n return getEndpointKinds(apiClass)[methodName];\n}\n\n/**\n * Get the @Endpoint options for one method (empty object if the method had no options).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointOptions(apiClass: Function, methodName: string): EndpointOptions {\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, apiClass) || {};\n return opts[methodName] ?? {};\n}\n\n/**\n * Fail-fast at wiring time when an `external` endpoint declared no caller. The {@link Endpoint}\n * overloads already make that a COMPILE error; this is the backstop for the ways TS is bypassed —\n * a JS caller, an `as any` options object, a hand-rolled Reflect.defineMetadata.\n * @throws Error naming the first external endpoint with no `calledBy`.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertEveryExternalEndpointDeclaresCaller(apiClass: Function): void {\n const kinds = getEndpointKinds(apiClass);\n for (const methodName of Object.keys(kinds)) {\n if (kinds[methodName] !== 'external' || getEndpointCaller(apiClass, methodName) !== undefined) continue;\n throw new Error(\n `External endpoint '${methodName}' in ${apiClass.name || 'Unknown'} declares no caller. Say WHO ` +\n `posts to it: @Endpoint(path, 'external', { calledBy: '<vendor>' }) — the runtime architecture ` +\n `graph cannot name an inbound caller it was never told about.`,\n );\n }\n}\n\n/**\n * True when the method's @Endpoint declared `{ formPost: true }` — its body is\n * application/x-www-form-urlencoded (flat), not JSON.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function isFormPost(apiClass: Function, methodName: string): boolean {\n return getEndpointOptions(apiClass, methodName).formPost === true;\n}\n\n/**\n * Check if a class has @ApiPath decorator.\n */\nexport function isApiPath(apiClass: Function): boolean {\n return Reflect.hasMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get auth metadata for a specific method, falling back to class-level auth.\n * Method-level auth takes precedence over class-level auth.\n */\nexport function getAuthMeta(apiClass: Function, methodName?: string): AuthMeta | undefined {\n // Check method-level first\n if (methodName) {\n const methodAuth = Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n if (methodAuth) {\n return methodAuth;\n }\n }\n\n // Fall back to class-level\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n}\n\n/**\n * Get the auth mode for a method (falling back to class-level), or undefined.\n * Convenience wrapper over getAuthMeta for callers that only want the mode.\n */\nexport function getAuthMode(apiClass: Function, methodName?: string): AuthMode | undefined {\n return getAuthMeta(apiClass, methodName)?.mode;\n}\n\n/**\n * Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server\n * (ApiRoutingFactory) and the task/rpc clients call this so a missing auth\n * decorator is a startup error, never a silent open endpoint.\n * @throws Error naming the first endpoint with no @Authentication/@Public/@Auth* decorator.\n */\nexport function assertEveryEndpointHasAuthMode(apiClass: Function): void {\n const apiName = apiClass.name || 'Unknown';\n const endpoints = getEndpoints(apiClass) || {};\n for (const methodName of Object.keys(endpoints)) {\n if (!getAuthMeta(apiClass, methodName)) {\n throw new Error(\n `Endpoint '${methodName}' in ${apiName} has no auth decorator. ` +\n `Add @Public(), @AuthJwt(...), @AuthOidc(...) or @AuthSharedSecret(...) ` +\n `to the class or method.`,\n );\n }\n }\n}\n\n// ============================================================\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n// ============================================================\n\n/**\n * API kind. 'rpc' = synchronous request/response (http-client ↔ ApiRoutingFactory).\n * 'pubsub' = fire-and-forget cloud task; the enqueue client (cloudtasks-client)\n * schedules a Cloud Task that is later delivered to the SAME controller endpoint.\n */\nexport type ApiKind = 'rpc' | 'pubsub';\n\n/**\n * @Rpc() - marks an API class as synchronous request/response (the default kind).\n * Present mostly for symmetry/readability; an undecorated API is treated as 'rpc'.\n */\nexport function Rpc(): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_KIND, 'rpc' as ApiKind, target);\n };\n}\n\n/**\n * @PubSub() - marks an API class as fire-and-forget over Cloud Tasks. Every method\n * MUST return Promise<void> (a compile-time contract on the abstract API). The\n * enqueue client and the controller share this one class, exactly like RPC.\n */\nexport function PubSub(): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_KIND, 'pubsub' as ApiKind, target);\n };\n}\n\n/**\n * @Queue(name) - override the Cloud Tasks queue name for a @PubSub method. Default\n * (no decorator) is `${ApiClassName}-${methodName}`, matched 1:1 by Terraform.\n */\nexport function Queue(name: string): MethodDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n const overrides: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.QUEUE_OVERRIDE, metadataTarget) || {};\n overrides[propertyKey as string] = name;\n Reflect.defineMetadata(METADATA_KEYS.QUEUE_OVERRIDE, overrides, metadataTarget);\n };\n}\n\n/**\n * Get the API kind. Defaults to 'rpc' when neither @Rpc nor @PubSub is present.\n */\nexport function getApiKind(apiClass: Function): ApiKind {\n return (Reflect.getMetadata(METADATA_KEYS.API_KIND, apiClass) as ApiKind) ?? 'rpc';\n}\n\n/**\n * Assert the API class is of the expected kind (used by the clients: the RPC\n * client rejects a @PubSub api and vice-versa).\n * @throws Error if the kind doesn't match.\n */\nexport function assertApiKind(apiClass: Function, expected: ApiKind): void {\n const actual = getApiKind(apiClass);\n if (actual !== expected) {\n const apiName = apiClass.name || 'Unknown';\n throw new Error(\n `API ${apiName} is @${actual === 'pubsub' ? 'PubSub' : 'Rpc'} but a ` +\n `${expected === 'pubsub' ? '@PubSub (cloud task)' : '@Rpc'} API was required here.`,\n );\n }\n}\n\n/**\n * Which {@link EndpointKind}s each {@link ApiKind} may declare. A @PubSub contract is delivered\n * asynchronously by definition, so `rpc` is meaningless on it; an @Rpc contract has no queue, so\n * `cloudtasks`/`cron` on it would name a queue/schedule nothing could ever deliver to. `external`\n * is legal on both — a webhook posts synchronously, a push subscription does not.\n *\n * Shared so the wiring-time assert below and the build-time architecture scan enforce ONE rule.\n */\nexport const ENDPOINT_KINDS_BY_API_KIND: Record<ApiKind, readonly EndpointKind[]> = {\n rpc: ['rpc', 'external'],\n pubsub: ['cloudtasks', 'cron', 'external'],\n};\n\n/**\n * Validate @PubSub conventions at wiring time: the class must be @ApiPath + @PubSub, declare at\n * least one endpoint, and every endpoint must declare a kind this api kind can actually deliver.\n * (Return-type is Promise<void>, a compile-time contract — TS erases types at runtime so it cannot\n * be re-checked here.)\n * @throws Error if conventions are violated.\n */\nexport function assertPubSubConventions(apiClass: Function): void {\n assertApiKind(apiClass, 'pubsub');\n const apiName = apiClass.name || 'Unknown';\n if (!isApiPath(apiClass)) {\n throw new Error(`@PubSub API ${apiName} must also be decorated with @ApiPath()`);\n }\n const endpoints = getEndpoints(apiClass) || {};\n if (Object.keys(endpoints).length === 0) {\n throw new Error(`@PubSub API ${apiName} declares no @Endpoint methods`);\n }\n const allowed = ENDPOINT_KINDS_BY_API_KIND.pubsub;\n const kinds = getEndpointKinds(apiClass);\n for (const methodName of Object.keys(endpoints)) {\n const kind = kinds[methodName];\n if (kind !== undefined && allowed.includes(kind)) continue;\n throw new Error(\n `@PubSub API ${apiName}.${methodName} declares @Endpoint(..., '${kind ?? 'missing'}') — a ` +\n `@PubSub contract is delivered through a queue, so it must be one of: ${allowed.join(' | ')}.`,\n );\n }\n}\n\n/**\n * Resolve the Cloud Tasks queue name for a @PubSub method: the @Queue override if\n * present, else `${ApiClassName}-${methodName}`.\n */\nexport function getQueueName(apiClass: Function, methodName: string): string {\n const overrides: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.QUEUE_OVERRIDE, apiClass) || {};\n return overrides[methodName] ?? `${apiClass.name || 'Unknown'}-${methodName}`;\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple @Authentication decorators are found on the same target.\n */\nexport function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): void {\n const existing = methodName\n ? Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName)\n : Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n\n if (existing) {\n const targetName = apiClass.name || 'Unknown';\n const location = methodName ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;\n throw new Error(\n `Conflicting @Authentication on ${location}. ` +\n `Only one @Authentication() decorator allowed per target.`\n );\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"decorators.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/decorators.ts"],"names":[],"mappings":"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","sourcesContent":["import 'reflect-metadata';\nimport { MaskSpec, MaskMode } from './LogFieldMask';\nimport { DEFAULT_CALLER_KIND, ENDPOINT_CALLER_KEY, ExternalCaller, ExternalSystemKind, getEndpointCaller } from './external-caller';\n\n/**\n * Metadata keys for storing API routing information.\n * These keys are used by both server-side (routing) and client-side (client generation).\n */\nexport const METADATA_KEYS = {\n API_PATH: 'webpieces:api-path',\n ENDPOINTS: 'webpieces:endpoints',\n AUTH_META: 'webpieces:auth-meta',\n /** 'rpc' (default, sync request/response) vs 'pubsub' (fire-and-forget cloud task). */\n API_KIND: 'webpieces:api-kind',\n /** Per-method Cloud Tasks queue-name override (set via @Queue). */\n QUEUE_OVERRIDE: 'webpieces:queue-override',\n /** Per-method @Endpoint options (e.g. formPost), parallel to ENDPOINTS. */\n ENDPOINT_OPTIONS: 'webpieces:endpoint-options',\n /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external), parallel to ENDPOINTS. */\n ENDPOINT_KIND: 'webpieces:endpoint-kind',\n /** Per-method declared external CALLER (only for kind 'external'), parallel to ENDPOINTS. */\n ENDPOINT_CALLER: ENDPOINT_CALLER_KEY,\n /** Per-method @MaskLog spec (which DTO fields the LogApiCall path masks). */\n MASK_LOG: 'webpieces:mask-log',\n};\n\n/**\n * WHAT TRIGGERS an endpoint at runtime — the single fact that decides how the runtime architecture\n * graph draws it, and which Terraform resource must exist for it to ever fire:\n *\n * - `rpc` — a caller in this repo (or a browser) calls it synchronously. A direct arrow.\n * - `cloudtasks` — a producer ENQUEUES it; Cloud Tasks delivers it later. Drawn producer → queue →\n * consumer, one queue node per METHOD (see {@link Queue}). Producer and consumer\n * being the SAME service is legal and common — the queue decouples them.\n * - `cron` — a scheduler fires it on a clock. Nothing in-repo calls it; drawn hanging off a\n * clock symbol. Backed by a Cloud Scheduler job.\n * - `external` — a system OUTSIDE this repo drives it (a GCP Pub/Sub push subscription, a Twilio\n * or Gmail webhook). Drawn as an inbound dashed arrow from that system.\n *\n * Declared PER METHOD, because one api class routinely mixes them: an admin contract can have\n * caller-driven endpoints AND a nightly cron sweep. A class-level marker cannot express that, which\n * is exactly why the graph could not tell these apart before.\n */\nexport type EndpointKind = 'rpc' | 'cloudtasks' | 'cron' | 'external';\n\n/**\n * Options for a single @Endpoint. Kept in a metadata map PARALLEL to ENDPOINTS so the existing\n * `Record<methodName, path>` shape every consumer iterates stays unchanged.\n */\nexport interface EndpointOptions {\n /**\n * Parse the request body as application/x-www-form-urlencoded (flat key→value) instead of JSON.\n * For EXTERNAL webhooks (e.g. Twilio) that post form-encoded. The request DTO must be FLAT —\n * urlencoded has no nesting (unlike JSON). Default false = JSON.\n */\n formPost?: boolean;\n}\n\n/**\n * Options for an `external` @Endpoint: everything {@link EndpointOptions} carries, PLUS a REQUIRED\n * declaration of WHO is calling. See {@link Endpoint} for why, `external-caller.ts` for identity.\n */\nexport interface ExternalEndpointOptions extends EndpointOptions {\n /** The outside system that posts here (`'twilio'`) — the graph node IDENTITY, not display text. */\n calledBy: string;\n /** What that caller IS; picks the node's shape. Defaults to `'saas'` (see DEFAULT_CALLER_KIND). */\n callerKind?: ExternalSystemKind;\n}\n\n/**\n * Route metadata stored per-method at runtime.\n * Used internally by http-routing and http-client as the runtime representation\n * of a route. Constructed from @ApiPath + @Endpoint metadata by ProxyClient\n * and ApiRoutingFactory.\n */\nexport class RouteMetadata {\n httpMethod: string;\n path: string;\n methodName: string;\n controllerClassName?: string;\n authMeta?: AuthMeta;\n /** The API contract class name (e.g. 'SaveApi') — distinct from the controller name. */\n apiName?: string;\n /**\n * True when @Endpoint(..., { formPost: true }): the body is application/x-www-form-urlencoded\n * (flat key→value), not JSON. Rides the route metadata so the per-route body parse can branch\n * without knowing the apiClass/methodName. Default false = JSON.\n */\n readonly formPost: boolean;\n /**\n * The @MaskLog field-mask spec for this route, or undefined when the method declared none. Read\n * ONCE here at route-build time and handed to {@link LogApiCall} via ApiMethodInfo, so the per-call\n * log path pays for masking only on routes that opted in (the rest stay on plain JSON.stringify).\n */\n readonly mask?: MaskSpec;\n\n constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n apiName?: string,\n formPost: boolean = false,\n mask?: MaskSpec,\n ) {\n this.httpMethod = httpMethod;\n this.path = path;\n this.methodName = methodName;\n this.controllerClassName = controllerClassName;\n this.authMeta = authMeta;\n this.apiName = apiName;\n this.formPost = formPost;\n this.mask = mask;\n }\n}\n\n/**\n * The role decision for a JWT endpoint, as a union the COMPILER enforces — one spelling per decision,\n * every broken combination a compile error:\n *\n * ```typescript\n * @AuthJwt({ roles: ['admin'] }) // ✅ role-gated (any-of)\n * @AuthJwt({ allRolesAllowed: true }) // ✅ every authenticated user, said out loud\n * @AuthJwt({}) // ❌ pick a branch\n * @AuthJwt({ roles: [] }) // ❌ needs at least one role\n * ```\n *\n * `allRolesAllowed` exists ONLY on the wide branch (the dangerous half must be a greppable token, and\n * the narrow branch rejects it as a redundant second spelling); `roles` is a NON-EMPTY tuple so\n * \"declared roles, passed none\" — the old optional `string[]`'s silent widest grant — cannot be written.\n * All six bad cases are pinned in `AuthJwtCompileAssertions.ts` — a COMPILED file, not a spec: tsc\n * fails the build (TS2578) if any starts compiling. A spec cannot do this (see that file's header).\n *\n * WHY a type rather than the runtime `throw` this replaced: `.claude/review/backwards-compatibility.md`\n * shim shapes #4 and #5. Not restated here — three copies of one rationale is three things to drift.\n */\nexport type JwtRoles =\n | { allRolesAllowed: true; roles?: never }\n | { roles: readonly [string, ...string[]]; allRolesAllowed?: never };\n\n/**\n * JwtRequirement - the {@link JwtRoles} decision PLUS any app-defined authorization fields, e.g.\n * `@AuthJwt({ allRolesAllowed: true, inOrg: true })`. The framework authenticates (JwtHook.parseJwt)\n * and enforces the roles any-of; the app overrides JwtHook.authorizeJwt to enforce its own fields.\n *\n * This was a SECOND decorator (`@Auth`) whose `roles` was optional — so `@Auth({})` reached the exact\n * widest grant that {@link JwtRoles} exists to make un-typeable. Folding it in leaves one decorator per\n * credential kind and closes that route by construction.\n */\n// webpieces-disable no-any-unknown -- app-defined authorization fields (inOrg, tenant, ...)\nexport type JwtRequirement = JwtRoles & { [field: string]: unknown };\n\n/**\n * The service-to-service / user auth mode of an endpoint. Discriminated union so a filter can\n * `switch (mode.kind)` and get the data it needs, exhaustively.\n *\n * - `public` → no auth check\n * - `jwt` → user JWT; `requirement` carries the compiler-enforced role decision\n * ({@link JwtRoles}) plus any app-defined authorization fields\n * - `oidc` → Google OIDC service-to-service (Cloud Tasks delivery / cross-service RPC);\n * `callers` is the allow-list of caller SAs ('self' = this service's SA)\n * - `shared-secret` → constant-time compare of a header against the secret bound for `secretKey`\n */\nexport type AuthMode =\n | { kind: 'public' }\n | { kind: 'jwt'; requirement: JwtRequirement }\n | { kind: 'oidc'; callers: string[] }\n | { kind: 'shared-secret'; secretKey: string };\n\n/**\n * Auth metadata attached to a class or method via one of the auth decorators\n * (@Public / @AuthJwt / @AuthOidc / @AuthSharedSecret) — one per credential kind.\n *\n * Carries a discriminated {@link AuthMode} and nothing else. It USED to also expose\n * `authenticated`/`roles` getters \"for back-compat with readers that only understand the user-JWT\n * model\" — deleted, because nothing read them: every reader (AuthFilter, BrowserProxyClient,\n * ProxyClient) switches on `mode.kind`, which is the whole point of the discriminated union. A\n * flattened view of a union is a second spelling of it, and the flattened one silently answers\n * `authenticated: true` for oidc and shared-secret too.\n */\nexport class AuthMeta {\n mode: AuthMode;\n\n constructor(mode: AuthMode) {\n this.mode = mode;\n }\n}\n\n/**\n * @ApiPath(basePath) - Class decorator that marks a class as an API definition\n * and sets the base path for all endpoints.\n *\n * Usage:\n * ```typescript\n * @AuthJwt({ roles: ['admin'] })\n * @ApiPath('/api/save')\n * abstract class SaveApi {\n * @Endpoint('/item', 'rpc')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * }\n * ```\n */\nexport function ApiPath(basePath: string): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_PATH, basePath, target);\n\n // Initialize endpoints map if not exists\n if (!Reflect.hasMetadata(METADATA_KEYS.ENDPOINTS, target)) {\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, {}, target);\n }\n };\n}\n\n/**\n * @Endpoint(path, kind, options?) - Method decorator that registers a POST endpoint at the given\n * path and declares WHAT TRIGGERS it.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item', 'rpc')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n *\n * // enqueued by a producer, delivered later by Cloud Tasks:\n * @Endpoint('/send', 'cloudtasks')\n * send(request: SendRequest): Promise<void> { ... }\n *\n * // fired by Cloud Scheduler on a clock, called by nobody in this repo:\n * @Endpoint('/nightly', 'cron')\n * nightly(request: NightlyRequest): Promise<void> { ... }\n *\n * // EXTERNAL webhook posting application/x-www-form-urlencoded (e.g. Twilio):\n * @Endpoint('/hook', 'external', { formPost: true, calledBy: 'twilio' })\n * inbound(request: InboundRequest): Promise<InboundResponse> { ... }\n * ```\n *\n * `kind` is REQUIRED and deliberately positional: it makes every pre-existing single-argument\n * `@Endpoint('/x')` a COMPILE error rather than something a lint rule has to chase, so no endpoint\n * can slip into the runtime architecture graph with its trigger left to guesswork. See\n * {@link EndpointKind} for what each value draws and which Terraform resource backs it.\n *\n * `calledBy` is REQUIRED for `external` FOR EXACTLY THE SAME REASON, enforced by the overloads below:\n * the one box on the runtime graph whose whole job is to say who calls us from outside could only\n * restate OUR OWN contract name, because nothing in the source ever said who the caller was. This is\n * BREAKING for published consumers, intentionally — an existing `@Endpoint(p, 'external', {...})`\n * stops compiling until it names its caller. Migration is one property; see the migration note in\n * `external-caller.ts`. Non-`external` endpoints are completely unaffected.\n *\n * The path write to ENDPOINTS is UNCHANGED (every consumer iterates `[methodName, path]`); kind,\n * options and caller ride PARALLEL ENDPOINT_KIND / ENDPOINT_OPTIONS / ENDPOINT_CALLER maps.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, kind: 'external', options: ExternalEndpointOptions): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, kind: Exclude<EndpointKind, 'external'>, options?: EndpointOptions): MethodDecorator;\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, kind: EndpointKind, options: EndpointOptions = {}): MethodDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n\n const endpoints: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, metadataTarget) || {};\n\n endpoints[propertyKey as string] = path;\n\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);\n\n const kinds: Record<string, EndpointKind> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, metadataTarget) || {};\n kinds[propertyKey as string] = kind;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_KIND, kinds, metadataTarget);\n\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, metadataTarget) || {};\n opts[propertyKey as string] = options;\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, opts, metadataTarget);\n\n // ONLY for 'external', mirroring how a queue name is recorded only for the kinds that HAVE\n // a queue: a caller on an rpc endpoint would be a fact about nothing.\n const declared = options as ExternalEndpointOptions;\n if (kind !== 'external' || typeof declared.calledBy !== 'string' || declared.calledBy === '') return;\n const callers: Record<string, ExternalCaller> = Reflect.getMetadata(METADATA_KEYS.ENDPOINT_CALLER, metadataTarget) || {};\n callers[propertyKey as string] = new ExternalCaller(declared.callerKind ?? DEFAULT_CALLER_KIND, declared.calledBy);\n Reflect.defineMetadata(METADATA_KEYS.ENDPOINT_CALLER, callers, metadataTarget);\n };\n}\n\n/**\n * @MaskLog(fields) - declare which fields of THIS method's request/response DTOs the\n * {@link LogApiCall} logging path must mask, so a secret riding on a DTO (an OAuth refresh token, an\n * id-token JWT) is never written to the logs in cleartext. The REAL value still travels on the wire\n * untouched — masking lives in the logging path only.\n *\n * ```typescript\n * @Endpoint('/account', 'rpc')\n * @MaskLog({ refreshToken: 'full', accessToken: 'last4', credential: 'full' })\n * getEmailAccount(request: GetEmailAccountRequest): Promise<GetEmailAccountResponse> { ... }\n * ```\n *\n * Matching is by field NAME at any depth (nested objects + array elements), so\n * `response.account.refreshToken` is masked. Declared on the SHARED api contract, so BOTH the client\n * `[API-client-*]` and server `[API-server-*]` lines mask it. The spec is read ONCE at route-build\n * time and rides {@link RouteMetadata.mask}, so an unmasked method pays nothing at call time.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function MaskLog(fields: Record<string, MaskMode>): MethodDecorator {\n const spec = new MaskSpec(fields);\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, metadataTarget) || {};\n specs[propertyKey as string] = spec;\n Reflect.defineMetadata(METADATA_KEYS.MASK_LOG, specs, metadataTarget);\n };\n}\n\n/**\n * The @MaskLog spec for one method, or undefined if the method declared none (the common case — the\n * caller then logs the DTO verbatim on the plain JSON.stringify fast path).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointOptions\nexport function getMaskSpec(apiClass: Function, methodName: string): MaskSpec | undefined {\n const specs: Record<string, MaskSpec> =\n Reflect.getMetadata(METADATA_KEYS.MASK_LOG, apiClass) || {};\n return specs[methodName];\n}\n\n/**\n * Shared implementation for every auth decorator: stores an {@link AuthMeta} for\n * the given {@link AuthMode} at class- or method-level, rejecting a second auth\n * decorator on the same target.\n */\nfunction defineAuthMode(mode: AuthMode): ClassDecorator & MethodDecorator {\n const authMeta = new AuthMeta(mode);\n\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey?: string | symbol, _descriptor?: PropertyDescriptor) => {\n if (propertyKey !== undefined) {\n // Method decorator\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n validateNoConflictingDecorators(metadataTarget, propertyKey as string);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, metadataTarget, propertyKey);\n } else {\n // Class decorator\n validateNoConflictingDecorators(target, undefined);\n Reflect.defineMetadata(METADATA_KEYS.AUTH_META, authMeta, target);\n }\n };\n}\n\n/**\n * @Public() - endpoint requires no authentication. Class- or method-level.\n */\nexport function Public(): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'public' });\n}\n\n/**\n * @AuthJwt(requirement) - THE user-facing JWT decorator, covering the whole user-JWT axis: the\n * compiler-enforced role decision ({@link JwtRoles}) plus app-defined fields ({@link JwtRequirement}).\n *\n * ```typescript\n * @AuthJwt({ roles: ['admin', 'editor'] }) // any-of\n * @AuthJwt({ allRolesAllowed: true, inOrg: true }) // wide + an app rule enforced by authorizeJwt\n * ```\n *\n * It absorbed the former `@Auth(requirement)` — same argument, same AuthMode, so two spellings of one\n * decision. One decorator per credential kind now: `@Public` / `@AuthJwt` / `@AuthOidc` /\n * `@AuthSharedSecret`.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function AuthJwt(requirement: JwtRequirement): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'jwt', requirement });\n}\n\n/**\n * The roles an endpoint accepts, or [] when it accepts every authenticated user. The ONE reader of\n * the {@link JwtRoles} union, so no caller has to re-derive \"does absent mean wide?\" — a question\n * whose two plausible answers is how the widest grant kept hiding behind an absent field.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getAuthMode\nexport function rolesRequired(requirement: JwtRequirement): readonly string[] {\n return requirement.allRolesAllowed === true ? [] : requirement.roles;\n}\n\n/**\n * @AuthOidc(...callers) - Google OIDC service-to-service auth (Cloud Tasks delivery / cross-service\n * RPC). `callers` is an OPTIONAL app-level allow-list of caller service accounts.\n *\n * NO args = TRUST THE EDGE: accept any genuine Google-signed OIDC caller, because a PRIVATE Cloud\n * Run service's edge already gates WHO via `run.invoker` IAM (managed in terraform — one source of\n * truth, no hand-synced list in code). If the service is actually PUBLIC, the verifier logs a loud\n * warning (it can't be the gate then). Pass explicit SAs (`@AuthOidc('svc-a')`) only when you want\n * an additional app-level allow-list as defense-in-depth.\n */\nexport function AuthOidc(...callers: string[]): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'oidc', callers });\n}\n\n/**\n * @AuthSharedSecret(key) - constant-time compare of an inbound header against the secret bound for\n * `key`. `key` is a LOOKUP KEY (not an env var): the server looks up its accepted {@link SharedSecrets}\n * by this key, and each client looks up the value it sends by the SAME key (see {@link Secrets}).\n * For internal callers that cannot mint OIDC tokens.\n */\nexport function AuthSharedSecret(key: string): ClassDecorator & MethodDecorator {\n return defineAuthMode({ kind: 'shared-secret', secretKey: key });\n}\n\n// ============================================================\n// Helper functions\n// ============================================================\n\n/**\n * Get the base path from @ApiPath decorator.\n */\nexport function getApiPath(apiClass: Function): string | undefined {\n return Reflect.getMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get all endpoints from @Endpoint decorators.\n * Returns a record of methodName -> endpoint path.\n */\nexport function getEndpoints(apiClass: Function): Record<string, string> | undefined {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINTS, apiClass);\n}\n\n/**\n * Every method's declared trigger kind, as `methodName -> kind`. Parallel to {@link getEndpoints}.\n * Empty for a class carrying no @Endpoint at all.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKinds(apiClass: Function): Record<string, EndpointKind> {\n return Reflect.getMetadata(METADATA_KEYS.ENDPOINT_KIND, apiClass) || {};\n}\n\n/**\n * What triggers ONE method, or undefined when the method carries no @Endpoint.\n *\n * Defaults to nothing rather than to 'rpc': `kind` is a required argument, so a missing entry means\n * \"this is not an endpoint\", never \"an endpoint that forgot to say\". Silently defaulting here would\n * put an undeclared cron or webhook back into the graph as a normal rpc call — the exact blindness\n * the required argument exists to remove.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointKind(apiClass: Function, methodName: string): EndpointKind | undefined {\n return getEndpointKinds(apiClass)[methodName];\n}\n\n/**\n * Get the @Endpoint options for one method (empty object if the method had no options).\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function getEndpointOptions(apiClass: Function, methodName: string): EndpointOptions {\n const opts: Record<string, EndpointOptions> =\n Reflect.getMetadata(METADATA_KEYS.ENDPOINT_OPTIONS, apiClass) || {};\n return opts[methodName] ?? {};\n}\n\n/**\n * Fail-fast at wiring time when an `external` endpoint declared no caller. The {@link Endpoint}\n * overloads already make that a COMPILE error; this is the backstop for the ways TS is bypassed —\n * a JS caller, an `as any` options object, a hand-rolled Reflect.defineMetadata.\n * @throws Error naming the first external endpoint with no `calledBy`.\n */\n// webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode\nexport function assertEveryExternalEndpointDeclaresCaller(apiClass: Function): void {\n const kinds = getEndpointKinds(apiClass);\n for (const methodName of Object.keys(kinds)) {\n if (kinds[methodName] !== 'external' || getEndpointCaller(apiClass, methodName) !== undefined) continue;\n throw new Error(\n `External endpoint '${methodName}' in ${apiClass.name || 'Unknown'} declares no caller. Say WHO ` +\n `posts to it: @Endpoint(path, 'external', { calledBy: '<vendor>' }) — the runtime architecture ` +\n `graph cannot name an inbound caller it was never told about.`,\n );\n }\n}\n\n/**\n * True when the method's @Endpoint declared `{ formPost: true }` — its body is\n * application/x-www-form-urlencoded (flat), not JSON.\n */\n// webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints\nexport function isFormPost(apiClass: Function, methodName: string): boolean {\n return getEndpointOptions(apiClass, methodName).formPost === true;\n}\n\n/**\n * Check if a class has @ApiPath decorator.\n */\nexport function isApiPath(apiClass: Function): boolean {\n return Reflect.hasMetadata(METADATA_KEYS.API_PATH, apiClass);\n}\n\n/**\n * Get auth metadata for a specific method, falling back to class-level auth.\n * Method-level auth takes precedence over class-level auth.\n */\nexport function getAuthMeta(apiClass: Function, methodName?: string): AuthMeta | undefined {\n // Check method-level first\n if (methodName) {\n const methodAuth = Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName);\n if (methodAuth) {\n return methodAuth;\n }\n }\n\n // Fall back to class-level\n return Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n}\n\n/**\n * Get the auth mode for a method (falling back to class-level), or undefined.\n * Convenience wrapper over getAuthMeta for callers that only want the mode.\n */\nexport function getAuthMode(apiClass: Function, methodName?: string): AuthMode | undefined {\n return getAuthMeta(apiClass, methodName)?.mode;\n}\n\n/**\n * The ONE prescription for \"this endpoint declares no auth\", shared by the two places that raise it\n * (here and http-routing's ApiRoutingFactory) because they had drifted into teaching different menus.\n * A message teaching an incomplete API is the same defect as an API with two spellings: whichever menu\n * the caller hits becomes the API they believe exists. It leads with the ROLE-GATED member on purpose —\n * the first thing offered should not be the widest grant.\n */\nexport const MISSING_AUTH_DECORATOR_FIX =\n \"Add one of @AuthJwt({roles: ['admin']}) / @AuthJwt({allRolesAllowed: true}) / @Public() / \" +\n '@AuthOidc(...callers) / @AuthSharedSecret(key) to the class or method.';\n\n/**\n * Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server\n * (ApiRoutingFactory) and the task/rpc clients call this so a missing auth\n * decorator is a startup error, never a silent open endpoint.\n * @throws Error naming the first endpoint with no auth decorator, via {@link MISSING_AUTH_DECORATOR_FIX}.\n */\nexport function assertEveryEndpointHasAuthMode(apiClass: Function): void {\n const apiName = apiClass.name || 'Unknown';\n const endpoints = getEndpoints(apiClass) || {};\n for (const methodName of Object.keys(endpoints)) {\n if (!getAuthMeta(apiClass, methodName)) {\n throw new Error(\n `Endpoint '${methodName}' in ${apiName} has no auth decorator. ` +\n MISSING_AUTH_DECORATOR_FIX,\n );\n }\n }\n}\n\n// ============================================================\n// API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n// ============================================================\n\n/**\n * API kind. 'rpc' = synchronous request/response (http-client ↔ ApiRoutingFactory).\n * 'pubsub' = fire-and-forget cloud task; the enqueue client (cloudtasks-client)\n * schedules a Cloud Task that is later delivered to the SAME controller endpoint.\n */\nexport type ApiKind = 'rpc' | 'pubsub';\n\n/**\n * @Rpc() - marks an API class as synchronous request/response (the default kind).\n * Present mostly for symmetry/readability; an undecorated API is treated as 'rpc'.\n */\nexport function Rpc(): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_KIND, 'rpc' as ApiKind, target);\n };\n}\n\n/**\n * @PubSub() - marks an API class as fire-and-forget over Cloud Tasks. Every method\n * MUST return Promise<void> (a compile-time contract on the abstract API). The\n * enqueue client and the controller share this one class, exactly like RPC.\n */\nexport function PubSub(): ClassDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any) => {\n Reflect.defineMetadata(METADATA_KEYS.API_KIND, 'pubsub' as ApiKind, target);\n };\n}\n\n/**\n * @Queue(name) - override the Cloud Tasks queue name for a @PubSub method. Default\n * (no decorator) is `${ApiClassName}-${methodName}`, matched 1:1 by Terraform.\n */\nexport function Queue(name: string): MethodDecorator {\n // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any\n return (target: any, propertyKey: string | symbol, _descriptor: PropertyDescriptor) => {\n const metadataTarget = typeof target === 'function' ? target : target.constructor;\n const overrides: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.QUEUE_OVERRIDE, metadataTarget) || {};\n overrides[propertyKey as string] = name;\n Reflect.defineMetadata(METADATA_KEYS.QUEUE_OVERRIDE, overrides, metadataTarget);\n };\n}\n\n/**\n * Get the API kind. Defaults to 'rpc' when neither @Rpc nor @PubSub is present.\n */\nexport function getApiKind(apiClass: Function): ApiKind {\n return (Reflect.getMetadata(METADATA_KEYS.API_KIND, apiClass) as ApiKind) ?? 'rpc';\n}\n\n/**\n * Assert the API class is of the expected kind (used by the clients: the RPC\n * client rejects a @PubSub api and vice-versa).\n * @throws Error if the kind doesn't match.\n */\nexport function assertApiKind(apiClass: Function, expected: ApiKind): void {\n const actual = getApiKind(apiClass);\n if (actual !== expected) {\n const apiName = apiClass.name || 'Unknown';\n throw new Error(\n `API ${apiName} is @${actual === 'pubsub' ? 'PubSub' : 'Rpc'} but a ` +\n `${expected === 'pubsub' ? '@PubSub (cloud task)' : '@Rpc'} API was required here.`,\n );\n }\n}\n\n/**\n * Which {@link EndpointKind}s each {@link ApiKind} may declare. A @PubSub contract is delivered\n * asynchronously by definition, so `rpc` is meaningless on it; an @Rpc contract has no queue, so\n * `cloudtasks`/`cron` on it would name a queue/schedule nothing could ever deliver to. `external`\n * is legal on both — a webhook posts synchronously, a push subscription does not.\n *\n * Shared so the wiring-time assert below and the build-time architecture scan enforce ONE rule.\n */\nexport const ENDPOINT_KINDS_BY_API_KIND: Record<ApiKind, readonly EndpointKind[]> = {\n rpc: ['rpc', 'external'],\n pubsub: ['cloudtasks', 'cron', 'external'],\n};\n\n/**\n * Validate @PubSub conventions at wiring time: the class must be @ApiPath + @PubSub, declare at\n * least one endpoint, and every endpoint must declare a kind this api kind can actually deliver.\n * (Return-type is Promise<void>, a compile-time contract — TS erases types at runtime so it cannot\n * be re-checked here.)\n * @throws Error if conventions are violated.\n */\nexport function assertPubSubConventions(apiClass: Function): void {\n assertApiKind(apiClass, 'pubsub');\n const apiName = apiClass.name || 'Unknown';\n if (!isApiPath(apiClass)) {\n throw new Error(`@PubSub API ${apiName} must also be decorated with @ApiPath()`);\n }\n const endpoints = getEndpoints(apiClass) || {};\n if (Object.keys(endpoints).length === 0) {\n throw new Error(`@PubSub API ${apiName} declares no @Endpoint methods`);\n }\n const allowed = ENDPOINT_KINDS_BY_API_KIND.pubsub;\n const kinds = getEndpointKinds(apiClass);\n for (const methodName of Object.keys(endpoints)) {\n const kind = kinds[methodName];\n if (kind !== undefined && allowed.includes(kind)) continue;\n throw new Error(\n `@PubSub API ${apiName}.${methodName} declares @Endpoint(..., '${kind ?? 'missing'}') — a ` +\n `@PubSub contract is delivered through a queue, so it must be one of: ${allowed.join(' | ')}.`,\n );\n }\n}\n\n/**\n * Resolve the Cloud Tasks queue name for a @PubSub method: the @Queue override if\n * present, else `${ApiClassName}-${methodName}`.\n */\nexport function getQueueName(apiClass: Function, methodName: string): string {\n const overrides: Record<string, string> =\n Reflect.getMetadata(METADATA_KEYS.QUEUE_OVERRIDE, apiClass) || {};\n return overrides[methodName] ?? `${apiClass.name || 'Unknown'}-${methodName}`;\n}\n\n/**\n * Validate that a class/method doesn't have conflicting auth decorators.\n * @throws Error if multiple auth decorators are found on the same target.\n */\nexport function validateNoConflictingDecorators(apiClass: Function, methodName: string | undefined): void {\n const existing = methodName\n ? Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass, methodName)\n : Reflect.getMetadata(METADATA_KEYS.AUTH_META, apiClass);\n\n if (existing) {\n const targetName = apiClass.name || 'Unknown';\n const location = methodName ? `method '${methodName}' of ${targetName}` : `class ${targetName}`;\n throw new Error(\n `Conflicting auth decorator on ${location}. ` +\n `Only one of @Public() / @AuthJwt({...}) / @AuthOidc(...) / @AuthSharedSecret(...) ` +\n `is allowed per target.`\n );\n }\n}\n"]}
|
package/src/index.d.ts
CHANGED
|
@@ -17,8 +17,8 @@ export { ConsoleLogger } from './logging/ConsoleLogger';
|
|
|
17
17
|
export { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';
|
|
18
18
|
export { LogManager } from './logging/LogManager';
|
|
19
19
|
export { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';
|
|
20
|
-
export { ApiPath, Endpoint,
|
|
21
|
-
export type { AuthMode, ApiKind, EndpointKind, JwtRequirement, EndpointOptions, ExternalEndpointOptions } from './http/decorators';
|
|
20
|
+
export { ApiPath, Endpoint, Public, AuthJwt, rolesRequired, MISSING_AUTH_DECORATOR_FIX, AuthOidc, AuthSharedSecret, Rpc, PubSub, Queue, MaskLog, getApiPath, getEndpoints, getEndpointOptions, getEndpointKind, getEndpointKinds, ENDPOINT_KINDS_BY_API_KIND, getMaskSpec, isFormPost, isApiPath, getAuthMeta, getAuthMode, assertEveryEndpointHasAuthMode, assertEveryExternalEndpointDeclaresCaller, getApiKind, assertApiKind, assertPubSubConventions, getQueueName, validateNoConflictingDecorators, AuthMeta, RouteMetadata, METADATA_KEYS, } from './http/decorators';
|
|
21
|
+
export type { AuthMode, ApiKind, EndpointKind, JwtRoles, JwtRequirement, EndpointOptions, ExternalEndpointOptions } from './http/decorators';
|
|
22
22
|
export { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';
|
|
23
23
|
export type { ExternalSystemKind } from './http/external-caller';
|
|
24
24
|
export { Secrets, SECRETS } from './http/Secrets';
|
package/src/index.js
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* @packageDocumentation
|
|
9
9
|
*/
|
|
10
10
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
-
exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isFormPost = exports.getMaskSpec = exports.ENDPOINT_KINDS_BY_API_KIND = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.
|
|
12
|
-
exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ErrorWireForm = exports.ServiceInfo = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.OfflineError = exports.HttpUserError = exports.HttpVendorError = exports.HttpTooManyRequestsError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind =
|
|
13
|
-
exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder =
|
|
11
|
+
exports.ExternalCaller = exports.DEFAULT_CALLER_KIND = exports.EXTERNAL_SYSTEM_KINDS = exports.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryExternalEndpointDeclaresCaller = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isFormPost = exports.getMaskSpec = exports.ENDPOINT_KINDS_BY_API_KIND = exports.getEndpointKinds = exports.getEndpointKind = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.MaskLog = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.MISSING_AUTH_DECORATOR_FIX = exports.rolesRequired = exports.AuthJwt = exports.Public = exports.Endpoint = exports.ApiPath = exports.GCP_LOG_BUDGET_BYTES = exports.MAX_GCP_LOG_BYTES = exports.LogChunkInfo = exports.LogChunkerImpl = exports.LogChunker = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
|
|
12
|
+
exports.ApiMethodInfo = exports.LOG_API_CALL_LOGGER_NAME = exports.ApiCallLogNameImpl = exports.ApiCallLogName = exports.ApiCallInfo = exports.MaskSpec = exports.LogApiCallImpl = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.WEBPIECES_DEFAULT_FAILURE_CLASSIFIER = exports.WebpiecesDefaultFailureClassifier = exports.KeyedFailureClassifier = exports.ErrorWireForm = exports.ServiceInfo = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NetworkRejectClassifier = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = exports.ENTITY_NOT_FOUND = exports.OfflineError = exports.HttpUserError = exports.HttpVendorError = exports.HttpTooManyRequestsError = exports.HttpInternalServerError = exports.HttpGatewayTimeoutError = exports.HttpBadGatewayError = exports.HttpTimeoutError = exports.HttpForbiddenError = exports.HttpUnauthorizedError = exports.HttpBadRequestError = exports.EndpointNotFoundError = exports.HttpNotFoundError = exports.HttpError = exports.ProtocolError = exports.SECRETS = exports.Secrets = exports.getEndpointCaller = exports.isExternalSystemKind = void 0;
|
|
13
|
+
exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = void 0;
|
|
14
14
|
var errorUtils_1 = require("./lib/errorUtils");
|
|
15
15
|
Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
|
|
16
16
|
var ContextKey_1 = require("./ContextKey");
|
|
@@ -45,12 +45,11 @@ Object.defineProperty(exports, "GCP_LOG_BUDGET_BYTES", { enumerable: true, get:
|
|
|
45
45
|
var decorators_1 = require("./http/decorators");
|
|
46
46
|
Object.defineProperty(exports, "ApiPath", { enumerable: true, get: function () { return decorators_1.ApiPath; } });
|
|
47
47
|
Object.defineProperty(exports, "Endpoint", { enumerable: true, get: function () { return decorators_1.Endpoint; } });
|
|
48
|
-
Object.defineProperty(exports, "Authentication", { enumerable: true, get: function () { return decorators_1.Authentication; } });
|
|
49
|
-
Object.defineProperty(exports, "AuthenticationConfig", { enumerable: true, get: function () { return decorators_1.AuthenticationConfig; } });
|
|
50
48
|
// Auth mode decorators (clean service-to-service + user JWT model)
|
|
51
49
|
Object.defineProperty(exports, "Public", { enumerable: true, get: function () { return decorators_1.Public; } });
|
|
52
50
|
Object.defineProperty(exports, "AuthJwt", { enumerable: true, get: function () { return decorators_1.AuthJwt; } });
|
|
53
|
-
Object.defineProperty(exports, "
|
|
51
|
+
Object.defineProperty(exports, "rolesRequired", { enumerable: true, get: function () { return decorators_1.rolesRequired; } });
|
|
52
|
+
Object.defineProperty(exports, "MISSING_AUTH_DECORATOR_FIX", { enumerable: true, get: function () { return decorators_1.MISSING_AUTH_DECORATOR_FIX; } });
|
|
54
53
|
Object.defineProperty(exports, "AuthOidc", { enumerable: true, get: function () { return decorators_1.AuthOidc; } });
|
|
55
54
|
Object.defineProperty(exports, "AuthSharedSecret", { enumerable: true, get: function () { return decorators_1.AuthSharedSecret; } });
|
|
56
55
|
// API kind (RPC vs PubSub/Cloud Tasks) + queue naming
|
package/src/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type { AnyContextKey } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n Authentication,\n AuthenticationConfig,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n Auth,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n ENDPOINT_KINDS_BY_API_KIND,\n getMaskSpec,\n isFormPost,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, EndpointKind, JwtRequirement, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// WHO calls an `external` endpoint — the caller declaration @Endpoint(..., 'external', {calledBy})\n// requires, and the reader for it.\nexport { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). Singleton: use the LogApiCall constant, not `new`.\nexport { LogApiCall, LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl is installed by @webpieces/core-context; the browser gets the no-op.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport { ApiCallContextHolder } from './http/ApiCallContext';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/core/core-util/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * @webpieces/core-util\n *\n * Utility functions for WebPieces applications.\n * This package works in both browser and Node.js environments.\n *\n * @packageDocumentation\n */\n\nexport { toError } from './lib/errorUtils';\nexport { ContextKey } from './ContextKey';\nexport type { AnyContextKey } from './ContextKey';\nexport { ContextTuple } from './ContextTuple';\n\n// @DocumentDesign — DI-design-root marker. Applies to ANY project kind (server\n// controllers AND library impl classes), so it lives here (browser + Node) rather\n// than in a server-only routing package.\nexport { DocumentDesign, isDocumentDesign, DESIGN_METADATA_KEYS } from './DocumentDesign';\n\n// Logging (merged from former @webpieces/wp-logging).\n// Pluggable logging interface + a browser-safe console default; apps plug in\n// bunyan/winston/pino/etc. via LogManager.setFactory(...). Browser + Node.\nexport type { Logger, LogLevel } from './logging/Logger';\nexport type { LoggerFactory } from './logging/LoggerFactory';\nexport { ConsoleLogger } from './logging/ConsoleLogger';\nexport { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';\nexport { LogManager } from './logging/LogManager';\nexport { LogChunker, LogChunkerImpl, LogChunkInfo, MAX_GCP_LOG_BYTES, GCP_LOG_BUDGET_BYTES } from './logging/LogChunker';\n\n// HTTP API contract (merged from former @webpieces/http-api).\n// Shared HTTP API definition consumed by both client and server: REST\n// decorators, the HttpError hierarchy, datetime DTOs, platform-header\n// registry/readers, ValidateImplementation, and the test-case recorder\n// contract. Pure definitions — express-free, browser + Node safe.\n\n// API definition decorators\nexport {\n ApiPath,\n Endpoint,\n // Auth mode decorators (clean service-to-service + user JWT model)\n Public,\n AuthJwt,\n rolesRequired,\n MISSING_AUTH_DECORATOR_FIX,\n AuthOidc,\n AuthSharedSecret,\n // API kind (RPC vs PubSub/Cloud Tasks) + queue naming\n Rpc,\n PubSub,\n Queue,\n MaskLog,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointKind,\n getEndpointKinds,\n ENDPOINT_KINDS_BY_API_KIND,\n getMaskSpec,\n isFormPost,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\n assertEveryExternalEndpointDeclaresCaller,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n validateNoConflictingDecorators,\n AuthMeta,\n RouteMetadata,\n METADATA_KEYS,\n} from './http/decorators';\nexport type { AuthMode, ApiKind, EndpointKind, JwtRoles, JwtRequirement, EndpointOptions, ExternalEndpointOptions } from './http/decorators';\n// WHO calls an `external` endpoint — the caller declaration @Endpoint(..., 'external', {calledBy})\n// requires, and the reader for it.\nexport { EXTERNAL_SYSTEM_KINDS, DEFAULT_CALLER_KIND, ExternalCaller, isExternalSystemKind, getEndpointCaller } from './http/external-caller';\nexport type { ExternalSystemKind } from './http/external-caller';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { Secrets, SECRETS } from './http/Secrets';\n\n// Type validators\nexport { ValidateImplementation } from './http/validators';\n\n// HTTP errors\nexport {\n ProtocolError,\n HttpError,\n HttpNotFoundError,\n EndpointNotFoundError,\n HttpBadRequestError,\n HttpUnauthorizedError,\n HttpForbiddenError,\n HttpTimeoutError,\n HttpBadGatewayError,\n HttpGatewayTimeoutError,\n HttpInternalServerError,\n HttpTooManyRequestsError,\n HttpVendorError,\n HttpUserError,\n OfflineError,\n // Error subtype constants\n ENTITY_NOT_FOUND,\n WRONG_LOGIN_TYPE,\n WRONG_LOGIN,\n NOT_APPROVED,\n EMAIL_NOT_CONFIRMED,\n WRONG_DOMAIN,\n WRONG_COMPANY,\n NO_REG_CODE,\n} from './http/errors';\n\nexport { NetworkRejectClassifier } from './http/networkReject';\n\n// Date/Time DTOs and Utilities (inspired by Java Time / JSR-310)\nexport {\n InstantDto,\n DateDto,\n TimeDto,\n DateTimeDto,\n InstantUtil,\n DateUtil,\n TimeUtil,\n DateTimeUtil,\n} from './http/datetime';\n\n// Context keys + registry (the global magic-context header system)\nexport { HeaderRegistry } from './http/HeaderRegistry';\nexport { ClientRegistry } from './http/ClientRegistry';\nexport type { ServiceUrlDeriver } from './http/ClientRegistry';\n\n// \"What service am I\" — set once at startup, read by the logging backends and by\n// RequestContextHeaders (to stamp requestIdSource on ids this service mints).\nexport { ServiceInfo } from './http/ServiceInfo';\n// Pluggable, bidirectional error translation (app exception <-> wire form). Registered on\n// ClientRegistry at startup; consulted before the built-in webpieces mapping on BOTH sides.\nexport { ErrorWireForm } from './http/ErrorTranslation';\nexport type { ErrorTranslation } from './http/ErrorTranslation';\n// Pluggable per-client failure classification (is a thrown API-call error a real failure or an\n// expected non-failure?). Registered on ClientRegistry at startup; consulted by LogApiCall.\nexport type { FailureClassifier } from './http/FailureClassifier';\nexport { KeyedFailureClassifier } from './http/FailureClassifier';\nexport {\n WebpiecesDefaultFailureClassifier,\n WEBPIECES_DEFAULT_FAILURE_CLASSIFIER,\n} from './http/WebpiecesDefaultFailureClassifier';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\n\n// BROWSER-ONLY outbound-header propagation (app-held store + registry -> outbound HTTP headers).\n// Only @webpieces/http-client-browser may name it; the server reads RequestContext directly via\n// RequestContextHeaders in the Node-only @webpieces/core-context.\nexport { ContextMgr } from './http/ContextMgr';\n\n// API-call logging helper (uses LogManager above). Singleton: use the LogApiCall constant, not `new`.\nexport { LogApiCall, LogApiCallImpl } from './http/LogApiCall';\n\n// Opt-in field masking for the LogApiCall log path — declare per-api sensitive fields so secrets\n// (OAuth refresh tokens, id-token JWTs) are masked in the logs while the real value stays on the wire.\nexport { MaskSpec } from './http/LogFieldMask';\nexport type { MaskMode } from './http/LogFieldMask';\n\n// The structured `api` tag + the context-writer seam LogApiCall stamps through. The Node\n// RequestContext-backed impl is installed by @webpieces/core-context; the browser gets the no-op.\nexport { ApiCallInfo } from './http/ApiCallInfo';\nexport type { ApiType, ApiResult } from './http/ApiCallInfo';\n// Console-render bridge: turns LogApiCall's [LogApiCall] bracket into [API.{side}.{phase}] locally.\nexport { ApiCallLogName, ApiCallLogNameImpl, LOG_API_CALL_LOGGER_NAME } from './http/ApiCallLogName';\nexport { ApiMethodInfo } from './http/ApiMethodInfo';\nexport type { ApiSide } from './http/ApiMethodInfo';\nexport { ApiCallContextHolder } from './http/ApiCallContext';\nexport type { ApiCallContext } from './http/ApiCallContext';\n\n// Test-case recording contract (impl lives in http-server; hooks in http-client)\nexport { TestCaseRecorder, RecorderKeys } from './http/recorder/TestCaseRecorder';\nexport { RecordedEndpoint, RecordedError, RecordedTestCase } from './http/recorder/RecordedEndpoint';\nexport { DoNotRecord, getDoNotRecordFields } from './http/recorder/DoNotRecord';\nexport { RecordSerializer, SerializedMap, SerializedError } from './http/recorder/RecordSerializer';\n"]}
|