@webpieces/core-util 0.4.957 → 0.4.958

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.
Files changed (54) hide show
  1. package/package.json +1 -1
  2. package/src/http/ApiCallInfo.d.ts +71 -11
  3. package/src/http/ApiCallInfo.js +81 -7
  4. package/src/http/ApiCallInfo.js.map +1 -1
  5. package/src/http/ContextMgr.js +12 -2
  6. package/src/http/ContextMgr.js.map +1 -1
  7. package/src/http/DeletedAuthCompileAssertions.js +3 -1
  8. package/src/http/DeletedAuthCompileAssertions.js.map +1 -1
  9. package/src/http/DestinationTrust.d.ts +33 -4
  10. package/src/http/DestinationTrust.js +54 -11
  11. package/src/http/DestinationTrust.js.map +1 -1
  12. package/src/http/EndpointCompileAssertions.js +12 -6
  13. package/src/http/EndpointCompileAssertions.js.map +1 -1
  14. package/src/http/ExternalContextKeys.d.ts +31 -0
  15. package/src/http/ExternalContextKeys.js +77 -0
  16. package/src/http/ExternalContextKeys.js.map +1 -0
  17. package/src/http/HttpContract.d.ts +45 -7
  18. package/src/http/HttpContract.js +84 -6
  19. package/src/http/HttpContract.js.map +1 -1
  20. package/src/http/HttpEndpointOptions.d.ts +22 -3
  21. package/src/http/HttpEndpointOptions.js +8 -1
  22. package/src/http/HttpEndpointOptions.js.map +1 -1
  23. package/src/http/LogApiCall.d.ts +4 -1
  24. package/src/http/LogApiCall.js +7 -5
  25. package/src/http/LogApiCall.js.map +1 -1
  26. package/src/http/QueryParams.d.ts +47 -0
  27. package/src/http/QueryParams.js +89 -0
  28. package/src/http/QueryParams.js.map +1 -0
  29. package/src/http/RouteMetadata.d.ts +16 -2
  30. package/src/http/RouteMetadata.js +19 -3
  31. package/src/http/RouteMetadata.js.map +1 -1
  32. package/src/http/RouteMetadataFactory.d.ts +25 -0
  33. package/src/http/RouteMetadataFactory.js +97 -12
  34. package/src/http/RouteMetadataFactory.js.map +1 -1
  35. package/src/http/WpAuthApiKeyCompileAssertions.js +2 -2
  36. package/src/http/WpAuthApiKeyCompileAssertions.js.map +1 -1
  37. package/src/http/api-kind.d.ts +3 -2
  38. package/src/http/api-kind.js +5 -4
  39. package/src/http/api-kind.js.map +1 -1
  40. package/src/http/auth-mode.d.ts +8 -2
  41. package/src/http/auth-mode.js +11 -5
  42. package/src/http/auth-mode.js.map +1 -1
  43. package/src/http/decorators.d.ts +39 -21
  44. package/src/http/decorators.js +69 -37
  45. package/src/http/decorators.js.map +1 -1
  46. package/src/http/external-caller.d.ts +12 -17
  47. package/src/http/external-caller.js +12 -17
  48. package/src/http/external-caller.js.map +1 -1
  49. package/src/http/http-parameter-decorators.d.ts +17 -3
  50. package/src/http/http-parameter-decorators.js +27 -6
  51. package/src/http/http-parameter-decorators.js.map +1 -1
  52. package/src/index.d.ts +11 -7
  53. package/src/index.js +30 -12
  54. package/src/index.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"auth-mode.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/auth-mode.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyContextKey } from '../ContextKey';\n\n/**\n * WHERE one api-key credential rides.\n *\n * A header credential names the {@link AnyContextKey ContextKey} that carries it — NEVER a header-name\n * string. The key's `httpHeader` is the wire name, so the ONE definition of each header (the\n * ContextKey) is reused on every surface: `RequestContext` put/get, the client/server header\n * transfer, this declaration, and the published OpenAPI document. A string here was a second copy a\n * rename left stale (#1249). The key MUST declare an `httpHeader` — {@link AuthDeclaration} refuses\n * one that does not, and the OpenAPI generator reads the header name off the key's\n * `ContextKey.trusted(...)` / `ContextKey.untrusted(...)` definition.\n */\nexport type ApiKeyCredential =\n | { in: 'header'; key: AnyContextKey; description?: string }\n | { in: 'bearer'; key?: never; description?: string };\n\nexport type ApiKeyCredentials = readonly [ApiKeyCredential, ...ApiKeyCredential[]];\n\n/**\n * The service accounts an `oidcSvcs([...])` endpoint names, at least one: an empty list is not a\n * policy anybody could mean, so it does not compile. Each entry resolves as `'self'` (this service's\n * runtime SA), a bare id (`<id>@<project>.iam.gserviceaccount.com`) or a full email (verbatim).\n */\nexport type OidcCallers = readonly [string, ...string[]];\n\n/**\n * WHICH Google-signed callers an OIDC endpoint accepts, named by `callerScope`:\n * - `'same-project'` (`oidcSameProject()`): any service account of THIS GCP project. Off GCP there is\n * no project, so the check is open.\n * - `'services'` (`oidcSvcs([...])`): exactly the listed service accounts, which may belong to other\n * projects.\n * One endpoint may declare one of each; see {@link OidcAuthMethods}.\n */\nexport type OidcAuthMethod =\n | { kind: 'oidc'; callerScope: 'same-project' }\n | { kind: 'oidc'; callerScope: 'services'; callers: OidcCallers };\n\n/**\n * The OIDC methods ONE endpoint declares, in declaration order: one, or one per caller scope. They are\n * ONE bearer credential, so a verifier receives them together, checks the token's signature once, and\n * admits the call when ANY of them admits the caller.\n */\nexport type OidcAuthMethods = readonly [OidcAuthMethod, ...OidcAuthMethod[]];\n\n/** One concrete mechanism; user policy belongs exclusively to WpAuthorization. */\nexport type AuthMethod =\n | { kind: 'jwt' }\n | OidcAuthMethod\n | { kind: 'shared-secret'; secretKey: string }\n | { kind: 'webhook'; name: string }\n | { kind: 'apikey'; regime: string; credentials: ApiKeyCredentials };\n\nexport type AuthMethods = readonly [AuthMethod, ...AuthMethod[]];\nexport type AuthMode = AuthMethod | { kind: 'public' };\n\n/** Alternatives remain unresolved until a concrete mechanism is chosen for this hop. */\nexport class AuthMeta {\n readonly methods: readonly [AuthMode, ...AuthMode[]];\n\n constructor(\n methods: readonly [AuthMode, ...AuthMode[]],\n public readonly publicReason: string | undefined,\n ) {\n this.methods = methods;\n AuthDeclaration.validate(this);\n this.methods = Object.freeze(methods.map((method: AuthMode) => AuthMeta.snapshot(method))) as readonly [AuthMode, ...AuthMode[]];\n Object.freeze(this);\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static snapshot(method: AuthMode): AuthMode {\n switch (method.kind) {\n case 'oidc': return method.callerScope === 'services'\n ? Object.freeze({ ...method, callers: Object.freeze([...method.callers]) as OidcCallers })\n : Object.freeze({ ...method });\n case 'apikey': return Object.freeze({ ...method, credentials: Object.freeze(method.credentials.map((credential: ApiKeyCredential) => Object.freeze({ ...credential }))) as ApiKeyCredentials });\n default: return Object.freeze({ ...method });\n }\n }\n}\n\nexport class AuthDeclaration {\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n static validate(meta: AuthMeta): void {\n if (!Array.isArray(meta.methods) || meta.methods.length === 0) throw new Error('@WpAuth requires a nonempty methods tuple.');\n const kinds = new Set<string>();\n for (const method of meta.methods) {\n if (!method || typeof method !== 'object' || kinds.has(this.uniquenessKey(method))) throw new Error('@WpAuth rejects duplicate or invalid credential methods.');\n kinds.add(this.uniquenessKey(method));\n this.validateMethod(method);\n }\n if (kinds.has('public')) {\n if (meta.methods.length !== 1 || typeof meta.publicReason !== 'string' || !meta.publicReason.trim()) {\n throw new Error('Public auth must be declared separately with @WpAuthPublic(reason).');\n }\n } else if (meta.publicReason !== undefined) throw new Error('Protected @WpAuth rejects public reasons.');\n }\n\n /**\n * One method per credential kind, except OIDC: one per `callerScope`, so `oidcSameProject()` and\n * `oidcSvcs([...])` may pair on one endpoint (this project's accounts OR the listed ones). They are\n * still ONE bearer credential, which AuthFilter verifies once for both.\n */\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static uniquenessKey(method: AuthMode): string {\n return method.kind === 'oidc' ? `oidc:${method.callerScope}` : method.kind;\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static validateMethod(method: AuthMode): void {\n let keys: readonly string[];\n switch (method.kind) {\n case 'jwt':\n case 'public': keys = ['kind']; break;\n case 'oidc': keys = this.oidcKeys(method); break;\n case 'shared-secret':\n keys = ['kind', 'secretKey'];\n this.requireText(method.secretKey, 'sharedSecret'); break;\n case 'webhook':\n keys = ['kind', 'name'];\n this.requireText(method.name, 'webhook'); break;\n case 'apikey':\n keys = ['kind', 'regime', 'credentials'];\n this.requireText(method.regime, 'apiKey');\n if (!Array.isArray(method.credentials) || !method.credentials.length) throw new Error('apiKey(...) requires a nonempty credentials tuple.');\n for (const credential of method.credentials) {\n if (credential.in === 'header') this.requireHeaderKey(credential.key);\n else if (credential.in !== 'bearer' || Object.hasOwn(credential, 'key')) throw new Error('Invalid API-key credential location.');\n }\n break;\n default: throw new Error('Unknown @WpAuth method; use jwt/oidcSameProject/oidcSvcs/sharedSecret/webhook/apiKey descriptors.');\n }\n for (const key of Object.keys(method)) {\n if (!keys.includes(key)) throw new Error(`Auth method rejects '${key}'; operation policy belongs in @WpAuthorization.`);\n }\n }\n\n /**\n * The keys an OIDC descriptor may carry, after checking its scope. `OidcCallers` already makes an\n * empty `oidcSvcs([])` a compile error; this is the backstop for a list that reached the decorator\n * around the type (a cast, a spread of a runtime array, plain JS), so it fails when the decorator\n * is evaluated instead of reaching a verifier.\n */\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static oidcKeys(method: OidcAuthMethod): readonly string[] {\n if (method.callerScope === 'same-project') return ['kind', 'callerScope'];\n if (method.callerScope !== 'services') throw new Error(\"Unknown OIDC callerScope; use oidcSameProject() or oidcSvcs(['svc-a']).\");\n if (!Array.isArray(method.callers) || method.callers.length === 0 || !method.callers.every((caller: string) => typeof caller === 'string' && caller.trim().length > 0)) {\n throw new Error(\"oidcSvcs([...]) requires at least one nonblank service account; use oidcSameProject() to accept this GCP project's service accounts.\");\n }\n return ['kind', 'callerScope', 'callers'];\n }\n\n /**\n * A header credential's key must be a ContextKey that rides the wire. `httpHeader` is optional on\n * ContextKey itself (most keys never leave the process), and the factories' explicit value-type\n * argument (`ContextKey.untrusted<string>(...)`) stops TypeScript inferring whether one was\n * passed, so the type cannot say \"a key WITH a header\". It is checked once, when the contract is\n * decorated, and the OpenAPI generator refuses the same key statically.\n */\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static requireHeaderKey(key: AnyContextKey | undefined): void {\n if (!key || typeof key !== 'object' || typeof key.name !== 'string') {\n throw new Error(\"apiKey header credential requires `key`: the ContextKey carrying it, e.g. { in: 'header', key: MyKeys.API_KEY }.\");\n }\n if (typeof key.httpHeader !== 'string' || !key.httpHeader.trim()) {\n throw new Error(`apiKey header credential key '${key.name}' declares no httpHeader; define it with ContextKey.trusted(name, provenance, '<header>') or ContextKey.untrusted(name, '<header>').`);\n }\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static requireText(value: string, label: string): void {\n if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} requires a nonempty name.`);\n }\n}\n\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function jwt(): AuthMethod { return { kind: 'jwt' }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function oidcSameProject(): OidcAuthMethod { return { kind: 'oidc', callerScope: 'same-project' }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function oidcSvcs(callers: OidcCallers): OidcAuthMethod { return { kind: 'oidc', callerScope: 'services', callers }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function sharedSecret(secretKey: string): AuthMethod { return { kind: 'shared-secret', secretKey }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function webhook(name: string): AuthMethod { return { kind: 'webhook', name }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function apiKey(regime: string, credentials: ApiKeyCredentials): AuthMethod { return { kind: 'apikey', regime, credentials }; }\n"]}
