@webpieces/http-routing 0.4.989 → 0.4.991

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/README.md CHANGED
@@ -97,9 +97,18 @@ same final handler. Denied calls do not import the implementation. A mixed API i
97
97
  feature: its first permitted method loads that controller, rather than independently loading each
98
98
  method's JavaScript.
99
99
 
100
- `LazyImplementation` declares four things: the concrete controller, binding modules, private concrete
100
+ Use `new ApplicationImplementation(feature.AdminController)` when the implementation uses the
101
+ application's existing bindings and shared concrete singleton graph. This is an import-only boundary:
102
+ first authorized use imports the class and resolves it in the original application container, in the
103
+ same detached initialization context. Different deferred APIs continue to share their application
104
+ singletons. It accepts no late binding modules and performs no rebinding; binding declarations remain
105
+ at startup. As with existing eager DI, constructing a legitimate shared collaborator before a later
106
+ constructor fails is not an atomic transaction and is not rolled back. Remaining eager binding-barrel
107
+ imports must be measured separately; this mode alone does not remove them.
108
+
109
+ Use `LazyImplementation` for an isolated feature with private bindings. It declares four things: the concrete controller, binding modules, private concrete
101
110
  singletons (excluding the controller, which is implicit), and explicitly shared application dependencies.
102
- Private bindings live in an isolated container with autobinding disabled. Each shared dependency must
111
+ In isolated mode, private bindings live in an isolated container with autobinding disabled. Each shared dependency must
103
112
  already be explicitly bound in the application; the bridge returns the application's instance and
104
113
  preserves its scope instead of creating a second singleton. Bind shared services explicitly at startup
105
114
  when no essential route would otherwise resolve them. Merely importing a class does not bind it.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@webpieces/http-routing",
3
- "version": "0.4.989",
3
+ "version": "0.4.991",
4
4
  "description": "Decorator-based routing with auto-wiring for WebPieces",
5
5
  "type": "commonjs",
6
6
  "main": "./src/index.js",
@@ -22,14 +22,14 @@
22
22
  },
23
23
  "dependencies": {
24
24
  "@inversifyjs/binding-decorators": "1.1.5",
25
- "@webpieces/core-context": "0.4.989",
26
- "@webpieces/core-util": "0.4.989",
27
- "@webpieces/gcp-identity": "0.4.989",
25
+ "@webpieces/core-context": "0.4.991",
26
+ "@webpieces/core-util": "0.4.991",
27
+ "@webpieces/gcp-identity": "0.4.991",
28
28
  "inversify": "7.10.4",
29
29
  "jsonwebtoken": "9.0.2",
30
30
  "minimatch": "10.0.1",
31
- "@webpieces/http-client-core": "0.4.989",
32
- "@webpieces/http-client-node": "0.4.989",
33
- "@webpieces/cloudtasks-client": "0.4.989"
31
+ "@webpieces/http-client-core": "0.4.991",
32
+ "@webpieces/http-client-node": "0.4.991",
33
+ "@webpieces/cloudtasks-client": "0.4.991"
34
34
  }
35
35
  }
@@ -0,0 +1,55 @@
1
+ import { HttpRequest } from '@webpieces/core-context';
2
+ import { AuthMethod, OidcAuthMethods } from '@webpieces/core-util';
3
+ import { AuthConfig, AuthenticatedCaller, AuthenticatedMachineIdentity, SharedSecrets } from './AuthConfig';
4
+ import { ApiKeyHook, JwtHook, OidcHook } from './AuthHooks';
5
+ import { DefaultOidcVerifier } from './DefaultOidcVerifier';
6
+ /**
7
+ * The ONE constant-time comparison behind `sharedSecret(...)`, shared by {@link AuthFilter} and
8
+ * {@link CredentialVerifiers} so the two doors cannot disagree about what a matching secret is.
9
+ */
10
+ export declare class SharedSecretComparison {
11
+ /** EITHER secret1 or secret2 passes — the rotation window. Constant-time on each non-empty slot. */
12
+ static matchesEither(provided: string, accepted: SharedSecrets): boolean;
13
+ private static constantTimeEquals;
14
+ }
15
+ /**
16
+ * The credential VERIFIERS an app bound, for a door that is not a route — today the MCP HTTP endpoint
17
+ * (`@webpieces/mcp-server`), whose `setAuth([...])` takes the same `@WpAuth` builders an endpoint
18
+ * does. It holds exactly the pieces {@link AuthFilter} holds, resolved from the same container with the
19
+ * same tokens, so a `jwt()` on an MCP door is verified by the SAME `JwtHook` that verifies a `jwt()`
20
+ * endpoint, and `oidcSameProject()` by the same `OidcHook` / {@link DefaultOidcVerifier}:
21
+ *
22
+ * - `oidc` → the bound {@link OidcHook} if any, else the framework {@link DefaultOidcVerifier}.
23
+ * - `jwt` → the bound {@link JwtHook}.
24
+ * - `shared-secret` → the bound {@link AuthConfig}'s values, compared in constant time.
25
+ * - `apikey` → the bound {@link ApiKeyHook}.
26
+ *
27
+ * It verifies; it does not publish. Publishing the caller into `RequestContext` and reconciling inbound
28
+ * trusted headers stay the door's job, exactly as they are {@link AuthFilter}'s.
29
+ *
30
+ * Get it with `router.credentialVerifiers()`. {@link missingVerifier} is the startup check: a door that
31
+ * accepts a mode nobody can verify refuses to boot, naming the binding line, the same rule #1362 gives
32
+ * served routes.
33
+ */
34
+ export declare class CredentialVerifiers {
35
+ private readonly oidcVerifier;
36
+ private readonly authConfig?;
37
+ private readonly jwtHook?;
38
+ private readonly oidcHook?;
39
+ private readonly apiKeyHook?;
40
+ constructor(oidcVerifier: DefaultOidcVerifier, authConfig?: AuthConfig | undefined, jwtHook?: JwtHook<never> | undefined, oidcHook?: OidcHook | undefined, apiKeyHook?: ApiKeyHook | undefined);
41
+ /**
42
+ * The binding line a door must add before it can accept `method`, or `undefined` when the method
43
+ * is verifiable with what is bound. OIDC always is (the framework verifier). `vendorAuth` and
44
+ * `public` are not credential verifiers a door can use, so they are reported too.
45
+ */
46
+ missingVerifier(method: AuthMethod): string | undefined;
47
+ /** `jwt()`: the app's {@link JwtHook}. Throws `ApiUnauthorizedError` to deny. */
48
+ verifyJwt(token: string): Promise<AuthenticatedCaller>;
49
+ /** `oidcSameProject()` / `oidcSvcs([...])`: one token, verified once against every declared scope. */
50
+ verifyOidc(token: string, methods: OidcAuthMethods): Promise<AuthenticatedMachineIdentity>;
51
+ /** `sharedSecret(key)`: the `Webpieces <secret>` value against the bound secrets for `key`. */
52
+ verifySharedSecret(provided: string, secretKey: string): void;
53
+ /** `apiKey(regime, credentials)`: the app's {@link ApiKeyHook}, over the whole inbound request. */
54
+ verifyApiKey(regime: string, request: HttpRequest): Promise<AuthenticatedCaller>;
55
+ }
@@ -0,0 +1,142 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CredentialVerifiers = exports.SharedSecretComparison = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const inversify_1 = require("inversify");
6
+ const crypto_1 = require("crypto");
7
+ const core_context_1 = require("@webpieces/core-context");
8
+ const core_util_1 = require("@webpieces/core-util");
9
+ const AuthConfig_1 = require("./AuthConfig");
10
+ const AuthHooks_1 = require("./AuthHooks");
11
+ const DefaultOidcVerifier_1 = require("./DefaultOidcVerifier");
12
+ /**
13
+ * The ONE constant-time comparison behind `sharedSecret(...)`, shared by {@link AuthFilter} and
14
+ * {@link CredentialVerifiers} so the two doors cannot disagree about what a matching secret is.
15
+ */
16
+ class SharedSecretComparison {
17
+ /** EITHER secret1 or secret2 passes — the rotation window. Constant-time on each non-empty slot. */
18
+ // webpieces-disable no-function-outside-class -- stateless comparison shared by two verifiers
19
+ static matchesEither(provided, accepted) {
20
+ return ((accepted.secret1 !== '' &&
21
+ SharedSecretComparison.constantTimeEquals(provided, accepted.secret1)) ||
22
+ (accepted.secret2 !== '' &&
23
+ SharedSecretComparison.constantTimeEquals(provided, accepted.secret2)));
24
+ }
25
+ // webpieces-disable no-function-outside-class -- stateless comparison shared by two verifiers
26
+ static constantTimeEquals(a, b) {
27
+ const bufA = Buffer.from(a, 'utf8');
28
+ const bufB = Buffer.from(b, 'utf8');
29
+ if (bufA.length !== bufB.length) {
30
+ return false;
31
+ }
32
+ return (0, crypto_1.timingSafeEqual)(bufA, bufB);
33
+ }
34
+ }
35
+ exports.SharedSecretComparison = SharedSecretComparison;
36
+ /**
37
+ * The credential VERIFIERS an app bound, for a door that is not a route — today the MCP HTTP endpoint
38
+ * (`@webpieces/mcp-server`), whose `setAuth([...])` takes the same `@WpAuth` builders an endpoint
39
+ * does. It holds exactly the pieces {@link AuthFilter} holds, resolved from the same container with the
40
+ * same tokens, so a `jwt()` on an MCP door is verified by the SAME `JwtHook` that verifies a `jwt()`
41
+ * endpoint, and `oidcSameProject()` by the same `OidcHook` / {@link DefaultOidcVerifier}:
42
+ *
43
+ * - `oidc` → the bound {@link OidcHook} if any, else the framework {@link DefaultOidcVerifier}.
44
+ * - `jwt` → the bound {@link JwtHook}.
45
+ * - `shared-secret` → the bound {@link AuthConfig}'s values, compared in constant time.
46
+ * - `apikey` → the bound {@link ApiKeyHook}.
47
+ *
48
+ * It verifies; it does not publish. Publishing the caller into `RequestContext` and reconciling inbound
49
+ * trusted headers stay the door's job, exactly as they are {@link AuthFilter}'s.
50
+ *
51
+ * Get it with `router.credentialVerifiers()`. {@link missingVerifier} is the startup check: a door that
52
+ * accepts a mode nobody can verify refuses to boot, naming the binding line, the same rule #1362 gives
53
+ * served routes.
54
+ */
55
+ let CredentialVerifiers = class CredentialVerifiers {
56
+ oidcVerifier;
57
+ authConfig;
58
+ jwtHook;
59
+ oidcHook;
60
+ apiKeyHook;
61
+ constructor(oidcVerifier, authConfig, jwtHook, oidcHook, apiKeyHook) {
62
+ this.oidcVerifier = oidcVerifier;
63
+ this.authConfig = authConfig;
64
+ this.jwtHook = jwtHook;
65
+ this.oidcHook = oidcHook;
66
+ this.apiKeyHook = apiKeyHook;
67
+ }
68
+ /**
69
+ * The binding line a door must add before it can accept `method`, or `undefined` when the method
70
+ * is verifiable with what is bound. OIDC always is (the framework verifier). `vendorAuth` and
71
+ * `public` are not credential verifiers a door can use, so they are reported too.
72
+ */
73
+ missingVerifier(method) {
74
+ switch (method.kind) {
75
+ case 'oidc':
76
+ return undefined;
77
+ case 'jwt':
78
+ return this.jwtHook
79
+ ? undefined
80
+ : 'binder.bind(JWT_HOOK).to(YourJwtHook).inSingletonScope();';
81
+ case 'apikey':
82
+ return this.apiKeyHook
83
+ ? undefined
84
+ : 'binder.bind(API_KEY_HOOK).to(YourApiKeyHook).inSingletonScope();';
85
+ case 'shared-secret':
86
+ return this.authConfig?.sharedSecrets[method.secretKey]
87
+ ? undefined
88
+ : `binder.bind(AUTH_CONFIG).to(YourAuthConfig).inSingletonScope(); // holding { '${method.secretKey}': new SharedSecrets(secret1, secret2) }`;
89
+ case 'vendorAuth':
90
+ return `vendorAuth('${method.name}') identifies a vendor, not a caller to authorize; it cannot be verified on this door`;
91
+ }
92
+ }
93
+ /** `jwt()`: the app's {@link JwtHook}. Throws `ApiUnauthorizedError` to deny. */
94
+ async verifyJwt(token) {
95
+ if (!this.jwtHook)
96
+ throw new core_util_1.ApiImplementationError('jwt() was accepted with no JwtHook bound.');
97
+ return this.jwtHook.parseJwt(token);
98
+ }
99
+ /** `oidcSameProject()` / `oidcSvcs([...])`: one token, verified once against every declared scope. */
100
+ async verifyOidc(token, methods) {
101
+ const identity = this.oidcHook
102
+ ? await this.oidcHook.verifyOidc(token, methods)
103
+ : await this.oidcVerifier.verify(token, methods);
104
+ if (!identity)
105
+ throw new core_util_1.ApiImplementationError('OIDC verifier must return its verified machine identity.');
106
+ return identity;
107
+ }
108
+ /** `sharedSecret(key)`: the `Webpieces <secret>` value against the bound secrets for `key`. */
109
+ verifySharedSecret(provided, secretKey) {
110
+ const accepted = this.authConfig?.sharedSecrets[secretKey];
111
+ if (!accepted)
112
+ throw new core_util_1.ApiImplementationError(`sharedSecret('${secretKey}') was accepted with no AuthConfig value bound.`);
113
+ if (!SharedSecretComparison.matchesEither(provided, accepted)) {
114
+ throw new core_util_1.ApiUnauthorizedError('Invalid shared secret for sharedSecret(...) credential');
115
+ }
116
+ }
117
+ /** `apiKey(regime, credentials)`: the app's {@link ApiKeyHook}, over the whole inbound request. */
118
+ async verifyApiKey(regime, request) {
119
+ if (!this.apiKeyHook)
120
+ throw new core_util_1.ApiImplementationError('apiKey(...) was accepted with no ApiKeyHook bound.');
121
+ return this.apiKeyHook.verifyApiKey(regime, request);
122
+ }
123
+ };
124
+ exports.CredentialVerifiers = CredentialVerifiers;
125
+ exports.CredentialVerifiers = CredentialVerifiers = tslib_1.__decorate([
126
+ (0, core_context_1.provideFrameworkSingleton)(),
127
+ tslib_1.__param(0, (0, inversify_1.inject)(DefaultOidcVerifier_1.DefaultOidcVerifier)),
128
+ tslib_1.__param(1, (0, inversify_1.optional)()),
129
+ tslib_1.__param(1, (0, inversify_1.inject)(AuthConfig_1.AUTH_CONFIG)),
130
+ tslib_1.__param(2, (0, inversify_1.optional)()),
131
+ tslib_1.__param(2, (0, inversify_1.inject)(AuthHooks_1.JWT_HOOK)),
132
+ tslib_1.__param(3, (0, inversify_1.optional)()),
133
+ tslib_1.__param(3, (0, inversify_1.inject)(AuthHooks_1.OIDC_HOOK)),
134
+ tslib_1.__param(4, (0, inversify_1.optional)()),
135
+ tslib_1.__param(4, (0, inversify_1.inject)(AuthHooks_1.API_KEY_HOOK)),
136
+ tslib_1.__metadata("design:paramtypes", [DefaultOidcVerifier_1.DefaultOidcVerifier,
137
+ AuthConfig_1.AuthConfig,
138
+ AuthHooks_1.JwtHook,
139
+ AuthHooks_1.OidcHook,
140
+ AuthHooks_1.ApiKeyHook])
141
+ ], CredentialVerifiers);
142
+ //# sourceMappingURL=CredentialVerifiers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CredentialVerifiers.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/CredentialVerifiers.ts"],"names":[],"mappings":"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","sourcesContent":["import { inject, optional } from 'inversify';\nimport { timingSafeEqual } from 'crypto';\nimport { HttpRequest, provideFrameworkSingleton } from '@webpieces/core-context';\nimport {\n ApiImplementationError,\n ApiUnauthorizedError,\n AuthMethod,\n OidcAuthMethods,\n} from '@webpieces/core-util';\nimport {\n AuthConfig,\n AUTH_CONFIG,\n AuthenticatedCaller,\n AuthenticatedMachineIdentity,\n SharedSecrets,\n} from './AuthConfig';\nimport { ApiKeyHook, API_KEY_HOOK, JwtHook, JWT_HOOK, OidcHook, OIDC_HOOK } from './AuthHooks';\nimport { DefaultOidcVerifier } from './DefaultOidcVerifier';\n\n/**\n * The ONE constant-time comparison behind `sharedSecret(...)`, shared by {@link AuthFilter} and\n * {@link CredentialVerifiers} so the two doors cannot disagree about what a matching secret is.\n */\nexport class SharedSecretComparison {\n /** EITHER secret1 or secret2 passes — the rotation window. Constant-time on each non-empty slot. */\n // webpieces-disable no-function-outside-class -- stateless comparison shared by two verifiers\n static matchesEither(provided: string, accepted: SharedSecrets): boolean {\n return (\n (accepted.secret1 !== '' &&\n SharedSecretComparison.constantTimeEquals(provided, accepted.secret1)) ||\n (accepted.secret2 !== '' &&\n SharedSecretComparison.constantTimeEquals(provided, accepted.secret2))\n );\n }\n\n // webpieces-disable no-function-outside-class -- stateless comparison shared by two verifiers\n private static constantTimeEquals(a: string, b: string): boolean {\n const bufA = Buffer.from(a, 'utf8');\n const bufB = Buffer.from(b, 'utf8');\n if (bufA.length !== bufB.length) {\n return false;\n }\n return timingSafeEqual(bufA, bufB);\n }\n}\n\n/**\n * The credential VERIFIERS an app bound, for a door that is not a route — today the MCP HTTP endpoint\n * (`@webpieces/mcp-server`), whose `setAuth([...])` takes the same `@WpAuth` builders an endpoint\n * does. It holds exactly the pieces {@link AuthFilter} holds, resolved from the same container with the\n * same tokens, so a `jwt()` on an MCP door is verified by the SAME `JwtHook` that verifies a `jwt()`\n * endpoint, and `oidcSameProject()` by the same `OidcHook` / {@link DefaultOidcVerifier}:\n *\n * - `oidc` → the bound {@link OidcHook} if any, else the framework {@link DefaultOidcVerifier}.\n * - `jwt` → the bound {@link JwtHook}.\n * - `shared-secret` → the bound {@link AuthConfig}'s values, compared in constant time.\n * - `apikey` → the bound {@link ApiKeyHook}.\n *\n * It verifies; it does not publish. Publishing the caller into `RequestContext` and reconciling inbound\n * trusted headers stay the door's job, exactly as they are {@link AuthFilter}'s.\n *\n * Get it with `router.credentialVerifiers()`. {@link missingVerifier} is the startup check: a door that\n * accepts a mode nobody can verify refuses to boot, naming the binding line, the same rule #1362 gives\n * served routes.\n */\n@provideFrameworkSingleton()\nexport class CredentialVerifiers {\n constructor(\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- DI-resolved param; the esbuild/vitest path elides type-only imports (no design:paramtypes), so the explicit token is required\n @inject(DefaultOidcVerifier) private readonly oidcVerifier: DefaultOidcVerifier,\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- explicit token required for DI-resolved param\n @optional() @inject(AUTH_CONFIG) private readonly authConfig?: AuthConfig,\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- explicit token required for DI-resolved param\n @optional() @inject(JWT_HOOK) private readonly jwtHook?: JwtHook<never>,\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- explicit token required for DI-resolved param\n @optional() @inject(OIDC_HOOK) private readonly oidcHook?: OidcHook,\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- explicit token required for DI-resolved param\n @optional() @inject(API_KEY_HOOK) private readonly apiKeyHook?: ApiKeyHook,\n ) {}\n\n /**\n * The binding line a door must add before it can accept `method`, or `undefined` when the method\n * is verifiable with what is bound. OIDC always is (the framework verifier). `vendorAuth` and\n * `public` are not credential verifiers a door can use, so they are reported too.\n */\n missingVerifier(method: AuthMethod): string | undefined {\n switch (method.kind) {\n case 'oidc':\n return undefined;\n case 'jwt':\n return this.jwtHook\n ? undefined\n : 'binder.bind(JWT_HOOK).to(YourJwtHook).inSingletonScope();';\n case 'apikey':\n return this.apiKeyHook\n ? undefined\n : 'binder.bind(API_KEY_HOOK).to(YourApiKeyHook).inSingletonScope();';\n case 'shared-secret':\n return this.authConfig?.sharedSecrets[method.secretKey]\n ? undefined\n : `binder.bind(AUTH_CONFIG).to(YourAuthConfig).inSingletonScope(); // holding { '${method.secretKey}': new SharedSecrets(secret1, secret2) }`;\n case 'vendorAuth':\n return `vendorAuth('${method.name}') identifies a vendor, not a caller to authorize; it cannot be verified on this door`;\n }\n }\n\n /** `jwt()`: the app's {@link JwtHook}. Throws `ApiUnauthorizedError` to deny. */\n async verifyJwt(token: string): Promise<AuthenticatedCaller> {\n if (!this.jwtHook)\n throw new ApiImplementationError('jwt() was accepted with no JwtHook bound.');\n return this.jwtHook.parseJwt(token);\n }\n\n /** `oidcSameProject()` / `oidcSvcs([...])`: one token, verified once against every declared scope. */\n async verifyOidc(\n token: string,\n methods: OidcAuthMethods,\n ): Promise<AuthenticatedMachineIdentity> {\n const identity = this.oidcHook\n ? await this.oidcHook.verifyOidc(token, methods)\n : await this.oidcVerifier.verify(token, methods);\n if (!identity)\n throw new ApiImplementationError(\n 'OIDC verifier must return its verified machine identity.',\n );\n return identity;\n }\n\n /** `sharedSecret(key)`: the `Webpieces <secret>` value against the bound secrets for `key`. */\n verifySharedSecret(provided: string, secretKey: string): void {\n const accepted = this.authConfig?.sharedSecrets[secretKey];\n if (!accepted)\n throw new ApiImplementationError(\n `sharedSecret('${secretKey}') was accepted with no AuthConfig value bound.`,\n );\n if (!SharedSecretComparison.matchesEither(provided, accepted)) {\n throw new ApiUnauthorizedError(\n 'Invalid shared secret for sharedSecret(...) credential',\n );\n }\n }\n\n /** `apiKey(regime, credentials)`: the app's {@link ApiKeyHook}, over the whole inbound request. */\n async verifyApiKey(regime: string, request: HttpRequest): Promise<AuthenticatedCaller> {\n if (!this.apiKeyHook)\n throw new ApiImplementationError('apiKey(...) was accepted with no ApiKeyHook bound.');\n return this.apiKeyHook.verifyApiKey(regime, request);\n }\n}\n"]}
@@ -18,6 +18,11 @@ export declare class LazyImplementation<T extends object> {
18
18
  readonly shared: readonly ServiceIdentifier[];
19
19
  constructor(controller: Newable<T>, bindings: readonly BindModule[], singletons: readonly Newable<object>[], shared: readonly ServiceIdentifier[]);
20
20
  }
