@webpieces/core-util 0.3.385 → 0.4.386

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/core-util",
3
- "version": "0.3.385",
3
+ "version": "0.4.386",
4
4
  "description": "Utility functions for WebPieces - works in browser and Node.js",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -11,7 +11,21 @@ export declare const METADATA_KEYS: {
11
11
  API_KIND: string;
12
12
  /** Per-method Cloud Tasks queue-name override (set via @Queue). */
13
13
  QUEUE_OVERRIDE: string;
14
+ /** Per-method @Endpoint options (e.g. formPost), parallel to ENDPOINTS. */
15
+ ENDPOINT_OPTIONS: string;
14
16
  };
17
+ /**
18
+ * Options for a single @Endpoint. Kept in a metadata map PARALLEL to ENDPOINTS so the existing
19
+ * `Record<methodName, path>` shape every consumer iterates stays unchanged.
20
+ */
21
+ export interface EndpointOptions {
22
+ /**
23
+ * Parse the request body as application/x-www-form-urlencoded (flat key→value) instead of JSON.
24
+ * For EXTERNAL webhooks (e.g. Twilio) that post form-encoded. The request DTO must be FLAT —
25
+ * urlencoded has no nesting (unlike JSON). Default false = JSON.
26
+ */
27
+ formPost?: boolean;
28
+ }
15
29
  /**
16
30
  * Route metadata stored per-method at runtime.
17
31
  * Used internally by http-routing and http-client as the runtime representation
@@ -26,7 +40,13 @@ export declare class RouteMetadata {
26
40
  authMeta?: AuthMeta;
27
41
  /** The API contract class name (e.g. 'SaveApi') — distinct from the controller name. */
28
42
  apiName?: string;
29
- constructor(httpMethod: string, path: string, methodName: string, controllerClassName?: string, authMeta?: AuthMeta, apiName?: string);
43
+ /**
44
+ * True when @Endpoint(..., { formPost: true }): the body is application/x-www-form-urlencoded
45
+ * (flat key→value), not JSON. Rides the route metadata so the per-route body parse can branch
46
+ * without knowing the apiClass/methodName. Default false = JSON.
47
+ */
48
+ readonly formPost: boolean;
49
+ constructor(httpMethod: string, path: string, methodName: string, controllerClassName?: string, authMeta?: AuthMeta, apiName?: string, formPost?: boolean);
30
50
  }
31
51
  /**
32
52
  * The service-to-service / user auth mode of an endpoint. Discriminated union so
@@ -92,7 +112,7 @@ export declare class AuthMeta {
92
112
  */
93
113
  export declare function ApiPath(basePath: string): ClassDecorator;
94
114
  /**
95
- * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.
115
+ * @Endpoint(path, options?) - Method decorator that registers a POST endpoint at the given path.
96
116
  *
97
117
  * All endpoints are POST-only (matching gRPC/thrift style).
98
118
  *
@@ -100,9 +120,16 @@ export declare function ApiPath(basePath: string): ClassDecorator;
100
120
  * ```typescript
101
121
  * @Endpoint('/item')
102
122
  * save(request: SaveRequest): Promise<SaveResponse> { ... }
123
+ *
124
+ * // EXTERNAL webhook posting application/x-www-form-urlencoded (e.g. Twilio):
125
+ * @Endpoint('/hook', { formPost: true })
126
+ * inbound(request: InboundRequest): Promise<InboundResponse> { ... }
103
127
  * ```
128
+ *
129
+ * The path write to ENDPOINTS is UNCHANGED (every consumer iterates `[methodName, path]`); options
130
+ * ride a PARALLEL ENDPOINT_OPTIONS map so nothing downstream changes shape.
104
131
  */
105
- export declare function Endpoint(path: string): MethodDecorator;
132
+ export declare function Endpoint(path: string, options?: EndpointOptions): MethodDecorator;
106
133
  /**
107
134
  * Authentication config passed to @Authentication() decorator.
108
135
  */
@@ -167,6 +194,15 @@ export declare function getApiPath(apiClass: Function): string | undefined;
167
194
  * Returns a record of methodName -> endpoint path.
168
195
  */
169
196
  export declare function getEndpoints(apiClass: Function): Record<string, string> | undefined;
