@getstrata/bootstrap 0.2.48 → 0.2.49

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
@@ -28,7 +28,9 @@ import {
28
28
  import { createHttpKernel, createAppContext, coreProviders } from "@getstrata/bootstrap";
29
29
  ```
30
30
 
31
- `assertProductionSecrets` (`@getstrata/bootstrap/secretsGuard`) is env-only. It still rejects the well-known WorkHub test tokens.
31
+ `createAppContext()` does not call `assertProductionSecrets`. WorkHub calls that from `App.serve()` / `queue:work`. Sibling apps should call it only if they want those WorkHub production gates (API tokens, Stripe, SCIM, CORS).
32
+
33
+ `wrapWeb` applies the web group (CSRF + flash) and `withErrorHandling`, so CSRF `ForbiddenError` becomes an HTML 403 in `FRONTEND_MODE=server-htmx`. `wrapWebLogin` / `wrapWebRegister` include that web group plus throttle — do not wrap them with `wrapWeb` again.
32
34
 
33
35
  These subpaths remain WorkHub-oriented and are not a generic starter API: `@getstrata/bootstrap/createRoutes` (includes SCIM), `@getstrata/bootstrap/schedule`, and `@getstrata/bootstrap/createWebRoutes` (redirects `/` to `/organizations`).
34
36
 
@@ -14,9 +14,9 @@ export declare function wrapSecuredRouteModelByKey<TParams extends Record<string
14
14
  resource: string;
15
15
  action: "view" | "create" | "update" | "delete";
16
16
  }, handler: (request: RouteRequest<TParams>, model: TModel) => Response | Promise<Response>): RouteHandler;
17
- /** Converts kernel login throttle 429 JSON into an HTML response for web forms. */
17
+ /** Web group (CSRF + flash + HTML errors) plus login throttle. Do not wrap with wrapWeb again. */
18
18
  export declare function wrapWebLogin(kernel: HttpKernel, handler: RouteHandler, onThrottled: (request: Request) => Response | Promise<Response>): RouteHandler;
19
- /** Converts kernel register throttle 429 JSON into an HTML response for web forms. */
19
+ /** Web group (CSRF + flash + HTML errors) plus register throttle. Do not wrap with wrapWeb again. */
20
20
  export declare function wrapWebRegister(kernel: HttpKernel, handler: RouteHandler, onThrottled: (request: Request) => Response | Promise<Response>): RouteHandler;
21
21
  /** Convenience alias: HttpKernel is the Laravel-style router middleware wrapper. */
22
22
  export declare function createRouteKernel(dependencies: AppDependencies): HttpKernel;
@@ -21,6 +21,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
21
21
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
22
22
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
23
23
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
24
+ import { withErrorHandling } from "@getstrata/core/http/response";
24
25
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
25
26
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
26
27
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -173,7 +174,7 @@ class HttpKernel {
173
174
  return this.wrap(["api", "authenticated"], handler);
174
175
  }
175
176
  wrapWeb(handler) {
176
- return this.wrap("web", handler);
177
+ return withErrorHandling(this.wrap("web", handler));
177
178
  }
178
179
  wrapWebPublicRead(handler) {
179
180
  if (isPublicReadsEnabled()) {
@@ -183,19 +184,19 @@ class HttpKernel {
183
184
  }
184
185
  wrapWebAuthenticated(handler) {
185
186
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
186
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
187
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
187
188
  }
188
189
  wrapWebAbility(ability, handler) {
189
190
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
190
191
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
191
192
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
192
193
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
193
- return this.wrap("web", withMiddleware(...middleware)(handler));
194
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
194
195
  }
195
196
  wrapWebGlobalAdmin(handler) {
196
197
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
197
198
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
198
- return this.wrap("web", withMiddleware(...middleware)(handler));
199
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
199
200
  }
200
201
  wrapAuthenticated(handler) {
201
202
  return this.wrap("authenticated", handler);
@@ -24,6 +24,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
24
24
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
25
25
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
26
26
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
27
+ import { withErrorHandling } from "@getstrata/core/http/response";
27
28
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
28
29
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
29
30
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -176,7 +177,7 @@ class HttpKernel {
176
177
  return this.wrap(["api", "authenticated"], handler);
177
178
  }
178
179
  wrapWeb(handler) {
179
- return this.wrap("web", handler);
180
+ return withErrorHandling(this.wrap("web", handler));
180
181
  }
181
182
  wrapWebPublicRead(handler) {
182
183
  if (isPublicReadsEnabled()) {
@@ -186,19 +187,19 @@ class HttpKernel {
186
187
  }
187
188
  wrapWebAuthenticated(handler) {
188
189
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
189
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
190
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
190
191
  }
191
192
  wrapWebAbility(ability, handler) {
192
193
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
193
194
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
194
195
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
195
196
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
196
- return this.wrap("web", withMiddleware(...middleware)(handler));
197
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
197
198
  }
198
199
  wrapWebGlobalAdmin(handler) {
199
200
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
200
201
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
201
- return this.wrap("web", withMiddleware(...middleware)(handler));
202
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
202
203
  }
203
204
  wrapAuthenticated(handler) {
204
205
  return this.wrap("authenticated", handler);
@@ -506,68 +506,6 @@ var coreProviders = [
506
506
  viewProvider
507
507
  ];
508
508
 
509
- // ../../src/bootstrap/secretsGuard.ts
510
- var DEFAULT_ADMIN_API_TOKEN = "workhub-admin-test-token";
511
- var DEFAULT_MEMBER_API_TOKEN = "workhub-member-test-token";
512
- var DEFAULT_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
513
- var DEFAULT_TOKENS = new Set([DEFAULT_ADMIN_API_TOKEN, DEFAULT_MEMBER_API_TOKEN]);
514
- var DEFAULT_SCIM_TOKENS = new Set([DEFAULT_SCIM_BEARER_TOKEN]);
515
- function isEnabled(value, defaultEnabled) {
516
- if (value === undefined) {
517
- return defaultEnabled;
518
- }
519
- return defaultEnabled ? value !== "false" : value === "true";
520
- }
521
- function assertProductionSecrets(env = process.env) {
522
- const appEnv = env.APP_ENV ?? "local";
523
- if (appEnv !== "production") {
524
- return;
525
- }
526
- const adminToken = env.ADMIN_API_TOKEN ?? DEFAULT_ADMIN_API_TOKEN;
527
- const memberToken = env.MEMBER_API_TOKEN ?? DEFAULT_MEMBER_API_TOKEN;
528
- const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
529
- const encryptionEnabled = isEnabled(env.FEATURE_FIELD_ENCRYPTION, true);
530
- const devHeadersEnabled = isEnabled(env.AUTH_DEV_HEADERS, true);
531
- if (devHeadersEnabled) {
532
- throw new Error("Production startup blocked: set AUTH_DEV_HEADERS=false to disable development auth headers.");
533
- }
534
- if (DEFAULT_TOKENS.has(adminToken) || DEFAULT_TOKENS.has(memberToken)) {
535
- throw new Error("Production startup blocked: rotate ADMIN_API_TOKEN and MEMBER_API_TOKEN away from default WorkHub test values.");
536
- }
537
- if (DEFAULT_SCIM_TOKENS.has(scimToken)) {
538
- throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from default WorkHub test values.");
539
- }
540
- if (encryptionEnabled && !env.KMS_ENCRYPTION_KEY?.trim()) {
541
- throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
542
- }
543
- if (!env.SIEM_EXPORT_URL?.trim() && isEnabled(env.FEATURE_SIEM_EXPORT, true)) {
544
- console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
545
- }
546
- if (isEnabled(env.FEATURE_BILLING, true) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
547
- throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
548
- }
549
- const corsOrigins = (env.CORS_ALLOWED_ORIGINS ?? "*").split(",").map((origin) => origin.trim());
550
- if (corsOrigins.includes("*")) {
551
- throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
552
- }
553
- if (isEnabled(env.FEATURE_PUBLIC_READS, true)) {
554
- throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
555
- }
556
- if (!env.OAUTH_STATE_SECRET?.trim()) {
557
- throw new Error("Production startup blocked: set OAUTH_STATE_SECRET for OAuth CSRF protection.");
558
- }
559
- if (!env.TOKEN_HASH_PEPPER?.trim()) {
560
- throw new Error("Production startup blocked: set TOKEN_HASH_PEPPER for API token hashing.");
561
- }
562
- if (!env.API_TOKEN_DEFAULT_EXPIRY_DAYS?.trim()) {
563
- throw new Error("Production startup blocked: set API_TOKEN_DEFAULT_EXPIRY_DAYS to enforce token rotation.");
564
- }
565
- const frontendMode = (env.FRONTEND_MODE ?? "api").trim();
566
- if (frontendMode === "server-htmx" && !env.SESSION_SECRET?.trim()) {
567
- throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx.");
568
- }
569
- }
570
-
571
509
  // ../../src/bootstrap/context.ts
572
510
  function collectProviders(modules = discoverModules()) {
573
511
  return [...coreProviders, ...modules.flatMap((module) => module.providers ?? [])];
@@ -578,7 +516,6 @@ function runProviderPhase(providers, phase, context) {
578
516
  }
579
517
  }
580
518
  function createAppContext() {
581
- assertProductionSecrets();
582
519
  const container = new ServiceContainer;
583
520
  const config = new ConfigStore;
584
521
  const dependencies = {
@@ -77,6 +77,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
77
77
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
78
78
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
79
79
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
80
+ import { withErrorHandling } from "@getstrata/core/http/response";
80
81
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
81
82
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
82
83
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -211,7 +212,7 @@ class HttpKernel {
211
212
  return this.wrap(["api", "authenticated"], handler);
212
213
  }
213
214
  wrapWeb(handler) {
214
- return this.wrap("web", handler);
215
+ return withErrorHandling(this.wrap("web", handler));
215
216
  }
216
217
  wrapWebPublicRead(handler) {
217
218
  if (isPublicReadsEnabled()) {
@@ -221,19 +222,19 @@ class HttpKernel {
221
222
  }
222
223
  wrapWebAuthenticated(handler) {
223
224
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
224
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
225
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
225
226
  }
226
227
  wrapWebAbility(ability, handler) {
227
228
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
228
229
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
229
230
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
230
231
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
231
- return this.wrap("web", withMiddleware(...middleware)(handler));
232
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
232
233
  }
233
234
  wrapWebGlobalAdmin(handler) {
234
235
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
235
236
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
236
- return this.wrap("web", withMiddleware(...middleware)(handler));
237
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
237
238
  }
238
239
  wrapAuthenticated(handler) {
239
240
  return this.wrap("authenticated", handler);
@@ -781,7 +782,7 @@ import { withMiddleware as withMiddleware2 } from "@getstrata/core/http/routeMid
781
782
  import { createScimThrottleMiddleware } from "@getstrata/core/http/scimThrottleMiddleware";
782
783
 
783
784
  // ../../src/modules/scim/controller.ts
784
- import { withErrorHandling } from "@getstrata/core/http/response";
785
+ import { withErrorHandling as withErrorHandling2 } from "@getstrata/core/http/response";
785
786
 
786
787
  // ../../src/modules/scim/scimResponse.ts
787
788
  import {
@@ -1246,30 +1247,30 @@ class ScimController {
1246
1247
  constructor(dependencies, service = createScimService(dependencies)) {
1247
1248
  this.service = service;
1248
1249
  }
1249
- serviceProviderConfig = withErrorHandling(async () => {
1250
+ serviceProviderConfig = withErrorHandling2(async () => {
1250
1251
  return scimResponse(this.service.serviceProviderConfig());
1251
1252
  });
1252
- listUsers = withErrorHandling(async (request) => {
1253
+ listUsers = withErrorHandling2(async (request) => {
1253
1254
  const url = new URL(request.url);
1254
1255
  const startIndex = Number.parseInt(url.searchParams.get("startIndex") ?? "1", 10);
1255
1256
  const count = Number.parseInt(url.searchParams.get("count") ?? "100", 10);
1256
1257
  return scimResponse(await this.service.listUsers(startIndex, count));
1257
1258
  });
1258
- createUser = withErrorHandling(async (request) => {
1259
+ createUser = withErrorHandling2(async (request) => {
1259
1260
  const payload = await request.json();
1260
1261
  const user = await this.service.createUser(payload);
1261
1262
  const id = Number.parseInt(user.id, 10);
1262
1263
  const record = await this.service.findUserRecord(id);
1263
1264
  return scimResponse(user, { status: 201, etagSource: record });
1264
1265
  });
1265
- showUser = withErrorHandling(async (request) => {
1266
+ showUser = withErrorHandling2(async (request) => {
1266
1267
  const params = request.params;
1267
1268
  const id = Number.parseInt(params?.id ?? "", 10);
1268
1269
  const record = await this.service.findUserRecord(id);
1269
1270
  const user = await this.service.getUser(id);
1270
1271
  return scimResponse(user, { request, etagSource: record });
1271
1272
  });
1272
- patchUser = withErrorHandling(async (request) => {
1273
+ patchUser = withErrorHandling2(async (request) => {
1273
1274
  const params = request.params;
1274
1275
  const id = Number.parseInt(params?.id ?? "", 10);
1275
1276
  const record = await this.service.findUserRecord(id);
@@ -1279,7 +1280,7 @@ class ScimController {
1279
1280
  const updated = await this.service.findUserRecord(id);
1280
1281
  return scimResponse(user, { etagSource: updated });
1281
1282
  });
1282
- deleteUser = withErrorHandling(async (request) => {
1283
+ deleteUser = withErrorHandling2(async (request) => {
1283
1284
  const params = request.params;
1284
1285
  const id = Number.parseInt(params?.id ?? "", 10);
1285
1286
  const record = await this.service.findUserRecord(id);
@@ -1287,20 +1288,20 @@ class ScimController {
1287
1288
  await this.service.deleteUser(id);
1288
1289
  return new Response(null, { status: 204 });
1289
1290
  });
1290
- listGroups = withErrorHandling(async (request) => {
1291
+ listGroups = withErrorHandling2(async (request) => {
1291
1292
  const url = new URL(request.url);
1292
1293
  const startIndex = Number.parseInt(url.searchParams.get("startIndex") ?? "1", 10);
1293
1294
  const count = Number.parseInt(url.searchParams.get("count") ?? "100", 10);
1294
1295
  return scimResponse(await this.service.listGroups(startIndex, count));
1295
1296
  });
1296
- showGroup = withErrorHandling(async (request) => {
1297
+ showGroup = withErrorHandling2(async (request) => {
1297
1298
  const params = request.params;
1298
1299
  const id = Number.parseInt(params?.id ?? "", 10);
1299
1300
  const record = await this.service.findOrganizationRecord(id);
1300
1301
  const group = await this.service.getGroup(id);
1301
1302
  return scimResponse(group, { request, etagSource: record });
1302
1303
  });
1303
- patchGroup = withErrorHandling(async (request) => {
1304
+ patchGroup = withErrorHandling2(async (request) => {
1304
1305
  const params = request.params;
1305
1306
  const id = Number.parseInt(params?.id ?? "", 10);
1306
1307
  const record = await this.service.findOrganizationRecord(id);
@@ -46,6 +46,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
46
46
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
47
47
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
48
48
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
49
+ import { withErrorHandling } from "@getstrata/core/http/response";
49
50
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
50
51
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
51
52
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -180,7 +181,7 @@ class HttpKernel {
180
181
  return this.wrap(["api", "authenticated"], handler);
181
182
  }
182
183
  wrapWeb(handler) {
183
- return this.wrap("web", handler);
184
+ return withErrorHandling(this.wrap("web", handler));
184
185
  }
185
186
  wrapWebPublicRead(handler) {
186
187
  if (isPublicReadsEnabled()) {
@@ -190,19 +191,19 @@ class HttpKernel {
190
191
  }
191
192
  wrapWebAuthenticated(handler) {
192
193
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
193
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
194
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
194
195
  }
195
196
  wrapWebAbility(ability, handler) {
196
197
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
197
198
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
198
199
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
199
200
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
200
- return this.wrap("web", withMiddleware(...middleware)(handler));
201
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
201
202
  }
202
203
  wrapWebGlobalAdmin(handler) {
203
204
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
204
205
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
205
- return this.wrap("web", withMiddleware(...middleware)(handler));
206
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
206
207
  }
207
208
  wrapAuthenticated(handler) {
208
209
  return this.wrap("authenticated", handler);
@@ -506,68 +506,6 @@ var coreProviders = [
506
506
  viewProvider
507
507
  ];
508
508
 
509
- // ../../src/bootstrap/secretsGuard.ts
510
- var DEFAULT_ADMIN_API_TOKEN = "workhub-admin-test-token";
511
- var DEFAULT_MEMBER_API_TOKEN = "workhub-member-test-token";
512
- var DEFAULT_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
513
- var DEFAULT_TOKENS = new Set([DEFAULT_ADMIN_API_TOKEN, DEFAULT_MEMBER_API_TOKEN]);
514
- var DEFAULT_SCIM_TOKENS = new Set([DEFAULT_SCIM_BEARER_TOKEN]);
515
- function isEnabled(value, defaultEnabled) {
516
- if (value === undefined) {
517
- return defaultEnabled;
518
- }
519
- return defaultEnabled ? value !== "false" : value === "true";
520
- }
521
- function assertProductionSecrets(env = process.env) {
522
- const appEnv = env.APP_ENV ?? "local";
523
- if (appEnv !== "production") {
524
- return;
525
- }
526
- const adminToken = env.ADMIN_API_TOKEN ?? DEFAULT_ADMIN_API_TOKEN;
527
- const memberToken = env.MEMBER_API_TOKEN ?? DEFAULT_MEMBER_API_TOKEN;
528
- const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
529
- const encryptionEnabled = isEnabled(env.FEATURE_FIELD_ENCRYPTION, true);
530
- const devHeadersEnabled = isEnabled(env.AUTH_DEV_HEADERS, true);
531
- if (devHeadersEnabled) {
532
- throw new Error("Production startup blocked: set AUTH_DEV_HEADERS=false to disable development auth headers.");
533
- }
534
- if (DEFAULT_TOKENS.has(adminToken) || DEFAULT_TOKENS.has(memberToken)) {
535
- throw new Error("Production startup blocked: rotate ADMIN_API_TOKEN and MEMBER_API_TOKEN away from default WorkHub test values.");
536
- }
537
- if (DEFAULT_SCIM_TOKENS.has(scimToken)) {
538
- throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from default WorkHub test values.");
539
- }
540
- if (encryptionEnabled && !env.KMS_ENCRYPTION_KEY?.trim()) {
541
- throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
542
- }
543
- if (!env.SIEM_EXPORT_URL?.trim() && isEnabled(env.FEATURE_SIEM_EXPORT, true)) {
544
- console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
545
- }
546
- if (isEnabled(env.FEATURE_BILLING, true) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
547
- throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
548
- }
549
- const corsOrigins = (env.CORS_ALLOWED_ORIGINS ?? "*").split(",").map((origin) => origin.trim());
550
- if (corsOrigins.includes("*")) {
551
- throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
552
- }
553
- if (isEnabled(env.FEATURE_PUBLIC_READS, true)) {
554
- throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
555
- }
556
- if (!env.OAUTH_STATE_SECRET?.trim()) {
557
- throw new Error("Production startup blocked: set OAUTH_STATE_SECRET for OAuth CSRF protection.");
558
- }
559
- if (!env.TOKEN_HASH_PEPPER?.trim()) {
560
- throw new Error("Production startup blocked: set TOKEN_HASH_PEPPER for API token hashing.");
561
- }
562
- if (!env.API_TOKEN_DEFAULT_EXPIRY_DAYS?.trim()) {
563
- throw new Error("Production startup blocked: set API_TOKEN_DEFAULT_EXPIRY_DAYS to enforce token rotation.");
564
- }
565
- const frontendMode = (env.FRONTEND_MODE ?? "api").trim();
566
- if (frontendMode === "server-htmx" && !env.SESSION_SECRET?.trim()) {
567
- throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx.");
568
- }
569
- }
570
-
571
509
  // ../../src/bootstrap/context.ts
572
510
  function collectProviders(modules = discoverModules()) {
573
511
  return [...coreProviders, ...modules.flatMap((module) => module.providers ?? [])];
@@ -578,7 +516,6 @@ function runProviderPhase(providers, phase, context) {
578
516
  }
579
517
  }
580
518
  function createAppContext() {
581
- assertProductionSecrets();
582
519
  const container = new ServiceContainer;
583
520
  const config = new ConfigStore;
584
521
  const dependencies = {
@@ -17,6 +17,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
17
17
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
18
18
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
19
19
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
20
+ import { withErrorHandling } from "@getstrata/core/http/response";
20
21
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
21
22
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
22
23
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -169,7 +170,7 @@ class HttpKernel {
169
170
  return this.wrap(["api", "authenticated"], handler);
170
171
  }
171
172
  wrapWeb(handler) {
172
- return this.wrap("web", handler);
173
+ return withErrorHandling(this.wrap("web", handler));
173
174
  }
174
175
  wrapWebPublicRead(handler) {
175
176
  if (isPublicReadsEnabled()) {
@@ -179,19 +180,19 @@ class HttpKernel {
179
180
  }
180
181
  wrapWebAuthenticated(handler) {
181
182
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
182
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
183
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
183
184
  }
184
185
  wrapWebAbility(ability, handler) {
185
186
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
186
187
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
187
188
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
188
189
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
189
- return this.wrap("web", withMiddleware(...middleware)(handler));
190
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
190
191
  }
191
192
  wrapWebGlobalAdmin(handler) {
192
193
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
193
194
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
194
- return this.wrap("web", withMiddleware(...middleware)(handler));
195
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
195
196
  }
196
197
  wrapAuthenticated(handler) {
197
198
  return this.wrap("authenticated", handler);
@@ -2,7 +2,7 @@
2
2
  var __jsonParse = (a) => JSON.parse(a);
3
3
 
4
4
  // ../../src/bootstrap/web/routing.ts
5
- import { withErrorHandling } from "@getstrata/core/http/response";
5
+ import { withErrorHandling as withErrorHandling2 } from "@getstrata/core/http/response";
6
6
 
7
7
  // ../../src/bootstrap/http/securedRouteModelBinding.ts
8
8
  import {
@@ -26,6 +26,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
26
26
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
27
27
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
28
28
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
29
+ import { withErrorHandling } from "@getstrata/core/http/response";
29
30
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
30
31
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
31
32
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -178,7 +179,7 @@ class HttpKernel {
178
179
  return this.wrap(["api", "authenticated"], handler);
179
180
  }
180
181
  wrapWeb(handler) {
181
- return this.wrap("web", handler);
182
+ return withErrorHandling(this.wrap("web", handler));
182
183
  }
183
184
  wrapWebPublicRead(handler) {
184
185
  if (isPublicReadsEnabled()) {
@@ -188,19 +189,19 @@ class HttpKernel {
188
189
  }
189
190
  wrapWebAuthenticated(handler) {
190
191
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
191
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
192
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
192
193
  }
193
194
  wrapWebAbility(ability, handler) {
194
195
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
195
196
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
196
197
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
197
198
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
198
- return this.wrap("web", withMiddleware(...middleware)(handler));
199
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
199
200
  }
200
201
  wrapWebGlobalAdmin(handler) {
201
202
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
202
203
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
203
- return this.wrap("web", withMiddleware(...middleware)(handler));
204
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
204
205
  }
205
206
  wrapAuthenticated(handler) {
206
207
  return this.wrap("authenticated", handler);
@@ -296,14 +297,14 @@ function toRouteRequest(request) {
296
297
  return request;
297
298
  }
298
299
  function wrapSecuredRouteModelByKey(param, resolver, authorization, handler) {
299
- const bound = withErrorHandling(securedBindRouteModelByKey(param, resolver, authorization, handler));
300
+ const bound = withErrorHandling2(securedBindRouteModelByKey(param, resolver, authorization, handler));
300
301
  return async (request) => bound(toRouteRequest(request));
301
302
  }
302
303
  function wrapWebLogin(kernel, handler, onThrottled) {
303
- return wrapWebThrottle(kernel, "login", handler, onThrottled);
304
+ return kernel.wrapWeb(wrapWebThrottle(kernel, "login", handler, onThrottled));
304
305
  }
305
306
  function wrapWebRegister(kernel, handler, onThrottled) {
306
- return wrapWebThrottle(kernel, "register", handler, onThrottled);
307
+ return kernel.wrapWeb(wrapWebThrottle(kernel, "register", handler, onThrottled));
307
308
  }
308
309
  function wrapWebThrottle(kernel, scope, handler, onThrottled) {
309
310
  const throttled = scope === "login" ? kernel.wrapLogin(handler) : kernel.wrapRegister(handler);
package/dist/index.js CHANGED
@@ -34,6 +34,7 @@ import { createRequireAbilityMiddleware } from "@getstrata/core/http/requireAbil
34
34
  import { createRequireAuthMiddleware } from "@getstrata/core/http/requireAuthMiddleware";
35
35
  import { createRequireGlobalAdminMiddleware } from "@getstrata/core/http/requireGlobalAdminMiddleware";
36
36
  import { createRequireWebAuthMiddleware } from "@getstrata/core/http/requireWebAuthMiddleware";
37
+ import { withErrorHandling } from "@getstrata/core/http/response";
37
38
  import { withMiddleware } from "@getstrata/core/http/routeMiddleware";
38
39
  import { createSecurityHeadersMiddleware } from "@getstrata/core/http/securityHeadersMiddleware";
39
40
  import { createThrottleMiddleware } from "@getstrata/core/http/throttleMiddleware";
@@ -182,7 +183,7 @@ class HttpKernel {
182
183
  return this.wrap(["api", "authenticated"], handler);
183
184
  }
184
185
  wrapWeb(handler) {
185
- return this.wrap("web", handler);
186
+ return withErrorHandling(this.wrap("web", handler));
186
187
  }
187
188
  wrapWebPublicRead(handler) {
188
189
  if (isPublicReadsEnabled()) {
@@ -192,19 +193,19 @@ class HttpKernel {
192
193
  }
193
194
  wrapWebAuthenticated(handler) {
194
195
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
195
- return this.wrap("web", withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
196
+ return this.wrapWeb(withMiddleware(createRequireWebAuthMiddleware(auth))(handler));
196
197
  }
197
198
  wrapWebAbility(ability, handler) {
198
199
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
199
200
  const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
200
201
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
201
202
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
202
- return this.wrap("web", withMiddleware(...middleware)(handler));
203
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
203
204
  }
204
205
  wrapWebGlobalAdmin(handler) {
205
206
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
206
207
  const middleware = [createRequireWebAuthMiddleware(auth), createRequireGlobalAdminMiddleware()];
207
- return this.wrap("web", withMiddleware(...middleware)(handler));
208
+ return this.wrapWeb(withMiddleware(...middleware)(handler));
208
209
  }
209
210
  wrapAuthenticated(handler) {
210
211
  return this.wrap("authenticated", handler);
@@ -818,68 +819,6 @@ var coreProviders = [
818
819
  viewProvider
819
820
  ];
820
821
 
821
- // ../../src/bootstrap/secretsGuard.ts
822
- var DEFAULT_ADMIN_API_TOKEN = "workhub-admin-test-token";
823
- var DEFAULT_MEMBER_API_TOKEN = "workhub-member-test-token";
824
- var DEFAULT_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
825
- var DEFAULT_TOKENS = new Set([DEFAULT_ADMIN_API_TOKEN, DEFAULT_MEMBER_API_TOKEN]);
826
- var DEFAULT_SCIM_TOKENS = new Set([DEFAULT_SCIM_BEARER_TOKEN]);
827
- function isEnabled(value, defaultEnabled) {
828
- if (value === undefined) {
829
- return defaultEnabled;
830
- }
831
- return defaultEnabled ? value !== "false" : value === "true";
832
- }
833
- function assertProductionSecrets(env = process.env) {
834
- const appEnv = env.APP_ENV ?? "local";
835
- if (appEnv !== "production") {
836
- return;
837
- }
838
- const adminToken = env.ADMIN_API_TOKEN ?? DEFAULT_ADMIN_API_TOKEN;
839
- const memberToken = env.MEMBER_API_TOKEN ?? DEFAULT_MEMBER_API_TOKEN;
840
- const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
841
- const encryptionEnabled = isEnabled(env.FEATURE_FIELD_ENCRYPTION, true);
842
- const devHeadersEnabled = isEnabled(env.AUTH_DEV_HEADERS, true);
843
- if (devHeadersEnabled) {
844
- throw new Error("Production startup blocked: set AUTH_DEV_HEADERS=false to disable development auth headers.");
845
- }
846
- if (DEFAULT_TOKENS.has(adminToken) || DEFAULT_TOKENS.has(memberToken)) {
847
- throw new Error("Production startup blocked: rotate ADMIN_API_TOKEN and MEMBER_API_TOKEN away from default WorkHub test values.");
848
- }
849
- if (DEFAULT_SCIM_TOKENS.has(scimToken)) {
850
- throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from default WorkHub test values.");
851
- }
852
- if (encryptionEnabled && !env.KMS_ENCRYPTION_KEY?.trim()) {
853
- throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
854
- }
855
- if (!env.SIEM_EXPORT_URL?.trim() && isEnabled(env.FEATURE_SIEM_EXPORT, true)) {
856
- console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
857
- }
858
- if (isEnabled(env.FEATURE_BILLING, true) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
859
- throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
860
- }
861
- const corsOrigins = (env.CORS_ALLOWED_ORIGINS ?? "*").split(",").map((origin) => origin.trim());
862
- if (corsOrigins.includes("*")) {
863
- throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
864
- }
865
- if (isEnabled(env.FEATURE_PUBLIC_READS, true)) {
866
- throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
867
- }
868
- if (!env.OAUTH_STATE_SECRET?.trim()) {
869
- throw new Error("Production startup blocked: set OAUTH_STATE_SECRET for OAuth CSRF protection.");
870
- }
871
- if (!env.TOKEN_HASH_PEPPER?.trim()) {
872
- throw new Error("Production startup blocked: set TOKEN_HASH_PEPPER for API token hashing.");
873
- }
874
- if (!env.API_TOKEN_DEFAULT_EXPIRY_DAYS?.trim()) {
875
- throw new Error("Production startup blocked: set API_TOKEN_DEFAULT_EXPIRY_DAYS to enforce token rotation.");
876
- }
877
- const frontendMode = (env.FRONTEND_MODE ?? "api").trim();
878
- if (frontendMode === "server-htmx" && !env.SESSION_SECRET?.trim()) {
879
- throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx.");
880
- }
881
- }
882
-
883
822
  // ../../src/bootstrap/context.ts
884
823
  function collectProviders(modules = discoverModules()) {
885
824
  return [...coreProviders, ...modules.flatMap((module) => module.providers ?? [])];
@@ -890,7 +829,6 @@ function runProviderPhase(providers, phase, context) {
890
829
  }
891
830
  }
892
831
  function createAppContext() {
893
- assertProductionSecrets();
894
832
  const container = new ServiceContainer;
895
833
  const config = new ConfigStore;
896
834
  const dependencies = {
@@ -960,6 +898,67 @@ import {
960
898
  } from "@getstrata/core/http/securedRouteModelBinding";
961
899
  // ../../src/bootstrap/membershipService.ts
962
900
  import { resolveMembershipService } from "@getstrata/core/auth/membershipService";
901
+ // ../../src/bootstrap/secretsGuard.ts
902
+ var DEFAULT_ADMIN_API_TOKEN = "workhub-admin-test-token";
903
+ var DEFAULT_MEMBER_API_TOKEN = "workhub-member-test-token";
904
+ var DEFAULT_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
905
+ var DEFAULT_TOKENS = new Set([DEFAULT_ADMIN_API_TOKEN, DEFAULT_MEMBER_API_TOKEN]);
906
+ var DEFAULT_SCIM_TOKENS = new Set([DEFAULT_SCIM_BEARER_TOKEN]);
907
+ function isEnabled(value, defaultEnabled) {
908
+ if (value === undefined) {
909
+ return defaultEnabled;
910
+ }
911
+ return defaultEnabled ? value !== "false" : value === "true";
912
+ }
913
+ function assertProductionSecrets(env = process.env) {
914
+ const appEnv = env.APP_ENV ?? "local";
915
+ if (appEnv !== "production") {
916
+ return;
917
+ }
918
+ const adminToken = env.ADMIN_API_TOKEN ?? DEFAULT_ADMIN_API_TOKEN;
919
+ const memberToken = env.MEMBER_API_TOKEN ?? DEFAULT_MEMBER_API_TOKEN;
920
+ const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
921
+ const encryptionEnabled = isEnabled(env.FEATURE_FIELD_ENCRYPTION, true);
922
+ const devHeadersEnabled = isEnabled(env.AUTH_DEV_HEADERS, true);
923
+ if (devHeadersEnabled) {
924
+ throw new Error("Production startup blocked: set AUTH_DEV_HEADERS=false to disable development auth headers.");
925
+ }
926
+ if (DEFAULT_TOKENS.has(adminToken) || DEFAULT_TOKENS.has(memberToken)) {
927
+ throw new Error("Production startup blocked: rotate ADMIN_API_TOKEN and MEMBER_API_TOKEN away from default WorkHub test values.");
928
+ }
929
+ if (DEFAULT_SCIM_TOKENS.has(scimToken)) {
930
+ throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from default WorkHub test values.");
931
+ }
932
+ if (encryptionEnabled && !env.KMS_ENCRYPTION_KEY?.trim()) {
933
+ throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
934
+ }
935
+ if (!env.SIEM_EXPORT_URL?.trim() && isEnabled(env.FEATURE_SIEM_EXPORT, true)) {
936
+ console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
937
+ }
938
+ if (isEnabled(env.FEATURE_BILLING, true) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
939
+ throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
940
+ }
941
+ const corsOrigins = (env.CORS_ALLOWED_ORIGINS ?? "*").split(",").map((origin) => origin.trim());
942
+ if (corsOrigins.includes("*")) {
943
+ throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
944
+ }
945
+ if (isEnabled(env.FEATURE_PUBLIC_READS, true)) {
946
+ throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
947
+ }
948
+ if (!env.OAUTH_STATE_SECRET?.trim()) {
949
+ throw new Error("Production startup blocked: set OAUTH_STATE_SECRET for OAuth CSRF protection.");
950
+ }
951
+ if (!env.TOKEN_HASH_PEPPER?.trim()) {
952
+ throw new Error("Production startup blocked: set TOKEN_HASH_PEPPER for API token hashing.");
953
+ }
954
+ if (!env.API_TOKEN_DEFAULT_EXPIRY_DAYS?.trim()) {
955
+ throw new Error("Production startup blocked: set API_TOKEN_DEFAULT_EXPIRY_DAYS to enforce token rotation.");
956
+ }
957
+ const frontendMode = (env.FRONTEND_MODE ?? "api").trim();
958
+ if (frontendMode === "server-htmx" && !env.SESSION_SECRET?.trim()) {
959
+ throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx.");
960
+ }
961
+ }
963
962
  // ../../src/bootstrap/web/forms.ts
964
963
  import {
965
964
  createCsrfProtection
@@ -993,7 +992,7 @@ async function parseFormBody(request) {
993
992
  return { fields, files };
994
993
  }
995
994
  // ../../src/bootstrap/web/routing.ts
996
- import { withErrorHandling } from "@getstrata/core/http/response";
995
+ import { withErrorHandling as withErrorHandling2 } from "@getstrata/core/http/response";
997
996
  function routeParams(request) {
998
997
  const normalized = {};
999
998
  const raw = request.params;
@@ -1015,14 +1014,14 @@ function toRouteRequest(request) {
1015
1014
  return request;
1016
1015
  }
1017
1016
  function wrapSecuredRouteModelByKey(param, resolver, authorization, handler) {
1018
- const bound = withErrorHandling(securedBindRouteModelByKey(param, resolver, authorization, handler));
1017
+ const bound = withErrorHandling2(securedBindRouteModelByKey(param, resolver, authorization, handler));
1019
1018
  return async (request) => bound(toRouteRequest(request));
1020
1019
  }
1021
1020
  function wrapWebLogin(kernel, handler, onThrottled) {
1022
- return wrapWebThrottle(kernel, "login", handler, onThrottled);
1021
+ return kernel.wrapWeb(wrapWebThrottle(kernel, "login", handler, onThrottled));
1023
1022
  }
1024
1023
  function wrapWebRegister(kernel, handler, onThrottled) {
1025
- return wrapWebThrottle(kernel, "register", handler, onThrottled);
1024
+ return kernel.wrapWeb(wrapWebThrottle(kernel, "register", handler, onThrottled));
1026
1025
  }
1027
1026
  function wrapWebThrottle(kernel, scope, handler, onThrottled) {
1028
1027
  const throttled = scope === "login" ? kernel.wrapLogin(handler) : kernel.wrapRegister(handler);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "0.2.48",
3
+ "version": "0.2.49",
4
4
  "description": "Strata application bootstrap — HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -179,7 +179,7 @@
179
179
  "access": "public"
180
180
  },
181
181
  "peerDependencies": {
182
- "@getstrata/core": "^0.5.57",
182
+ "@getstrata/core": "^0.5.58",
183
183
  "typescript": "^5.9.0"
184
184
  }
185
185
  }