21
+ /** Import-only boundary: resolve in the existing app container and preserve its singleton graph. */
22
+ export declare class ApplicationImplementation<T extends object> {
23
+ readonly controller: Newable<T>;
24
+ constructor(controller: Newable<T>);
25
+ }
21
26
  /**
22
27
  * Eager identity/security metadata, deferred implementation. The loader imports one feature and
23
28
  * returns declarative bindings; it must not open resources or perform request work. A failure is
@@ -27,8 +32,8 @@ export declare class LazyController<T extends object> {
27
32
  readonly name: string;
28
33
  readonly filepath: string;
29
34
  readonly timeoutMillis: number;
30
- readonly load: () => Promise<LazyImplementation<T>>;
31
- constructor(name: string, filepath: string, timeoutMillis: number, load: () => Promise<LazyImplementation<T>>);
35
+ readonly load: () => Promise<LazyImplementation<T> | ApplicationImplementation<T>>;
36
+ constructor(name: string, filepath: string, timeoutMillis: number, load: () => Promise<LazyImplementation<T> | ApplicationImplementation<T>>);
32
37
  }
33
38
  /** One retained initialization promise per descriptor per router; callers keep their own context. */
34
39
  export declare class LazyControllerState<T extends object> {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.LazyControllerState = exports.LazyController = exports.LazyImplementation = exports.LazyFeatureTimer = exports.LazyFeatureDeadline = void 0;
3
+ exports.LazyControllerState = exports.LazyController = exports.ApplicationImplementation = exports.LazyImplementation = exports.LazyFeatureTimer = exports.LazyFeatureDeadline = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const decorators_1 = require("./decorators");
6
6
  const inversify_1 = require("inversify");
@@ -41,6 +41,14 @@ class LazyImplementation {
41
41
  }
42
42
  }
43
43
  exports.LazyImplementation = LazyImplementation;
44
+ /** Import-only boundary: resolve in the existing app container and preserve its singleton graph. */
45
+ class ApplicationImplementation {
46
+ controller;
47
+ constructor(controller) {
48
+ this.controller = controller;
49
+ }
50
+ }
51
+ exports.ApplicationImplementation = ApplicationImplementation;
44
52
  /**
45
53
  * Eager identity/security metadata, deferred implementation. The loader imports one feature and
46
54
  * returns declarative bindings; it must not open resources or perform request work. A failure is
@@ -158,6 +166,8 @@ class LazyControllerState {
158
166
  Reflect.get(this.api.prototype, method))
159
167
  throw new Error(`Lazy controller ${this.definition.name} must implement ${this.api.name}.${method}.`);
160
168
  }
169
+ if (implementation instanceof ApplicationImplementation)
170
+ return this.parent.get(controller);
161
171
  for (const token of implementation.shared) {
162
172
  if (!this.parent.isCurrentBound(token) &&
163
173
  !(typeof token !== 'function' && this.parent.isBound(token)))
@@ -1 +1 @@
1
- {"version":3,"file":"LazyController.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/LazyController.ts"],"names":[],"mappings":"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","sourcesContent":["import { ROUTING_METADATA_KEYS } from './decorators';\nimport {\n Container,\n ContainerModule,\n type ContainerModuleLoadOptions,\n type Newable,\n type ServiceIdentifier,\n type BindToFluentSyntax,\n} from 'inversify';\nimport { RequestContext, provideFrameworkSingletonDefaultForApi } from '@webpieces/core-context';\nimport { type ApiPrototype } from '@webpieces/http-client-core';\nimport type { ClassType } from './ClassType';\nimport { ContainerBinder, type ClientBindOptions, type PubSubBindOptions } from './Binder';\nimport type { BindModule } from './BindModule';\n\n/** The cancellable native deadline edge; feature tests replace only this clock. */\nexport class LazyFeatureDeadline {\n constructor(public readonly cancel: () => void) {}\n}\n\n/** Schedules a bounded first-use initialization; never a per-request authorization cache. */\nexport abstract class LazyFeatureTimer {\n abstract schedule(timeoutMillis: number, expire: () => void): Promise<LazyFeatureDeadline>;\n}\n\n@provideFrameworkSingletonDefaultForApi(LazyFeatureTimer)\nclass NodeLazyFeatureTimer extends LazyFeatureTimer {\n override async schedule(\n timeoutMillis: number,\n expire: () => void,\n ): Promise<LazyFeatureDeadline> {\n const timer = setTimeout(expire, timeoutMillis);\n return new LazyFeatureDeadline(() => clearTimeout(timer));\n }\n}\n\n/** Imported only on first permitted use. Explicitly list private concrete singletons. */\nexport class LazyImplementation<T extends object> {\n constructor(\n public readonly controller: Newable<T>,\n public readonly bindings: readonly BindModule[],\n public readonly singletons: readonly Newable<object>[],\n public readonly shared: readonly ServiceIdentifier[],\n ) {}\n}\n\n/**\n * Eager identity/security metadata, deferred implementation. The loader imports one feature and\n * returns declarative bindings; it must not open resources or perform request work. A failure is\n * retained per router until restart. The required deadline bounds waiting, not JavaScript CPU.\n */\nexport class LazyController<T extends object> {\n constructor(\n public readonly name: string,\n public readonly filepath: string,\n public readonly timeoutMillis: number,\n public readonly load: () => Promise<LazyImplementation<T>>,\n ) {\n if (!name.trim() || !filepath.trim())\n throw new Error('LazyController requires a stable name and filter filepath.');\n if (!Number.isFinite(timeoutMillis) || timeoutMillis <= 0 || timeoutMillis > 2_147_483_647)\n throw new Error(\n 'LazyController timeoutMillis must be positive, finite and at most 2147483647.',\n );\n }\n}\n\n/** Isolated scope: cannot replace application bindings; explicitly inherited external overrides win. */\nclass LazyFeatureBinder extends ContainerBinder {\n constructor(\n options: ContainerModuleLoadOptions,\n private readonly scope: Container,\n private readonly parent: Container,\n ) {\n super(options);\n }\n\n override bind<T>(token: ServiceIdentifier<T>): BindToFluentSyntax<T> {\n if (this.scope.isCurrentBound(token) || this.parent.isCurrentBound(token))\n throw new Error(`Lazy feature cannot shadow or duplicate binding ${String(token)}.`);\n return super.bind(token);\n }\n\n override createRpcClientAndBind<T extends object>(\n api: ApiPrototype<T>,\n deployment: string,\n options?: ClientBindOptions<NoInfer<T>>,\n ): void {\n if (!this.inherit(options?.token ?? api))\n super.createRpcClientAndBind(api, deployment, options);\n }\n\n override createPubSubClientAndBind<T extends object>(\n api: ApiPrototype<T>,\n deployment: string,\n options?: PubSubBindOptions<NoInfer<T>>,\n ): void {\n if (!this.inherit(options?.token ?? api))\n super.createPubSubClientAndBind(api, deployment, options);\n }\n\n override bindExternal<T extends object>(\n api: ApiPrototype<T>,\n implementation: Newable<NoInfer<T>>,\n ): void {\n if (!this.inherit(api)) super.bindExternal(api, implementation);\n }\n private inherit<T>(token: ServiceIdentifier<T>): boolean {\n if (!this.parent.isCurrentBound(token)) return false;\n if (!this.scope.isCurrentBound(token))\n this.scope.bind(token).toDynamicValue(() => this.parent.get(token));\n return true;\n }\n}\n\n/** One retained initialization promise per descriptor per router; callers keep their own context. */\nexport class LazyControllerState<T extends object> {\n private initialized?: Promise<T>;\n\n constructor(\n private readonly parent: Container,\n private readonly api: ClassType<T>,\n private readonly definition: LazyController<T>,\n private readonly timer: LazyFeatureTimer,\n private readonly methods: readonly string[],\n ) {}\n\n matchesApi(api: ClassType<object>): boolean {\n return this.api === api;\n }\n\n resolve(): Promise<T> {\n this.initialized ??= RequestContext.runDetachedScope(() => this.initialize());\n return this.initialized;\n }\n\n private async initialize(): Promise<T> {\n // No parent-container autobinding: private classes and shared dependencies are explicit.\n // Inversify parent isBound/get can autobind into the parent, even when this child disables it.\n const scope = new Container();\n let abandoned = false;\n let rejectDeadline!: (error: Error) => void;\n const expired = new Promise<never>((_resolve, reject) => {\n rejectDeadline = reject;\n });\n const deadline = await this.timer.schedule(this.definition.timeoutMillis, () => {\n abandoned = true;\n rejectDeadline(\n new Error(\n `Lazy feature ${this.definition.name} initialization exceeded ${this.definition.timeoutMillis}ms.`,\n ),\n );\n });\n // webpieces-disable no-unmanaged-exceptions -- terminal initialization boundary: release deadline on success, import/binding failure or timeout; rejection is retained and reported by normal dispatch\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return await Promise.race([expired, this.build(scope, () => abandoned)]);\n } finally {\n deadline.cancel();\n }\n }\n\n private async build(scope: Container, abandoned: () => boolean): Promise<T> {\n const implementation = await this.definition.load();\n if (abandoned())\n throw new Error(`Lazy feature ${this.definition.name} initialization was abandoned.`);\n const controller = implementation.controller;\n if (\n controller.name !== this.definition.name ||\n !this.api.prototype.isPrototypeOf(controller.prototype)\n )\n throw new Error(\n `Lazy controller ${this.definition.name} must preserve its declared name and extend ${this.api.name}.`,\n );\n const source: string | undefined = Reflect.getMetadata(\n ROUTING_METADATA_KEYS.SOURCE_FILEPATH,\n controller,\n );\n if (source !== undefined && source !== this.definition.filepath)\n throw new Error(\n `Lazy controller ${controller.name} filepath must match its @SourceFile metadata.`,\n );\n for (const method of this.methods) {\n if (\n typeof Reflect.get(controller.prototype, method) !== 'function' ||\n Reflect.get(controller.prototype, method) ===\n Reflect.get(this.api.prototype, method)\n )\n throw new Error(\n `Lazy controller ${this.definition.name} must implement ${this.api.name}.${method}.`,\n );\n }\n for (const token of implementation.shared) {\n if (\n !this.parent.isCurrentBound(token) &&\n !(typeof token !== 'function' && this.parent.isBound(token))\n )\n throw new Error(\n `Lazy feature requires an explicitly bound shared dependency ${String(token)}.`,\n );\n if (scope.isCurrentBound(token))\n throw new Error(`Duplicate shared feature dependency ${String(token)}.`);\n scope.bind(token).toDynamicValue(() => this.parent.get(token));\n }\n // Candidate bindings never mutate the shared container, including when a module fails halfway.\n for (const module of implementation.bindings) {\n await scope.load(\n new ContainerModule((options: ContainerModuleLoadOptions) =>\n module.configure(new LazyFeatureBinder(options, scope, this.parent)),\n ),\n );\n if (abandoned())\n throw new Error(\n `Lazy feature ${this.definition.name} initialization was abandoned.`,\n );\n }\n for (const singleton of [...implementation.singletons, controller]) {\n if (scope.isCurrentBound(singleton) || this.parent.isCurrentBound(singleton))\n throw new Error(\n `Lazy feature cannot shadow or duplicate singleton ${singleton.name}.`,\n );\n scope.bind(singleton).toSelf().inSingletonScope();\n }\n return scope.get(controller);\n }\n}\n"]}
1
+ {"version":3,"file":"LazyController.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/LazyController.ts"],"names":[],"mappings":"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","sourcesContent":["import { ROUTING_METADATA_KEYS } from './decorators';\nimport {\n Container,\n ContainerModule,\n type ContainerModuleLoadOptions,\n type Newable,\n type ServiceIdentifier,\n type BindToFluentSyntax,\n} from 'inversify';\nimport { RequestContext, provideFrameworkSingletonDefaultForApi } from '@webpieces/core-context';\nimport { type ApiPrototype } from '@webpieces/http-client-core';\nimport type { ClassType } from './ClassType';\nimport { ContainerBinder, type ClientBindOptions, type PubSubBindOptions } from './Binder';\nimport type { BindModule } from './BindModule';\n\n/** The cancellable native deadline edge; feature tests replace only this clock. */\nexport class LazyFeatureDeadline {\n constructor(public readonly cancel: () => void) {}\n}\n\n/** Schedules a bounded first-use initialization; never a per-request authorization cache. */\nexport abstract class LazyFeatureTimer {\n abstract schedule(timeoutMillis: number, expire: () => void): Promise<LazyFeatureDeadline>;\n}\n\n@provideFrameworkSingletonDefaultForApi(LazyFeatureTimer)\nclass NodeLazyFeatureTimer extends LazyFeatureTimer {\n override async schedule(\n timeoutMillis: number,\n expire: () => void,\n ): Promise<LazyFeatureDeadline> {\n const timer = setTimeout(expire, timeoutMillis);\n return new LazyFeatureDeadline(() => clearTimeout(timer));\n }\n}\n\n/** Imported only on first permitted use. Explicitly list private concrete singletons. */\nexport class LazyImplementation<T extends object> {\n constructor(\n public readonly controller: Newable<T>,\n public readonly bindings: readonly BindModule[],\n public readonly singletons: readonly Newable<object>[],\n public readonly shared: readonly ServiceIdentifier[],\n ) {}\n}\n\n/** Import-only boundary: resolve in the existing app container and preserve its singleton graph. */\nexport class ApplicationImplementation<T extends object> {\n constructor(public readonly controller: Newable<T>) {}\n}\n\n/**\n * Eager identity/security metadata, deferred implementation. The loader imports one feature and\n * returns declarative bindings; it must not open resources or perform request work. A failure is\n * retained per router until restart. The required deadline bounds waiting, not JavaScript CPU.\n */\nexport class LazyController<T extends object> {\n constructor(\n public readonly name: string,\n public readonly filepath: string,\n public readonly timeoutMillis: number,\n public readonly load: () => Promise<LazyImplementation<T> | ApplicationImplementation<T>>,\n ) {\n if (!name.trim() || !filepath.trim())\n throw new Error('LazyController requires a stable name and filter filepath.');\n if (!Number.isFinite(timeoutMillis) || timeoutMillis <= 0 || timeoutMillis > 2_147_483_647)\n throw new Error(\n 'LazyController timeoutMillis must be positive, finite and at most 2147483647.',\n );\n }\n}\n\n/** Isolated scope: cannot replace application bindings; explicitly inherited external overrides win. */\nclass LazyFeatureBinder extends ContainerBinder {\n constructor(\n options: ContainerModuleLoadOptions,\n private readonly scope: Container,\n private readonly parent: Container,\n ) {\n super(options);\n }\n\n override bind<T>(token: ServiceIdentifier<T>): BindToFluentSyntax<T> {\n if (this.scope.isCurrentBound(token) || this.parent.isCurrentBound(token))\n throw new Error(`Lazy feature cannot shadow or duplicate binding ${String(token)}.`);\n return super.bind(token);\n }\n\n override createRpcClientAndBind<T extends object>(\n api: ApiPrototype<T>,\n deployment: string,\n options?: ClientBindOptions<NoInfer<T>>,\n ): void {\n if (!this.inherit(options?.token ?? api))\n super.createRpcClientAndBind(api, deployment, options);\n }\n\n override createPubSubClientAndBind<T extends object>(\n api: ApiPrototype<T>,\n deployment: string,\n options?: PubSubBindOptions<NoInfer<T>>,\n ): void {\n if (!this.inherit(options?.token ?? api))\n super.createPubSubClientAndBind(api, deployment, options);\n }\n\n override bindExternal<T extends object>(\n api: ApiPrototype<T>,\n implementation: Newable<NoInfer<T>>,\n ): void {\n if (!this.inherit(api)) super.bindExternal(api, implementation);\n }\n private inherit<T>(token: ServiceIdentifier<T>): boolean {\n if (!this.parent.isCurrentBound(token)) return false;\n if (!this.scope.isCurrentBound(token))\n this.scope.bind(token).toDynamicValue(() => this.parent.get(token));\n return true;\n }\n}\n\n/** One retained initialization promise per descriptor per router; callers keep their own context. */\nexport class LazyControllerState<T extends object> {\n private initialized?: Promise<T>;\n\n constructor(\n private readonly parent: Container,\n private readonly api: ClassType<T>,\n private readonly definition: LazyController<T>,\n private readonly timer: LazyFeatureTimer,\n private readonly methods: readonly string[],\n ) {}\n\n matchesApi(api: ClassType<object>): boolean {\n return this.api === api;\n }\n\n resolve(): Promise<T> {\n this.initialized ??= RequestContext.runDetachedScope(() => this.initialize());\n return this.initialized;\n }\n\n private async initialize(): Promise<T> {\n // No parent-container autobinding: private classes and shared dependencies are explicit.\n // Inversify parent isBound/get can autobind into the parent, even when this child disables it.\n const scope = new Container();\n let abandoned = false;\n let rejectDeadline!: (error: Error) => void;\n const expired = new Promise<never>((_resolve, reject) => {\n rejectDeadline = reject;\n });\n const deadline = await this.timer.schedule(this.definition.timeoutMillis, () => {\n abandoned = true;\n rejectDeadline(\n new Error(\n `Lazy feature ${this.definition.name} initialization exceeded ${this.definition.timeoutMillis}ms.`,\n ),\n );\n });\n // webpieces-disable no-unmanaged-exceptions -- terminal initialization boundary: release deadline on success, import/binding failure or timeout; rejection is retained and reported by normal dispatch\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions\n try {\n return await Promise.race([expired, this.build(scope, () => abandoned)]);\n } finally {\n deadline.cancel();\n }\n }\n\n private async build(scope: Container, abandoned: () => boolean): Promise<T> {\n const implementation = await this.definition.load();\n if (abandoned())\n throw new Error(`Lazy feature ${this.definition.name} initialization was abandoned.`);\n const controller = implementation.controller;\n if (\n controller.name !== this.definition.name ||\n !this.api.prototype.isPrototypeOf(controller.prototype)\n )\n throw new Error(\n `Lazy controller ${this.definition.name} must preserve its declared name and extend ${this.api.name}.`,\n );\n const source: string | undefined = Reflect.getMetadata(\n ROUTING_METADATA_KEYS.SOURCE_FILEPATH,\n controller,\n );\n if (source !== undefined && source !== this.definition.filepath)\n throw new Error(\n `Lazy controller ${controller.name} filepath must match its @SourceFile metadata.`,\n );\n for (const method of this.methods) {\n if (\n typeof Reflect.get(controller.prototype, method) !== 'function' ||\n Reflect.get(controller.prototype, method) ===\n Reflect.get(this.api.prototype, method)\n )\n throw new Error(\n `Lazy controller ${this.definition.name} must implement ${this.api.name}.${method}.`,\n );\n }\n if (implementation instanceof ApplicationImplementation) return this.parent.get(controller);\n\n for (const token of implementation.shared) {\n if (\n !this.parent.isCurrentBound(token) &&\n !(typeof token !== 'function' && this.parent.isBound(token))\n )\n throw new Error(\n `Lazy feature requires an explicitly bound shared dependency ${String(token)}.`,\n );\n if (scope.isCurrentBound(token))\n throw new Error(`Duplicate shared feature dependency ${String(token)}.`);\n scope.bind(token).toDynamicValue(() => this.parent.get(token));\n }\n // Candidate bindings never mutate the shared container, including when a module fails halfway.\n for (const module of implementation.bindings) {\n await scope.load(\n new ContainerModule((options: ContainerModuleLoadOptions) =>\n module.configure(new LazyFeatureBinder(options, scope, this.parent)),\n ),\n );\n if (abandoned())\n throw new Error(\n `Lazy feature ${this.definition.name} initialization was abandoned.`,\n );\n }\n for (const singleton of [...implementation.singletons, controller]) {\n if (scope.isCurrentBound(singleton) || this.parent.isCurrentBound(singleton))\n throw new Error(\n `Lazy feature cannot shadow or duplicate singleton ${singleton.name}.`,\n );\n scope.bind(singleton).toSelf().inSingletonScope();\n }\n return scope.get(controller);\n }\n}\n"]}
@@ -1,5 +1,5 @@
1
1
  import type { WebpiecesRouter } from './WebpiecesRouter';
2
- import type { LazyController, LazyImplementation } from './LazyController';
2
+ import type { LazyController, LazyImplementation, ApplicationImplementation } from './LazyController';
3
3
  declare abstract class StringApi {
4
4
  abstract read(): Promise<string>;
5
5
  }
@@ -10,5 +10,6 @@ declare abstract class NumberApi {
10
10
  export declare class LazyControllerCompileAssertions {
11
11
  routes(router: WebpiecesRouter, strings: LazyController<StringApi>, numbers: LazyController<NumberApi>): void;
12
12
  implementation(strings: LazyImplementation<StringApi>, numbers: LazyImplementation<NumberApi>): void;
13
+ application(strings: ApplicationImplementation<StringApi>, numbers: ApplicationImplementation<NumberApi>): void;
13
14
  }
14
15
  export {};
@@ -18,6 +18,12 @@ class LazyControllerCompileAssertions {
18
18
  void invalid;
19
19
  void strings;
20
20
  }
21
+ application(strings, numbers) {
22
+ // @ts-expect-error Application-container mode still enforces the API's result type.
23
+ const invalid = numbers;
24
+ void invalid;
25
+ void strings;
26
+ }
21
27
  }
22
28
  exports.LazyControllerCompileAssertions = LazyControllerCompileAssertions;
23
29
  //# sourceMappingURL=LazyControllerCompileAssertions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LazyControllerCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/LazyControllerCompileAssertions.ts"],"names":[],"mappings":";;;AAGA,MAAe,SAAS;CAEvB;AACD,MAAe,SAAS;CAEvB;AAED,+FAA+F;AAC/F,MAAa,+BAA+B;IACxC,MAAM,CACF,MAAuB,EACvB,OAAkC,EAClC,OAAkC;QAElC,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACrC,uFAAuF;QACvF,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IACD,cAAc,CACV,OAAsC,EACtC,OAAsC;QAEtC,4FAA4F;QAC5F,MAAM,OAAO,GAAkC,OAAO,CAAC;QACvD,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;IACjB,CAAC;CACJ;AAnBD,0EAmBC","sourcesContent":["import type { WebpiecesRouter } from './WebpiecesRouter';\nimport type { LazyController, LazyImplementation } from './LazyController';\n\nabstract class StringApi {\n abstract read(): Promise<string>;\n}\nabstract class NumberApi {\n abstract read(): Promise<number>;\n}\n\n/** Compiled by tsc, never invoked: implementation types must agree with the registered API. */\nexport class LazyControllerCompileAssertions {\n routes(\n router: WebpiecesRouter,\n strings: LazyController<StringApi>,\n numbers: LazyController<NumberApi>,\n ): void {\n router.addRoutes(StringApi, strings);\n // @ts-expect-error The deferred controller must implement this contract's result type.\n router.addRoutes(StringApi, numbers);\n }\n implementation(\n strings: LazyImplementation<StringApi>,\n numbers: LazyImplementation<NumberApi>,\n ): void {\n // @ts-expect-error A number-result implementation cannot be a string-result implementation.\n const invalid: LazyImplementation<StringApi> = numbers;\n void invalid;\n void strings;\n }\n}\n"]}
1
+ {"version":3,"file":"LazyControllerCompileAssertions.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/LazyControllerCompileAssertions.ts"],"names":[],"mappings":";;;AAOA,MAAe,SAAS;CAEvB;AACD,MAAe,SAAS;CAEvB;AAED,+FAA+F;AAC/F,MAAa,+BAA+B;IACxC,MAAM,CACF,MAAuB,EACvB,OAAkC,EAClC,OAAkC;QAElC,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACrC,uFAAuF;QACvF,MAAM,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACzC,CAAC;IACD,cAAc,CACV,OAAsC,EACtC,OAAsC;QAEtC,4FAA4F;QAC5F,MAAM,OAAO,GAAkC,OAAO,CAAC;QACvD,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;IACjB,CAAC;IACD,WAAW,CACP,OAA6C,EAC7C,OAA6C;QAE7C,oFAAoF;QACpF,MAAM,OAAO,GAAyC,OAAO,CAAC;QAC9D,KAAK,OAAO,CAAC;QACb,KAAK,OAAO,CAAC;IACjB,CAAC;CACJ;AA5BD,0EA4BC","sourcesContent":["import type { WebpiecesRouter } from './WebpiecesRouter';\nimport type {\n LazyController,\n LazyImplementation,\n ApplicationImplementation,\n} from './LazyController';\n\nabstract class StringApi {\n abstract read(): Promise<string>;\n}\nabstract class NumberApi {\n abstract read(): Promise<number>;\n}\n\n/** Compiled by tsc, never invoked: implementation types must agree with the registered API. */\nexport class LazyControllerCompileAssertions {\n routes(\n router: WebpiecesRouter,\n strings: LazyController<StringApi>,\n numbers: LazyController<NumberApi>,\n ): void {\n router.addRoutes(StringApi, strings);\n // @ts-expect-error The deferred controller must implement this contract's result type.\n router.addRoutes(StringApi, numbers);\n }\n implementation(\n strings: LazyImplementation<StringApi>,\n numbers: LazyImplementation<NumberApi>,\n ): void {\n // @ts-expect-error A number-result implementation cannot be a string-result implementation.\n const invalid: LazyImplementation<StringApi> = numbers;\n void invalid;\n void strings;\n }\n application(\n strings: ApplicationImplementation<StringApi>,\n numbers: ApplicationImplementation<NumberApi>,\n ): void {\n // @ts-expect-error Application-container mode still enforces the API's result type.\n const invalid: ApplicationImplementation<StringApi> = numbers;\n void invalid;\n void strings;\n }\n}\n"]}
@@ -1,5 +1,6 @@
1
1
  import { LazyController } from './LazyController';