197
+ /**
198
+ * Get the @Endpoint options for one method (empty object if the method had no options).
199
+ */
200
+ export declare function getEndpointOptions(apiClass: Function, methodName: string): EndpointOptions;
201
+ /**
202
+ * True when the method's @Endpoint declared `{ formPost: true }` — its body is
203
+ * application/x-www-form-urlencoded (flat), not JSON.
204
+ */
205
+ export declare function isFormPost(apiClass: Function, methodName: string): boolean;
170
206
  /**
171
207
  * Check if a class has @ApiPath decorator.
172
208
  */
@@ -11,6 +11,8 @@ exports.AuthOidc = AuthOidc;
11
11
  exports.AuthSharedSecret = AuthSharedSecret;
12
12
  exports.getApiPath = getApiPath;
13
13
  exports.getEndpoints = getEndpoints;
14
+ exports.getEndpointOptions = getEndpointOptions;
15
+ exports.isFormPost = isFormPost;
14
16
  exports.isApiPath = isApiPath;
15
17
  exports.getAuthMeta = getAuthMeta;
16
18
  exports.getAuthMode = getAuthMode;
@@ -36,6 +38,8 @@ exports.METADATA_KEYS = {
36
38
  API_KIND: 'webpieces:api-kind',
37
39
  /** Per-method Cloud Tasks queue-name override (set via @Queue). */
38
40
  QUEUE_OVERRIDE: 'webpieces:queue-override',
41
+ /** Per-method @Endpoint options (e.g. formPost), parallel to ENDPOINTS. */
42
+ ENDPOINT_OPTIONS: 'webpieces:endpoint-options',
39
43
  };
40
44
  /**
41
45
  * Route metadata stored per-method at runtime.
@@ -51,13 +55,20 @@ class RouteMetadata {
51
55
  authMeta;
52
56
  /** The API contract class name (e.g. 'SaveApi') — distinct from the controller name. */
53
57
  apiName;
54
- constructor(httpMethod, path, methodName, controllerClassName, authMeta, apiName) {
58
+ /**
59
+ * True when @Endpoint(..., { formPost: true }): the body is application/x-www-form-urlencoded
60
+ * (flat key→value), not JSON. Rides the route metadata so the per-route body parse can branch
61
+ * without knowing the apiClass/methodName. Default false = JSON.
62
+ */
63
+ formPost;
64
+ constructor(httpMethod, path, methodName, controllerClassName, authMeta, apiName, formPost = false) {
55
65
  this.httpMethod = httpMethod;
56
66
  this.path = path;
57
67
  this.methodName = methodName;
58
68
  this.controllerClassName = controllerClassName;
59
69
  this.authMeta = authMeta;
60
70
  this.apiName = apiName;
71
+ this.formPost = formPost;
61
72
  }
62
73
  }
63
74
  exports.RouteMetadata = RouteMetadata;
@@ -109,7 +120,7 @@ function ApiPath(basePath) {
109
120
  };
110
121
  }
111
122
  /**
112
- * @Endpoint(path) - Method decorator that registers a POST endpoint at the given path.
123
+ * @Endpoint(path, options?) - Method decorator that registers a POST endpoint at the given path.
113
124
  *
114
125
  * All endpoints are POST-only (matching gRPC/thrift style).
115
126
  *
@@ -117,15 +128,26 @@ function ApiPath(basePath) {
117
128
  * ```typescript
118
129
  * @Endpoint('/item')
119
130
  * save(request: SaveRequest): Promise<SaveResponse> { ... }
131
+ *
132
+ * // EXTERNAL webhook posting application/x-www-form-urlencoded (e.g. Twilio):
133
+ * @Endpoint('/hook', { formPost: true })
134
+ * inbound(request: InboundRequest): Promise<InboundResponse> { ... }
120
135
  * ```
136
+ *
137
+ * The path write to ENDPOINTS is UNCHANGED (every consumer iterates `[methodName, path]`); options
138
+ * ride a PARALLEL ENDPOINT_OPTIONS map so nothing downstream changes shape.
121
139
  */