1
+ {"version":3,"file":"auth-mode.js","sourceRoot":"","sources":["../../../../../../packages/core/core-util/src/http/auth-mode.ts"],"names":[],"mappings":"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","sourcesContent":["import type { AnyContextKey } from '../ContextKey';\n\n/**\n * WHERE one api-key credential rides.\n *\n * A header credential names the {@link AnyContextKey ContextKey} that carries it — NEVER a header-name\n * string. The key's `httpHeader` is the wire name, so the ONE definition of each header (the\n * ContextKey) is reused on every surface: `RequestContext` put/get, the client/server header\n * transfer, this declaration, and the published OpenAPI document. A string here was a second copy a\n * rename left stale (#1249). The key MUST declare an `httpHeader` — {@link AuthDeclaration} refuses\n * one that does not, and the OpenAPI generator reads the header name off the key's\n * `ContextKey.trusted(...)` / `ContextKey.untrusted(...)` definition.\n */\nexport type ApiKeyCredential =\n | { in: 'header'; key: AnyContextKey; description?: string }\n | { in: 'bearer'; key?: never; description?: string };\n\nexport type ApiKeyCredentials = readonly [ApiKeyCredential, ...ApiKeyCredential[]];\n\n/**\n * The service accounts an `oidcSvcs([...])` endpoint names, at least one: an empty list is not a\n * policy anybody could mean, so it does not compile. Each entry resolves as `'self'` (this service's\n * runtime SA), a bare id (`<id>@<project>.iam.gserviceaccount.com`) or a full email (verbatim).\n */\nexport type OidcCallers = readonly [string, ...string[]];\n\n/**\n * WHICH Google-signed callers an OIDC endpoint accepts, named by `callerScope`:\n * - `'same-project'` (`oidcSameProject()`): any service account of THIS GCP project. Off GCP there is\n * no project, so the check is open.\n * - `'services'` (`oidcSvcs([...])`): exactly the listed service accounts, which may belong to other\n * projects.\n * One endpoint may declare one of each; see {@link OidcAuthMethods}.\n */\nexport type OidcAuthMethod =\n | { kind: 'oidc'; callerScope: 'same-project' }\n | { kind: 'oidc'; callerScope: 'services'; callers: OidcCallers };\n\n/**\n * The OIDC methods ONE endpoint declares, in declaration order: one, or one per caller scope. They are\n * ONE bearer credential, so a verifier receives them together, checks the token's signature once, and\n * admits the call when ANY of them admits the caller.\n */\nexport type OidcAuthMethods = readonly [OidcAuthMethod, ...OidcAuthMethod[]];\n\n/** One concrete mechanism; user policy belongs exclusively to WpAuthorization. */\nexport type AuthMethod =\n | { kind: 'jwt' }\n | OidcAuthMethod\n | { kind: 'shared-secret'; secretKey: string }\n | { kind: 'vendorAuth'; name: string }\n | { kind: 'apikey'; regime: string; credentials: ApiKeyCredentials };\n\nexport type AuthMethods = readonly [AuthMethod, ...AuthMethod[]];\nexport type AuthMode = AuthMethod | { kind: 'public' };\n\n/** Alternatives remain unresolved until a concrete mechanism is chosen for this hop. */\nexport class AuthMeta {\n readonly methods: readonly [AuthMode, ...AuthMode[]];\n\n constructor(\n methods: readonly [AuthMode, ...AuthMode[]],\n public readonly publicReason: string | undefined,\n ) {\n this.methods = methods;\n AuthDeclaration.validate(this);\n this.methods = Object.freeze(methods.map((method: AuthMode) => AuthMeta.snapshot(method))) as readonly [AuthMode, ...AuthMode[]];\n Object.freeze(this);\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static snapshot(method: AuthMode): AuthMode {\n switch (method.kind) {\n case 'oidc': return method.callerScope === 'services'\n ? Object.freeze({ ...method, callers: Object.freeze([...method.callers]) as OidcCallers })\n : Object.freeze({ ...method });\n case 'apikey': return Object.freeze({ ...method, credentials: Object.freeze(method.credentials.map((credential: ApiKeyCredential) => Object.freeze({ ...credential }))) as ApiKeyCredentials });\n default: return Object.freeze({ ...method });\n }\n }\n}\n\nexport class AuthDeclaration {\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n static validate(meta: AuthMeta): void {\n if (!Array.isArray(meta.methods) || meta.methods.length === 0) throw new Error('@WpAuth requires a nonempty methods tuple.');\n const kinds = new Set<string>();\n for (const method of meta.methods) {\n if (!method || typeof method !== 'object' || kinds.has(this.uniquenessKey(method))) throw new Error('@WpAuth rejects duplicate or invalid credential methods.');\n kinds.add(this.uniquenessKey(method));\n this.validateMethod(method);\n }\n if (kinds.has('public')) {\n if (meta.methods.length !== 1 || typeof meta.publicReason !== 'string' || !meta.publicReason.trim()) {\n throw new Error('Public auth must be declared separately with @WpAuthPublic(reason).');\n }\n } else if (meta.publicReason !== undefined) throw new Error('Protected @WpAuth rejects public reasons.');\n }\n\n /**\n * One method per credential kind, except OIDC: one per `callerScope`, so `oidcSameProject()` and\n * `oidcSvcs([...])` may pair on one endpoint (this project's accounts OR the listed ones). They are\n * still ONE bearer credential, which AuthFilter verifies once for both.\n */\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static uniquenessKey(method: AuthMode): string {\n return method.kind === 'oidc' ? `oidc:${method.callerScope}` : method.kind;\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static validateMethod(method: AuthMode): void {\n let keys: readonly string[];\n switch (method.kind) {\n case 'jwt':\n case 'public': keys = ['kind']; break;\n case 'oidc': keys = this.oidcKeys(method); break;\n case 'shared-secret':\n keys = ['kind', 'secretKey'];\n this.requireText(method.secretKey, 'sharedSecret'); break;\n case 'vendorAuth':\n keys = ['kind', 'name'];\n this.requireText(method.name, 'vendorAuth'); break;\n case 'apikey':\n keys = ['kind', 'regime', 'credentials'];\n this.requireText(method.regime, 'apiKey');\n if (!Array.isArray(method.credentials) || !method.credentials.length) throw new Error('apiKey(...) requires a nonempty credentials tuple.');\n for (const credential of method.credentials) {\n if (credential.in === 'header') this.requireHeaderKey(credential.key);\n else if (credential.in !== 'bearer' || Object.hasOwn(credential, 'key')) throw new Error('Invalid API-key credential location.');\n }\n break;\n default: throw new Error('Unknown @WpAuth method; use jwt/oidcSameProject/oidcSvcs/sharedSecret/vendorAuth/apiKey descriptors.');\n }\n for (const key of Object.keys(method)) {\n if (!keys.includes(key)) throw new Error(`Auth method rejects '${key}'; operation policy belongs in @WpAuthorization.`);\n }\n }\n\n /**\n * The keys an OIDC descriptor may carry, after checking its scope. `OidcCallers` already makes an\n * empty `oidcSvcs([])` a compile error; this is the backstop for a list that reached the decorator\n * around the type (a cast, a spread of a runtime array, plain JS), so it fails when the decorator\n * is evaluated instead of reaching a verifier.\n */\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static oidcKeys(method: OidcAuthMethod): readonly string[] {\n if (method.callerScope === 'same-project') return ['kind', 'callerScope'];\n if (method.callerScope !== 'services') throw new Error(\"Unknown OIDC callerScope; use oidcSameProject() or oidcSvcs(['svc-a']).\");\n if (!Array.isArray(method.callers) || method.callers.length === 0 || !method.callers.every((caller: string) => typeof caller === 'string' && caller.trim().length > 0)) {\n throw new Error(\"oidcSvcs([...]) requires at least one nonblank service account; use oidcSameProject() to accept this GCP project's service accounts.\");\n }\n return ['kind', 'callerScope', 'callers'];\n }\n\n /**\n * A header credential's key must be a ContextKey that rides the wire. `httpHeader` is optional on\n * ContextKey itself (most keys never leave the process), and the factories' explicit value-type\n * argument (`ContextKey.untrusted<string>(...)`) stops TypeScript inferring whether one was\n * passed, so the type cannot say \"a key WITH a header\". It is checked once, when the contract is\n * decorated, and the OpenAPI generator refuses the same key statically.\n */\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static requireHeaderKey(key: AnyContextKey | undefined): void {\n if (!key || typeof key !== 'object' || typeof key.name !== 'string') {\n throw new Error(\"apiKey header credential requires `key`: the ContextKey carrying it, e.g. { in: 'header', key: MyKeys.API_KEY }.\");\n }\n if (typeof key.httpHeader !== 'string' || !key.httpHeader.trim()) {\n throw new Error(`apiKey header credential key '${key.name}' declares no httpHeader; define it with ContextKey.trusted(name, provenance, '<header>') or ContextKey.untrusted(name, '<header>').`);\n }\n }\n\n // webpieces-disable no-function-outside-class -- stateless contract validation or scope metadata reader used before DI registration\n private static requireText(value: string, label: string): void {\n if (typeof value !== 'string' || !value.trim()) throw new Error(`${label} requires a nonempty name.`);\n }\n}\n\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function jwt(): AuthMethod { return { kind: 'jwt' }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function oidcSameProject(): OidcAuthMethod { return { kind: 'oidc', callerScope: 'same-project' }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function oidcSvcs(callers: OidcCallers): OidcAuthMethod { return { kind: 'oidc', callerScope: 'services', callers }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function sharedSecret(secretKey: string): AuthMethod { return { kind: 'shared-secret', secretKey }; }\n/**\n * A credential in a VENDOR's scheme, on both ends of a hop: the app-bound `VendorAuthHook` (core-context)\n * mints it when WE call the vendor (and refreshes it once on a 401), and verifies it when the vendor\n * calls US. `name` selects the vendor. A served `vendorAuth(...)` endpoint requires\n * `@Endpoint(..., { rawBody: true })` so the hook has the exact bytes to verify.\n */\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function vendorAuth(name: string): AuthMethod { return { kind: 'vendorAuth', name }; }\n// webpieces-disable no-function-outside-class -- canonical credential descriptor factory\nexport function apiKey(regime: string, credentials: ApiKeyCredentials): AuthMethod { return { kind: 'apikey', regime, credentials }; }\n"]}
@@ -5,10 +5,11 @@ import { EndpointOperation, EndpointOptions, ExternalEndpointOptions } from './H
5
5
  import { HttpMethod } from './HttpContract';