2
2
  import { AuthorizationService } from './AuthorizationHook';
3
+ import { CredentialVerifiers } from './CredentialVerifiers';
3
4
  import { InvocationAuthentication } from './InvocationAuthentication';
4
5
  import { Container, ContainerModule } from 'inversify';
5
6
  import { RouteBuilderImpl } from './RouteBuilderImpl';
@@ -105,6 +106,12 @@ export declare class WebpiecesRouter implements ApiFactory {
105
106
  */
106
107
  apiClients(): ApiClient[];
107
108
  authorizationService(): AuthorizationService;
109
+ /**
110
+ * The credential verifiers this app bound (JwtHook, OidcHook / DefaultOidcVerifier, AuthConfig,
111
+ * ApiKeyHook), for a door that is not a route — the MCP HTTP endpoint's `setAuth([...])` verifies
112
+ * its `@WpAuth` modes with exactly these, so it cannot drift from {@link AuthFilter}.
113
+ */
114
+ credentialVerifiers(): CredentialVerifiers;
108
115
  /**
109
116
  * Warm and validate every optional implementation in a SEPARATE build/integration process with
110
117
  * fake external providers. Never invoke this in the production entry point or from a request.
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WebpiecesRouterFactory = exports.WebpiecesRouter = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const AuthorizationHook_1 = require("./AuthorizationHook");
6
+ const CredentialVerifiers_1 = require("./CredentialVerifiers");
6
7
  const inversify_1 = require("inversify");
7
8
  const binding_decorators_1 = require("@inversifyjs/binding-decorators");
8
9
  const core_util_1 = require("@webpieces/core-util");
@@ -146,6 +147,14 @@ let WebpiecesRouter = class WebpiecesRouter {
146
147
  authorizationService() {
147
148
  return this.appContainer.get(AuthorizationHook_1.AuthorizationService);
148
149
  }
150
+ /**
151
+ * The credential verifiers this app bound (JwtHook, OidcHook / DefaultOidcVerifier, AuthConfig,
152
+ * ApiKeyHook), for a door that is not a route — the MCP HTTP endpoint's `setAuth([...])` verifies
153
+ * its `@WpAuth` modes with exactly these, so it cannot drift from {@link AuthFilter}.
154
+ */
155
+ credentialVerifiers() {
156
+ return this.appContainer.get(CredentialVerifiers_1.CredentialVerifiers);
157
+ }
149
158
  /**
150
159
  * Warm and validate every optional implementation in a SEPARATE build/integration process with
151
160
  * fake external providers. Never invoke this in the production entry point or from a request.
@@ -1 +1 @@
1
- {"version":3,"file":"WebpiecesRouter.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/WebpiecesRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import { LazyController } from './LazyController';\nimport { AuthorizationService } from './AuthorizationHook';\nimport { InvocationAuthentication } from './InvocationAuthentication';\nimport { Container, ContainerModule, inject } from 'inversify';\nimport { buildProviderModule } from '@inversifyjs/binding-decorators';\nimport { DocumentDesign } from '@webpieces/core-util';\nimport {\n provideFrameworkSingleton,\n buildFrameworkModule,\n RequestContext,\n} from '@webpieces/core-context';\nimport { RouteBuilderImpl } from './RouteBuilderImpl';\nimport { ApiRoutingFactory } from './ApiRoutingFactory';\nimport type { ClassType } from './ClassType';\nimport { FilterDefinition } from './WebAppMeta';\nimport { WebpiecesConfig, WEBPIECES_CONFIG_TOKEN } from './WebpiecesConfig';\nimport { ApiClientFactory } from './ApiClientFactory';\nimport { ApiFactory } from './ApiFactory';\nimport { ApiClient } from './ApiClient';\nimport { LogApiFilter } from './filters/LogApiFilter';\nimport { AuthFilter } from './filters/AuthFilter';\nimport { AuthorizationFilter } from './filters/AuthorizationFilter';\nimport {\n RuntimeStartupObserver,\n RuntimeStartupPhase,\n RuntimeStartupTiming,\n} from './RuntimeStartupTiming';\n\n/**\n * Options for {@link WebpiecesRouterFactory.create} — one object (config lives inside it).\n *\n * appBindings - DI ContainerModules to load (framework + app). Loaded after the\n * @provideSingleton auto-scan so they can add/override bindings.\n * appOverrides - A single ContainerModule loaded LAST so tests can rebind real\n * controllers/clients to mocks (see @webpieces/core-mock createMock()).\n * config - Optional {@link WebpiecesConfig} (recording flags, etc.); defaults to a fresh one.\n */\nexport interface WebpiecesRouterOptions {\n appBindings: ContainerModule[];\n appOverrides?: ContainerModule;\n config?: WebpiecesConfig;\n /** Optional startup spans; no observer means no clock or timing-output work. */\n startupObserver?: RuntimeStartupObserver;\n}\n\n/**\n * WebpiecesRouter - the node-only heart of a webpieces app: a DI container + a filter\n * chain + an in-process API client. It has NO express dependency, so it runs anywhere\n * node runs and is fully testable with zero HTTP.\n *\n * DI-resolved from the platform container (like the old WebpiecesServerImpl):\n * `@provideSingleton @injectable`, RouteBuilderImpl injected, and the two containers set in\n * initialize(). Built by {@link WebpiecesRouterFactory.create} — never `new`ed by callers.\n *\n * Two-container pattern (mirrors Java WebPieces):\n * - webpiecesContainer : framework bindings (config token, @DocumentDesign design roots)\n * - appContainer : your controllers/filters/modules (a child of the framework one)\n *\n * Usage:\n * ```typescript\n * const router = await WebpiecesRouterFactory.create({ appBindings: [AppModule] });\n * router.addRoutes(SaveApi, SaveController);\n * router.addFilter(new FilterDefinition(1800, MyFilter, '*')); // your own filters\n * // (LogApiFilter + AuthFilter are auto-installed above yours; auth is AuthMode-driven.\n * // LogApiFilter logs request+response for EVERY call — do NOT install it yourself.)\n *\n * // test (no express): runs the SAME filter chain (incl. auth) -> controller\n * const api = router.createApiClient(SaveApi);\n * await api.save(new SaveRequest(...));\n * ```\n *\n * To serve real HTTP, hand this router to the express adapter in @webpieces/http-server\n * (bindExpress / bindAndStartExpress) — express lifecycle lives THERE, never here.\n *\n * @DocumentDesign marks it a design root so it appears in http-routing's designed-lib graph.\n */\n@DocumentDesign()\n@provideFrameworkSingleton()\nexport class WebpiecesRouter implements ApiFactory {\n private webpiecesContainer!: Container;\n private appContainer!: Container;\n private startupTiming = new RuntimeStartupTiming();\n\n constructor(\n @inject(RouteBuilderImpl) private readonly routeBuilder: RouteBuilderImpl,\n @inject(ApiClientFactory) private readonly apiClientFactory: ApiClientFactory,\n ) {}\n\n /**\n * Build the app container (child of the framework container), load the @provideSingleton\n * auto-scan + appBindings + appOverrides, and point the RouteBuilder at it. Called once by\n * the factory after this router is resolved from the framework container.\n */\n async initialize(\n webpiecesContainer: Container,\n options: WebpiecesRouterOptions,\n ): Promise<void> {\n this.webpiecesContainer = webpiecesContainer;\n this.startupTiming = new RuntimeStartupTiming(options.startupObserver);\n this.routeBuilder.setStartupObserver(options.startupObserver);\n\n // App container is a child so app bindings see framework bindings while staying separate.\n // autobind:true lets a concrete @injectable(Singleton) app class self-bind on first resolve\n // (inject-by-type, no @provideSingleton). Framework singletons stay explicit in the parent\n // (provideFrameworkSingleton -> buildFrameworkModule), found before child-autobind kicks in.\n this.appContainer = new Container({ parent: webpiecesContainer, autobind: true });\n this.routeBuilder.setContainer(this.appContainer);\n\n await this.loadDIModules(options);\n this.startupTiming.run(RuntimeStartupPhase.FIXED_FILTERS, 'fixed-filters', () =>\n this.installFixedFilters(),\n );\n }\n\n /**\n * Auto-install the two fixed framework filters on every route (apps add only their own\n * filters below these): LogApiFilter outermost (logs request + response/failure for every\n * call and stamps [Controller.method], then re-throws for the transport to translate), then\n * AuthFilter (enforces the endpoint's AuthMode off the HttpRequest). Both run over HTTP AND\n * in-process — there is no transport tier. Because LogApiFilter is outermost, requests that\n * AuthFilter rejects (401) are still logged with their body + controller identity.\n */\n private installFixedFilters(): void {\n this.addFilter(new FilterDefinition(1_000_000, LogApiFilter, '*'));\n this.addFilter(new FilterDefinition(900_000, AuthFilter, '*'));\n this.addFilter(new FilterDefinition(800_000, AuthorizationFilter, '*'));\n }\n\n private async loadDIModules(options: WebpiecesRouterOptions): Promise<void> {\n // Load BOTH registries: framework classes (provideFrameworkSingleton) + the client's\n // own @provideSingleton classes (binding-decorators global). A client's\n // buildProviderModule() only ever contains the client's classes — never framework internals.\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.FRAMEWORK_DI,\n 'application-framework-bindings',\n () => this.appContainer.load(buildFrameworkModule()),\n );\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.APPLICATION_DI,\n 'provider-bindings',\n () => this.appContainer.load(buildProviderModule()),\n );\n\n // Load all app modules into application container\n // (webpiecesContainer is currently empty, reserved for future framework bindings)\n for (const [index, module] of options.appBindings.entries()) {\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.APPLICATION_DI,\n `app-binding-${index}`,\n () => this.appContainer.load(module),\n );\n }\n\n // Load appOverrides LAST so they can override existing bindings\n if (options.appOverrides) {\n const overrides = options.appOverrides;\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.APPLICATION_DI,\n 'app-overrides',\n () => this.appContainer.load(overrides),\n );\n }\n }\n\n /**\n * Wire an API prototype (with @ApiPath/@Endpoint decorators) to its controller.\n * A class resolves eagerly. A LazyController retains metadata now and imports its implementation\n * only after the ordinary filter chain permits an invocation.\n */\n addRoutes<TApi extends object, TController extends TApi>(\n api: ClassType<TApi>,\n controller: ClassType<TController> | LazyController<TController>,\n ): this {\n this.startupTiming.run(RuntimeStartupPhase.ROUTES, `${api.name}:${controller.name}`, () =>\n new ApiRoutingFactory(api, controller).configure(this.routeBuilder),\n );\n return this;\n }\n\n /**\n * Register a user filter (runs in-process AND over HTTP, below the auto-installed fixed\n * LogApiFilter + AuthFilter).\n */\n addFilter(filter: FilterDefinition): this {\n this.routeBuilder.addFilter(filter);\n return this;\n }\n\n /**\n * Create an in-process API client that runs the api-tier filter chain + controller\n * with NO express/HTTP. The primary path for tests and node-only callers.\n */\n // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args\n createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T {\n return this.apiClientFactory.createApiClient(apiPrototype);\n }\n\n /** Transport-only credential bridge; ordinary endpoint filters remain mandatory. */\n // webpieces-disable no-any-unknown -- abstract API constructor signature\n createInvocationApiClient<T>(\n apiPrototype: abstract new (...args: any[]) => T,\n authentication: InvocationAuthentication,\n ): T {\n return this.apiClientFactory.createInvocationApiClient(apiPrototype, authentication);\n }\n\n /**\n * Reify the registered APIs as {@link ApiClient}s (contract + the createApiClient proxy) via\n * the shared {@link ApiClientFactory}. This is the ONLY handoff to the express layer — the\n * internal RouteBuilder never leaves.\n */\n apiClients(): ApiClient[] {\n return this.apiClientFactory.apiClients();\n }\n\n authorizationService(): AuthorizationService {\n return this.appContainer.get(AuthorizationService);\n }\n\n /**\n * Warm and validate every optional implementation in a SEPARATE build/integration process with\n * fake external providers. Never invoke this in the production entry point or from a request.\n */\n async validateLazyRoutesForBuild(): Promise<void> {\n if (RequestContext.isActive())\n throw new Error('Lazy implementation build validation cannot run inside a request.');\n await this.routeBuilder.validateLazyRoutesForBuild();\n }\n\n /** The application DI container (child of the framework container). */\n getContainer(): Container {\n return this.appContainer;\n }\n}\n\n/**\n * Builds a {@link WebpiecesRouter}: constructs the platform container (mirrors\n * WebpiecesServerFactory.create), RESOLVES the router from DI, then initializes its app child\n * container with the @provideSingleton auto-scan + appBindings + optional test overrides.\n */\nexport class WebpiecesRouterFactory {\n static async create(options: WebpiecesRouterOptions): Promise<WebpiecesRouter> {\n // Platform (framework) container — build via buildFrameworkModule so framework\n // singletons (WebpiecesRouter, RouteBuilderImpl) come from the webpieces registry,\n // NOT the client's global one.\n const timing = new RuntimeStartupTiming(options.startupObserver);\n const webpiecesContainer = new Container();\n webpiecesContainer\n .bind(WEBPIECES_CONFIG_TOKEN)\n .toConstantValue(options.config ?? new WebpiecesConfig());\n await timing.runAsync(RuntimeStartupPhase.FRAMEWORK_DI, 'platform-framework-bindings', () =>\n webpiecesContainer.load(buildFrameworkModule()),\n );\n\n // Resolve the router from the container (NOT new'd) so @DocumentDesign + DI hold.\n const router = timing.run(RuntimeStartupPhase.FRAMEWORK_DI, 'router-resolution', () =>\n webpiecesContainer.get(WebpiecesRouter),\n );\n await router.initialize(webpiecesContainer, options);\n return router;\n }\n}\n"]}
1
+ {"version":3,"file":"WebpiecesRouter.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/WebpiecesRouter.ts"],"names":[],"mappings":"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","sourcesContent":["import { LazyController } from './LazyController';\nimport { AuthorizationService } from './AuthorizationHook';\nimport { CredentialVerifiers } from './CredentialVerifiers';\nimport { InvocationAuthentication } from './InvocationAuthentication';\nimport { Container, ContainerModule, inject } from 'inversify';\nimport { buildProviderModule } from '@inversifyjs/binding-decorators';\nimport { DocumentDesign } from '@webpieces/core-util';\nimport {\n provideFrameworkSingleton,\n buildFrameworkModule,\n RequestContext,\n} from '@webpieces/core-context';\nimport { RouteBuilderImpl } from './RouteBuilderImpl';\nimport { ApiRoutingFactory } from './ApiRoutingFactory';\nimport type { ClassType } from './ClassType';\nimport { FilterDefinition } from './WebAppMeta';\nimport { WebpiecesConfig, WEBPIECES_CONFIG_TOKEN } from './WebpiecesConfig';\nimport { ApiClientFactory } from './ApiClientFactory';\nimport { ApiFactory } from './ApiFactory';\nimport { ApiClient } from './ApiClient';\nimport { LogApiFilter } from './filters/LogApiFilter';\nimport { AuthFilter } from './filters/AuthFilter';\nimport { AuthorizationFilter } from './filters/AuthorizationFilter';\nimport {\n RuntimeStartupObserver,\n RuntimeStartupPhase,\n RuntimeStartupTiming,\n} from './RuntimeStartupTiming';\n\n/**\n * Options for {@link WebpiecesRouterFactory.create} — one object (config lives inside it).\n *\n * appBindings - DI ContainerModules to load (framework + app). Loaded after the\n * @provideSingleton auto-scan so they can add/override bindings.\n * appOverrides - A single ContainerModule loaded LAST so tests can rebind real\n * controllers/clients to mocks (see @webpieces/core-mock createMock()).\n * config - Optional {@link WebpiecesConfig} (recording flags, etc.); defaults to a fresh one.\n */\nexport interface WebpiecesRouterOptions {\n appBindings: ContainerModule[];\n appOverrides?: ContainerModule;\n config?: WebpiecesConfig;\n /** Optional startup spans; no observer means no clock or timing-output work. */\n startupObserver?: RuntimeStartupObserver;\n}\n\n/**\n * WebpiecesRouter - the node-only heart of a webpieces app: a DI container + a filter\n * chain + an in-process API client. It has NO express dependency, so it runs anywhere\n * node runs and is fully testable with zero HTTP.\n *\n * DI-resolved from the platform container (like the old WebpiecesServerImpl):\n * `@provideSingleton @injectable`, RouteBuilderImpl injected, and the two containers set in\n * initialize(). Built by {@link WebpiecesRouterFactory.create} — never `new`ed by callers.\n *\n * Two-container pattern (mirrors Java WebPieces):\n * - webpiecesContainer : framework bindings (config token, @DocumentDesign design roots)\n * - appContainer : your controllers/filters/modules (a child of the framework one)\n *\n * Usage:\n * ```typescript\n * const router = await WebpiecesRouterFactory.create({ appBindings: [AppModule] });\n * router.addRoutes(SaveApi, SaveController);\n * router.addFilter(new FilterDefinition(1800, MyFilter, '*')); // your own filters\n * // (LogApiFilter + AuthFilter are auto-installed above yours; auth is AuthMode-driven.\n * // LogApiFilter logs request+response for EVERY call — do NOT install it yourself.)\n *\n * // test (no express): runs the SAME filter chain (incl. auth) -> controller\n * const api = router.createApiClient(SaveApi);\n * await api.save(new SaveRequest(...));\n * ```\n *\n * To serve real HTTP, hand this router to the express adapter in @webpieces/http-server\n * (bindExpress / bindAndStartExpress) — express lifecycle lives THERE, never here.\n *\n * @DocumentDesign marks it a design root so it appears in http-routing's designed-lib graph.\n */\n@DocumentDesign()\n@provideFrameworkSingleton()\nexport class WebpiecesRouter implements ApiFactory {\n private webpiecesContainer!: Container;\n private appContainer!: Container;\n private startupTiming = new RuntimeStartupTiming();\n\n constructor(\n @inject(RouteBuilderImpl) private readonly routeBuilder: RouteBuilderImpl,\n @inject(ApiClientFactory) private readonly apiClientFactory: ApiClientFactory,\n ) {}\n\n /**\n * Build the app container (child of the framework container), load the @provideSingleton\n * auto-scan + appBindings + appOverrides, and point the RouteBuilder at it. Called once by\n * the factory after this router is resolved from the framework container.\n */\n async initialize(\n webpiecesContainer: Container,\n options: WebpiecesRouterOptions,\n ): Promise<void> {\n this.webpiecesContainer = webpiecesContainer;\n this.startupTiming = new RuntimeStartupTiming(options.startupObserver);\n this.routeBuilder.setStartupObserver(options.startupObserver);\n\n // App container is a child so app bindings see framework bindings while staying separate.\n // autobind:true lets a concrete @injectable(Singleton) app class self-bind on first resolve\n // (inject-by-type, no @provideSingleton). Framework singletons stay explicit in the parent\n // (provideFrameworkSingleton -> buildFrameworkModule), found before child-autobind kicks in.\n this.appContainer = new Container({ parent: webpiecesContainer, autobind: true });\n this.routeBuilder.setContainer(this.appContainer);\n\n await this.loadDIModules(options);\n this.startupTiming.run(RuntimeStartupPhase.FIXED_FILTERS, 'fixed-filters', () =>\n this.installFixedFilters(),\n );\n }\n\n /**\n * Auto-install the two fixed framework filters on every route (apps add only their own\n * filters below these): LogApiFilter outermost (logs request + response/failure for every\n * call and stamps [Controller.method], then re-throws for the transport to translate), then\n * AuthFilter (enforces the endpoint's AuthMode off the HttpRequest). Both run over HTTP AND\n * in-process — there is no transport tier. Because LogApiFilter is outermost, requests that\n * AuthFilter rejects (401) are still logged with their body + controller identity.\n */\n private installFixedFilters(): void {\n this.addFilter(new FilterDefinition(1_000_000, LogApiFilter, '*'));\n this.addFilter(new FilterDefinition(900_000, AuthFilter, '*'));\n this.addFilter(new FilterDefinition(800_000, AuthorizationFilter, '*'));\n }\n\n private async loadDIModules(options: WebpiecesRouterOptions): Promise<void> {\n // Load BOTH registries: framework classes (provideFrameworkSingleton) + the client's\n // own @provideSingleton classes (binding-decorators global). A client's\n // buildProviderModule() only ever contains the client's classes — never framework internals.\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.FRAMEWORK_DI,\n 'application-framework-bindings',\n () => this.appContainer.load(buildFrameworkModule()),\n );\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.APPLICATION_DI,\n 'provider-bindings',\n () => this.appContainer.load(buildProviderModule()),\n );\n\n // Load all app modules into application container\n // (webpiecesContainer is currently empty, reserved for future framework bindings)\n for (const [index, module] of options.appBindings.entries()) {\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.APPLICATION_DI,\n `app-binding-${index}`,\n () => this.appContainer.load(module),\n );\n }\n\n // Load appOverrides LAST so they can override existing bindings\n if (options.appOverrides) {\n const overrides = options.appOverrides;\n await this.startupTiming.runAsync(\n RuntimeStartupPhase.APPLICATION_DI,\n 'app-overrides',\n () => this.appContainer.load(overrides),\n );\n }\n }\n\n /**\n * Wire an API prototype (with @ApiPath/@Endpoint decorators) to its controller.\n * A class resolves eagerly. A LazyController retains metadata now and imports its implementation\n * only after the ordinary filter chain permits an invocation.\n */\n addRoutes<TApi extends object, TController extends TApi>(\n api: ClassType<TApi>,\n controller: ClassType<TController> | LazyController<TController>,\n ): this {\n this.startupTiming.run(RuntimeStartupPhase.ROUTES, `${api.name}:${controller.name}`, () =>\n new ApiRoutingFactory(api, controller).configure(this.routeBuilder),\n );\n return this;\n }\n\n /**\n * Register a user filter (runs in-process AND over HTTP, below the auto-installed fixed\n * LogApiFilter + AuthFilter).\n */\n addFilter(filter: FilterDefinition): this {\n this.routeBuilder.addFilter(filter);\n return this;\n }\n\n /**\n * Create an in-process API client that runs the api-tier filter chain + controller\n * with NO express/HTTP. The primary path for tests and node-only callers.\n */\n // webpieces-disable no-any-unknown -- abstract constructor signature requires any[] args\n createApiClient<T>(apiPrototype: abstract new (...args: any[]) => T): T {\n return this.apiClientFactory.createApiClient(apiPrototype);\n }\n\n /** Transport-only credential bridge; ordinary endpoint filters remain mandatory. */\n // webpieces-disable no-any-unknown -- abstract API constructor signature\n createInvocationApiClient<T>(\n apiPrototype: abstract new (...args: any[]) => T,\n authentication: InvocationAuthentication,\n ): T {\n return this.apiClientFactory.createInvocationApiClient(apiPrototype, authentication);\n }\n\n /**\n * Reify the registered APIs as {@link ApiClient}s (contract + the createApiClient proxy) via\n * the shared {@link ApiClientFactory}. This is the ONLY handoff to the express layer — the\n * internal RouteBuilder never leaves.\n */\n apiClients(): ApiClient[] {\n return this.apiClientFactory.apiClients();\n }\n\n authorizationService(): AuthorizationService {\n return this.appContainer.get(AuthorizationService);\n }\n\n /**\n * The credential verifiers this app bound (JwtHook, OidcHook / DefaultOidcVerifier, AuthConfig,\n * ApiKeyHook), for a door that is not a route — the MCP HTTP endpoint's `setAuth([...])` verifies\n * its `@WpAuth` modes with exactly these, so it cannot drift from {@link AuthFilter}.\n */\n credentialVerifiers(): CredentialVerifiers {\n return this.appContainer.get(CredentialVerifiers);\n }\n\n /**\n * Warm and validate every optional implementation in a SEPARATE build/integration process with\n * fake external providers. Never invoke this in the production entry point or from a request.\n */\n async validateLazyRoutesForBuild(): Promise<void> {\n if (RequestContext.isActive())\n throw new Error('Lazy implementation build validation cannot run inside a request.');\n await this.routeBuilder.validateLazyRoutesForBuild();\n }\n\n /** The application DI container (child of the framework container). */\n getContainer(): Container {\n return this.appContainer;\n }\n}\n\n/**\n * Builds a {@link WebpiecesRouter}: constructs the platform container (mirrors\n * WebpiecesServerFactory.create), RESOLVES the router from DI, then initializes its app child\n * container with the @provideSingleton auto-scan + appBindings + optional test overrides.\n */\nexport class WebpiecesRouterFactory {\n static async create(options: WebpiecesRouterOptions): Promise<WebpiecesRouter> {\n // Platform (framework) container — build via buildFrameworkModule so framework\n // singletons (WebpiecesRouter, RouteBuilderImpl) come from the webpieces registry,\n // NOT the client's global one.\n const timing = new RuntimeStartupTiming(options.startupObserver);\n const webpiecesContainer = new Container();\n webpiecesContainer\n .bind(WEBPIECES_CONFIG_TOKEN)\n .toConstantValue(options.config ?? new WebpiecesConfig());\n await timing.runAsync(RuntimeStartupPhase.FRAMEWORK_DI, 'platform-framework-bindings', () =>\n webpiecesContainer.load(buildFrameworkModule()),\n );\n\n // Resolve the router from the container (NOT new'd) so @DocumentDesign + DI hold.\n const router = timing.run(RuntimeStartupPhase.FRAMEWORK_DI, 'router-resolution', () =>\n webpiecesContainer.get(WebpiecesRouter),\n );\n await router.initialize(webpiecesContainer, options);\n return router;\n }\n}\n"]}
@@ -199,8 +199,6 @@ export declare class AuthFilter extends Filter<MethodMeta, WpResponse<unknown>>
199
199
  private enforceOidc;
200
200
  /** `provided` is the Authorization bearer value — the secret itself, same header as a JWT. */
201
201
  private enforceSharedSecret;
202
- /** EITHER secret1 or secret2 passes — the rotation window. Constant-time on each non-empty slot. */
203
- private matchesEither;
204
202
  /** Parse a JWT if one is present, else do nothing — used on public routes; never throws. */
205
203
  private bestEffortJwt;
206
204
  /**
@@ -210,5 +208,4 @@ export declare class AuthFilter extends Filter<MethodMeta, WpResponse<unknown>>
210
208
  * where a JWT is expected), yields undefined and the caller 401s.
211
209
  */
212
210
  private credential;
213
- private constantTimeEquals;
214
211
  }
@@ -4,7 +4,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
4
4
  exports.AuthFilter = void 0;
5
5
  const tslib_1 = require("tslib");
6
6
  const inversify_1 = require("inversify");
7
- const crypto_1 = require("crypto");
8
7
  const core_context_1 = require("@webpieces/core-context");
9
8
  const core_util_1 = require("@webpieces/core-util");
10
9
  const core_util_2 = require("@webpieces/core-util");
@@ -13,6 +12,7 @@ const AuthHooks_1 = require("../AuthHooks");
13
12
  const AuthenticatedCallerContext_1 = require("../AuthenticatedCallerContext");
14
13
  const AuthorizationHook_1 = require("../AuthorizationHook");
15
14
  const DefaultOidcVerifier_1 = require("../DefaultOidcVerifier");
15
+ const CredentialVerifiers_1 = require("../CredentialVerifiers");
16
16
  const UnverifiableAuthModeError_1 = require("../UnverifiableAuthModeError");
17
17
  const log = core_util_1.LogManager.getLogger('AuthFilter');
18
18
  /**
@@ -205,7 +205,8 @@ let AuthFilter = AuthFilter_1 = class AuthFilter extends core_util_2.Filter {
205
205
  await this.authenticateMethod(method, header, meta, oidc);
206
206
  return method;
207
207
  }
208
- catch (err) { // webpieces-disable no-any-unknown -- structured credential rejection at the boundary
208
+ catch (err) {
209
+ // webpieces-disable no-any-unknown -- structured credential rejection at the boundary
209
210
  const error = (0, core_util_1.toError)(err);
210
211
  if (!(error instanceof core_util_1.ApiUnauthorizedError) || alternatives.length === 1)
211
212
  throw error;
@@ -238,7 +239,8 @@ let AuthFilter = AuthFilter_1 = class AuthFilter extends core_util_2.Filter {
238
239
  case 'apikey':
239
240
  await this.enforceApiKey(mode.regime, meta);
240
241
  return;
241
- case 'public': throw new core_util_1.ApiImplementationError('Public is not a protected auth alternative.');
242
+ case 'public':
243
+ throw new core_util_1.ApiImplementationError('Public is not a protected auth alternative.');
242
244
  }
243
245
  }
244
246
  /**
@@ -459,15 +461,10 @@ let AuthFilter = AuthFilter_1 = class AuthFilter extends core_util_2.Filter {
459
461
  /** `provided` is the Authorization bearer value — the secret itself, same header as a JWT. */
460
462
  enforceSharedSecret(provided, secretKey, routeMeta) {
461
463
  const accepted = this.requireSharedSecrets(secretKey, routeMeta);
462
- if (!provided || !this.matchesEither(provided, accepted)) {
464
+ if (!provided || !CredentialVerifiers_1.SharedSecretComparison.matchesEither(provided, accepted)) {
463
465
  throw new core_util_1.ApiUnauthorizedError('Invalid shared secret for sharedSecret(...) endpoint');
464
466
  }
465
467
  }
466
- /** EITHER secret1 or secret2 passes — the rotation window. Constant-time on each non-empty slot. */
467
- matchesEither(provided, accepted) {
468
- return ((accepted.secret1 !== '' && this.constantTimeEquals(provided, accepted.secret1)) ||
469
- (accepted.secret2 !== '' && this.constantTimeEquals(provided, accepted.secret2)));
470
- }
471
468
  /** Parse a JWT if one is present, else do nothing — used on public routes; never throws. */
472
469
  async bestEffortJwt(header) {
473
470
  const token = this.credential(header, BEARER_SCHEME);
@@ -498,14 +495,6 @@ let AuthFilter = AuthFilter_1 = class AuthFilter extends core_util_2.Filter {
498
495
  const prefix = `${scheme} `;
499
496
  return header.startsWith(prefix) ? header.substring(prefix.length) : undefined;
500
497
  }
501
- constantTimeEquals(a, b) {
502
- const bufA = Buffer.from(a, 'utf8');
503
- const bufB = Buffer.from(b, 'utf8');
504
- if (bufA.length !== bufB.length) {
505
- return false;
506
- }
507
- return (0, crypto_1.timingSafeEqual)(bufA, bufB);
508
- }
509
498
  };
510
499
  exports.AuthFilter = AuthFilter;
511
500
  exports.AuthFilter = AuthFilter = AuthFilter_1 = tslib_1.__decorate([
@@ -1 +1 @@
1
- {"version":3,"file":"AuthFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-routing/src/filters/AuthFilter.ts"],"names":[],"mappings":"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","sourcesContent":["import { inject, optional } from 'inversify';\nimport { timingSafeEqual } from 'crypto';\nimport {\n provideFrameworkSingleton,\n HttpRequest,\n RawHttpRequest,\n RequestContext,\n VendorAuthVerifier,\n VENDOR_AUTH_VERIFIER,\n} from '@webpieces/core-context';\nimport {\n AuthMode,\n OidcAuthMethod,\n OidcAuthMethods,\n ApiEndpointNotFoundError,\n ApiUnauthorizedError,\n ApiImplementationError,\n LogManager,\n RouteMetadata,\n RuntimeLocality,\n Surface,\n toError,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { Filter, Service } from '@webpieces/core-util';\nimport { WpResponse } from '../WpResponse';\nimport { MethodMeta } from '../MethodMeta';\nimport { AuthConfig, AUTH_CONFIG, SharedSecrets } from '../AuthConfig';\nimport {\n ApiKeyHook,\n API_KEY_HOOK,\n JwtHook,\n JWT_HOOK,\n OidcHook,\n OIDC_HOOK,\n} from '../AuthHooks';\nimport { AuthenticatedCallerContext } from '../AuthenticatedCallerContext';\nimport { VERIFIED_MACHINE_CALLER, VerifiedMachineCaller } from '../AuthorizationHook';\nimport { DefaultOidcVerifier } from '../DefaultOidcVerifier';\nimport { UnverifiableAuthModeError } from '../UnverifiableAuthModeError';\n\nconst log = LogManager.getLogger('AuthFilter');\n\n/**\n * The ONE credential header, read straight off the inbound HttpRequest.\n *\n * Deliberately NOT a ContextKey: a ContextKey with an httpHeader is a TRANSFERRED key, which would\n * put the caller's credential into RequestContext and hence onto every outbound call this service\n * makes, and onto every Cloud Task it enqueues. A credential belongs to ONE request hop.\n */\nconst AUTHORIZATION_HEADER = 'authorization';\n\n/**\n * The scheme (first word of the Authorization value) names WHICH credential follows, so a secret\n * can never be mistaken for a token, nor accepted where the other was expected:\n *\n * Authorization: Bearer <user JWT | service OIDC token>\n * Authorization: Webpieces <sharedSecret(...) value>\n *\n * The scheme is REQUIRED. A bare value with no scheme is rejected.\n */\nconst BEARER_SCHEME = 'Bearer';\nconst SHARED_SECRET_SCHEME = 'Webpieces';\n\n/**\n * AuthFilter - the ONE framework auth filter, auto-installed just below the error filter on every\n * route. It is TRANSPORT-NEUTRAL: it reads the raw credential from the {@link HttpRequest} in\n * RequestContext (never express), so the SAME check runs over HTTP and via createApiClient.\n *\n * It enforces the endpoint's AuthMode from separately-bound pieces, each OPTIONAL except the OIDC\n * default. An app binds only the pieces its SERVED routes declare — and a served route that declares\n * a mode whose piece is absent FAILS STARTUP ({@link assertRouteVerifiable}, #1362), so none of the\n * rows below is ever \"not enabled\" at request time:\n * - shared-secret → constant-time compare vs the {@link AuthConfig} secret VALUE (state). Startup\n * requires an AuthConfig holding the route's key.\n * - jwt → the bound {@link JwtHook} (`parseJwt`, awaited — an app's\n * strategy may reach a JWKS or a datastore). Startup requires a JwtHook: JWT needs\n * an app secret + payload shape.\n * - oidc → the bound {@link OidcHook} if any, else the framework {@link DefaultOidcVerifier}\n * run DIRECTLY — so a server that wires NOTHING still verifies Google OIDC against\n * the endpoint's caller scope (`oidcSameProject()` or `oidcSvcs([...])`).\n * - vendorAuth → the bound {@link VendorAuthVerifier} `verify`s the VENDOR's credential over the\n * retained raw request. Startup requires a verifier whose `supportedVendorNames()` lists the\n * route's name: an unverified vendor call is never waved through because wiring was\n * forgotten. The INBOUND role only — this filter never depends on the outbound\n * VendorAuthMinter (#1358).\n * - apikey → the bound {@link ApiKeyHook} looks the CUSTOMER's key up (async, over the whole\n * header set) and returns the context to seed. Startup requires an ApiKeyHook.\n * - public → BEST-EFFORT jwt parse (only if a JwtHook is bound): stamp the user's context so\n * a logged-out page still knows who is logged in; never fails.\n * - local-only → serve only when {@link RuntimeLocality} says this process is a developer's\n * machine; otherwise 404, indistinguishable from the route not existing (which,\n * off-local, it does not — `ApiRoutingFactory` never registered it).\n *\n * Zero wiring = OIDC just works; an app only binds the hooks it actually uses.\n */\n@provideFrameworkSingleton()\n// webpieces-disable no-any-unknown -- Filter generic params use unknown for response flexibility\nexport class AuthFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n // Framework default, always available — verifies Google OIDC with zero app wiring.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- AuthFilter is DI-resolved via the esbuild/vitest path, which elides type-only imports (no design:paramtypes), so every param needs its explicit token\n @inject(DefaultOidcVerifier) private readonly oidcVerifier: DefaultOidcVerifier,\n // @optional: only bind an AuthConfig to enable sharedSecret(...) endpoints (startup refuses a\n // served one without it — see assertRouteVerifiable).\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(AUTH_CONFIG) private readonly authConfig?: AuthConfig,\n // @optional: only bind a JwtHook to enable jwt() endpoints.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(JWT_HOOK) private readonly jwtHook?: JwtHook<never>,\n // @optional: only bind an OidcHook to OVERRIDE the DefaultOidcVerifier caller policy.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(OIDC_HOOK) private readonly oidcHook?: OidcHook,\n // @optional: only bind a VendorAuthVerifier to enable vendorAuth(...) endpoints. Unbound = a\n // server serving one refuses to boot (assertRouteVerifiable), never an allow-all.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional()\n @inject(VENDOR_AUTH_VERIFIER)\n private readonly vendorAuthVerifier?: VendorAuthVerifier,\n // @optional: only bind an ApiKeyHook to enable apiKey(...) endpoints. Unbound = a server serving\n // one refuses to boot, for the same reason as the vendor verifier above.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(API_KEY_HOOK) private readonly apiKeyHook?: ApiKeyHook,\n ) {\n super();\n }\n\n /**\n * STARTUP check (#1362), called by route registration for EVERY served route: each auth mode the\n * route declares must be verifiable by what is bound, or boot fails naming the endpoint, the mode\n * and the exact binding line. STRICT — a multi-method route (`[oidcSameProject(), vendorAuth('x')]`)\n * with no verifier fails too: a declared alternative nobody can verify is dead, and is almost\n * always a wiring mistake. OIDC needs no app binding (the framework verifier) and a public route\n * none.\n *\n * Reads bindings this filter already holds and nothing else: it never calls `verify`, `parseJwt`\n * or `verifyApiKey`, touches no network, and constructs nothing (#1358's cycle stays closed).\n * The request path resolves each piece through the SAME `require*` methods, so there is one\n * decision, made at boot. The one boot-only check is a verifier's `supportedVendorNames()`: at request time\n * the verifier's OWN dispatch stays the backstop for a name it does not know, and refuses (401).\n */\n assertRouteVerifiable(routeMeta: RouteMetadata): void {\n const auth = routeMeta.authMeta;\n if (!auth) throw new ApiImplementationError('Every endpoint requires @WpAuth or @WpAuthPublic.');\n for (const method of auth.methods) {\n switch (method.kind) {\n case 'jwt': this.requireJwtHook(routeMeta); break;\n case 'shared-secret': this.requireSharedSecrets(method.secretKey, routeMeta); break;\n case 'vendorAuth': this.assertVendorDeclared(this.requireVendorVerifier(method.name, routeMeta), method.name, routeMeta); break;\n case 'apikey': this.requireApiKeyHook(method.regime, routeMeta); break;\n case 'oidc':\n case 'public':\n break;\n }\n }\n }\n\n // webpieces-disable no-function-outside-class -- static pure formatter of the route identity, kept beside its only callers\n private static endpointOf(routeMeta: RouteMetadata): string {\n return `${routeMeta.apiName ?? routeMeta.controllerClassName ?? 'UnknownApi'}.${routeMeta.methodName} (${routeMeta.httpMethod} ${routeMeta.path})`;\n }\n\n private unverifiable(routeMeta: RouteMetadata, mode: string, problem: string, bindLine: string): UnverifiableAuthModeError {\n const endpoint = AuthFilter.endpointOf(routeMeta);\n return new UnverifiableAuthModeError(\n `Endpoint ${endpoint} declares ${mode}, but ${problem}, so this server cannot verify it and ` +\n `refuses to start. Add:\\n ${bindLine}\\n` +\n `(Or remove ${mode} from the endpoint's @WpAuth if this server should not accept it.)`,\n endpoint,\n mode,\n );\n }\n\n private requireJwtHook(routeMeta: RouteMetadata): JwtHook<never> {\n if (this.jwtHook) return this.jwtHook;\n throw this.unverifiable(routeMeta, 'jwt()', 'no JwtHook is bound',\n 'binder.bind(JWT_HOOK).to(YourJwtHook).inSingletonScope();');\n }\n\n private requireSharedSecrets(secretKey: string, routeMeta: RouteMetadata): SharedSecrets {\n const config = this.authConfig;\n const mode = `sharedSecret('${secretKey}')`;\n if (!config) {\n throw this.unverifiable(routeMeta, mode, 'no AuthConfig is bound',\n `binder.bind(AUTH_CONFIG).to(YourAuthConfig).inSingletonScope(); // YourAuthConfig extends AuthConfig and passes { '${secretKey}': new SharedSecrets(secret1, secret2) }`);\n }\n const accepted = config.sharedSecrets[secretKey];\n if (accepted) return accepted;\n throw this.unverifiable(routeMeta, mode,\n `the bound AuthConfig (${config.constructor.name}) holds no SharedSecrets for '${secretKey}'`,\n `'${secretKey}': new SharedSecrets(secret1, secret2), // add to the sharedSecrets ${config.constructor.name} passes to AuthConfig, which AUTH_CONFIG is bound to`);\n }\n\n private requireVendorVerifier(name: string, routeMeta: RouteMetadata): VendorAuthVerifier {\n const verifier = this.vendorAuthVerifier;\n const mode = `vendorAuth('${name}')`;\n if (!verifier) {\n throw this.unverifiable(routeMeta, mode,\n 'no VendorAuthVerifier is bound (a VENDOR_AUTH_MINTER binding only signs OUTBOUND calls)',\n 'binder.bind(VENDOR_AUTH_VERIFIER).to(YourVendorAuthVerifier).inSingletonScope();');\n }\n return verifier;\n }\n\n /** Boot only: the bound verifier must DECLARE the route's vendor (#1362). Never calls verify. */\n private assertVendorDeclared(verifier: VendorAuthVerifier, name: string, routeMeta: RouteMetadata): void {\n const supported = verifier.supportedVendorNames();\n if (supported.includes(name)) return;\n const mode = `vendorAuth('${name}')`;\n const declared = supported.map((n: string) => `'${n}'`).join(', ');\n throw this.unverifiable(routeMeta, mode,\n `the bound VendorAuthVerifier (${verifier.constructor.name}) returns [${declared}] from supportedVendorNames(), which does not include '${name}'`,\n `'${name}' to ${verifier.constructor.name}.supportedVendorNames() (return [${declared}, '${name}']) AND handle '${name}' in its verify()`);\n }\n\n private requireApiKeyHook(regime: string, routeMeta: RouteMetadata): ApiKeyHook {\n if (this.apiKeyHook) return this.apiKeyHook;\n throw this.unverifiable(routeMeta, `apiKey('${regime}', credentials)`, 'no ApiKeyHook is bound',\n 'binder.bind(API_KEY_HOOK).to(YourApiKeyHook).inSingletonScope();');\n }\n\n // webpieces-disable no-any-unknown -- Filter generic params use unknown for response flexibility\n override async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n const auth = meta.routeMeta.authMeta;\n if (!auth) throw new ApiImplementationError('Every endpoint requires @WpAuth or @WpAuthPublic.');\n if (meta.routeMeta.localOnly) this.enforceLocalOnly(meta);\n if (meta.invocationAuthentication) {\n meta.invocationAuthentication.assertTarget(meta.routeMeta);\n return nextFilter.invoke(meta);\n }\n const header = RequestContext.getRequest()?.getHeader(AUTHORIZATION_HEADER);\n if (auth.methods[0].kind === 'public') {\n await this.bestEffortJwt(header);\n this.callerContext.reconcileWireTrust(false);\n return nextFilter.invoke(meta);\n }\n const selected = await this.authenticateAlternatives(auth.methods, header, meta);\n this.applySurface(selected);\n this.callerContext.reconcileWireTrust(AuthFilter.verifiesCaller(selected));\n return nextFilter.invoke(meta);\n }\n\n /**\n * Try each declared credential in order. The endpoint's OIDC methods (one `oidcSameProject()` and\n * one `oidcSvcs([...])` may pair) are ONE bearer credential, so they are ONE alternative, tried at\n * the position of the first: the token is verified once against all of them.\n */\n private async authenticateAlternatives(methods: readonly AuthMode[], header: string | undefined, meta: MethodMeta): Promise<AuthMode> {\n const oidc = AuthFilter.oidcMethodsOf(methods);\n const alternatives = methods.filter((method: AuthMode) => method.kind !== 'oidc' || method === oidc?.[0]);\n for (const method of alternatives) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- failed credentials may try another declared authenticator; invariants propagate\n try {\n await this.authenticateMethod(method, header, meta, oidc);\n return method;\n } catch (err: unknown) { // webpieces-disable no-any-unknown -- structured credential rejection at the boundary\n const error = toError(err);\n if (!(error instanceof ApiUnauthorizedError) || alternatives.length === 1) throw error;\n }\n }\n throw new ApiUnauthorizedError('No declared credential mechanism authenticated this request.');\n }\n\n /** The endpoint's OIDC methods in declaration order, or undefined when it declares none. */\n // webpieces-disable no-function-outside-class -- static pure reader of the AuthMode list, kept beside its only caller\n private static oidcMethodsOf(methods: readonly AuthMode[]): OidcAuthMethods | undefined {\n const oidc = methods.filter((method: AuthMode): method is OidcAuthMethod => method.kind === 'oidc');\n const first = oidc[0];\n return first === undefined ? undefined : [first, ...oidc.slice(1)];\n }\n\n private async authenticateMethod(mode: AuthMode, header: string | undefined, meta: MethodMeta, oidc: OidcAuthMethods | undefined): Promise<void> {\n switch (mode.kind) {\n case 'jwt': await this.enforceJwt(header, meta.routeMeta); return;\n case 'oidc': await this.enforceOidc(header, oidc ?? [mode]); return;\n case 'shared-secret':\n this.enforceSharedSecret(this.credential(header, SHARED_SECRET_SCHEME), mode.secretKey, meta.routeMeta);\n RequestContext.putTrusted(VERIFIED_MACHINE_CALLER, new VerifiedMachineCaller('shared-secret', mode.secretKey));\n return;\n case 'vendorAuth': await this.enforceVendorAuth(mode.name, meta); return;\n case 'apikey': await this.enforceApiKey(mode.regime, meta); return;\n case 'public': throw new ApiImplementationError('Public is not a protected auth alternative.');\n }\n }\n\n /**\n * Stamp WHICH KIND OF CALLER this is, from the auth mode that matched — `jwt()` is a browser\n * GUI, `apiKey(...)` is an external partner. See {@link Surface}.\n *\n * SET AT THE EDGE ONLY. The surface belongs to the ORIGINAL caller, so a hop that already has one\n * INHERITS it unchanged and this returns immediately. Two different callers put a value there\n * before this runs, and neither may be overwritten:\n *\n * - the MCP bridge, which stamps `llm` before invoking a `@WpAuthorization` tool through `ApiFactory`\n * — that in-process hop still runs this filter, and its HTTP auth mode says nothing about the\n * model on the other end;\n * - an OIDC (`oidcSameProject()` / `oidcSvcs(...)`) service-to-service hop, whose caller's surface arrived on the wire. OIDC is an\n * internal hop, NOT a surface of its own, so it deliberately maps to `undefined` below and the\n * propagated value survives.\n *\n * Running BEFORE {@link reconcileWireTrust} is what makes the trusted-header rule work for this\n * key: on a `jwt()` edge a caller who also SENDS `x-wp-surface` must match the value the\n * framework just derived, and on a public route nothing vouches for it at all, so it is a 401.\n */\n private applySurface(mode: AuthMode): void {\n if (RequestContext.getTrusted(WebpiecesCoreHeaders.SURFACE) !== undefined) {\n return;\n }\n const surface = AuthFilter.surfaceFor(mode);\n if (surface !== undefined) {\n RequestContext.putTrusted(WebpiecesCoreHeaders.SURFACE, surface);\n }\n }\n\n /**\n * The auth mode -> {@link Surface} table, and the ONE place it is written down.\n *\n * `undefined` means \"this mode is not a surface\", which is a different statement from \"unknown\":\n * `oidc` and `shared-secret` are internal hops that RELAY somebody else's surface, `vendorAuth`'s\n * sender is a vendor with no webpieces surface at all, and `local-only` is a developer's own box.\n *\n * An exhaustive switch with NO `default`, like {@link verifiesCaller} and\n * `DestinationTrust.forAuthMode`: a new AuthMode kind must fail to compile here rather than\n * silently landing on `gui` (which would publish 266 to a partner) or on `public-api` (which would\n * publish a real 4xx to a GUI).\n */\n // webpieces-disable no-function-outside-class -- static pure mapping from the AuthMode union, kept beside its only caller (mirrors verifiesCaller)\n private static surfaceFor(mode: AuthMode): Surface | undefined {\n switch (mode.kind) {\n case 'jwt':\n return 'gui';\n case 'apikey':\n return 'public-api';\n case 'oidc':\n case 'shared-secret':\n case 'vendorAuth':\n case 'public':\n return undefined;\n }\n }\n\n /**\n * `vendorAuth(name)`: hand the app's {@link VendorAuthVerifier} the verbatim request and let it call the\n * VENDOR's own validator. A missing verifier, or one not declaring `name`, is not a case here: it\n * failed STARTUP ({@link assertRouteVerifiable}). Two ways to fail, both 401, both before the\n * controller is entered:\n *\n * 1. NO raw request — the transport kept no bytes. `assertEveryVendorAuthEndpointRetainsRawBody`\n * normally makes this a startup error, so reaching it means either a hand-registered route or\n * an in-process caller (a spec) that published an HttpRequest with no {@link RawRequest}. The\n * message says which fix applies rather than leaving a bare 401.\n * 2. The verifier threw — the credential did not verify (including a name its own dispatch does\n * not know: that stays the request-time backstop).\n *\n * Case 1 is checked HERE and nowhere else. {@link hasRawBytes} narrows the request to\n * {@link RawHttpRequest} at this one gate, so the verifier's signature promises `raw` is present and\n * no vendor implementation ever writes `raw!` or a guard of its own.\n */\n private async enforceVendorAuth(name: string, meta: MethodMeta): Promise<void> {\n const verifier = this.requireVendorVerifier(name, meta.routeMeta);\n const request = RequestContext.getRequest();\n if (!this.hasRawBytes(request)) {\n log.warn(\n `Refusing vendorAuth('${name}') endpoint ${meta.routeMeta.path}: the inbound request carries ` +\n `no raw bytes. Declare @Endpoint(POST, path, WRITE, EXTERNAL_INBOUND, { calledBy: '${name}', rawBody: true }); a ` +\n `spec driving this route in-process must publish an HttpRequest built with a RawRequest.`,\n );\n throw new ApiUnauthorizedError(\n 'Vendor credential cannot be verified: no raw request was retained',\n );\n }\n // Throws ApiUnauthorizedError to deny. On success the vendor account the signature proved is\n // stamped through the SAME path a jwt or api-key caller takes.\n const caller = await verifier.verify(name, request);\n this.callerContext.publish(caller);\n }\n\n /**\n * The ONE place the framework decides a request carries the verbatim bytes. A TYPE PREDICATE, so\n * the `true` branch hands {@link enforceVendorAuth} a {@link RawHttpRequest} with no cast and no\n * non-null assertion — the bad state stops being representable past this line rather than being\n * re-thrown about by every hook.\n */\n private hasRawBytes(request: HttpRequest | undefined): request is RawHttpRequest {\n return request?.raw !== undefined;\n }\n\n /**\n * `apiKey(regime, credentials)`: hand the app's {@link ApiKeyHook} the regime name and the inbound\n * headers and let it look the CUSTOMER's key up. The declared `credentials` are NOT read here — they\n * describe the contract for generators; the hook owns extraction. A missing hook is not a case here:\n * it failed STARTUP ({@link assertRouteVerifiable}). Two ways to fail, both 401, both before the\n * controller:\n *\n * 1. NO inbound request in scope — there are no headers to read, so there is nothing to verify. That\n * means a caller drove this route without publishing an HttpRequest; the message says so rather\n * than leaving a bare 401.\n * 2. The hook threw — the key, or the key/organization pair, did not check out.\n *\n * On success the hook's {@link AuthenticatedCaller} is stamped exactly as a jwt parse's is, which is\n * what puts the resolved organization into `RequestContext` for every downstream repository call.\n */\n private async enforceApiKey(regime: string, meta: MethodMeta): Promise<void> {\n const hook = this.requireApiKeyHook(regime, meta.routeMeta);\n const request = RequestContext.getRequest();\n if (!request) {\n log.warn(\n `Refusing apiKey('${regime}') endpoint ${meta.routeMeta.path}: no inbound HttpRequest is ` +\n `in scope, so the hook has no headers to read. A spec driving this route in-process must ` +\n `publish an HttpRequest carrying the api-key headers.`,\n );\n throw new ApiUnauthorizedError(\n 'API key cannot be verified: no inbound request was published',\n );\n }\n // Throws ApiUnauthorizedError to deny. The hook gets the WHOLE request so it can cross-check\n // the key against a second header (the organization the customer is acting for).\n const caller = await hook.verifyApiKey(regime, request);\n this.callerContext.publish(caller);\n }\n\n /**\n * Does this mode authenticate the CALLER ITSELF (as opposed to a user, or nobody)? The INBOUND\n * twin of {@link DestinationTrust.forAuthMode}, and deliberately the same question: the client\n * omits trusted keys for a destination that cannot verify it, and the server rejects trusted keys\n * on a route that cannot verify the sender. One rule, two ends — if they disagreed, every call\n * would fail with a 401 that looks like a framework bug.\n *\n * - `oidc` / `shared-secret` → TRUE. An internal service is on the other end and the trusted\n * context it forwarded may be believed. This is what makes cross-service identity propagation\n * work.\n * - `jwt` / `public` → FALSE. A user JWT proves who the USER is; the SENDER is still whoever\n * holds the token, i.e. a browser.\n * - `local-only` → FALSE. It verifies WHERE WE ARE RUNNING, not who is calling — anything on\n * localhost reaches it, and it has no authenticator, so nothing can ever vouch for an inbound\n * trusted header. Any such header therefore rejects the request, which is exactly right.\n *\n * - `apikey` → FALSE. See the comment on that branch: the sender is a CUSTOMER.\n *\n * An exhaustive switch with NO `default`, returning on every branch: a new AuthMode kind is a\n * COMPILE error here (TS7030, no ending return) rather than silently landing on one posture. The\n * boolean expression this replaced defaulted every future mode to \"not verified\" — the safe\n * answer, but arrived at by accident rather than by decision.\n */\n // webpieces-disable no-function-outside-class -- static pure mapping from the AuthMode union, kept beside its only caller (mirrors DestinationTrust.forAuthMode)\n private static verifiesCaller(mode: AuthMode): boolean {\n switch (mode.kind) {\n case 'oidc':\n case 'shared-secret':\n return true;\n case 'jwt':\n case 'public':\n // `vendorAuth` DOES authenticate its sender — but the sender is an outside VENDOR, not a\n // peer in this repo, and a vendor neither speaks nor forwards webpieces context headers.\n // So there is no forwarded identity to believe, and admitting one would mean trusting a\n // key a vendor's payload could carry. Same answer as the OUTBOUND half\n // (DestinationTrust.forAuthMode), which is the invariant that keeps the two ends agreeing.\n case 'vendorAuth':\n // `apikey` authenticates the SENDER — but the sender is a CUSTOMER's codebase, not a peer\n // service in this repo, so its forwarded trusted context is exactly what must NOT be\n // believed: admitting it would let a partner assert another customer's org id on the wire.\n // The hook's OWN derived entries still land (applyAuthenticatedCaller), and reconcileWireTrust then\n // admits an inbound trusted header only when the hook independently derived the same value.\n case 'apikey':\n return false;\n }\n }\n\n /**\n * `@WpLocalOnly`: serve only on a developer's machine, and off-local behave EXACTLY as if the\n * endpoint did not exist.\n *\n * WHY 404 AND NOT THE 403 APPS HAND-ROLLED. Off-local the route is not registered at all\n * (`ApiRoutingFactory` skips it), so the ordinary way to reach this path already answers 404. A\n * 403 from here would be a DIFFERENT answer from the same framework for the same endpoint, and\n * the difference is itself the leak: 403 confirms \"this path exists in production, you merely\n * lack permission\", which is a map of the dev-only surface for anyone probing. A local-only\n * endpoint should not admit it exists. Both gates therefore return the same 404, and this one is\n * the backstop for routes registered by hand through `RouteBuilder` rather than by\n * `ApiRoutingFactory`.\n *\n * The log line names WHICH reason applies, because \"you are deployed\" and \"nobody declared a\n * locality\" have completely different fixes and both look like a bare 404 from outside.\n */\n private enforceLocalOnly(meta: MethodMeta): void {\n if (RuntimeLocality.isLocalDevelopment()) {\n return;\n }\n log.warn(\n `Refusing @WpLocalOnly endpoint ${meta.routeMeta.path}: ` +\n (RuntimeLocality.isDeclared()\n ? 'this process declared itself DEPLOYED.'\n : 'no startup declared a RuntimeLocality, so this process is treated as DEPLOYED. ' +\n 'Pass the locality into RuntimeSetupOptions if this really is a developer machine.'),\n );\n // Same shape as an unregistered route — see the method doc for why this is not a 403.\n throw new ApiEndpointNotFoundError(`No endpoint at ${meta.routeMeta.path}`);\n }\n\n private async enforceJwt(header: string | undefined, routeMeta: RouteMetadata): Promise<void> {\n const hook = this.requireJwtHook(routeMeta);\n const token = this.credential(header, BEARER_SCHEME);\n if (!token) throw new ApiUnauthorizedError('Authentication required');\n this.callerContext.publish(await hook.parseJwt(token));\n }\n\n private async enforceOidc(header: string | undefined, methods: OidcAuthMethods): Promise<void> {\n const token = this.credential(header, BEARER_SCHEME);\n if (!token) throw new ApiUnauthorizedError('Missing OIDC bearer token');\n const identity = this.oidcHook\n ? await this.oidcHook.verifyOidc(token, methods)\n : await this.oidcVerifier.verify(token, methods);\n if (!identity) throw new ApiImplementationError('OIDC verifier must return its verified machine identity.');\n RequestContext.putTrusted(VERIFIED_MACHINE_CALLER, new VerifiedMachineCaller(identity.mechanism, identity.identity));\n }\n\n /** `provided` is the Authorization bearer value — the secret itself, same header as a JWT. */\n private enforceSharedSecret(provided: string | undefined, secretKey: string, routeMeta: RouteMetadata): void {\n const accepted = this.requireSharedSecrets(secretKey, routeMeta);\n if (!provided || !this.matchesEither(provided, accepted)) {\n throw new ApiUnauthorizedError(\n 'Invalid shared secret for sharedSecret(...) endpoint',\n );\n }\n }\n\n /** EITHER secret1 or secret2 passes — the rotation window. Constant-time on each non-empty slot. */\n private matchesEither(provided: string, accepted: SharedSecrets): boolean {\n return (\n (accepted.secret1 !== '' && this.constantTimeEquals(provided, accepted.secret1)) ||\n (accepted.secret2 !== '' && this.constantTimeEquals(provided, accepted.secret2))\n );\n }\n\n /** Parse a JWT if one is present, else do nothing — used on public routes; never throws. */\n private async bestEffortJwt(header: string | undefined): Promise<void> {\n const token = this.credential(header, BEARER_SCHEME);\n if (!this.jwtHook || !token) {\n return;\n }\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- best-effort on a public route: a bad/absent token just means \"not logged in\", must not fail the request\n try {\n this.callerContext.publish(await this.jwtHook.parseJwt(token));\n } catch (err: unknown) {\n const error = toError(err);\n if (!(error instanceof ApiUnauthorizedError)) throw error;\n log.debug(\n 'Best-effort JWT parse on a public endpoint failed (treating as anonymous): ',\n error,\n );\n }\n }\n\n /**\n * The credential value IF the header carries the expected scheme, else undefined.\n *\n * Strict: a bare value with no scheme, or a value under the WRONG scheme (a shared secret sent\n * where a JWT is expected), yields undefined and the caller 401s.\n */\n private credential(header: string | undefined, scheme: string): string | undefined {\n if (!header) {\n return undefined;\n }\n const prefix = `${scheme} `;\n return header.startsWith(prefix) ? header.substring(prefix.length) : undefined;\n }\n\n private constantTimeEquals(a: string, b: string): boolean {\n const bufA = Buffer.from(a, 'utf8');\n const bufB = Buffer.from(b, 'utf8');\n if (bufA.length !== bufB.length) {\n return false;\n }\n return timingSafeEqual(bufA, bufB);\n }\n}\n"]}
1
+ {"version":3,"file":"AuthFilter.js","sourceRoot":"","sources":["../../../../../../packages/http/http-routing/src/filters/AuthFilter.ts"],"names":[],"mappings":";;;;;AAAA,yCAA6C;AAC7C,0DAOiC;AACjC,oDAa8B;AAC9B,oDAAuD;AAGvD,8CAAuE;AACvE,4CAAgG;AAChG,8EAA2E;AAC3E,4DAAsF;AACtF,gEAA6D;AAC7D,gEAAgE;AAChE,4EAAyE;AAEzE,MAAM,GAAG,GAAG,sBAAU,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,oBAAoB,GAAG,eAAe,CAAC;AAE7C;;;;;;;;GAQG;AACH,MAAM,aAAa,GAAG,QAAQ,CAAC;AAC/B,MAAM,oBAAoB,GAAG,WAAW,CAAC;AAEzC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAGI,IAAM,UAAU,kBAAhB,MAAM,UAAW,SAAQ,kBAAuC;IAMjB;IAII;IAGH;IAGC;IAM/B;IAIkC;IAzBtC,aAAa,GAAG,IAAI,uDAA0B,EAAE,CAAC;IAElE,YAGkD,YAAiC,EAI7B,UAAuB,EAG1B,OAAwB,EAGvB,QAAmB,EAMlD,kBAAuC,EAIL,UAAuB;QAE1E,KAAK,EAAE,CAAC;QAtBsC,iBAAY,GAAZ,YAAY,CAAqB;QAI7B,eAAU,GAAV,UAAU,CAAa;QAG1B,YAAO,GAAP,OAAO,CAAiB;QAGvB,aAAQ,GAAR,QAAQ,CAAW;QAMlD,uBAAkB,GAAlB,kBAAkB,CAAqB;QAIL,eAAU,GAAV,UAAU,CAAa;IAG9E,CAAC;IAED;;;;;;;;;;;;;OAaG;IACH,qBAAqB,CAAC,SAAwB;QAC1C,MAAM,IAAI,GAAG,SAAS,CAAC,QAAQ,CAAC;QAChC,IAAI,CAAC,IAAI;YACL,MAAM,IAAI,kCAAsB,CAAC,mDAAmD,CAAC,CAAC;QAC1F,KAAK,MAAM,MAAM,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAChC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;gBAClB,KAAK,KAAK;oBACN,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;oBAC/B,MAAM;gBACV,KAAK,eAAe;oBAChB,IAAI,CAAC,oBAAoB,CAAC,MAAM,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;oBACvD,MAAM;gBACV,KAAK,YAAY;oBACb,IAAI,CAAC,oBAAoB,CACrB,IAAI,CAAC,qBAAqB,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,EAClD,MAAM,CAAC,IAAI,EACX,SAAS,CACZ,CAAC;oBACF,MAAM;gBACV,KAAK,QAAQ;oBACT,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;oBACjD,MAAM;gBACV,KAAK,MAAM,CAAC;gBACZ,KAAK,QAAQ;oBACT,MAAM;YACd,CAAC;QACL,CAAC;IACL,CAAC;IAED,2HAA2H;IACnH,MAAM,CAAC,UAAU,CAAC,SAAwB;QAC9C,OAAO,GAAG,SAAS,CAAC,OAAO,IAAI,SAAS,CAAC,mBAAmB,IAAI,YAAY,IAAI,SAAS,CAAC,UAAU,KAAK,SAAS,CAAC,UAAU,IAAI,SAAS,CAAC,IAAI,GAAG,CAAC;IACvJ,CAAC;IAEO,YAAY,CAChB,SAAwB,EACxB,IAAY,EACZ,OAAe,EACf,QAAgB;QAEhB,MAAM,QAAQ,GAAG,YAAU,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAClD,OAAO,IAAI,qDAAyB,CAChC,YAAY,QAAQ,aAAa,IAAI,SAAS,OAAO,wCAAwC;YACzF,+BAA+B,QAAQ,IAAI;YAC3C,cAAc,IAAI,oEAAoE,EAC1F,QAAQ,EACR,IAAI,CACP,CAAC;IACN,CAAC;IAEO,cAAc,CAAC,SAAwB;QAC3C,IAAI,IAAI,CAAC,OAAO;YAAE,OAAO,IAAI,CAAC,OAAO,CAAC;QACtC,MAAM,IAAI,CAAC,YAAY,CACnB,SAAS,EACT,OAAO,EACP,qBAAqB,EACrB,2DAA2D,CAC9D,CAAC;IACN,CAAC;IAEO,oBAAoB,CAAC,SAAiB,EAAE,SAAwB;QACpE,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;QAC/B,MAAM,IAAI,GAAG,iBAAiB,SAAS,IAAI,CAAC;QAC5C,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,MAAM,IAAI,CAAC,YAAY,CACnB,SAAS,EACT,IAAI,EACJ,wBAAwB,EACxB,wHAAwH,SAAS,0CAA0C,CAC9K,CAAC;QACN,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;QACjD,IAAI,QAAQ;YAAE,OAAO,QAAQ,CAAC;QAC9B,MAAM,IAAI,CAAC,YAAY,CACnB,SAAS,EACT,IAAI,EACJ,yBAAyB,MAAM,CAAC,WAAW,CAAC,IAAI,iCAAiC,SAAS,GAAG,EAC7F,IAAI,SAAS,yEAAyE,MAAM,CAAC,WAAW,CAAC,IAAI,sDAAsD,CACtK,CAAC;IACN,CAAC;IAEO,qBAAqB,CAAC,IAAY,EAAE,SAAwB;QAChE,MAAM,QAAQ,GAAG,IAAI,CAAC,kBAAkB,CAAC;QACzC,MAAM,IAAI,GAAG,eAAe,IAAI,IAAI,CAAC;QACrC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACZ,MAAM,IAAI,CAAC,YAAY,CACnB,SAAS,EACT,IAAI,EACJ,yFAAyF,EACzF,kFAAkF,CACrF,CAAC;QACN,CAAC;QACD,OAAO,QAAQ,CAAC;IACpB,CAAC;IAED,iGAAiG;IACzF,oBAAoB,CACxB,QAA4B,EAC5B,IAAY,EACZ,SAAwB;QAExB,MAAM,SAAS,GAAG,QAAQ,CAAC,oBAAoB,EAAE,CAAC;QAClD,IAAI,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;YAAE,OAAO;QACrC,MAAM,IAAI,GAAG,eAAe,IAAI,IAAI,CAAC;QACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC,CAAS,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACnE,MAAM,IAAI,CAAC,YAAY,CACnB,SAAS,EACT,IAAI,EACJ,iCAAiC,QAAQ,CAAC,WAAW,CAAC,IAAI,cAAc,QAAQ,0DAA0D,IAAI,GAAG,EACjJ,IAAI,IAAI,QAAQ,QAAQ,CAAC,WAAW,CAAC,IAAI,oCAAoC,QAAQ,MAAM,IAAI,mBAAmB,IAAI,mBAAmB,CAC5I,CAAC;IACN,CAAC;IAEO,iBAAiB,CAAC,MAAc,EAAE,SAAwB;QAC9D,IAAI,IAAI,CAAC,UAAU;YAAE,OAAO,IAAI,CAAC,UAAU,CAAC;QAC5C,MAAM,IAAI,CAAC,YAAY,CACnB,SAAS,EACT,WAAW,MAAM,iBAAiB,EAClC,wBAAwB,EACxB,kEAAkE,CACrE,CAAC;IACN,CAAC;IAED,iGAAiG;IACxF,KAAK,CAAC,MAAM,CACjB,IAAgB,EAChB,UAAoD;QAEpD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC;QACrC,IAAI,CAAC,IAAI;YACL,MAAM,IAAI,kCAAsB,CAAC,mDAAmD,CAAC,CAAC;QAC1F,IAAI,IAAI,CAAC,SAAS,CAAC,SAAS;YAAE,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,CAAC;QAC1D,IAAI,IAAI,CAAC,wBAAwB,EAAE,CAAC;YAChC,IAAI,CAAC,wBAAwB,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;YAC3D,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,MAAM,GAAG,6BAAc,CAAC,UAAU,EAAE,EAAE,SAAS,CAAC,oBAAoB,CAAC,CAAC;QAC5E,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACpC,MAAM,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YACjC,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;YAC7C,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QACnC,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;QACjF,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5B,IAAI,CAAC,aAAa,CAAC,kBAAkB,CAAC,YAAU,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC,CAAC;QAC3E,OAAO,UAAU,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;IAED;;;;OAIG;IACK,KAAK,CAAC,wBAAwB,CAClC,OAA4B,EAC5B,MAA0B,EAC1B,IAAgB;QAEhB,MAAM,IAAI,GAAG,YAAU,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QAC/C,MAAM,YAAY,GAAG,OAAO,CAAC,MAAM,CAC/B,CAAC,MAAgB,EAAE,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC,CAAC,CAAC,CACvE,CAAC;QACF,KAAK,MAAM,MAAM,IAAI,YAAY,EAAE,CAAC;YAChC,iJAAiJ;YACjJ,IAAI,CAAC;gBACD,MAAM,IAAI,CAAC,kBAAkB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC1D,OAAO,MAAM,CAAC;YAClB,CAAC;YAAC,OAAO,GAAY,EAAE,CAAC;gBACpB,sFAAsF;gBACtF,MAAM,KAAK,GAAG,IAAA,mBAAO,EAAC,GAAG,CAAC,CAAC;gBAC3B,IAAI,CAAC,CAAC,KAAK,YAAY,gCAAoB,CAAC,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;oBACrE,MAAM,KAAK,CAAC;YACpB,CAAC;QACL,CAAC;QACD,MAAM,IAAI,gCAAoB,CAC1B,8DAA8D,CACjE,CAAC;IACN,CAAC;IAED,4FAA4F;IAC5F,sHAAsH;IAC9G,MAAM,CAAC,aAAa,CAAC,OAA4B;QACrD,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CACvB,CAAC,MAAgB,EAA4B,EAAE,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CACzE,CAAC;QACF,MAAM,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACtB,OAAO,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IACvE,CAAC;IAEO,KAAK,CAAC,kBAAkB,CAC5B,IAAc,EACd,MAA0B,EAC1B,IAAgB,EAChB,IAAiC;QAEjC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,KAAK,KAAK;gBACN,MAAM,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;gBAC9C,OAAO;YACX,KAAK,MAAM;gBACP,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;gBAC/C,OAAO;YACX,KAAK,eAAe;gBAChB,IAAI,CAAC,mBAAmB,CACpB,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,oBAAoB,CAAC,EAC7C,IAAI,CAAC,SAAS,EACd,IAAI,CAAC,SAAS,CACjB,CAAC;gBACF,6BAAc,CAAC,UAAU,CACrB,2CAAuB,EACvB,IAAI,yCAAqB,CAAC,eAAe,EAAE,IAAI,CAAC,SAAS,CAAC,CAC7D,CAAC;gBACF,OAAO;YACX,KAAK,YAAY;gBACb,MAAM,IAAI,CAAC,iBAAiB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;gBAC9C,OAAO;YACX,KAAK,QAAQ;gBACT,MAAM,IAAI,CAAC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;gBAC5C,OAAO;YACX,KAAK,QAAQ;gBACT,MAAM,IAAI,kCAAsB,CAAC,6CAA6C,CAAC,CAAC;QACxF,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;;;OAkBG;IACK,YAAY,CAAC,IAAc;QAC/B,IAAI,6BAAc,CAAC,UAAU,CAAC,gCAAoB,CAAC,OAAO,CAAC,KAAK,SAAS,EAAE,CAAC;YACxE,OAAO;QACX,CAAC;QACD,MAAM,OAAO,GAAG,YAAU,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;QAC5C,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YACxB,6BAAc,CAAC,UAAU,CAAC,gCAAoB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;QACrE,CAAC;IACL,CAAC;IAED;;;;;;;;;;;OAWG;IACH,mJAAmJ;IAC3I,MAAM,CAAC,UAAU,CAAC,IAAc;QACpC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,KAAK,KAAK;gBACN,OAAO,KAAK,CAAC;YACjB,KAAK,QAAQ;gBACT,OAAO,YAAY,CAAC;YACxB,KAAK,MAAM,CAAC;YACZ,KAAK,eAAe,CAAC;YACrB,KAAK,YAAY,CAAC;YAClB,KAAK,QAAQ;gBACT,OAAO,SAAS,CAAC;QACzB,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;;OAgBG;IACK,KAAK,CAAC,iBAAiB,CAAC,IAAY,EAAE,IAAgB;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAClE,MAAM,OAAO,GAAG,6BAAc,CAAC,UAAU,EAAE,CAAC;QAC5C,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,EAAE,CAAC;YAC7B,GAAG,CAAC,IAAI,CACJ,wBAAwB,IAAI,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,gCAAgC;gBAC1F,qFAAqF,IAAI,yBAAyB;gBAClH,yFAAyF,CAChG,CAAC;YACF,MAAM,IAAI,gCAAoB,CAC1B,mEAAmE,CACtE,CAAC;QACN,CAAC;QACD,6FAA6F;QAC7F,+DAA+D;QAC/D,MAAM,MAAM,GAAG,MAAM,QAAQ,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACpD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED;;;;;OAKG;IACK,WAAW,CAAC,OAAgC;QAChD,OAAO,OAAO,EAAE,GAAG,KAAK,SAAS,CAAC;IACtC,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACK,KAAK,CAAC,aAAa,CAAC,MAAc,EAAE,IAAgB;QACxD,MAAM,IAAI,GAAG,IAAI,CAAC,iBAAiB,CAAC,MAAM,EAAE,IAAI,CAAC,SAAS,CAAC,CAAC;QAC5D,MAAM,OAAO,GAAG,6BAAc,CAAC,UAAU,EAAE,CAAC;QAC5C,IAAI,CAAC,OAAO,EAAE,CAAC;YACX,GAAG,CAAC,IAAI,CACJ,oBAAoB,MAAM,eAAe,IAAI,CAAC,SAAS,CAAC,IAAI,8BAA8B;gBACtF,0FAA0F;gBAC1F,sDAAsD,CAC7D,CAAC;YACF,MAAM,IAAI,gCAAoB,CAC1B,8DAA8D,CACjE,CAAC;QACN,CAAC;QACD,6FAA6F;QAC7F,iFAAiF;QACjF,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACxD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACvC,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,iKAAiK;IACzJ,MAAM,CAAC,cAAc,CAAC,IAAc;QACxC,QAAQ,IAAI,CAAC,IAAI,EAAE,CAAC;YAChB,KAAK,MAAM,CAAC;YACZ,KAAK,eAAe;gBAChB,OAAO,IAAI,CAAC;YAChB,KAAK,KAAK,CAAC;YACX,KAAK,QAAQ,CAAC;YACd,yFAAyF;YACzF,yFAAyF;YACzF,wFAAwF;YACxF,uEAAuE;YACvE,2FAA2F;YAC3F,KAAK,YAAY,CAAC;YAClB,0FAA0F;YAC1F,qFAAqF;YACrF,2FAA2F;YAC3F,oGAAoG;YACpG,4FAA4F;YAC5F,KAAK,QAAQ;gBACT,OAAO,KAAK,CAAC;QACrB,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACK,gBAAgB,CAAC,IAAgB;QACrC,IAAI,2BAAe,CAAC,kBAAkB,EAAE,EAAE,CAAC;YACvC,OAAO;QACX,CAAC;QACD,GAAG,CAAC,IAAI,CACJ,kCAAkC,IAAI,CAAC,SAAS,CAAC,IAAI,IAAI;YACrD,CAAC,2BAAe,CAAC,UAAU,EAAE;gBACzB,CAAC,CAAC,wCAAwC;gBAC1C,CAAC,CAAC,iFAAiF;oBACjF,mFAAmF,CAAC,CACjG,CAAC;QACF,sFAAsF;QACtF,MAAM,IAAI,oCAAwB,CAAC,kBAAkB,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC,CAAC;IAChF,CAAC;IAEO,KAAK,CAAC,UAAU,CAAC,MAA0B,EAAE,SAAwB;QACzE,MAAM,IAAI,GAAG,IAAI,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,gCAAoB,CAAC,yBAAyB,CAAC,CAAC;QACtE,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;IAC3D,CAAC;IAEO,KAAK,CAAC,WAAW,CAAC,MAA0B,EAAE,OAAwB;QAC1E,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACrD,IAAI,CAAC,KAAK;YAAE,MAAM,IAAI,gCAAoB,CAAC,2BAA2B,CAAC,CAAC;QACxE,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ;YAC1B,CAAC,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,CAAC;YAChD,CAAC,CAAC,MAAM,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,CAAC;QACrD,IAAI,CAAC,QAAQ;YACT,MAAM,IAAI,kCAAsB,CAC5B,0DAA0D,CAC7D,CAAC;QACN,6BAAc,CAAC,UAAU,CACrB,2CAAuB,EACvB,IAAI,yCAAqB,CAAC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,QAAQ,CAAC,CACnE,CAAC;IACN,CAAC;IAED,8FAA8F;IACtF,mBAAmB,CACvB,QAA4B,EAC5B,SAAiB,EACjB,SAAwB;QAExB,MAAM,QAAQ,GAAG,IAAI,CAAC,oBAAoB,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;QACjE,IAAI,CAAC,QAAQ,IAAI,CAAC,4CAAsB,CAAC,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,EAAE,CAAC;YACzE,MAAM,IAAI,gCAAoB,CAAC,sDAAsD,CAAC,CAAC;QAC3F,CAAC;IACL,CAAC;IAED,4FAA4F;IACpF,KAAK,CAAC,aAAa,CAAC,MAA0B;QAClD,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,KAAK,EAAE,CAAC;YAC1B,OAAO;QACX,CAAC;QACD,yKAAyK;QACzK,IAAI,CAAC;YACD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,GAAY,EAAE,CAAC;YACpB,MAAM,KAAK,GAAG,IAAA,mBAAO,EAAC,GAAG,CAAC,CAAC;YAC3B,IAAI,CAAC,CAAC,KAAK,YAAY,gCAAoB,CAAC;gBAAE,MAAM,KAAK,CAAC;YAC1D,GAAG,CAAC,KAAK,CACL,6EAA6E,EAC7E,KAAK,CACR,CAAC;QACN,CAAC;IACL,CAAC;IAED;;;;;OAKG;IACK,UAAU,CAAC,MAA0B,EAAE,MAAc;QACzD,IAAI,CAAC,MAAM,EAAE,CAAC;YACV,OAAO,SAAS,CAAC;QACrB,CAAC;QACD,MAAM,MAAM,GAAG,GAAG,MAAM,GAAG,CAAC;QAC5B,OAAO,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACnF,CAAC;CACJ,CAAA;AAtiBY,gCAAU;qBAAV,UAAU;IAFtB,IAAA,wCAAyB,GAAE;IAC5B,iGAAiG;;IAOxF,mBAAA,IAAA,kBAAM,EAAC,yCAAmB,CAAC,CAAA;IAI3B,mBAAA,IAAA,oBAAQ,GAAE,CAAA;IAAE,mBAAA,IAAA,kBAAM,EAAC,wBAAW,CAAC,CAAA;IAG/B,mBAAA,IAAA,oBAAQ,GAAE,CAAA;IAAE,mBAAA,IAAA,kBAAM,EAAC,oBAAQ,CAAC,CAAA;IAG5B,mBAAA,IAAA,oBAAQ,GAAE,CAAA;IAAE,mBAAA,IAAA,kBAAM,EAAC,qBAAS,CAAC,CAAA;IAI7B,mBAAA,IAAA,oBAAQ,GAAE,CAAA;IACV,mBAAA,IAAA,kBAAM,EAAC,mCAAoB,CAAC,CAAA;IAK5B,mBAAA,IAAA,oBAAQ,GAAE,CAAA;IAAE,mBAAA,IAAA,kBAAM,EAAC,wBAAY,CAAC,CAAA;6CApB2B,yCAAmB;QAIhB,uBAAU;QAGhB,mBAAO;QAGL,oBAAQ;QAM7B,iCAAkB;QAIQ,sBAAU;GA1BrE,UAAU,CAsiBtB","sourcesContent":["import { inject, optional } from 'inversify';\nimport {\n provideFrameworkSingleton,\n HttpRequest,\n RawHttpRequest,\n RequestContext,\n VendorAuthVerifier,\n VENDOR_AUTH_VERIFIER,\n} from '@webpieces/core-context';\nimport {\n AuthMode,\n OidcAuthMethod,\n OidcAuthMethods,\n ApiEndpointNotFoundError,\n ApiUnauthorizedError,\n ApiImplementationError,\n LogManager,\n RouteMetadata,\n RuntimeLocality,\n Surface,\n toError,\n WebpiecesCoreHeaders,\n} from '@webpieces/core-util';\nimport { Filter, Service } from '@webpieces/core-util';\nimport { WpResponse } from '../WpResponse';\nimport { MethodMeta } from '../MethodMeta';\nimport { AuthConfig, AUTH_CONFIG, SharedSecrets } from '../AuthConfig';\nimport { ApiKeyHook, API_KEY_HOOK, JwtHook, JWT_HOOK, OidcHook, OIDC_HOOK } from '../AuthHooks';\nimport { AuthenticatedCallerContext } from '../AuthenticatedCallerContext';\nimport { VERIFIED_MACHINE_CALLER, VerifiedMachineCaller } from '../AuthorizationHook';\nimport { DefaultOidcVerifier } from '../DefaultOidcVerifier';\nimport { SharedSecretComparison } from '../CredentialVerifiers';\nimport { UnverifiableAuthModeError } from '../UnverifiableAuthModeError';\n\nconst log = LogManager.getLogger('AuthFilter');\n\n/**\n * The ONE credential header, read straight off the inbound HttpRequest.\n *\n * Deliberately NOT a ContextKey: a ContextKey with an httpHeader is a TRANSFERRED key, which would\n * put the caller's credential into RequestContext and hence onto every outbound call this service\n * makes, and onto every Cloud Task it enqueues. A credential belongs to ONE request hop.\n */\nconst AUTHORIZATION_HEADER = 'authorization';\n\n/**\n * The scheme (first word of the Authorization value) names WHICH credential follows, so a secret\n * can never be mistaken for a token, nor accepted where the other was expected:\n *\n * Authorization: Bearer <user JWT | service OIDC token>\n * Authorization: Webpieces <sharedSecret(...) value>\n *\n * The scheme is REQUIRED. A bare value with no scheme is rejected.\n */\nconst BEARER_SCHEME = 'Bearer';\nconst SHARED_SECRET_SCHEME = 'Webpieces';\n\n/**\n * AuthFilter - the ONE framework auth filter, auto-installed just below the error filter on every\n * route. It is TRANSPORT-NEUTRAL: it reads the raw credential from the {@link HttpRequest} in\n * RequestContext (never express), so the SAME check runs over HTTP and via createApiClient.\n *\n * It enforces the endpoint's AuthMode from separately-bound pieces, each OPTIONAL except the OIDC\n * default. An app binds only the pieces its SERVED routes declare — and a served route that declares\n * a mode whose piece is absent FAILS STARTUP ({@link assertRouteVerifiable}, #1362), so none of the\n * rows below is ever \"not enabled\" at request time:\n * - shared-secret → constant-time compare vs the {@link AuthConfig} secret VALUE (state). Startup\n * requires an AuthConfig holding the route's key.\n * - jwt → the bound {@link JwtHook} (`parseJwt`, awaited — an app's\n * strategy may reach a JWKS or a datastore). Startup requires a JwtHook: JWT needs\n * an app secret + payload shape.\n * - oidc → the bound {@link OidcHook} if any, else the framework {@link DefaultOidcVerifier}\n * run DIRECTLY — so a server that wires NOTHING still verifies Google OIDC against\n * the endpoint's caller scope (`oidcSameProject()` or `oidcSvcs([...])`).\n * - vendorAuth → the bound {@link VendorAuthVerifier} `verify`s the VENDOR's credential over the\n * retained raw request. Startup requires a verifier whose `supportedVendorNames()` lists the\n * route's name: an unverified vendor call is never waved through because wiring was\n * forgotten. The INBOUND role only — this filter never depends on the outbound\n * VendorAuthMinter (#1358).\n * - apikey → the bound {@link ApiKeyHook} looks the CUSTOMER's key up (async, over the whole\n * header set) and returns the context to seed. Startup requires an ApiKeyHook.\n * - public → BEST-EFFORT jwt parse (only if a JwtHook is bound): stamp the user's context so\n * a logged-out page still knows who is logged in; never fails.\n * - local-only → serve only when {@link RuntimeLocality} says this process is a developer's\n * machine; otherwise 404, indistinguishable from the route not existing (which,\n * off-local, it does not — `ApiRoutingFactory` never registered it).\n *\n * Zero wiring = OIDC just works; an app only binds the hooks it actually uses.\n */\n@provideFrameworkSingleton()\n// webpieces-disable no-any-unknown -- Filter generic params use unknown for response flexibility\nexport class AuthFilter extends Filter<MethodMeta, WpResponse<unknown>> {\n private readonly callerContext = new AuthenticatedCallerContext();\n\n constructor(\n // Framework default, always available — verifies Google OIDC with zero app wiring.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- AuthFilter is DI-resolved via the esbuild/vitest path, which elides type-only imports (no design:paramtypes), so every param needs its explicit token\n @inject(DefaultOidcVerifier) private readonly oidcVerifier: DefaultOidcVerifier,\n // @optional: only bind an AuthConfig to enable sharedSecret(...) endpoints (startup refuses a\n // served one without it — see assertRouteVerifiable).\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(AUTH_CONFIG) private readonly authConfig?: AuthConfig,\n // @optional: only bind a JwtHook to enable jwt() endpoints.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(JWT_HOOK) private readonly jwtHook?: JwtHook<never>,\n // @optional: only bind an OidcHook to OVERRIDE the DefaultOidcVerifier caller policy.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(OIDC_HOOK) private readonly oidcHook?: OidcHook,\n // @optional: only bind a VendorAuthVerifier to enable vendorAuth(...) endpoints. Unbound = a\n // server serving one refuses to boot (assertRouteVerifiable), never an allow-all.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional()\n @inject(VENDOR_AUTH_VERIFIER)\n private readonly vendorAuthVerifier?: VendorAuthVerifier,\n // @optional: only bind an ApiKeyHook to enable apiKey(...) endpoints. Unbound = a server serving\n // one refuses to boot, for the same reason as the vendor verifier above.\n // webpieces-disable inject-annotation-not-needed-for-concrete-class -- see above: explicit token required for DI-resolved param\n @optional() @inject(API_KEY_HOOK) private readonly apiKeyHook?: ApiKeyHook,\n ) {\n super();\n }\n\n /**\n * STARTUP check (#1362), called by route registration for EVERY served route: each auth mode the\n * route declares must be verifiable by what is bound, or boot fails naming the endpoint, the mode\n * and the exact binding line. STRICT — a multi-method route (`[oidcSameProject(), vendorAuth('x')]`)\n * with no verifier fails too: a declared alternative nobody can verify is dead, and is almost\n * always a wiring mistake. OIDC needs no app binding (the framework verifier) and a public route\n * none.\n *\n * Reads bindings this filter already holds and nothing else: it never calls `verify`, `parseJwt`\n * or `verifyApiKey`, touches no network, and constructs nothing (#1358's cycle stays closed).\n * The request path resolves each piece through the SAME `require*` methods, so there is one\n * decision, made at boot. The one boot-only check is a verifier's `supportedVendorNames()`: at request time\n * the verifier's OWN dispatch stays the backstop for a name it does not know, and refuses (401).\n */\n assertRouteVerifiable(routeMeta: RouteMetadata): void {\n const auth = routeMeta.authMeta;\n if (!auth)\n throw new ApiImplementationError('Every endpoint requires @WpAuth or @WpAuthPublic.');\n for (const method of auth.methods) {\n switch (method.kind) {\n case 'jwt':\n this.requireJwtHook(routeMeta);\n break;\n case 'shared-secret':\n this.requireSharedSecrets(method.secretKey, routeMeta);\n break;\n case 'vendorAuth':\n this.assertVendorDeclared(\n this.requireVendorVerifier(method.name, routeMeta),\n method.name,\n routeMeta,\n );\n break;\n case 'apikey':\n this.requireApiKeyHook(method.regime, routeMeta);\n break;\n case 'oidc':\n case 'public':\n break;\n }\n }\n }\n\n // webpieces-disable no-function-outside-class -- static pure formatter of the route identity, kept beside its only callers\n private static endpointOf(routeMeta: RouteMetadata): string {\n return `${routeMeta.apiName ?? routeMeta.controllerClassName ?? 'UnknownApi'}.${routeMeta.methodName} (${routeMeta.httpMethod} ${routeMeta.path})`;\n }\n\n private unverifiable(\n routeMeta: RouteMetadata,\n mode: string,\n problem: string,\n bindLine: string,\n ): UnverifiableAuthModeError {\n const endpoint = AuthFilter.endpointOf(routeMeta);\n return new UnverifiableAuthModeError(\n `Endpoint ${endpoint} declares ${mode}, but ${problem}, so this server cannot verify it and ` +\n `refuses to start. Add:\\n ${bindLine}\\n` +\n `(Or remove ${mode} from the endpoint's @WpAuth if this server should not accept it.)`,\n endpoint,\n mode,\n );\n }\n\n private requireJwtHook(routeMeta: RouteMetadata): JwtHook<never> {\n if (this.jwtHook) return this.jwtHook;\n throw this.unverifiable(\n routeMeta,\n 'jwt()',\n 'no JwtHook is bound',\n 'binder.bind(JWT_HOOK).to(YourJwtHook).inSingletonScope();',\n );\n }\n\n private requireSharedSecrets(secretKey: string, routeMeta: RouteMetadata): SharedSecrets {\n const config = this.authConfig;\n const mode = `sharedSecret('${secretKey}')`;\n if (!config) {\n throw this.unverifiable(\n routeMeta,\n mode,\n 'no AuthConfig is bound',\n `binder.bind(AUTH_CONFIG).to(YourAuthConfig).inSingletonScope(); // YourAuthConfig extends AuthConfig and passes { '${secretKey}': new SharedSecrets(secret1, secret2) }`,\n );\n }\n const accepted = config.sharedSecrets[secretKey];\n if (accepted) return accepted;\n throw this.unverifiable(\n routeMeta,\n mode,\n `the bound AuthConfig (${config.constructor.name}) holds no SharedSecrets for '${secretKey}'`,\n `'${secretKey}': new SharedSecrets(secret1, secret2), // add to the sharedSecrets ${config.constructor.name} passes to AuthConfig, which AUTH_CONFIG is bound to`,\n );\n }\n\n private requireVendorVerifier(name: string, routeMeta: RouteMetadata): VendorAuthVerifier {\n const verifier = this.vendorAuthVerifier;\n const mode = `vendorAuth('${name}')`;\n if (!verifier) {\n throw this.unverifiable(\n routeMeta,\n mode,\n 'no VendorAuthVerifier is bound (a VENDOR_AUTH_MINTER binding only signs OUTBOUND calls)',\n 'binder.bind(VENDOR_AUTH_VERIFIER).to(YourVendorAuthVerifier).inSingletonScope();',\n );\n }\n return verifier;\n }\n\n /** Boot only: the bound verifier must DECLARE the route's vendor (#1362). Never calls verify. */\n private assertVendorDeclared(\n verifier: VendorAuthVerifier,\n name: string,\n routeMeta: RouteMetadata,\n ): void {\n const supported = verifier.supportedVendorNames();\n if (supported.includes(name)) return;\n const mode = `vendorAuth('${name}')`;\n const declared = supported.map((n: string) => `'${n}'`).join(', ');\n throw this.unverifiable(\n routeMeta,\n mode,\n `the bound VendorAuthVerifier (${verifier.constructor.name}) returns [${declared}] from supportedVendorNames(), which does not include '${name}'`,\n `'${name}' to ${verifier.constructor.name}.supportedVendorNames() (return [${declared}, '${name}']) AND handle '${name}' in its verify()`,\n );\n }\n\n private requireApiKeyHook(regime: string, routeMeta: RouteMetadata): ApiKeyHook {\n if (this.apiKeyHook) return this.apiKeyHook;\n throw this.unverifiable(\n routeMeta,\n `apiKey('${regime}', credentials)`,\n 'no ApiKeyHook is bound',\n 'binder.bind(API_KEY_HOOK).to(YourApiKeyHook).inSingletonScope();',\n );\n }\n\n // webpieces-disable no-any-unknown -- Filter generic params use unknown for response flexibility\n override async filter(\n meta: MethodMeta,\n nextFilter: Service<MethodMeta, WpResponse<unknown>>,\n ): Promise<WpResponse<unknown>> {\n const auth = meta.routeMeta.authMeta;\n if (!auth)\n throw new ApiImplementationError('Every endpoint requires @WpAuth or @WpAuthPublic.');\n if (meta.routeMeta.localOnly) this.enforceLocalOnly(meta);\n if (meta.invocationAuthentication) {\n meta.invocationAuthentication.assertTarget(meta.routeMeta);\n return nextFilter.invoke(meta);\n }\n const header = RequestContext.getRequest()?.getHeader(AUTHORIZATION_HEADER);\n if (auth.methods[0].kind === 'public') {\n await this.bestEffortJwt(header);\n this.callerContext.reconcileWireTrust(false);\n return nextFilter.invoke(meta);\n }\n const selected = await this.authenticateAlternatives(auth.methods, header, meta);\n this.applySurface(selected);\n this.callerContext.reconcileWireTrust(AuthFilter.verifiesCaller(selected));\n return nextFilter.invoke(meta);\n }\n\n /**\n * Try each declared credential in order. The endpoint's OIDC methods (one `oidcSameProject()` and\n * one `oidcSvcs([...])` may pair) are ONE bearer credential, so they are ONE alternative, tried at\n * the position of the first: the token is verified once against all of them.\n */\n private async authenticateAlternatives(\n methods: readonly AuthMode[],\n header: string | undefined,\n meta: MethodMeta,\n ): Promise<AuthMode> {\n const oidc = AuthFilter.oidcMethodsOf(methods);\n const alternatives = methods.filter(\n (method: AuthMode) => method.kind !== 'oidc' || method === oidc?.[0],\n );\n for (const method of alternatives) {\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- failed credentials may try another declared authenticator; invariants propagate\n try {\n await this.authenticateMethod(method, header, meta, oidc);\n return method;\n } catch (err: unknown) {\n // webpieces-disable no-any-unknown -- structured credential rejection at the boundary\n const error = toError(err);\n if (!(error instanceof ApiUnauthorizedError) || alternatives.length === 1)\n throw error;\n }\n }\n throw new ApiUnauthorizedError(\n 'No declared credential mechanism authenticated this request.',\n );\n }\n\n /** The endpoint's OIDC methods in declaration order, or undefined when it declares none. */\n // webpieces-disable no-function-outside-class -- static pure reader of the AuthMode list, kept beside its only caller\n private static oidcMethodsOf(methods: readonly AuthMode[]): OidcAuthMethods | undefined {\n const oidc = methods.filter(\n (method: AuthMode): method is OidcAuthMethod => method.kind === 'oidc',\n );\n const first = oidc[0];\n return first === undefined ? undefined : [first, ...oidc.slice(1)];\n }\n\n private async authenticateMethod(\n mode: AuthMode,\n header: string | undefined,\n meta: MethodMeta,\n oidc: OidcAuthMethods | undefined,\n ): Promise<void> {\n switch (mode.kind) {\n case 'jwt':\n await this.enforceJwt(header, meta.routeMeta);\n return;\n case 'oidc':\n await this.enforceOidc(header, oidc ?? [mode]);\n return;\n case 'shared-secret':\n this.enforceSharedSecret(\n this.credential(header, SHARED_SECRET_SCHEME),\n mode.secretKey,\n meta.routeMeta,\n );\n RequestContext.putTrusted(\n VERIFIED_MACHINE_CALLER,\n new VerifiedMachineCaller('shared-secret', mode.secretKey),\n );\n return;\n case 'vendorAuth':\n await this.enforceVendorAuth(mode.name, meta);\n return;\n case 'apikey':\n await this.enforceApiKey(mode.regime, meta);\n return;\n case 'public':\n throw new ApiImplementationError('Public is not a protected auth alternative.');\n }\n }\n\n /**\n * Stamp WHICH KIND OF CALLER this is, from the auth mode that matched — `jwt()` is a browser\n * GUI, `apiKey(...)` is an external partner. See {@link Surface}.\n *\n * SET AT THE EDGE ONLY. The surface belongs to the ORIGINAL caller, so a hop that already has one\n * INHERITS it unchanged and this returns immediately. Two different callers put a value there\n * before this runs, and neither may be overwritten:\n *\n * - the MCP bridge, which stamps `llm` before invoking a `@WpAuthorization` tool through `ApiFactory`\n * — that in-process hop still runs this filter, and its HTTP auth mode says nothing about the\n * model on the other end;\n * - an OIDC (`oidcSameProject()` / `oidcSvcs(...)`) service-to-service hop, whose caller's surface arrived on the wire. OIDC is an\n * internal hop, NOT a surface of its own, so it deliberately maps to `undefined` below and the\n * propagated value survives.\n *\n * Running BEFORE {@link reconcileWireTrust} is what makes the trusted-header rule work for this\n * key: on a `jwt()` edge a caller who also SENDS `x-wp-surface` must match the value the\n * framework just derived, and on a public route nothing vouches for it at all, so it is a 401.\n */\n private applySurface(mode: AuthMode): void {\n if (RequestContext.getTrusted(WebpiecesCoreHeaders.SURFACE) !== undefined) {\n return;\n }\n const surface = AuthFilter.surfaceFor(mode);\n if (surface !== undefined) {\n RequestContext.putTrusted(WebpiecesCoreHeaders.SURFACE, surface);\n }\n }\n\n /**\n * The auth mode -> {@link Surface} table, and the ONE place it is written down.\n *\n * `undefined` means \"this mode is not a surface\", which is a different statement from \"unknown\":\n * `oidc` and `shared-secret` are internal hops that RELAY somebody else's surface, `vendorAuth`'s\n * sender is a vendor with no webpieces surface at all, and `local-only` is a developer's own box.\n *\n * An exhaustive switch with NO `default`, like {@link verifiesCaller} and\n * `DestinationTrust.forAuthMode`: a new AuthMode kind must fail to compile here rather than\n * silently landing on `gui` (which would publish 266 to a partner) or on `public-api` (which would\n * publish a real 4xx to a GUI).\n */\n // webpieces-disable no-function-outside-class -- static pure mapping from the AuthMode union, kept beside its only caller (mirrors verifiesCaller)\n private static surfaceFor(mode: AuthMode): Surface | undefined {\n switch (mode.kind) {\n case 'jwt':\n return 'gui';\n case 'apikey':\n return 'public-api';\n case 'oidc':\n case 'shared-secret':\n case 'vendorAuth':\n case 'public':\n return undefined;\n }\n }\n\n /**\n * `vendorAuth(name)`: hand the app's {@link VendorAuthVerifier} the verbatim request and let it call the\n * VENDOR's own validator. A missing verifier, or one not declaring `name`, is not a case here: it\n * failed STARTUP ({@link assertRouteVerifiable}). Two ways to fail, both 401, both before the\n * controller is entered:\n *\n * 1. NO raw request — the transport kept no bytes. `assertEveryVendorAuthEndpointRetainsRawBody`\n * normally makes this a startup error, so reaching it means either a hand-registered route or\n * an in-process caller (a spec) that published an HttpRequest with no {@link RawRequest}. The\n * message says which fix applies rather than leaving a bare 401.\n * 2. The verifier threw — the credential did not verify (including a name its own dispatch does\n * not know: that stays the request-time backstop).\n *\n * Case 1 is checked HERE and nowhere else. {@link hasRawBytes} narrows the request to\n * {@link RawHttpRequest} at this one gate, so the verifier's signature promises `raw` is present and\n * no vendor implementation ever writes `raw!` or a guard of its own.\n */\n private async enforceVendorAuth(name: string, meta: MethodMeta): Promise<void> {\n const verifier = this.requireVendorVerifier(name, meta.routeMeta);\n const request = RequestContext.getRequest();\n if (!this.hasRawBytes(request)) {\n log.warn(\n `Refusing vendorAuth('${name}') endpoint ${meta.routeMeta.path}: the inbound request carries ` +\n `no raw bytes. Declare @Endpoint(POST, path, WRITE, EXTERNAL_INBOUND, { calledBy: '${name}', rawBody: true }); a ` +\n `spec driving this route in-process must publish an HttpRequest built with a RawRequest.`,\n );\n throw new ApiUnauthorizedError(\n 'Vendor credential cannot be verified: no raw request was retained',\n );\n }\n // Throws ApiUnauthorizedError to deny. On success the vendor account the signature proved is\n // stamped through the SAME path a jwt or api-key caller takes.\n const caller = await verifier.verify(name, request);\n this.callerContext.publish(caller);\n }\n\n /**\n * The ONE place the framework decides a request carries the verbatim bytes. A TYPE PREDICATE, so\n * the `true` branch hands {@link enforceVendorAuth} a {@link RawHttpRequest} with no cast and no\n * non-null assertion — the bad state stops being representable past this line rather than being\n * re-thrown about by every hook.\n */\n private hasRawBytes(request: HttpRequest | undefined): request is RawHttpRequest {\n return request?.raw !== undefined;\n }\n\n /**\n * `apiKey(regime, credentials)`: hand the app's {@link ApiKeyHook} the regime name and the inbound\n * headers and let it look the CUSTOMER's key up. The declared `credentials` are NOT read here — they\n * describe the contract for generators; the hook owns extraction. A missing hook is not a case here:\n * it failed STARTUP ({@link assertRouteVerifiable}). Two ways to fail, both 401, both before the\n * controller:\n *\n * 1. NO inbound request in scope — there are no headers to read, so there is nothing to verify. That\n * means a caller drove this route without publishing an HttpRequest; the message says so rather\n * than leaving a bare 401.\n * 2. The hook threw — the key, or the key/organization pair, did not check out.\n *\n * On success the hook's {@link AuthenticatedCaller} is stamped exactly as a jwt parse's is, which is\n * what puts the resolved organization into `RequestContext` for every downstream repository call.\n */\n private async enforceApiKey(regime: string, meta: MethodMeta): Promise<void> {\n const hook = this.requireApiKeyHook(regime, meta.routeMeta);\n const request = RequestContext.getRequest();\n if (!request) {\n log.warn(\n `Refusing apiKey('${regime}') endpoint ${meta.routeMeta.path}: no inbound HttpRequest is ` +\n `in scope, so the hook has no headers to read. A spec driving this route in-process must ` +\n `publish an HttpRequest carrying the api-key headers.`,\n );\n throw new ApiUnauthorizedError(\n 'API key cannot be verified: no inbound request was published',\n );\n }\n // Throws ApiUnauthorizedError to deny. The hook gets the WHOLE request so it can cross-check\n // the key against a second header (the organization the customer is acting for).\n const caller = await hook.verifyApiKey(regime, request);\n this.callerContext.publish(caller);\n }\n\n /**\n * Does this mode authenticate the CALLER ITSELF (as opposed to a user, or nobody)? The INBOUND\n * twin of {@link DestinationTrust.forAuthMode}, and deliberately the same question: the client\n * omits trusted keys for a destination that cannot verify it, and the server rejects trusted keys\n * on a route that cannot verify the sender. One rule, two ends — if they disagreed, every call\n * would fail with a 401 that looks like a framework bug.\n *\n * - `oidc` / `shared-secret` → TRUE. An internal service is on the other end and the trusted\n * context it forwarded may be believed. This is what makes cross-service identity propagation\n * work.\n * - `jwt` / `public` → FALSE. A user JWT proves who the USER is; the SENDER is still whoever\n * holds the token, i.e. a browser.\n * - `local-only` → FALSE. It verifies WHERE WE ARE RUNNING, not who is calling — anything on\n * localhost reaches it, and it has no authenticator, so nothing can ever vouch for an inbound\n * trusted header. Any such header therefore rejects the request, which is exactly right.\n *\n * - `apikey` → FALSE. See the comment on that branch: the sender is a CUSTOMER.\n *\n * An exhaustive switch with NO `default`, returning on every branch: a new AuthMode kind is a\n * COMPILE error here (TS7030, no ending return) rather than silently landing on one posture. The\n * boolean expression this replaced defaulted every future mode to \"not verified\" — the safe\n * answer, but arrived at by accident rather than by decision.\n */\n // webpieces-disable no-function-outside-class -- static pure mapping from the AuthMode union, kept beside its only caller (mirrors DestinationTrust.forAuthMode)\n private static verifiesCaller(mode: AuthMode): boolean {\n switch (mode.kind) {\n case 'oidc':\n case 'shared-secret':\n return true;\n case 'jwt':\n case 'public':\n // `vendorAuth` DOES authenticate its sender — but the sender is an outside VENDOR, not a\n // peer in this repo, and a vendor neither speaks nor forwards webpieces context headers.\n // So there is no forwarded identity to believe, and admitting one would mean trusting a\n // key a vendor's payload could carry. Same answer as the OUTBOUND half\n // (DestinationTrust.forAuthMode), which is the invariant that keeps the two ends agreeing.\n case 'vendorAuth':\n // `apikey` authenticates the SENDER — but the sender is a CUSTOMER's codebase, not a peer\n // service in this repo, so its forwarded trusted context is exactly what must NOT be\n // believed: admitting it would let a partner assert another customer's org id on the wire.\n // The hook's OWN derived entries still land (applyAuthenticatedCaller), and reconcileWireTrust then\n // admits an inbound trusted header only when the hook independently derived the same value.\n case 'apikey':\n return false;\n }\n }\n\n /**\n * `@WpLocalOnly`: serve only on a developer's machine, and off-local behave EXACTLY as if the\n * endpoint did not exist.\n *\n * WHY 404 AND NOT THE 403 APPS HAND-ROLLED. Off-local the route is not registered at all\n * (`ApiRoutingFactory` skips it), so the ordinary way to reach this path already answers 404. A\n * 403 from here would be a DIFFERENT answer from the same framework for the same endpoint, and\n * the difference is itself the leak: 403 confirms \"this path exists in production, you merely\n * lack permission\", which is a map of the dev-only surface for anyone probing. A local-only\n * endpoint should not admit it exists. Both gates therefore return the same 404, and this one is\n * the backstop for routes registered by hand through `RouteBuilder` rather than by\n * `ApiRoutingFactory`.\n *\n * The log line names WHICH reason applies, because \"you are deployed\" and \"nobody declared a\n * locality\" have completely different fixes and both look like a bare 404 from outside.\n */\n private enforceLocalOnly(meta: MethodMeta): void {\n if (RuntimeLocality.isLocalDevelopment()) {\n return;\n }\n log.warn(\n `Refusing @WpLocalOnly endpoint ${meta.routeMeta.path}: ` +\n (RuntimeLocality.isDeclared()\n ? 'this process declared itself DEPLOYED.'\n : 'no startup declared a RuntimeLocality, so this process is treated as DEPLOYED. ' +\n 'Pass the locality into RuntimeSetupOptions if this really is a developer machine.'),\n );\n // Same shape as an unregistered route — see the method doc for why this is not a 403.\n throw new ApiEndpointNotFoundError(`No endpoint at ${meta.routeMeta.path}`);\n }\n\n private async enforceJwt(header: string | undefined, routeMeta: RouteMetadata): Promise<void> {\n const hook = this.requireJwtHook(routeMeta);\n const token = this.credential(header, BEARER_SCHEME);\n if (!token) throw new ApiUnauthorizedError('Authentication required');\n this.callerContext.publish(await hook.parseJwt(token));\n }\n\n private async enforceOidc(header: string | undefined, methods: OidcAuthMethods): Promise<void> {\n const token = this.credential(header, BEARER_SCHEME);\n if (!token) throw new ApiUnauthorizedError('Missing OIDC bearer token');\n const identity = this.oidcHook\n ? await this.oidcHook.verifyOidc(token, methods)\n : await this.oidcVerifier.verify(token, methods);\n if (!identity)\n throw new ApiImplementationError(\n 'OIDC verifier must return its verified machine identity.',\n );\n RequestContext.putTrusted(\n VERIFIED_MACHINE_CALLER,\n new VerifiedMachineCaller(identity.mechanism, identity.identity),\n );\n }\n\n /** `provided` is the Authorization bearer value — the secret itself, same header as a JWT. */\n private enforceSharedSecret(\n provided: string | undefined,\n secretKey: string,\n routeMeta: RouteMetadata,\n ): void {\n const accepted = this.requireSharedSecrets(secretKey, routeMeta);\n if (!provided || !SharedSecretComparison.matchesEither(provided, accepted)) {\n throw new ApiUnauthorizedError('Invalid shared secret for sharedSecret(...) endpoint');\n }\n }\n\n /** Parse a JWT if one is present, else do nothing — used on public routes; never throws. */\n private async bestEffortJwt(header: string | undefined): Promise<void> {\n const token = this.credential(header, BEARER_SCHEME);\n if (!this.jwtHook || !token) {\n return;\n }\n // eslint-disable-next-line @webpieces/no-unmanaged-exceptions -- best-effort on a public route: a bad/absent token just means \"not logged in\", must not fail the request\n try {\n this.callerContext.publish(await this.jwtHook.parseJwt(token));\n } catch (err: unknown) {\n const error = toError(err);\n if (!(error instanceof ApiUnauthorizedError)) throw error;\n log.debug(\n 'Best-effort JWT parse on a public endpoint failed (treating as anonymous): ',\n error,\n );\n }\n }\n\n /**\n * The credential value IF the header carries the expected scheme, else undefined.\n *\n * Strict: a bare value with no scheme, or a value under the WRONG scheme (a shared secret sent\n * where a JWT is expected), yields undefined and the caller 401s.\n */\n private credential(header: string | undefined, scheme: string): string | undefined {\n if (!header) {\n return undefined;\n }\n const prefix = `${scheme} `;\n return header.startsWith(prefix) ? header.substring(prefix.length) : undefined;\n }\n}\n"]}
package/src/index.d.ts CHANGED
@@ -26,14 +26,15 @@ export { JwtHook, MintedJwt, JWT_HOOK, OidcHook, OIDC_HOOK, ApiKeyHook, API_KEY_
26
26
  export { VendorAuthVerifier, VENDOR_AUTH_VERIFIER } from '@webpieces/core-context';
27
27
  export { UnverifiableAuthModeError } from './UnverifiableAuthModeError';
28
28
  export { DefaultOidcVerifier } from './DefaultOidcVerifier';
29
+ export { CredentialVerifiers, SharedSecretComparison } from './CredentialVerifiers';
29
30
  export { DefaultJwtHook, DefaultJwtMintRequest } from './DefaultJwtHook';
30
31
  export { WebpiecesRouter, WebpiecesRouterFactory, WebpiecesRouterOptions } from './WebpiecesRouter';
31
32
  export { setupRuntime, RuntimeSetupOptions } from './setupRuntime';
32
33
  export { WebpiecesConfig, WEBPIECES_CONFIG_TOKEN } from './WebpiecesConfig';
33
- export { AuthorizationHook, AuthorizationService, CanonicalUserRoles, AUTHORIZATION_HOOK, VERIFIED_MACHINE_CALLER, VerifiedMachineCaller } from './AuthorizationHook';
34
+ export { AuthorizationHook, AuthorizationService, CanonicalUserRoles, AUTHORIZATION_HOOK, VERIFIED_MACHINE_CALLER, VerifiedMachineCaller, } from './AuthorizationHook';
34
35
  export { AuthorizedApiDocument } from './AuthorizedApiDocument';
35
36
  export type { ApiDocumentObject, ApiDocumentValue } from './AuthorizedApiDocument';
36
37
  export { WiringPolicy } from '@webpieces/http-client-core';
37
38
  export { RuntimeStartupPhase, RuntimeStartupOperation } from './RuntimeStartupTiming';
38
39
  export type { RuntimeStartupObserver, RuntimeStartupSpan } from './RuntimeStartupTiming';
39
- export { LazyController, LazyImplementation, LazyFeatureTimer, LazyFeatureDeadline } from './LazyController';
40
+ export { LazyController, LazyImplementation, ApplicationImplementation, LazyFeatureTimer, LazyFeatureDeadline, } from './LazyController';
package/src/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.WRITE_IDEMPOTENT = exports.READ = exports.verbCarriesBody = exports.DELETE = exports.PATCH = exports.PUT = exports.POST = exports.GET = exports.METADATA_KEYS = exports.HttpContractMapper = exports.RouteMetadataFactory = exports.RouteMetadata = exports.AuthMeta = exports.getQueueName = exports.assertPubSubConventions = exports.assertApiKind = exports.getApiKind = exports.assertEveryEndpointHasAuthMode = exports.getAuthMeta = exports.isApiPath = exports.isRawBody = exports.isFormPost = exports.getHttpParameterDeclarations = exports.getEndpointOperation = exports.getEndpointHttpMethod = exports.getEndpointOptions = exports.getEndpoints = exports.getApiPath = exports.Queue = exports.PubSub = exports.Rpc = exports.getAuthorization = exports.AuthorizationType = exports.WpAuthorization = exports.apiKey = exports.vendorAuth = exports.sharedSecret = exports.oidcSvcs = exports.oidcSameProject = exports.jwt = exports.isLocalOnly = exports.WpLocalOnly = exports.WpAuth = exports.WpAuthPublic = exports.QueryParams = exports.QueryString = exports.QueryParam = exports.PathParam = exports.Endpoint = exports.ApiPath = void 0;
4
4
  exports.DefaultOidcVerifier = exports.UnverifiableAuthModeError = exports.VENDOR_AUTH_VERIFIER = exports.VendorAuthVerifier = exports.API_KEY_HOOK = exports.ApiKeyHook = exports.OIDC_HOOK = exports.OidcHook = exports.JWT_HOOK = exports.MintedJwt = exports.JwtHook = exports.SharedSecrets = exports.AUTHENTICATED_CALLER_KEY = exports.AuthenticatedMachineIdentity = exports.AuthenticatedCaller = exports.AUTH_CONFIG = exports.AuthConfig = exports.ApiClient = exports.PubSubBindOptions = exports.ClientBindOptions = exports.ContainerBinder = exports.NodeWiringModules = exports.FilterMatcher = exports.LogApiFilter = exports.RouteHandler = exports.MethodMeta = exports.InvocationAuthentication = exports.AuthenticatedCallerContext = exports.WpResponse = exports.RawRequest = exports.HttpRequest = exports.FilterDefinition = exports.RouteDefinition = exports.ApiRoutingFactory = exports.buildFrameworkModule = exports.provideFrameworkSingletonDefaultForApi = exports.provideFrameworkSingleton = exports.provideSingletonDefaultForApi = exports.ROUTING_METADATA_KEYS = exports.SourceFile = exports.isDocumentDesign = exports.DocumentDesign = exports.ExternalContextKeys = exports.EXTERNAL_OUTBOUND = exports.EXTERNAL_INBOUND = exports.CRON = exports.CLOUDTASKS = exports.RPC = exports.NONE = exports.WRITE = void 0;
5
- exports.LazyFeatureDeadline = exports.LazyFeatureTimer = exports.LazyImplementation = exports.LazyController = exports.RuntimeStartupOperation = exports.RuntimeStartupPhase = exports.WiringPolicy = exports.AuthorizedApiDocument = exports.VerifiedMachineCaller = exports.VERIFIED_MACHINE_CALLER = exports.AUTHORIZATION_HOOK = exports.CanonicalUserRoles = exports.AuthorizationService = exports.AuthorizationHook = exports.WEBPIECES_CONFIG_TOKEN = exports.WebpiecesConfig = exports.RuntimeSetupOptions = exports.setupRuntime = exports.WebpiecesRouterFactory = exports.WebpiecesRouter = exports.DefaultJwtMintRequest = exports.DefaultJwtHook = void 0;
5
+ exports.LazyFeatureDeadline = exports.LazyFeatureTimer = exports.ApplicationImplementation = exports.LazyImplementation = exports.LazyController = exports.RuntimeStartupOperation = exports.RuntimeStartupPhase = exports.WiringPolicy = exports.AuthorizedApiDocument = exports.VerifiedMachineCaller = exports.VERIFIED_MACHINE_CALLER = exports.AUTHORIZATION_HOOK = exports.CanonicalUserRoles = exports.AuthorizationService = exports.AuthorizationHook = exports.WEBPIECES_CONFIG_TOKEN = exports.WebpiecesConfig = exports.RuntimeSetupOptions = exports.setupRuntime = exports.WebpiecesRouterFactory = exports.WebpiecesRouter = exports.DefaultJwtMintRequest = exports.DefaultJwtHook = exports.SharedSecretComparison = exports.CredentialVerifiers = void 0;
6
6
  // Re-export API decorators from core-util for convenience
7
7
  var core_util_1 = require("@webpieces/core-util");
8
8
  Object.defineProperty(exports, "ApiPath", { enumerable: true, get: function () { return core_util_1.ApiPath; } });
@@ -151,6 +151,10 @@ var UnverifiableAuthModeError_1 = require("./UnverifiableAuthModeError");
151
151
  Object.defineProperty(exports, "UnverifiableAuthModeError", { enumerable: true, get: function () { return UnverifiableAuthModeError_1.UnverifiableAuthModeError; } });
152
152
  var DefaultOidcVerifier_1 = require("./DefaultOidcVerifier");
153
153
  Object.defineProperty(exports, "DefaultOidcVerifier", { enumerable: true, get: function () { return DefaultOidcVerifier_1.DefaultOidcVerifier; } });
154
+ // The same verifiers AuthFilter holds, for a door that is not a route (the MCP HTTP endpoint).
155
+ var CredentialVerifiers_1 = require("./CredentialVerifiers");
156
+ Object.defineProperty(exports, "CredentialVerifiers", { enumerable: true, get: function () { return CredentialVerifiers_1.CredentialVerifiers; } });
157
+ Object.defineProperty(exports, "SharedSecretComparison", { enumerable: true, get: function () { return CredentialVerifiers_1.SharedSecretComparison; } });
154
158
  // DefaultJwtHook: batteries-included HS256 JwtHook — `new DefaultJwtHook(secret)` and go.
155
159
  var DefaultJwtHook_1 = require("./DefaultJwtHook");
156
160
  Object.defineProperty(exports, "DefaultJwtHook", { enumerable: true, get: function () { return DefaultJwtHook_1.DefaultJwtHook; } });
@@ -186,6 +190,7 @@ Object.defineProperty(exports, "RuntimeStartupOperation", { enumerable: true, ge
186
190
  var LazyController_1 = require("./LazyController");
187
191
  Object.defineProperty(exports, "LazyController", { enumerable: true, get: function () { return LazyController_1.LazyController; } });
188
192
  Object.defineProperty(exports, "LazyImplementation", { enumerable: true, get: function () { return LazyController_1.LazyImplementation; } });
193
+ Object.defineProperty(exports, "ApplicationImplementation", { enumerable: true, get: function () { return LazyController_1.ApplicationImplementation; } });
189
194
  Object.defineProperty(exports, "LazyFeatureTimer", { enumerable: true, get: function () { return LazyController_1.LazyFeatureTimer; } });
190
195
  Object.defineProperty(exports, "LazyFeatureDeadline", { enumerable: true, get: function () { return LazyController_1.LazyFeatureDeadline; } });
191
196
  //# sourceMappingURL=index.js.map
package/src/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Re-export API decorators from core-util for convenience\nexport {\n ApiPath,\n Endpoint,\n PathParam,\n QueryParam,\n QueryString,\n QueryParams,\n WpAuthPublic,\n WpAuth, WpLocalOnly, isLocalOnly, jwt, oidcSameProject, oidcSvcs, sharedSecret, vendorAuth, apiKey,\n WpAuthorization, AuthorizationType, getAuthorization,\n Rpc,\n PubSub,\n Queue,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointHttpMethod,\n getEndpointOperation,\n getHttpParameterDeclarations,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n AuthMeta,\n RouteMetadata,\n RouteMetadataFactory,\n HttpContractMapper,\n METADATA_KEYS,\n ValidateImplementation,\n GET,\n POST,\n PUT,\n PATCH,\n DELETE,\n verbCarriesBody,\n READ,\n WRITE_IDEMPOTENT,\n WRITE,\n NONE,\n RPC,\n CLOUDTASKS,\n CRON,\n EXTERNAL_INBOUND,\n EXTERNAL_OUTBOUND,\n ExternalContextKeys,\n // @DocumentDesign moved to core-util (design-root marker, browser + Node);\n // re-exported here for back-compat.\n DocumentDesign,\n isDocumentDesign,\n} from '@webpieces/core-util';\nexport type {\n AuthMode,\n ApiKind,\n HttpMethod,\n EndpointOperation,\n EndpointKind,\n EndpointOptions,\n EndpointResponseType,\n} from '@webpieces/core-util';\n\n// Server-side routing decorators and utilities\nexport { SourceFile, ROUTING_METADATA_KEYS } from './decorators';\n\n// DI provider decorators moved to core-context; re-exported here for back-compat\nexport { provideSingletonDefaultForApi } from '@webpieces/core-context';\n// Framework-only DI registry (packages/** framework classes use these; see frameworkProvide.ts)\nexport {\n provideFrameworkSingleton,\n provideFrameworkSingletonDefaultForApi,\n buildFrameworkModule,\n} from '@webpieces/core-context';\n\nexport { ApiRoutingFactory } from './ApiRoutingFactory';\nexport type { ClassType } from './ClassType';\n\n// Core routing types\nexport { Routes, RouteBuilder, RouteDefinition, FilterDefinition } from './WebAppMeta';\n\n// The transport-neutral request type (defined in core-context; this is http-routing's\n// public request — a transport adapter builds one and the chain reads it from RequestContext).\nexport { HttpRequest, RawHttpRequest, RawRequest } from '@webpieces/core-context';\n\n// The INBOUND chain's response type. `Filter`, `Service` and `FilterChain` are NOT re-exported\n// here: they moved to @webpieces/core-util so the outbound client chain is the SAME abstraction\n// rather than a second spelling of it. Import them from '@webpieces/core-util'.\nexport { WpResponse } from './WpResponse';\nexport { AuthenticatedCallerContext } from './AuthenticatedCallerContext';\nexport { InvocationAuthentication } from './InvocationAuthentication';\nexport { MethodMeta } from './MethodMeta';\nexport { RouteHandler } from './RouteHandler';\n\n// LogApiFilter: the fixed OUTERMOST framework filter (auto-installed at 1,000,000 above\n// AuthFilter). Exported for reference/testing only — apps must NOT install it themselves.\nexport { LogApiFilter } from './filters/LogApiFilter';\n\n// RouteBuilderImpl (the route table + chain composer) is now INTERNAL — it is never\n// handed to upper layers. The express layer consumes ApiFactory.apiClients() instead.\n\n// Filter matching\nexport { FilterMatcher, HttpFilter } from './FilterMatcher';\n\n// The app's server-surface declaration: DI bind modules + route groups + headers.\nexport type { Wiring, AppWiring, RouteModule } from './Wiring';\nexport type { BindModule } from './BindModule';\nexport { NodeWiringModules } from './Wiring';\n// The Node binder every BindModule.configure receives.\nexport type { Binder } from './Binder';\nexport { ContainerBinder, ClientBindOptions, PubSubBindOptions } from './Binder';\n\n// The public API-surface abstraction: declare routes/filters, get them back as ApiClient[].\nexport { ApiFactory } from './ApiFactory';\nexport { ApiClient, ApiClientProxy } from './ApiClient';\n\n// Auth: the app-provided, container-bound pieces the framework AuthFilter injects.\n// - AuthConfig: shared-secret STATE (sharedSecret(...) values).\n// - JwtHook / OidcHook / VendorAuthVerifier / ApiKeyHook: OPTIONAL verification mechanisms\n// (bind only what your served routes declare; a served route declaring a mode whose piece is\n// unbound FAILS STARTUP with UnverifiableAuthModeError naming the binding, never open — #1362).\n// - DefaultOidcVerifier: the built-in Google OIDC verifier used when no OidcHook is bound.\nexport {\n AuthConfig,\n AUTH_CONFIG,\n AuthenticatedCaller,\n AuthenticatedMachineIdentity,\n AUTHENTICATED_CALLER_KEY,\n SharedSecrets,\n} from './AuthConfig';\nexport {\n JwtHook,\n MintedJwt,\n JWT_HOOK,\n OidcHook,\n OIDC_HOOK,\n ApiKeyHook,\n API_KEY_HOOK,\n} from './AuthHooks';\n// vendorAuth(name), INBOUND role: the verifier this server's AuthFilter calls. Defined in\n// @webpieces/core-context beside its OUTBOUND twin VendorAuthMinter (re-exported by\n// @webpieces/http-client-node); the two are independent DI roles, never one binding (#1358).\nexport { VendorAuthVerifier, VENDOR_AUTH_VERIFIER } from '@webpieces/core-context';\nexport { UnverifiableAuthModeError } from './UnverifiableAuthModeError';\nexport { DefaultOidcVerifier } from './DefaultOidcVerifier';\n// DefaultJwtHook: batteries-included HS256 JwtHook — `new DefaultJwtHook(secret)` and go.\nexport { DefaultJwtHook, DefaultJwtMintRequest } from './DefaultJwtHook';\n\n// Above-boundary context setup shared by every transport adapter.\n\n// Node-only router (the express-free heart: container + filter chain + in-process client)\nexport { WebpiecesRouter, WebpiecesRouterFactory, WebpiecesRouterOptions } from './WebpiecesRouter';\n\n// The ONE transport-free startup sequence (headers → logging → router → routes) → ApiFactory.\n// Reusable by any company/app and any framework adapter; a company wraps it with its own headers.\nexport { setupRuntime, RuntimeSetupOptions } from './setupRuntime';\n\n// Server configuration\nexport { WebpiecesConfig, WEBPIECES_CONFIG_TOKEN } from './WebpiecesConfig';\n\n\nexport { AuthorizationHook, AuthorizationService, CanonicalUserRoles, AUTHORIZATION_HOOK, VERIFIED_MACHINE_CALLER, VerifiedMachineCaller } from './AuthorizationHook';\nexport { AuthorizedApiDocument } from './AuthorizedApiDocument';\nexport type { ApiDocumentObject, ApiDocumentValue } from './AuthorizedApiDocument';\n\nexport { WiringPolicy } from '@webpieces/http-client-core';\n\nexport { RuntimeStartupPhase, RuntimeStartupOperation } from './RuntimeStartupTiming';\nexport type { RuntimeStartupObserver, RuntimeStartupSpan } from './RuntimeStartupTiming';\n\nexport { LazyController, LazyImplementation, LazyFeatureTimer, LazyFeatureDeadline } from './LazyController';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../../packages/http/http-routing/src/index.ts"],"names":[],"mappings":"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","sourcesContent":["// Re-export API decorators from core-util for convenience\nexport {\n ApiPath,\n Endpoint,\n PathParam,\n QueryParam,\n QueryString,\n QueryParams,\n WpAuthPublic,\n WpAuth,\n WpLocalOnly,\n isLocalOnly,\n jwt,\n oidcSameProject,\n oidcSvcs,\n sharedSecret,\n vendorAuth,\n apiKey,\n WpAuthorization,\n AuthorizationType,\n getAuthorization,\n Rpc,\n PubSub,\n Queue,\n getApiPath,\n getEndpoints,\n getEndpointOptions,\n getEndpointHttpMethod,\n getEndpointOperation,\n getHttpParameterDeclarations,\n isFormPost,\n isRawBody,\n isApiPath,\n getAuthMeta,\n assertEveryEndpointHasAuthMode,\n getApiKind,\n assertApiKind,\n assertPubSubConventions,\n getQueueName,\n AuthMeta,\n RouteMetadata,\n RouteMetadataFactory,\n HttpContractMapper,\n METADATA_KEYS,\n ValidateImplementation,\n GET,\n POST,\n PUT,\n PATCH,\n DELETE,\n verbCarriesBody,\n READ,\n WRITE_IDEMPOTENT,\n WRITE,\n NONE,\n RPC,\n CLOUDTASKS,\n CRON,\n EXTERNAL_INBOUND,\n EXTERNAL_OUTBOUND,\n ExternalContextKeys,\n // @DocumentDesign moved to core-util (design-root marker, browser + Node);\n // re-exported here for back-compat.\n DocumentDesign,\n isDocumentDesign,\n} from '@webpieces/core-util';\nexport type {\n AuthMode,\n ApiKind,\n HttpMethod,\n EndpointOperation,\n EndpointKind,\n EndpointOptions,\n EndpointResponseType,\n} from '@webpieces/core-util';\n\n// Server-side routing decorators and utilities\nexport { SourceFile, ROUTING_METADATA_KEYS } from './decorators';\n\n// DI provider decorators moved to core-context; re-exported here for back-compat\nexport { provideSingletonDefaultForApi } from '@webpieces/core-context';\n// Framework-only DI registry (packages/** framework classes use these; see frameworkProvide.ts)\nexport {\n provideFrameworkSingleton,\n provideFrameworkSingletonDefaultForApi,\n buildFrameworkModule,\n} from '@webpieces/core-context';\n\nexport { ApiRoutingFactory } from './ApiRoutingFactory';\nexport type { ClassType } from './ClassType';\n\n// Core routing types\nexport { Routes, RouteBuilder, RouteDefinition, FilterDefinition } from './WebAppMeta';\n\n// The transport-neutral request type (defined in core-context; this is http-routing's\n// public request — a transport adapter builds one and the chain reads it from RequestContext).\nexport { HttpRequest, RawHttpRequest, RawRequest } from '@webpieces/core-context';\n\n// The INBOUND chain's response type. `Filter`, `Service` and `FilterChain` are NOT re-exported\n// here: they moved to @webpieces/core-util so the outbound client chain is the SAME abstraction\n// rather than a second spelling of it. Import them from '@webpieces/core-util'.\nexport { WpResponse } from './WpResponse';\nexport { AuthenticatedCallerContext } from './AuthenticatedCallerContext';\nexport { InvocationAuthentication } from './InvocationAuthentication';\nexport { MethodMeta } from './MethodMeta';\nexport { RouteHandler } from './RouteHandler';\n\n// LogApiFilter: the fixed OUTERMOST framework filter (auto-installed at 1,000,000 above\n// AuthFilter). Exported for reference/testing only — apps must NOT install it themselves.\nexport { LogApiFilter } from './filters/LogApiFilter';\n\n// RouteBuilderImpl (the route table + chain composer) is now INTERNAL — it is never\n// handed to upper layers. The express layer consumes ApiFactory.apiClients() instead.\n\n// Filter matching\nexport { FilterMatcher, HttpFilter } from './FilterMatcher';\n\n// The app's server-surface declaration: DI bind modules + route groups + headers.\nexport type { Wiring, AppWiring, RouteModule } from './Wiring';\nexport type { BindModule } from './BindModule';\nexport { NodeWiringModules } from './Wiring';\n// The Node binder every BindModule.configure receives.\nexport type { Binder } from './Binder';\nexport { ContainerBinder, ClientBindOptions, PubSubBindOptions } from './Binder';\n\n// The public API-surface abstraction: declare routes/filters, get them back as ApiClient[].\nexport { ApiFactory } from './ApiFactory';\nexport { ApiClient, ApiClientProxy } from './ApiClient';\n\n// Auth: the app-provided, container-bound pieces the framework AuthFilter injects.\n// - AuthConfig: shared-secret STATE (sharedSecret(...) values).\n// - JwtHook / OidcHook / VendorAuthVerifier / ApiKeyHook: OPTIONAL verification mechanisms\n// (bind only what your served routes declare; a served route declaring a mode whose piece is\n// unbound FAILS STARTUP with UnverifiableAuthModeError naming the binding, never open — #1362).\n// - DefaultOidcVerifier: the built-in Google OIDC verifier used when no OidcHook is bound.\nexport {\n AuthConfig,\n AUTH_CONFIG,\n AuthenticatedCaller,\n AuthenticatedMachineIdentity,\n AUTHENTICATED_CALLER_KEY,\n SharedSecrets,\n} from './AuthConfig';\nexport {\n JwtHook,\n MintedJwt,\n JWT_HOOK,\n OidcHook,\n OIDC_HOOK,\n ApiKeyHook,\n API_KEY_HOOK,\n} from './AuthHooks';\n// vendorAuth(name), INBOUND role: the verifier this server's AuthFilter calls. Defined in\n// @webpieces/core-context beside its OUTBOUND twin VendorAuthMinter (re-exported by\n// @webpieces/http-client-node); the two are independent DI roles, never one binding (#1358).\nexport { VendorAuthVerifier, VENDOR_AUTH_VERIFIER } from '@webpieces/core-context';\nexport { UnverifiableAuthModeError } from './UnverifiableAuthModeError';\nexport { DefaultOidcVerifier } from './DefaultOidcVerifier';\n// The same verifiers AuthFilter holds, for a door that is not a route (the MCP HTTP endpoint).\nexport { CredentialVerifiers, SharedSecretComparison } from './CredentialVerifiers';\n// DefaultJwtHook: batteries-included HS256 JwtHook — `new DefaultJwtHook(secret)` and go.\nexport { DefaultJwtHook, DefaultJwtMintRequest } from './DefaultJwtHook';\n\n// Above-boundary context setup shared by every transport adapter.\n\n// Node-only router (the express-free heart: container + filter chain + in-process client)\nexport { WebpiecesRouter, WebpiecesRouterFactory, WebpiecesRouterOptions } from './WebpiecesRouter';\n\n// The ONE transport-free startup sequence (headers → logging → router → routes) → ApiFactory.\n// Reusable by any company/app and any framework adapter; a company wraps it with its own headers.\nexport { setupRuntime, RuntimeSetupOptions } from './setupRuntime';\n\n// Server configuration\nexport { WebpiecesConfig, WEBPIECES_CONFIG_TOKEN } from './WebpiecesConfig';\n\nexport {\n AuthorizationHook,\n AuthorizationService,\n CanonicalUserRoles,\n AUTHORIZATION_HOOK,\n VERIFIED_MACHINE_CALLER,\n VerifiedMachineCaller,\n} from './AuthorizationHook';\nexport { AuthorizedApiDocument } from './AuthorizedApiDocument';\nexport type { ApiDocumentObject, ApiDocumentValue } from './AuthorizedApiDocument';\n\nexport { WiringPolicy } from '@webpieces/http-client-core';\n\nexport { RuntimeStartupPhase, RuntimeStartupOperation } from './RuntimeStartupTiming';\nexport type { RuntimeStartupObserver, RuntimeStartupSpan } from './RuntimeStartupTiming';\n\nexport {\n LazyController,\n LazyImplementation,\n ApplicationImplementation,\n LazyFeatureTimer,\n LazyFeatureDeadline,\n} from './LazyController';\n"]}