122
- function Endpoint(path) {
140
+ // webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope
141
+ function Endpoint(path, options = {}) {
123
142
  // webpieces-disable no-any-unknown -- reflect-metadata decorator API requires any
124
143
  return (target, propertyKey, _descriptor) => {
125
144
  const metadataTarget = typeof target === 'function' ? target : target.constructor;
126
145
  const endpoints = Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINTS, metadataTarget) || {};
127
146
  endpoints[propertyKey] = path;
128
147
  Reflect.defineMetadata(exports.METADATA_KEYS.ENDPOINTS, endpoints, metadataTarget);
148
+ const opts = Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINT_OPTIONS, metadataTarget) || {};
149
+ opts[propertyKey] = options;
150
+ Reflect.defineMetadata(exports.METADATA_KEYS.ENDPOINT_OPTIONS, opts, metadataTarget);
129
151
  };
130
152
  }
131
153
  /**
@@ -245,6 +267,22 @@ function getApiPath(apiClass) {
245
267
  function getEndpoints(apiClass) {
246
268
  return Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINTS, apiClass);
247
269
  }
270
+ /**
271
+ * Get the @Endpoint options for one method (empty object if the method had no options).
272
+ */
273
+ // webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints
274
+ function getEndpointOptions(apiClass, methodName) {
275
+ const opts = Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINT_OPTIONS, apiClass) || {};
276
+ return opts[methodName] ?? {};
277
+ }
278
+ /**
279
+ * True when the method's @Endpoint declared `{ formPost: true }` — its body is
280
+ * application/x-www-form-urlencoded (flat), not JSON.
281
+ */
282
+ // webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpoints
283
+ function isFormPost(apiClass, methodName) {
284
+ return getEndpointOptions(apiClass, methodName).formPost === true;
285
+ }
248
286
  /**
249
287
  * Check if a class has @ApiPath decorator.
250
288
  */
@@ -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';\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};\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 constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n apiName?: string,\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 }\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')\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) - Method decorator that registers a POST endpoint at the given path.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n * ```\n */\nexport function Endpoint(path: 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\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}\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 * 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 * Validate @PubSub conventions at wiring time: the class must be @ApiPath + @PubSub\n * and declare at least one endpoint. (Return-type is Promise<void>, a compile-time\n * contract — TS erases types at runtime so it cannot 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}\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';\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};\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 * 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 constructor(\n httpMethod: string,\n path: string,\n methodName: string,\n controllerClassName?: string,\n authMeta?: AuthMeta,\n apiName?: string,\n formPost: boolean = false,\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 }\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')\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, options?) - Method decorator that registers a POST endpoint at the given path.\n *\n * All endpoints are POST-only (matching gRPC/thrift style).\n *\n * Usage:\n * ```typescript\n * @Endpoint('/item')\n * save(request: SaveRequest): Promise<SaveResponse> { ... }\n *\n * // EXTERNAL webhook posting application/x-www-form-urlencoded (e.g. Twilio):\n * @Endpoint('/hook', { formPost: true })\n * inbound(request: InboundRequest): Promise<InboundResponse> { ... }\n * ```\n *\n * The path write to ENDPOINTS is UNCHANGED (every consumer iterates `[methodName, path]`); options\n * ride a PARALLEL ENDPOINT_OPTIONS map so nothing downstream changes shape.\n */\n// webpieces-disable no-function-outside-class -- decorator factory; decorators are inherently module-scope\nexport function Endpoint(path: string, 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 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}\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 * 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 * 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 * Validate @PubSub conventions at wiring time: the class must be @ApiPath + @PubSub\n * and declare at least one endpoint. (Return-type is Promise<void>, a compile-time\n * contract — TS erases types at runtime so it cannot 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}\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"]}
package/src/index.d.ts CHANGED
@@ -15,8 +15,8 @@ export type { LoggerFactory } from './logging/LoggerFactory';
15
15
  export { ConsoleLogger } from './logging/ConsoleLogger';
16
16
  export { ConsoleLoggerFactory } from './logging/ConsoleLoggerFactory';
17
17
  export { LogManager } from './logging/LogManager';