6
6
  export type { EndpointOptions, ExternalEndpointOptions } from './HttpEndpointOptions';
7
7
  export type { EndpointOperation } from './HttpEndpointOptions';
8
- export { READ, WRITE_IDEMPOTENT, WRITE } from './HttpEndpointOptions';
8
+ export { READ, WRITE_IDEMPOTENT, WRITE, NONE } from './HttpEndpointOptions';
9
+ export type { ResponseBodyKind } from './HttpEndpointOptions';
9
10
  export type { HttpMethod } from './HttpContract';
10
- export { GET, POST } from './HttpContract';
11
- export { PathParam, QueryParam, getHttpParameterDeclarations } from './http-parameter-decorators';
11
+ export { GET, POST, PUT, PATCH, DELETE } from './HttpContract';
12
+ export { PathParam, QueryParam, QueryString, getHttpParameterDeclarations } from './http-parameter-decorators';
12
13
  /**
13
14
  * Metadata keys for storing API routing information.
14
15
  * These keys are used by both server-side (routing) and client-side (client generation).
@@ -29,9 +30,9 @@ export declare const METADATA_KEYS: {
29
30
  ENDPOINT_HTTP_METHOD: string;
30
31
  /** Per-method required read/write semantics, parallel to ENDPOINTS. */
31
32
  ENDPOINT_OPERATION: string;
32
- /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external), parallel to ENDPOINTS. */
33
+ /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external-inbound | external-outbound), parallel to ENDPOINTS. */
33
34
  ENDPOINT_KIND: string;
34
- /** Per-method declared external CALLER (only for kind 'external'), parallel to ENDPOINTS. */
35
+ /** Per-method declared external CALLER (only for kind 'external-inbound'), parallel to ENDPOINTS. */
35
36
  ENDPOINT_CALLER: string;
36
37
  /** Per-method @MaskLog spec (which DTO fields the LogApiCall path masks). */
37
38
  MASK_LOG: string;
@@ -51,24 +52,35 @@ declare enum EndpointKindValue {
51
52
  RPC = "rpc",
52
53
  CLOUDTASKS = "cloudtasks",
53
54
  CRON = "cron",
54
- EXTERNAL = "external"
55
+ EXTERNAL_INBOUND = "external-inbound",
56
+ EXTERNAL_OUTBOUND = "external-outbound"
55
57
  }
56
58
  /** Short, statically importable decorator arguments. */
57
59
  export declare const RPC = EndpointKindValue.RPC;
58
60
  export declare const CLOUDTASKS = EndpointKindValue.CLOUDTASKS;
59
61
  export declare const CRON = EndpointKindValue.CRON;