18
- export { ApiPath, Endpoint, Authentication, AuthenticationConfig, Public, AuthJwt, Auth, AuthOidc, AuthSharedSecret, Rpc, PubSub, Queue, getApiPath, getEndpoints, isApiPath, getAuthMeta, getAuthMode, assertEveryEndpointHasAuthMode, getApiKind, assertApiKind, assertPubSubConventions, getQueueName, validateNoConflictingDecorators, AuthMeta, RouteMetadata, METADATA_KEYS, } from './http/decorators';
19
- export type { AuthMode, ApiKind, JwtRequirement } from './http/decorators';
18
+ export { ApiPath, Endpoint, Authentication, AuthenticationConfig, Public, AuthJwt, Auth, AuthOidc, AuthSharedSecret, Rpc, PubSub, Queue, getApiPath, getEndpoints, getEndpointOptions, isFormPost, isApiPath, getAuthMeta, getAuthMode, assertEveryEndpointHasAuthMode, getApiKind, assertApiKind, assertPubSubConventions, getQueueName, validateNoConflictingDecorators, AuthMeta, RouteMetadata, METADATA_KEYS, } from './http/decorators';
19
+ export type { AuthMode, ApiKind, JwtRequirement, EndpointOptions } from './http/decorators';
20
20
  export { Secrets } from './http/Secrets';
21
21
  export { ValidateImplementation } from './http/validators';
22
22
  export { ProtocolError, HttpError, HttpNotFoundError, EndpointNotFoundError, HttpBadRequestError, HttpUnauthorizedError, HttpForbiddenError, HttpTimeoutError, HttpBadGatewayError, HttpGatewayTimeoutError, HttpInternalServerError, HttpVendorError, HttpUserError, ENTITY_NOT_FOUND, WRONG_LOGIN_TYPE, WRONG_LOGIN, NOT_APPROVED, EMAIL_NOT_CONFIRMED, WRONG_DOMAIN, WRONG_COMPANY, NO_REG_CODE, } from './http/errors';
package/src/index.js CHANGED
@@ -8,8 +8,8 @@
8
8
  * @packageDocumentation
9
9
  */
10
10
  Object.defineProperty(exports, "__esModule", { value: true });
11
- exports.ENTITY_NOT_FOUND = exports.HttpUserError = exports.HttpVendorError = 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.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.getEndpoints = exports.getApiPath = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.Auth = exports.AuthJwt = exports.Public = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
12
- exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = exports.ApiCallInfo = exports.LogApiCallImpl = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ErrorWireForm = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = exports.NO_REG_CODE = exports.WRONG_COMPANY = exports.WRONG_DOMAIN = exports.EMAIL_NOT_CONFIRMED = exports.NOT_APPROVED = exports.WRONG_LOGIN = exports.WRONG_LOGIN_TYPE = void 0;
11
+ exports.HttpVendorError = 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.METADATA_KEYS = exports.RouteMetadata = exports.AuthMeta = exports.validateNoConflictingDecorators = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryEndpointHasAuthMode = exports.getAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isFormPost = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.Queue = exports.PubSub = exports.Rpc = exports.AuthSharedSecret = exports.AuthOidc = exports.Auth = exports.AuthJwt = exports.Public = exports.AuthenticationConfig = exports.Authentication = exports.Endpoint = exports.ApiPath = exports.LogManager = exports.ConsoleLoggerFactory = exports.ConsoleLogger = exports.DESIGN_METADATA_KEYS = exports.isDocumentDesign = exports.DocumentDesign = exports.ContextTuple = exports.ContextKey = exports.toError = void 0;
12
+ exports.SerializedError = exports.SerializedMap = exports.RecordSerializer = exports.getDoNotRecordFields = exports.DoNotRecord = exports.RecordedTestCase = exports.RecordedError = exports.RecordedEndpoint = exports.RecorderKeys = exports.ApiCallContextHolder = exports.ApiCallInfo = exports.LogApiCallImpl = exports.LogApiCall = exports.ContextMgr = exports.WebpiecesCoreHeaders = exports.templateDeriver = exports.ErrorWireForm = exports.ClientRegistry = exports.HeaderRegistry = exports.DateTimeUtil = exports.TimeUtil = exports.DateUtil = exports.InstantUtil = 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.HttpUserError = void 0;
13
13
  var errorUtils_1 = require("./lib/errorUtils");