60
- export declare const EXTERNAL = EndpointKindValue.EXTERNAL;
62
+ /**
63
+ * An OUTSIDE system calls US (a vendor webhook, a push subscription). Requires `calledBy`, which names
64
+ * that system on the runtime architecture graph.
65
+ */
66
+ export declare const EXTERNAL_INBOUND = EndpointKindValue.EXTERNAL_INBOUND;
67
+ /**
68
+ * WE call an OUTSIDE system (a vendor API, a partner's webhook receiver). No server in this repo
69
+ * implements it, every method of the contract must be this kind, and the client sends ONLY the
70
+ * context keys listed with `@ExternalContextKeys(...)` and merges no response header into our context.
71
+ */
72
+ export declare const EXTERNAL_OUTBOUND = EndpointKindValue.EXTERNAL_OUTBOUND;
61
73
  /** Runtime trigger/delivery owner used by validation, architecture, and infrastructure tooling. */
62
- export type EndpointKind = typeof RPC | typeof CLOUDTASKS | typeof CRON | typeof EXTERNAL;
74
+ export type EndpointKind = typeof RPC | typeof CLOUDTASKS | typeof CRON | typeof EXTERNAL_INBOUND | typeof EXTERNAL_OUTBOUND;
63
75
  /** Class decorator that sets the shared base path for all endpoints. */
64
76
  export declare function ApiPath(basePath: string): ClassDecorator;
65
77
  /**
66
78
  * Registers `@Endpoint(POST, path, WRITE, RPC)`. Method, operation, and trigger are required enum
67
- * values. Operation is independent of the HTTP verb. EXTERNAL additionally requires `calledBy`.
79
+ * values. Operation is independent of the HTTP verb. EXTERNAL_INBOUND additionally requires `calledBy`.
68
80
  * Each classification rides a parallel metadata map while ENDPOINTS remains method-name -> path.
69
81
  */
70
- export declare function Endpoint(httpMethod: HttpMethod, path: string, operation: EndpointOperation, kind: typeof EXTERNAL, options: ExternalEndpointOptions): MethodDecorator;
71
- export declare function Endpoint(httpMethod: HttpMethod, path: string, operation: EndpointOperation, kind: Exclude<EndpointKind, typeof EXTERNAL>, options?: EndpointOptions): MethodDecorator;
82
+ export declare function Endpoint(httpMethod: HttpMethod, path: string, operation: EndpointOperation, kind: typeof EXTERNAL_INBOUND, options: ExternalEndpointOptions): MethodDecorator;
83
+ export declare function Endpoint(httpMethod: HttpMethod, path: string, operation: EndpointOperation, kind: Exclude<EndpointKind, typeof EXTERNAL_INBOUND>, options?: EndpointOptions): MethodDecorator;
72
84
  /** Declares request/response DTO field names that API logging must mask at any depth. */
73
85
  export declare function MaskLog(fields: Record<string, MaskMode>): MethodDecorator;
74
86
  /**
@@ -118,12 +130,18 @@ export declare function getEndpointOptions(apiClass: Function, methodName: strin
118
130
  /** Read one endpoint's required side-effect semantics. */
119
131
  export declare function getEndpointOperation(apiClass: Function, methodName: string): EndpointOperation;
120
132
  /**
121
- * Fail-fast at wiring time when an `external` endpoint declared no caller. The {@link Endpoint}
122
- * overloads already make that a COMPILE error; this is the backstop for the ways TS is bypassed —
123
- * a JS caller, an `as any` options object, a hand-rolled Reflect.defineMetadata.
124
- * @throws Error naming the first external endpoint with no `calledBy`.
133
+ * Fail-fast at wiring time on the two EXTERNAL_* rules, called by `RouteMetadataFactory` for every
134
+ * transport (server routes and every client). The {@link Endpoint} overloads already make a missing
135
+ * `calledBy` a COMPILE error; this is the backstop for the ways TS is bypassed — a JS caller, an
136
+ * `as any` options object, a hand-rolled Reflect.defineMetadata. The second rule cannot be a type:
137
+ * "every method of this class" spans several decorators.
138
+ *
139
+ * @throws Error naming the first EXTERNAL_INBOUND endpoint with no `calledBy`, or the first method of
140
+ * a contract that mixes EXTERNAL_OUTBOUND with any other kind.
125
141
  */
126
- export declare function assertEveryExternalEndpointDeclaresCaller(apiClass: Function): void;
142
+ export declare function assertExternalEndpointKinds(apiClass: Function): void;
143
+ /** True when the contract is an outside system's API: every method is EXTERNAL_OUTBOUND. */
144
+ export declare function isExternalOutboundApi(apiClass: Function): boolean;
127
145
  /**
128
146
  * True when the method's @Endpoint declared `{ formPost: true }` — its body is
129
147
  * application/x-www-form-urlencoded (flat), not JSON.
@@ -131,22 +149,22 @@ export declare function assertEveryExternalEndpointDeclaresCaller(apiClass: Func
131
149
  export declare function isFormPost(apiClass: Function, methodName: string): boolean;
132
150
  /**
133
151
  * True when the method's @Endpoint declared `{ rawBody: true }` — the transport must retain the
134
- * verbatim bytes + absolute url for an {@link WebhookAuthCallback} hook to verify.
152
+ * verbatim bytes + absolute url for a `VendorAuthHook.verify` to check.
135
153
  */
136
154
  export declare function isRawBody(apiClass: Function, methodName: string): boolean;
137
155
  /**
138
- * Fail-fast at wiring time when an `webhook(...)` endpoint did not ask the transport to keep the
156
+ * Fail-fast at wiring time when a SERVED `vendorAuth(...)` endpoint did not ask the transport to keep the
139
157
  * bytes it is supposed to verify. A hook with nothing to verify is a MISCONFIGURATION, and it must
140
158
  * surface at startup, naming the fix — not as a 401 in production on exactly the traffic the endpoint
141
159
  * exists for.
142
160
  *
143
161
  * This pairing is a runtime assert rather than a type because the two halves live on DIFFERENT
144
- * decorators (`webhook(...)` and `@Endpoint`), and no union over one decorator's argument can say
162
+ * decorators (`vendorAuth(...)` and `@Endpoint`), and no union over one decorator's argument can say
145
163
  * anything about the other's.
146
164
  *
147
- * @throws Error naming the first `webhook(...)` endpoint missing `{ rawBody: true }`.
165
+ * @throws Error naming the first `vendorAuth(...)` endpoint missing `{ rawBody: true }`.
148
166
  */
149
- export declare function assertEveryWebhookEndpointRetainsRawBody(apiClass: Function): void;
167
+ export declare function assertEveryVendorAuthEndpointRetainsRawBody(apiClass: Function): void;
150
168
  /**
151
169
  * Check if a class has @ApiPath decorator.
152
170
  */
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.MISSING_AUTH_DECORATOR_FIX = exports.EXTERNAL = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.METADATA_KEYS = exports.getHttpParameterDeclarations = exports.QueryParam = exports.PathParam = exports.POST = exports.GET = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = void 0;
3
+ exports.MISSING_AUTH_DECORATOR_FIX = exports.EXTERNAL_OUTBOUND = exports.EXTERNAL_INBOUND = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.METADATA_KEYS = exports.getHttpParameterDeclarations = exports.QueryString = exports.QueryParam = exports.PathParam = exports.DELETE = exports.PATCH = exports.PUT = exports.POST = exports.GET = exports.NONE = exports.WRITE = exports.WRITE_IDEMPOTENT = exports.READ = void 0;
4
4
  exports.ApiPath = ApiPath;
5
5
  exports.Endpoint = Endpoint;
6
6
  exports.MaskLog = MaskLog;
@@ -16,10 +16,11 @@ exports.getEndpointKind = getEndpointKind;
16
16
  exports.getEndpointHttpMethod = getEndpointHttpMethod;
17
17
  exports.getEndpointOptions = getEndpointOptions;
18
18
  exports.getEndpointOperation = getEndpointOperation;
19
- exports.assertEveryExternalEndpointDeclaresCaller = assertEveryExternalEndpointDeclaresCaller;
19
+ exports.assertExternalEndpointKinds = assertExternalEndpointKinds;
20
+ exports.isExternalOutboundApi = isExternalOutboundApi;
20
21
  exports.isFormPost = isFormPost;
21
22
  exports.isRawBody = isRawBody;
22
- exports.assertEveryWebhookEndpointRetainsRawBody = assertEveryWebhookEndpointRetainsRawBody;
23
+ exports.assertEveryVendorAuthEndpointRetainsRawBody = assertEveryVendorAuthEndpointRetainsRawBody;
23
24
  exports.isApiPath = isApiPath;
24
25
  exports.getAuthMeta = getAuthMeta;
25
26
  exports.assertEveryEndpointHasAuthMode = assertEveryEndpointHasAuthMode;
@@ -37,12 +38,17 @@ var HttpEndpointOptions_2 = require("./HttpEndpointOptions");
37
38
  Object.defineProperty(exports, "READ", { enumerable: true, get: function () { return HttpEndpointOptions_2.READ; } });
38
39
  Object.defineProperty(exports, "WRITE_IDEMPOTENT", { enumerable: true, get: function () { return HttpEndpointOptions_2.WRITE_IDEMPOTENT; } });
39
40
  Object.defineProperty(exports, "WRITE", { enumerable: true, get: function () { return HttpEndpointOptions_2.WRITE; } });
41
+ Object.defineProperty(exports, "NONE", { enumerable: true, get: function () { return HttpEndpointOptions_2.NONE; } });
40
42
  var HttpContract_2 = require("./HttpContract");
41
43
  Object.defineProperty(exports, "GET", { enumerable: true, get: function () { return HttpContract_2.GET; } });
42
44
  Object.defineProperty(exports, "POST", { enumerable: true, get: function () { return HttpContract_2.POST; } });
45
+ Object.defineProperty(exports, "PUT", { enumerable: true, get: function () { return HttpContract_2.PUT; } });
46
+ Object.defineProperty(exports, "PATCH", { enumerable: true, get: function () { return HttpContract_2.PATCH; } });
47
+ Object.defineProperty(exports, "DELETE", { enumerable: true, get: function () { return HttpContract_2.DELETE; } });
43
48
  var http_parameter_decorators_2 = require("./http-parameter-decorators");
44
49
  Object.defineProperty(exports, "PathParam", { enumerable: true, get: function () { return http_parameter_decorators_2.PathParam; } });
45
50
  Object.defineProperty(exports, "QueryParam", { enumerable: true, get: function () { return http_parameter_decorators_2.QueryParam; } });
51
+ Object.defineProperty(exports, "QueryString", { enumerable: true, get: function () { return http_parameter_decorators_2.QueryString; } });
46
52
  Object.defineProperty(exports, "getHttpParameterDeclarations", { enumerable: true, get: function () { return http_parameter_decorators_2.getHttpParameterDeclarations; } });
47
53
  /**
48
54
  * Metadata keys for storing API routing information.
@@ -64,9 +70,9 @@ exports.METADATA_KEYS = {
64
70
  ENDPOINT_HTTP_METHOD: 'webpieces:endpoint-http-method',
65
71
  /** Per-method required read/write semantics, parallel to ENDPOINTS. */
66
72
  ENDPOINT_OPERATION: 'webpieces:endpoint-operation',
67
- /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external), parallel to ENDPOINTS. */
73
+ /** Per-method @Endpoint trigger kind (rpc | cloudtasks | cron | external-inbound | external-outbound), parallel to ENDPOINTS. */
68
74
  ENDPOINT_KIND: 'webpieces:endpoint-kind',
69
- /** Per-method declared external CALLER (only for kind 'external'), parallel to ENDPOINTS. */
75
+ /** Per-method declared external CALLER (only for kind 'external-inbound'), parallel to ENDPOINTS. */
70
76
  ENDPOINT_CALLER: external_caller_1.ENDPOINT_CALLER_KEY,
71
77
  /** Per-method @MaskLog spec (which DTO fields the LogApiCall path masks). */
72
78
  MASK_LOG: 'webpieces:mask-log',
@@ -87,13 +93,24 @@ var EndpointKindValue;
87
93
  EndpointKindValue["RPC"] = "rpc";
88
94
  EndpointKindValue["CLOUDTASKS"] = "cloudtasks";
89
95
  EndpointKindValue["CRON"] = "cron";
90
- EndpointKindValue["EXTERNAL"] = "external";
96
+ EndpointKindValue["EXTERNAL_INBOUND"] = "external-inbound";
97
+ EndpointKindValue["EXTERNAL_OUTBOUND"] = "external-outbound";
91
98
  })(EndpointKindValue || (EndpointKindValue = {}));
92
99
  /** Short, statically importable decorator arguments. */
93
100
  exports.RPC = EndpointKindValue.RPC;
94
101
  exports.CLOUDTASKS = EndpointKindValue.CLOUDTASKS;
95
102
  exports.CRON = EndpointKindValue.CRON;