14
14
  Object.defineProperty(exports, "toError", { enumerable: true, get: function () { return errorUtils_1.toError; } });
15
15
  var ContextKey_1 = require("./ContextKey");
@@ -52,6 +52,8 @@ Object.defineProperty(exports, "PubSub", { enumerable: true, get: function () {
52
52
  Object.defineProperty(exports, "Queue", { enumerable: true, get: function () { return decorators_1.Queue; } });
53
53
  Object.defineProperty(exports, "getApiPath", { enumerable: true, get: function () { return decorators_1.getApiPath; } });
54
54
  Object.defineProperty(exports, "getEndpoints", { enumerable: true, get: function () { return decorators_1.getEndpoints; } });
55
+ Object.defineProperty(exports, "getEndpointOptions", { enumerable: true, get: function () { return decorators_1.getEndpointOptions; } });
56
+ Object.defineProperty(exports, "isFormPost", { enumerable: true, get: function () { return decorators_1.isFormPost; } });
55
57
  Object.defineProperty(exports, "isApiPath", { enumerable: true, get: function () { return decorators_1.isApiPath; } });
56
58
  Object.defineProperty(exports, "getAuthMeta", { enumerable: true, get: function () { return decorators_1.getAuthMeta; } });
57
59
  Object.defineProperty(exports, "getAuthMode", { enumerable: true, get: function () { return decorators_1.getAuthMode; } });
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 { 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';\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 getApiPath,\n getEndpoints,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\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, JwtRequirement } from './http/decorators';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { 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 HttpVendorError,\n HttpUserError,\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\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// 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';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport type { ContextRead, StructuredContextRead } 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// 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 { ApiSide, ApiType, ApiResult } from './http/ApiCallInfo';\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":";AAAA;;;;;;;GAOG;;;;AAEH,+CAA2C;AAAlC,qGAAA,OAAO,OAAA;AAChB,2CAA0C;AAAjC,wGAAA,UAAU,OAAA;AACnB,+CAA8C;AAArC,4GAAA,YAAY,OAAA;AAErB,+EAA+E;AAC/E,kFAAkF;AAClF,yCAAyC;AACzC,mDAA0F;AAAjF,gHAAA,cAAc,OAAA;AAAE,kHAAA,gBAAgB,OAAA;AAAE,sHAAA,oBAAoB,OAAA;AAO/D,yDAAwD;AAA/C,8GAAA,aAAa,OAAA;AACtB,uEAAsE;AAA7D,4HAAA,oBAAoB,OAAA;AAC7B,mDAAkD;AAAzC,wGAAA,UAAU,OAAA;AAEnB,8DAA8D;AAC9D,sEAAsE;AACtE,sEAAsE;AACtE,uEAAuE;AACvE,kEAAkE;AAElE,4BAA4B;AAC5B,gDA+B2B;AA9BvB,qGAAA,OAAO,OAAA;AACP,sGAAA,QAAQ,OAAA;AACR,4GAAA,cAAc,OAAA;AACd,kHAAA,oBAAoB,OAAA;AACpB,mEAAmE;AACnE,oGAAA,MAAM,OAAA;AACN,qGAAA,OAAO,OAAA;AACP,kGAAA,IAAI,OAAA;AACJ,sGAAA,QAAQ,OAAA;AACR,8GAAA,gBAAgB,OAAA;AAChB,sDAAsD;AACtD,iGAAA,GAAG,OAAA;AACH,oGAAA,MAAM,OAAA;AACN,mGAAA,KAAK,OAAA;AACL,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,gHAAA,kBAAkB,OAAA;AAClB,wGAAA,UAAU,OAAA;AACV,uGAAA,SAAS,OAAA;AACT,yGAAA,WAAW,OAAA;AACX,yGAAA,WAAW,OAAA;AACX,4HAAA,8BAA8B,OAAA;AAC9B,wGAAA,UAAU,OAAA;AACV,2GAAA,aAAa,OAAA;AACb,qHAAA,uBAAuB,OAAA;AACvB,0GAAA,YAAY,OAAA;AACZ,6HAAA,+BAA+B,OAAA;AAC/B,sGAAA,QAAQ,OAAA;AACR,2GAAA,aAAa,OAAA;AACb,2GAAA,aAAa,OAAA;AAGjB,4FAA4F;AAC5F,0CAAyC;AAAhC,kGAAA,OAAO,OAAA;AAKhB,cAAc;AACd,wCAuBuB;AAtBnB,uGAAA,aAAa,OAAA;AACb,mGAAA,SAAS,OAAA;AACT,2GAAA,iBAAiB,OAAA;AACjB,+GAAA,qBAAqB,OAAA;AACrB,6GAAA,mBAAmB,OAAA;AACnB,+GAAA,qBAAqB,OAAA;AACrB,4GAAA,kBAAkB,OAAA;AAClB,0GAAA,gBAAgB,OAAA;AAChB,6GAAA,mBAAmB,OAAA;AACnB,iHAAA,uBAAuB,OAAA;AACvB,iHAAA,uBAAuB,OAAA;AACvB,yGAAA,eAAe,OAAA;AACf,uGAAA,aAAa,OAAA;AACb,0BAA0B;AAC1B,0GAAA,gBAAgB,OAAA;AAChB,0GAAA,gBAAgB,OAAA;AAChB,qGAAA,WAAW,OAAA;AACX,sGAAA,YAAY,OAAA;AACZ,6GAAA,mBAAmB,OAAA;AACnB,sGAAA,YAAY,OAAA;AACZ,uGAAA,aAAa,OAAA;AACb,qGAAA,WAAW,OAAA;AAGf,iEAAiE;AACjE,4CASyB;AAJrB,uGAAA,WAAW,OAAA;AACX,oGAAA,QAAQ,OAAA;AACR,oGAAA,QAAQ,OAAA;AACR,wGAAA,YAAY,OAAA;AAGhB,mEAAmE;AACnE,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;AACvB,wDAAuD;AAA9C,gHAAA,cAAc,OAAA;AAEvB,0FAA0F;AAC1F,4FAA4F;AAC5F,4DAAwD;AAA/C,iHAAA,aAAa,OAAA;AAEtB,0DAAyD;AAAhD,kHAAA,eAAe,OAAA;AACxB,oEAAmE;AAA1D,4HAAA,oBAAoB,OAAA;AAI7B,iGAAiG;AACjG,gGAAgG;AAChG,kEAAkE;AAClE,gDAA+C;AAAtC,wGAAA,UAAU,OAAA;AAEnB,sGAAsG;AACtG,gDAA+D;AAAtD,wGAAA,UAAU,OAAA;AAAE,4GAAA,cAAc,OAAA;AAEnC,yFAAyF;AACzF,kGAAkG;AAClG,kDAAiD;AAAxC,0GAAA,WAAW,OAAA;AAEpB,wDAA6D;AAApD,sHAAA,oBAAoB,OAAA;AAG7B,iFAAiF;AACjF,qEAAkF;AAAvD,gHAAA,YAAY,OAAA;AACvC,qEAAqG;AAA5F,oHAAA,gBAAgB,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,oHAAA,gBAAgB,OAAA;AAC1D,2DAAgF;AAAvE,0GAAA,WAAW,OAAA;AAAE,mHAAA,oBAAoB,OAAA;AAC1C,qEAAoG;AAA3F,oHAAA,gBAAgB,OAAA;AAAE,iHAAA,aAAa,OAAA;AAAE,mHAAA,eAAe,OAAA","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 { 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';\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 getApiPath,\n getEndpoints,\n getEndpointOptions,\n isFormPost,\n isApiPath,\n getAuthMeta,\n getAuthMode,\n assertEveryEndpointHasAuthMode,\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, JwtRequirement, EndpointOptions } from './http/decorators';\n// Client-side shared-secret store (the value THIS service sends per @AuthSharedSecret key).\nexport { 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 HttpVendorError,\n HttpUserError,\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\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// 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';\nexport { templateDeriver } from './http/templateDeriver';\nexport { WebpiecesCoreHeaders } from './http/WebpiecesCoreHeaders';\nexport { ContextReader } from './http/ContextReader';\nexport type { ContextRead, StructuredContextRead } 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// 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 { ApiSide, ApiType, ApiResult } from './http/ApiCallInfo';\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"]}