96
- exports.EXTERNAL = EndpointKindValue.EXTERNAL;
103
+ /**
104
+ * An OUTSIDE system calls US (a vendor webhook, a push subscription). Requires `calledBy`, which names
105
+ * that system on the runtime architecture graph.
106
+ */
107
+ exports.EXTERNAL_INBOUND = EndpointKindValue.EXTERNAL_INBOUND;
108
+ /**
109
+ * WE call an OUTSIDE system (a vendor API, a partner's webhook receiver). No server in this repo
110
+ * implements it, every method of the contract must be this kind, and the client sends ONLY the
111
+ * context keys listed with `@ExternalContextKeys(...)` and merges no response header into our context.
112
+ */
113
+ exports.EXTERNAL_OUTBOUND = EndpointKindValue.EXTERNAL_OUTBOUND;
97
114
  /** Class decorator that sets the shared base path for all endpoints. */
98
115
  function ApiPath(basePath) {
99
116
  return (target) => {
@@ -118,14 +135,14 @@ function Endpoint(httpMethod, path, operation, kind, options = {}) {
118
135
  }
119
136
  // webpieces-disable no-function-outside-class -- runtime validation backs up nominal TS enums
120
137
  function validateEndpointClassification(httpMethod, operation, kind) {
121
- if (!isHttpMethod(httpMethod)) {
122
- throw new Error(`@Endpoint httpMethod must be GET or POST, received '${String(httpMethod)}'.`);
138
+ if (!(0, HttpContract_1.isHttpMethod)(httpMethod)) {
139
+ throw new Error(`@Endpoint httpMethod must be one of ${HttpContract_1.HTTP_METHODS.join(', ')}, received '${String(httpMethod)}'.`);
123
140
  }
124
141
  if (!isEndpointOperation(operation)) {
125
142
  throw new Error(`@Endpoint operation must be READ, WRITE_IDEMPOTENT, or WRITE, received '${String(operation)}'.`);
126
143
  }
127
144
  if (!isEndpointKind(kind)) {
128
- throw new Error(`@Endpoint trigger must be RPC, CLOUDTASKS, CRON, or EXTERNAL, received '${String(kind)}'.`);
145
+ throw new Error(`@Endpoint trigger must be RPC, CLOUDTASKS, CRON, EXTERNAL_INBOUND, or EXTERNAL_OUTBOUND, received '${String(kind)}'.`);
129
146
  }
130
147
  }
131
148
  // webpieces-disable no-function-outside-class -- one metadata write shared by the decorator overloads
@@ -146,7 +163,7 @@ function recordEndpointMetadata(metadataTarget, propertyKey, httpMethod, path, o
146
163
  opts[propertyKey] = options;
147
164
  Reflect.defineMetadata(exports.METADATA_KEYS.ENDPOINT_OPTIONS, opts, metadataTarget);
148
165
  const declared = options;
149
- if (kind !== exports.EXTERNAL ||
166
+ if (kind !== exports.EXTERNAL_INBOUND ||
150
167
  typeof declared.calledBy !== 'string' ||
151
168
  declared.calledBy === '')
152
169
  return;
@@ -267,8 +284,8 @@ function getEndpointKind(apiClass, methodName) {
267
284
  function getEndpointHttpMethod(apiClass, methodName) {
268
285
  const methods = Reflect.getMetadata(exports.METADATA_KEYS.ENDPOINT_HTTP_METHOD, apiClass) || {};
269
286
  const method = methods[methodName];
270
- if (!isHttpMethod(method)) {
271
- throw new Error(`@Endpoint ${apiClass.name || 'Unknown'}.${methodName} must declare GET or POST.`);
287
+ if (!(0, HttpContract_1.isHttpMethod)(method)) {
288
+ throw new Error(`@Endpoint ${apiClass.name || 'Unknown'}.${methodName} must declare one of ${HttpContract_1.HTTP_METHODS.join(', ')}.`);
272
289
  }
273
290
  return method;
274
291
  }
@@ -302,35 +319,50 @@ function isEndpointOperation(value) {
302
319
  value === HttpEndpointOptions_1.WRITE_IDEMPOTENT ||
303
320
  value === HttpEndpointOptions_1.WRITE);
304
321
  }
305
- // webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an HTTP method
306
- function isHttpMethod(value) {
307
- return value === HttpContract_1.GET || value === HttpContract_1.POST;
308
- }
309
322
  // webpieces-disable no-function-outside-class -- runtime backstop for JavaScript and `as any` callers; webpieces-disable no-any-unknown -- untrusted runtime value is narrowed to an endpoint kind
310
323
  function isEndpointKind(value) {
311
324
  return (value === exports.RPC ||
312
325
  value === exports.CLOUDTASKS ||
313
326
  value === exports.CRON ||
314
- value === exports.EXTERNAL);
327
+ value === exports.EXTERNAL_INBOUND ||
328
+ value === exports.EXTERNAL_OUTBOUND);
315
329
  }
316
330
  /**
317
- * Fail-fast at wiring time when an `external` endpoint declared no caller. The {@link Endpoint}
318
- * overloads already make that a COMPILE error; this is the backstop for the ways TS is bypassed —
319
- * a JS caller, an `as any` options object, a hand-rolled Reflect.defineMetadata.
320
- * @throws Error naming the first external endpoint with no `calledBy`.
331
+ * Fail-fast at wiring time on the two EXTERNAL_* rules, called by `RouteMetadataFactory` for every
332
+ * transport (server routes and every client). The {@link Endpoint} overloads already make a missing
333
+ * `calledBy` a COMPILE error; this is the backstop for the ways TS is bypassed — a JS caller, an
334
+ * `as any` options object, a hand-rolled Reflect.defineMetadata. The second rule cannot be a type:
335
+ * "every method of this class" spans several decorators.
336
+ *
337
+ * @throws Error naming the first EXTERNAL_INBOUND endpoint with no `calledBy`, or the first method of
338
+ * a contract that mixes EXTERNAL_OUTBOUND with any other kind.
321
339
  */
322
340
  // webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode
323
- function assertEveryExternalEndpointDeclaresCaller(apiClass) {
341
+ function assertExternalEndpointKinds(apiClass) {
324
342
  const kinds = getEndpointKinds(apiClass);
325
- for (const methodName of Object.keys(kinds)) {
326
- if (kinds[methodName] !== 'external' ||
327
- (0, external_caller_1.getEndpointCaller)(apiClass, methodName) !== undefined)
343
+ const apiName = apiClass.name || 'Unknown';
344
+ const methods = Object.keys(kinds);
345
+ const outbound = methods.filter((methodName) => kinds[methodName] === exports.EXTERNAL_OUTBOUND);
346
+ if (outbound.length > 0 && outbound.length !== methods.length) {
347
+ const other = methods.find((methodName) => kinds[methodName] !== exports.EXTERNAL_OUTBOUND);
348
+ throw new Error(`${apiName} mixes EXTERNAL_OUTBOUND (${outbound[0]}) with ${String(kinds[other]).toUpperCase()} (${other}). ` +
349
+ `An EXTERNAL_OUTBOUND contract is an outside system's API that no server in this repo implements, so ` +
350
+ `EVERY method of it must be EXTERNAL_OUTBOUND; move the other methods to their own contract.`);
351
+ }
352
+ for (const methodName of methods) {
353
+ if (kinds[methodName] !== exports.EXTERNAL_INBOUND || (0, external_caller_1.getEndpointCaller)(apiClass, methodName) !== undefined)
328
354
  continue;
329
- throw new Error(`External endpoint '${methodName}' in ${apiClass.name || 'Unknown'} declares no caller. Say WHO ` +
330
- `posts to it: @Endpoint(POST, path, WRITE, EXTERNAL, { calledBy: '<vendor>' }) — the runtime architecture ` +
355
+ throw new Error(`External endpoint '${methodName}' in ${apiName} declares no caller. Say WHO ` +
356
+ `posts to it: @Endpoint(POST, path, WRITE, EXTERNAL_INBOUND, { calledBy: '<vendor>' }) — the runtime architecture ` +
331
357
  `graph cannot name an inbound caller it was never told about.`);
332
358
  }
333
359
  }
360
+ /** True when the contract is an outside system's API: every method is EXTERNAL_OUTBOUND. */
361
+ // webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of getEndpointKinds
362
+ function isExternalOutboundApi(apiClass) {
363
+ const kinds = Object.values(getEndpointKinds(apiClass));
364
+ return kinds.length > 0 && kinds.every((kind) => kind === exports.EXTERNAL_OUTBOUND);
365
+ }
334
366
  /**
335
367
  * True when the method's @Endpoint declared `{ formPost: true }` — its body is
336
368
  * application/x-www-form-urlencoded (flat), not JSON.
@@ -341,33 +373,33 @@ function isFormPost(apiClass, methodName) {
341
373
  }
342
374
  /**
343
375
  * True when the method's @Endpoint declared `{ rawBody: true }` — the transport must retain the
344
- * verbatim bytes + absolute url for an {@link WebhookAuthCallback} hook to verify.
376
+ * verbatim bytes + absolute url for a `VendorAuthHook.verify` to check.
345
377
  */
346
378
  // webpieces-disable no-function-outside-class -- reflect-metadata reader, sibling of isFormPost
347
379
  function isRawBody(apiClass, methodName) {
348
380
  return getEndpointOptions(apiClass, methodName).rawBody === true;
349
381
  }
350
382
  /**
351
- * Fail-fast at wiring time when an `webhook(...)` endpoint did not ask the transport to keep the
383
+ * Fail-fast at wiring time when a SERVED `vendorAuth(...)` endpoint did not ask the transport to keep the
352
384
  * bytes it is supposed to verify. A hook with nothing to verify is a MISCONFIGURATION, and it must
353
385
  * surface at startup, naming the fix — not as a 401 in production on exactly the traffic the endpoint
354
386
  * exists for.
355
387
  *
356
388
  * This pairing is a runtime assert rather than a type because the two halves live on DIFFERENT
357
- * decorators (`webhook(...)` and `@Endpoint`), and no union over one decorator's argument can say
389
+ * decorators (`vendorAuth(...)` and `@Endpoint`), and no union over one decorator's argument can say
358
390
  * anything about the other's.
359
391
  *
360
- * @throws Error naming the first `webhook(...)` endpoint missing `{ rawBody: true }`.
392
+ * @throws Error naming the first `vendorAuth(...)` endpoint missing `{ rawBody: true }`.
361
393
  */
362
394
  // webpieces-disable no-function-outside-class -- wiring-time assert, sibling of assertEveryEndpointHasAuthMode
363
- function assertEveryWebhookEndpointRetainsRawBody(apiClass) {
395
+ function assertEveryVendorAuthEndpointRetainsRawBody(apiClass) {
364
396
  const endpoints = getEndpoints(apiClass) || {};
365
397
  for (const methodName of Object.keys(endpoints)) {
366
- if (!getAuthMeta(apiClass, methodName)?.methods.some((method) => method.kind === 'webhook') ||
398
+ if (!getAuthMeta(apiClass, methodName)?.methods.some((method) => method.kind === 'vendorAuth') ||
367
399
  isRawBody(apiClass, methodName))
368
400
  continue;
369
- throw new Error(`Endpoint '${methodName}' in ${apiClass.name || 'Unknown'} is webhook(...) but its @Endpoint ` +
370
- `does not declare { rawBody: true }. A webhook hook verifies a signature over the bytes and ` +
401
+ throw new Error(`Endpoint '${methodName}' in ${apiClass.name || 'Unknown'} is vendorAuth(...) but its @Endpoint ` +
402
+ `does not declare { rawBody: true }. VendorAuthHook.verify checks a credential over the bytes and ` +
371
403
  `the url the SENDER transmitted, and without that option the transport parses the body and ` +
372
404
  `throws them away — leaving the hook nothing to check.`);
373
405
  }
@@ -392,7 +424,7 @@ function getAuthMeta(apiClass, methodName) {
392
424
  * the caller hits becomes the API they believe exists. It leads with the ROLE-GATED member on purpose —
393
425
  * the first thing offered should not be the widest grant.
394
426
  */
395
- exports.MISSING_AUTH_DECORATOR_FIX = "Declare @WpAuth([jwt(), oidcSameProject() or oidcSvcs(['svc-a']), sharedSecret(key), webhook(name), apiKey('regime', [{in: 'header', key: MyKeys.API_KEY}])]) " +
427
+ exports.MISSING_AUTH_DECORATOR_FIX = "Declare @WpAuth([jwt(), oidcSameProject() or oidcSvcs(['svc-a']), sharedSecret(key), vendorAuth(name), apiKey('regime', [{in: 'header', key: MyKeys.API_KEY}])]) " +
396
428
  "or @WpAuthPublic('reason'), and exactly one @WpAuthorization on every endpoint. Add @WpLocalOnly() separately when locality is required.";
397
429
  /**
398
430
  * Fail-fast at wiring time if any endpoint lacks an auth mode. Both the server