@getstrata/bootstrap 1.0.8 → 1.1.0

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/dist/index.js CHANGED
@@ -17,6 +17,7 @@ import {
17
17
  // ../../src/bootstrap/buildModuleRoutes.ts
18
18
  import { conditionalJsonResponse } from "@getstrata/core/http/conditionalResponse";
19
19
  import { applyMiddlewareToRoutes } from "@getstrata/core/http/middleware";
20
+ import { createJsonErrorMiddleware } from "@getstrata/core/http/response";
20
21
 
21
22
  // ../../src/bootstrap/httpKernel.ts
22
23
  import {
@@ -152,8 +153,9 @@ class HttpKernel {
152
153
  case "web":
153
154
  return isViewsEnabled() ? [createFlashMiddleware(), createCsrfMiddleware()] : [];
154
155
  case "api": {
156
+ const csrf = createCsrfMiddleware();
155
157
  if (!this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
156
- return [];
158
+ return [csrf];
157
159
  }
158
160
  const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
159
161
  const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
@@ -163,7 +165,8 @@ class HttpKernel {
163
165
  createMemoryThrottleMiddleware({
164
166
  maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
165
167
  decaySeconds: 60
166
- })
168
+ }),
169
+ csrf
167
170
  ];
168
171
  }
169
172
  const maxAttempts = Number(process.env.RATE_LIMIT_PER_MINUTE ?? "120");
@@ -172,7 +175,8 @@ class HttpKernel {
172
175
  redisUrl,
173
176
  maxAttempts: Number.isFinite(maxAttempts) ? maxAttempts : 120,
174
177
  decaySeconds: 60
175
- })
178
+ }),
179
+ csrf
176
180
  ];
177
181
  }
178
182
  default:
@@ -182,13 +186,14 @@ class HttpKernel {
182
186
  wrap(groups, handler) {
183
187
  const names = Array.isArray(groups) ? groups : [groups];
184
188
  const middleware = names.flatMap((name) => this.group(name));
185
- if (middleware.length === 0) {
186
- return handler;
189
+ const wrapped = middleware.length === 0 ? handler : withMiddleware(...middleware)(handler);
190
+ if (names.includes("api")) {
191
+ return withJsonErrorHandling(wrapped);
187
192
  }
188
- return withMiddleware(...middleware)(handler);
193
+ return wrapped;
189
194
  }
190
195
  wrapApi(handler) {
191
- return withJsonErrorHandling(this.wrap(["api", "authenticated"], handler));
196
+ return this.wrap(["api", "authenticated"], handler);
192
197
  }
193
198
  wrapWeb(handler) {
194
199
  return withErrorHandling(this.wrap("web", handler));
@@ -297,41 +302,28 @@ class HttpKernel {
297
302
  return [createRequireVerifiedMiddleware(auth)];
298
303
  }
299
304
  wrapThrottle(scope, rateLimit, handler) {
300
- const middleware = [];
301
305
  const memoryKeyPrefix = scope === "login" ? "login-throttle:" : "register-throttle:";
302
- if (this.dependencies.container.has(CORE_CONFIG_TOKEN)) {
303
- const config = this.dependencies.container.resolve(CORE_CONFIG_TOKEN);
304
- const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim() ?? "";
305
- if (redisUrl) {
306
- const throttle = scope === "login" ? createLoginThrottleMiddleware({
307
- redisUrl,
308
- maxAttempts: rateLimit.maxAttempts,
309
- decaySeconds: rateLimit.decaySeconds
310
- }) : createThrottleMiddleware({
311
- redisUrl,
312
- maxAttempts: rateLimit.maxAttempts,
313
- decaySeconds: rateLimit.decaySeconds,
314
- keyPrefix: memoryKeyPrefix
315
- });
316
- middleware.push(throttle);
317
- } else {
318
- middleware.push(createMemoryThrottleMiddleware({
319
- maxAttempts: rateLimit.maxAttempts,
320
- decaySeconds: rateLimit.decaySeconds,
321
- keyPrefix: memoryKeyPrefix
322
- }));
323
- }
324
- } else {
325
- middleware.push(createMemoryThrottleMiddleware({
306
+ const redisUrl = this.dependencies.container.has(CORE_CONFIG_TOKEN) ? this.dependencies.container.resolve(CORE_CONFIG_TOKEN).get(REDIS_URL_CONFIG_KEY)?.trim() ?? "" : "";
307
+ if (scope === "login") {
308
+ return withMiddleware(createLoginThrottleMiddleware({
309
+ ...redisUrl ? { redisUrl } : {},
310
+ maxAttempts: rateLimit.maxAttempts,
311
+ decaySeconds: rateLimit.decaySeconds
312
+ }))(handler);
313
+ }
314
+ if (redisUrl) {
315
+ return withMiddleware(createThrottleMiddleware({
316
+ redisUrl,
326
317
  maxAttempts: rateLimit.maxAttempts,
327
318
  decaySeconds: rateLimit.decaySeconds,
328
319
  keyPrefix: memoryKeyPrefix
329
- }));
330
- }
331
- if (middleware.length === 0) {
332
- return handler;
320
+ }))(handler);
333
321
  }
334
- return withMiddleware(...middleware)(handler);
322
+ return withMiddleware(createMemoryThrottleMiddleware({
323
+ maxAttempts: rateLimit.maxAttempts,
324
+ decaySeconds: rateLimit.decaySeconds,
325
+ keyPrefix: memoryKeyPrefix
326
+ }))(handler);
335
327
  }
336
328
  }
337
329
  function createHttpKernel(dependencies) {
@@ -470,7 +462,11 @@ function buildModuleRoutes(dependencies, options = {}) {
470
462
  routeRegistry.clear();
471
463
  }
472
464
  const kernel = createHttpKernel(dependencies);
473
- const middleware = [...kernel.globalMiddleware(), ...kernel.group("api")];
465
+ const middleware = [
466
+ ...kernel.globalMiddleware(),
467
+ createJsonErrorMiddleware(),
468
+ ...kernel.group("api")
469
+ ];
474
470
  const cachedJson = createCachedJson(dependencies);
475
471
  const moduleRoutes = {};
476
472
  for (const module of modules) {
@@ -541,7 +537,7 @@ import { SessionGuard } from "@getstrata/core/auth/sessionGuard";
541
537
  import { envFlagEnabled } from "@getstrata/core/runtime/appEnv";
542
538
  var authConfig = {
543
539
  allowDevHeaders: envFlagEnabled(process.env.AUTH_DEV_HEADERS),
544
- tokenDefaultAbilities: ["*"]
540
+ tokenDefaultAbilities: []
545
541
  };
546
542
 
547
543
  // ../../src/bootstrap/providers/auth.ts
@@ -551,7 +547,7 @@ var authProvider = {
551
547
  config.set("auth.allowDevHeaders", authConfig.allowDevHeaders);
552
548
  const apiGuard = new DatabaseTokenGuard(container);
553
549
  const sessionGuard = new SessionGuard(container);
554
- const jwtGuard = new JwtGuard;
550
+ const jwtGuard = new JwtGuard(container);
555
551
  const basicGuard = new BasicAuthGuard(container);
556
552
  const guards = [apiGuard, jwtGuard, basicGuard, sessionGuard];
557
553
  if (authConfig.allowDevHeaders) {
@@ -596,7 +592,7 @@ import { validateEnv } from "@getstrata/core/config/envSchema";
596
592
  var appConfig = {
597
593
  name: process.env.APP_NAME?.trim() || "Strata",
598
594
  env: process.env.APP_ENV ?? "local",
599
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
595
+ debug: (process.env.APP_DEBUG ?? "false") === "true",
600
596
  url: process.env.APP_URL ?? "http://localhost:3000",
601
597
  apiPrefix: process.env.API_PREFIX ?? "/api/v1"
602
598
  };
@@ -1001,6 +997,7 @@ function createAppContext() {
1001
997
  // ../../src/bootstrap/createWebRoutes.ts
1002
998
  import { join as join3 } from "path";
1003
999
  import { isViewsEnabled as isViewsEnabled3 } from "@getstrata/core/runtime/frontendMode";
1000
+ import { assertUrlPathUnderRoot } from "@getstrata/core/security/safePath";
1004
1001
  import { notFoundHtmlResponse } from "@getstrata/core/view";
1005
1002
  function registerRoute(method, path, middleware) {
1006
1003
  routeRegistry.register({ method, path, middleware });
@@ -1014,12 +1011,16 @@ function createWebRoutes(dependencies, options = {}) {
1014
1011
  wrappedRoutes["/assets/*"] = async (request) => {
1015
1012
  registerRoute("GET", "/assets/*", ["global", "web"]);
1016
1013
  const pathname = new URL(request.url).pathname;
1017
- const relativePath = pathname.replace(/^\//, "");
1018
- const file = Bun.file(join3(process.cwd(), "public", relativePath));
1019
- if (!await file.exists()) {
1014
+ try {
1015
+ const filePath = assertUrlPathUnderRoot(join3(process.cwd(), "public"), pathname);
1016
+ const file = Bun.file(filePath);
1017
+ if (!await file.exists()) {
1018
+ return notFoundHtmlResponse();
1019
+ }
1020
+ return new Response(file);
1021
+ } catch {
1020
1022
  return notFoundHtmlResponse();
1021
1023
  }
1022
- return new Response(file);
1023
1024
  };
1024
1025
  registerRoute("GET", "/assets/*", ["global", "web"]);
1025
1026
  return wrappedRoutes;
@@ -1041,6 +1042,7 @@ function createAppDependencies() {
1041
1042
  import { getBoundDatabaseConnection } from "@getstrata/core/database/boundConnection";
1042
1043
  import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
1043
1044
  import { jsonResponse } from "@getstrata/core/http/response";
1045
+ import { envFlagEnabled as envFlagEnabled2 } from "@getstrata/core/runtime/appEnv";
1044
1046
  var {RedisClient } = globalThis.Bun;
1045
1047
  function resolveRedisUrl(dependencies) {
1046
1048
  if (!dependencies.container.has(CORE_CONFIG_TOKEN)) {
@@ -1124,10 +1126,10 @@ function createHealthRoutes(dependencies, options = {}) {
1124
1126
  "/ready": async () => {
1125
1127
  const extra = await resolveExtraFields(options.extra);
1126
1128
  const { checks, ready } = await collectDependencyChecks(dependencies);
1129
+ const debug = envFlagEnabled2(process.env.APP_DEBUG);
1127
1130
  return jsonResponse({
1128
1131
  status: ready ? "ready" : "not_ready",
1129
- checks,
1130
- ...extra
1132
+ ...debug ? { checks, ...extra } : {}
1131
1133
  }, { status: ready ? 200 : 503 });
1132
1134
  }
1133
1135
  };
@@ -1140,8 +1142,16 @@ import {
1140
1142
  // ../../src/bootstrap/membershipService.ts
1141
1143
  import { resolveMembershipService } from "@getstrata/core/auth/membershipService";
1142
1144
  // ../../src/bootstrap/secretsGuard.ts
1143
- import { envFlagEnabled as envFlagEnabled2, isProductionEnv } from "@getstrata/core/runtime/appEnv";
1145
+ import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
1146
+ import { envFlagEnabled as envFlagEnabled3, isProductionEnv } from "@getstrata/core/runtime/appEnv";
1144
1147
  import { isViewsMode, parseFrontendMode } from "@getstrata/core/runtime/frontendMode";
1148
+ import {
1149
+ assertPostgresRoleCannotBypassRls,
1150
+ inspectCurrentPostgresRole,
1151
+ isPostgresUrl,
1152
+ postgresUrlUsername
1153
+ } from "@getstrata/core/tenant/enableTenantRls";
1154
+ import { isRlsTenancy } from "@getstrata/core/tenant/tenancyConfig";
1145
1155
  var PUBLISHED_TEST_ADMIN_API_TOKEN = "strata-admin-test-token";
1146
1156
  var PUBLISHED_TEST_MEMBER_API_TOKEN = "strata-member-test-token";
1147
1157
  var PUBLISHED_TEST_SCIM_BEARER_TOKEN = "strata-scim-test-token";
@@ -1162,8 +1172,11 @@ var SECRETS_TO_ROTATE = [
1162
1172
  "KMS_ENCRYPTION_KEY",
1163
1173
  "STRIPE_WEBHOOK_SECRET",
1164
1174
  "ADMIN_API_TOKEN",
1165
- "MEMBER_API_TOKEN"
1175
+ "MEMBER_API_TOKEN",
1176
+ "DATABASE_URL",
1177
+ "APP_DATABASE_URL"
1166
1178
  ];
1179
+ var RLS_BYPASS_DATABASE_USERS = new Set(["postgres", "root"]);
1167
1180
  function assertNoPlaceholderSecrets(env) {
1168
1181
  const unrotated = SECRETS_TO_ROTATE.filter((name) => PLACEHOLDER_SECRET_PATTERN.test(env[name]?.trim() ?? ""));
1169
1182
  if (unrotated.length > 0) {
@@ -1171,10 +1184,14 @@ function assertNoPlaceholderSecrets(env) {
1171
1184
  }
1172
1185
  }
1173
1186
  function isTokenAuthEnabled(env) {
1174
- return Boolean(env.ADMIN_API_TOKEN?.trim()) || Boolean(env.MEMBER_API_TOKEN?.trim()) || envFlagEnabled2(env.FEATURE_API_TOKENS);
1187
+ return Boolean(env.ADMIN_API_TOKEN?.trim()) || Boolean(env.MEMBER_API_TOKEN?.trim()) || envFlagEnabled3(env.FEATURE_API_TOKENS);
1175
1188
  }
1176
1189
  function isOAuthEnabled(env) {
1177
- return envFlagEnabled2(env.FEATURE_OAUTH) || envFlagEnabled2(env.FEATURE_SAML) || Boolean(env.GITHUB_CLIENT_ID?.trim()) || Boolean(env.OIDC_ISSUER?.trim()) || Boolean(env.SAML_LOGIN_URL?.trim());
1190
+ return envFlagEnabled3(env.FEATURE_OAUTH) || envFlagEnabled3(env.FEATURE_SAML) || Boolean(env.GITHUB_CLIENT_ID?.trim()) || Boolean(env.OIDC_ISSUER?.trim()) || Boolean(env.SAML_IDP_SSO_URL?.trim()) || Boolean(env.SAML_LOGIN_URL?.trim());
1191
+ }
1192
+ function isHeaderOnlyAuth(env) {
1193
+ const frontend = env.FRONTEND_MODE?.trim() ?? "";
1194
+ return env.AUTH_MODE === "headers" || frontend === "api" && env.AUTH_DEV_HEADERS === "true";
1178
1195
  }
1179
1196
  function isCorsConfigured(env) {
1180
1197
  return env.CORS_ALLOWED_ORIGINS !== undefined;
@@ -1211,16 +1228,35 @@ function assertTokenAuthProductionSecrets(env) {
1211
1228
  }
1212
1229
  }
1213
1230
  function assertFeatureProductionSecrets(env) {
1214
- if (envFlagEnabled2(env.FEATURE_SCIM)) {
1231
+ if (envFlagEnabled3(env.FEATURE_SCIM)) {
1215
1232
  const scimToken = env.SCIM_BEARER_TOKEN ?? PUBLISHED_TEST_SCIM_BEARER_TOKEN;
1216
1233
  if (PUBLISHED_TEST_SCIM_TOKENS.has(scimToken) || !env.SCIM_BEARER_TOKEN?.trim()) {
1217
1234
  throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from published test defaults.");
1218
1235
  }
1219
1236
  }
1220
- if (envFlagEnabled2(env.FEATURE_FIELD_ENCRYPTION) && !env.KMS_ENCRYPTION_KEY?.trim()) {
1221
- throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
1237
+ if ((envFlagEnabled3(env.FEATURE_FIELD_ENCRYPTION) || envFlagEnabled3(env.FEATURE_MFA)) && !env.KMS_ENCRYPTION_KEY?.trim()) {
1238
+ throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption or MFA is enabled.");
1222
1239
  }
1223
- if (envFlagEnabled2(env.FEATURE_BILLING) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
1240
+ if (envFlagEnabled3(env.FEATURE_SAML)) {
1241
+ const required = [
1242
+ "SAML_IDP_CERT",
1243
+ "SAML_IDP_SSO_URL",
1244
+ "SAML_SP_ENTITY_ID",
1245
+ "SAML_ACS_URL",
1246
+ "SAML_IDP_ISSUER"
1247
+ ];
1248
+ const missing = required.filter((name) => !env[name]?.trim());
1249
+ if (missing.length > 0) {
1250
+ throw new Error(`Production startup blocked: set ${missing.join(", ")} when FEATURE_SAML=true.`);
1251
+ }
1252
+ if (env.SAML_WANT_RESPONSE_SIGNED === "false") {
1253
+ throw new Error("Production startup blocked: signed SAML responses are required (do not set SAML_WANT_RESPONSE_SIGNED=false).");
1254
+ }
1255
+ }
1256
+ if (envFlagEnabled3(env.UPLOAD_ALLOW_UNKNOWN_MIME)) {
1257
+ throw new Error("Production startup blocked: set UPLOAD_ALLOW_UNKNOWN_MIME=false (unknown MIME types are not allowed).");
1258
+ }
1259
+ if (envFlagEnabled3(env.FEATURE_BILLING) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
1224
1260
  throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
1225
1261
  }
1226
1262
  if (isOAuthEnabled(env) && !env.OAUTH_STATE_SECRET?.trim()) {
@@ -1232,14 +1268,44 @@ function assertFeatureProductionSecrets(env) {
1232
1268
  throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
1233
1269
  }
1234
1270
  }
1235
- if (envFlagEnabled2(env.FEATURE_PUBLIC_READS)) {
1271
+ if (envFlagEnabled3(env.FEATURE_PUBLIC_READS)) {
1236
1272
  throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
1237
1273
  }
1238
- if (!env.SIEM_EXPORT_URL?.trim() && envFlagEnabled2(env.FEATURE_SIEM_EXPORT)) {
1274
+ if (!env.SIEM_EXPORT_URL?.trim() && envFlagEnabled3(env.FEATURE_SIEM_EXPORT)) {
1239
1275
  console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
1240
1276
  }
1241
1277
  }
1242
1278
  var LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]);
1279
+ function rlsRuntimeDatabaseUrl(env) {
1280
+ const app = env.APP_DATABASE_URL?.trim();
1281
+ if (app) {
1282
+ return { name: "APP_DATABASE_URL", raw: app };
1283
+ }
1284
+ const database = env.DATABASE_URL?.trim();
1285
+ if (database) {
1286
+ return { name: "DATABASE_URL", raw: database };
1287
+ }
1288
+ return null;
1289
+ }
1290
+ function assertRlsUsesAppDatabaseRole(env) {
1291
+ if (!isRlsTenancy(env)) {
1292
+ return;
1293
+ }
1294
+ const runtime = rlsRuntimeDatabaseUrl(env);
1295
+ if (!runtime) {
1296
+ return;
1297
+ }
1298
+ const user = postgresUrlUsername(runtime.raw);
1299
+ if (user === null) {
1300
+ return;
1301
+ }
1302
+ if (user === "") {
1303
+ throw new Error(`TENANCY_DRIVER=rls startup blocked: ${runtime.name} must include a NOBYPASSRLS role username.`);
1304
+ }
1305
+ if (RLS_BYPASS_DATABASE_USERS.has(user.toLowerCase())) {
1306
+ throw new Error(`TENANCY_DRIVER=rls startup blocked: ${runtime.name} for TENANCY_DRIVER=rls must use a NOBYPASSRLS role, not ${user}. FORCE RLS does not apply to PostgreSQL superusers.`);
1307
+ }
1308
+ }
1243
1309
  function assertPublicAppUrl(env) {
1244
1310
  const raw = env.APP_URL?.trim() ?? "";
1245
1311
  let url = null;
@@ -1262,6 +1328,9 @@ function assertProductionSecrets(env = process.env) {
1262
1328
  assertNoPlaceholderSecrets(env);
1263
1329
  assertAuthDevHeadersDisabled(env);
1264
1330
  assertPublicAppUrl(env);
1331
+ if (isHeaderOnlyAuth(env)) {
1332
+ throw new Error("Production startup blocked: header-only authentication is not allowed. Configure cookie, token, or JWT auth.");
1333
+ }
1265
1334
  if (isTokenAuthEnabled(env)) {
1266
1335
  assertTokenAuthProductionSecrets(env);
1267
1336
  }
@@ -1269,6 +1338,7 @@ function assertProductionSecrets(env = process.env) {
1269
1338
  if (isViewsMode(parseFrontendMode(env.FRONTEND_MODE))) {
1270
1339
  assertSessionSecret(env);
1271
1340
  }
1341
+ assertRlsUsesAppDatabaseRole(env);
1272
1342
  }
1273
1343
  // ../../src/bootstrap/web/forms.ts
1274
1344
  import {
@@ -1350,6 +1420,7 @@ function createRouteKernel(dependencies) {
1350
1420
  }
1351
1421
  // ../../src/bootstrap/web/server.ts
1352
1422
  import { currentRequestMeta as currentRequestMeta2, runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
1423
+ import { assertUrlPathUnderRoot as assertUrlPathUnderRoot2 } from "@getstrata/core/security/safePath";
1353
1424
  import { notFoundHtmlResponse as notFoundHtmlResponse2 } from "@getstrata/core/view";
1354
1425
  function socketAddress(server, request) {
1355
1426
  const address = server?.requestIP(request)?.address;
@@ -1409,9 +1480,14 @@ function createWebServer(options) {
1409
1480
  await options.onRequest?.(request);
1410
1481
  const url = new URL(request.url);
1411
1482
  if (url.pathname.startsWith("/assets/")) {
1412
- const file = Bun.file(`${publicDir}${url.pathname}`);
1413
- if (await file.exists()) {
1414
- return new Response(file);
1483
+ try {
1484
+ const filePath = assertUrlPathUnderRoot2(publicDir, url.pathname);
1485
+ const file = Bun.file(filePath);
1486
+ if (await file.exists()) {
1487
+ return new Response(file);
1488
+ }
1489
+ } catch {
1490
+ return await missingHtmlResponse();
1415
1491
  }
1416
1492
  }
1417
1493
  if (options.handle) {
@@ -1426,13 +1502,22 @@ function createWebServer(options) {
1426
1502
  // ../../src/bootstrap/web/session.ts
1427
1503
  import { createHmac, randomBytes } from "crypto";
1428
1504
  import { AuthManager as AuthManager2 } from "@getstrata/core/auth/guard";
1505
+ import { isSessionInvalidated } from "@getstrata/core/auth/sessionCookie";
1429
1506
  import { getBoundDatabaseConnection as getBoundDatabaseConnection2 } from "@getstrata/core/database/boundConnection";
1430
- import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
1507
+ import {
1508
+ getActiveDatabaseConnection,
1509
+ hasActiveDatabaseConnection
1510
+ } from "@getstrata/core/database/connectionContext";
1511
+ import {
1512
+ getDefaultDatabasePool as getDefaultDatabasePool3,
1513
+ getDefaultDatabaseQuery
1514
+ } from "@getstrata/core/database/defaultConnection";
1431
1515
  import { currentSqlDialect, sqlTimestamp } from "@getstrata/core/database/dialect";
1432
1516
  import { readRequestCookie } from "@getstrata/core/http/cookies";
1433
1517
  import { currentRequestMeta as currentRequestMeta3 } from "@getstrata/core/http/requestMetaContext";
1434
1518
  import { isProductionEnv as isProductionEnv2 } from "@getstrata/core/runtime/appEnv";
1435
1519
  import { timingSafeCompareString } from "@getstrata/core/security/timingSafeCompare";
1520
+ import { runWithMigrationBypassForIdentifier } from "@getstrata/core/tenant/databaseTenantContext";
1436
1521
  function sqlPlaceholder(index) {
1437
1522
  return currentSqlDialect().placeholder(index);
1438
1523
  }
@@ -1443,17 +1528,25 @@ function isSqlClient(value) {
1443
1528
  return typeof value.unsafe === "function";
1444
1529
  }
1445
1530
  function resolveSql(source) {
1446
- if (isSqlClient(source)) {
1447
- return source;
1531
+ const client = isSqlClient(source) ? source : source();
1532
+ if (!hasActiveDatabaseConnection()) {
1533
+ return client;
1534
+ }
1535
+ try {
1536
+ if (client === getDefaultDatabasePool3() || client === getDefaultDatabaseQuery()) {
1537
+ return getActiveDatabaseConnection(client);
1538
+ }
1539
+ } catch {
1540
+ return client;
1448
1541
  }
1449
- return source();
1542
+ return client;
1450
1543
  }
1451
1544
  function defaultSessionSql() {
1452
1545
  const bound = getBoundDatabaseConnection2();
1453
1546
  if (bound) {
1454
1547
  return bound;
1455
1548
  }
1456
- return getDefaultDatabasePool2();
1549
+ return getDefaultDatabasePool3();
1457
1550
  }
1458
1551
  function defaultMapSessionUser(user) {
1459
1552
  return {
@@ -1483,14 +1576,32 @@ function mapSessionUserRow(row) {
1483
1576
  };
1484
1577
  }
1485
1578
  async function defaultLoadSessionUser(sql, sessionId) {
1486
- const rows = await sql.unsafe(`SELECT s.user_id, s.expires_at, u.*
1487
- FROM sessions s
1488
- INNER JOIN users u ON u.id = s.user_id
1489
- WHERE s.id = ${sqlPlaceholder(1)} AND s.expires_at > ${sqlNow()}`, [sessionId]);
1490
- const row = rows[0];
1491
- if (!row)
1579
+ const sessionRows = await runWithMigrationBypassForIdentifier(sessionId, async () => {
1580
+ return await sql.unsafe(`SELECT user_id, expires_at, created_at AS session_created_at
1581
+ FROM sessions
1582
+ WHERE id = ${sqlPlaceholder(1)} AND expires_at > ${sqlNow()}`, [sessionId]);
1583
+ });
1584
+ const session = sessionRows[0];
1585
+ if (!session)
1586
+ return null;
1587
+ const userId = Number(session.user_id);
1588
+ if (!Number.isInteger(userId) || userId <= 0) {
1492
1589
  return null;
1493
- return mapSessionUserRow(row);
1590
+ }
1591
+ return await runWithMigrationBypassForIdentifier(userId, async () => {
1592
+ const rows = await sql.unsafe(`SELECT * FROM users WHERE id = ${sqlPlaceholder(1)}`, [
1593
+ userId
1594
+ ]);
1595
+ const row = rows[0];
1596
+ if (!row)
1597
+ return null;
1598
+ const createdSource = session.session_created_at;
1599
+ const createdAt = createdSource instanceof Date ? createdSource.getTime() : createdSource ? Date.parse(String(createdSource)) : Number.NaN;
1600
+ if (!Number.isFinite(createdAt) || isSessionInvalidated(createdAt, row.session_valid_after)) {
1601
+ return null;
1602
+ }
1603
+ return mapSessionUserRow({ ...row, user_id: userId, session_created_at: createdSource });
1604
+ });
1494
1605
  }
1495
1606
  function redirectWithCookie(location, setCookie, status) {
1496
1607
  return new Response(null, {
@@ -1545,25 +1656,40 @@ class CookieSessionStore {
1545
1656
  async create(user, meta = {}) {
1546
1657
  const id = randomBytes(32).toString("hex");
1547
1658
  const expires = new Date(Date.now() + this.maxAgeSeconds * 1000);
1548
- await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at, user_agent, ip_address, last_active_at)
1549
- VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()})`, [id, user.id, sqlTimestamp(expires), meta.userAgent ?? null, meta.ipAddress ?? null]);
1659
+ await runWithMigrationBypassForIdentifier(user.id, async () => {
1660
+ await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at, user_agent, ip_address, last_active_at, created_at)
1661
+ VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()}, ${sqlNow()})`, [id, user.id, sqlTimestamp(expires), meta.userAgent ?? null, meta.ipAddress ?? null]);
1662
+ });
1550
1663
  return id;
1551
1664
  }
1552
1665
  async destroy(sessionId) {
1553
- await this.sql().unsafe(`DELETE FROM sessions WHERE id = ${sqlPlaceholder(1)}`, [sessionId]);
1666
+ await runWithMigrationBypassForIdentifier(sessionId, async () => {
1667
+ await this.sql().unsafe(`DELETE FROM sessions WHERE id = ${sqlPlaceholder(1)}`, [sessionId]);
1668
+ });
1669
+ }
1670
+ async destroyAllSessions(userId) {
1671
+ await runWithMigrationBypassForIdentifier(userId, async () => {
1672
+ await this.sql().unsafe(`DELETE FROM sessions WHERE user_id = ${sqlPlaceholder(1)}`, [
1673
+ userId
1674
+ ]);
1675
+ });
1554
1676
  }
1555
1677
  async destroyOtherSessions(userId, keepSessionId) {
1556
- await this.sql().unsafe(`DELETE FROM sessions WHERE user_id = ${sqlPlaceholder(1)} AND id <> ${sqlPlaceholder(2)}`, [userId, keepSessionId]);
1678
+ await runWithMigrationBypassForIdentifier(userId, async () => {
1679
+ await this.sql().unsafe(`DELETE FROM sessions WHERE user_id = ${sqlPlaceholder(1)} AND id <> ${sqlPlaceholder(2)}`, [userId, keepSessionId]);
1680
+ });
1557
1681
  }
1558
1682
  async listForUser(userId) {
1559
1683
  const dialect = currentSqlDialect();
1560
- return this.sql().unsafe(`SELECT id, user_id, user_agent, ip_address, last_active_at, expires_at
1684
+ return await runWithMigrationBypassForIdentifier(userId, () => this.sql().unsafe(`SELECT id, user_id, user_agent, ip_address, last_active_at, expires_at
1561
1685
  FROM sessions
1562
1686
  WHERE user_id = ${dialect.placeholder(1)} AND expires_at > ${dialect.nowExpression()}
1563
- ORDER BY last_active_at DESC${dialect.nullsLastSuffix()}, expires_at DESC`, [userId]);
1687
+ ORDER BY last_active_at DESC${dialect.nullsLastSuffix()}, expires_at DESC`, [userId]));
1564
1688
  }
1565
1689
  async touch(sessionId) {
1566
- await this.sql().unsafe(`UPDATE sessions SET last_active_at = ${sqlNow()} WHERE id = ${sqlPlaceholder(1)}`, [sessionId]);
1690
+ await runWithMigrationBypassForIdentifier(sessionId, async () => {
1691
+ await this.sql().unsafe(`UPDATE sessions SET last_active_at = ${sqlNow()} WHERE id = ${sqlPlaceholder(1)}`, [sessionId]);
1692
+ });
1567
1693
  }
1568
1694
  async read(request) {
1569
1695
  const sessionId = this.sessionIdFromRequest(request);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "1.0.8",
3
+ "version": "1.1.0",
4
4
  "description": "Strata application bootstrap: HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -168,14 +168,14 @@
168
168
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta --external mysql2 --external @getstrata/core",
169
169
  "build:types": "tsc -p tsconfig.types.json && bun ../../scripts/prune-bootstrap-dist-types.ts",
170
170
  "prepublishOnly": "bun run build",
171
- "build:subpaths": "bun build entries/applicationRegistry.ts entries/buildModuleRoutes.ts entries/buildWebModuleRoutes.ts entries/cache/modelCacheTags.ts entries/config.ts entries/context.ts entries/contracts.ts entries/createWebRoutes.ts entries/createSpaRoutes.ts entries/dependencies.ts entries/discoverModules.ts entries/health.ts entries/http/securedRouteModelBinding.ts entries/httpKernel.ts entries/listeners/invalidateCacheOnModelWrite.ts entries/membershipService.ts entries/metricsRoutes.ts entries/queue/defaultJobs.ts entries/providers.ts entries/providers/view.ts entries/routeRegistry.ts entries/schedule.ts entries/secretsGuard.ts entries/web/forms.ts entries/web/routing.ts entries/web/server.ts entries/web/session.ts entries/web/slug.ts --outdir dist --root . --target bun --external bun --external eta --external mysql2 --external @getstrata/core --external @getstrata/bootstrap --external @getstrata/bootstrap/applicationRegistry --external @getstrata/bootstrap/buildModuleRoutes --external @getstrata/bootstrap/buildWebModuleRoutes --external @getstrata/bootstrap/cache/modelCacheTags --external @getstrata/bootstrap/config --external @getstrata/bootstrap/context --external @getstrata/bootstrap/contracts --external @getstrata/bootstrap/createWebRoutes --external @getstrata/bootstrap/createSpaRoutes --external @getstrata/bootstrap/dependencies --external @getstrata/bootstrap/discoverModules --external @getstrata/bootstrap/health --external @getstrata/bootstrap/http/securedRouteModelBinding --external @getstrata/bootstrap/httpKernel --external @getstrata/bootstrap/listeners/invalidateCacheOnModelWrite --external @getstrata/bootstrap/membershipService --external @getstrata/bootstrap/metricsRoutes --external @getstrata/bootstrap/queue/defaultJobs --external @getstrata/bootstrap/providers --external @getstrata/bootstrap/providers/view --external @getstrata/bootstrap/routeRegistry --external @getstrata/bootstrap/schedule --external @getstrata/bootstrap/secretsGuard --external @getstrata/bootstrap/web/forms --external @getstrata/bootstrap/web/routing --external @getstrata/bootstrap/web/server --external @getstrata/bootstrap/web/session --external @getstrata/bootstrap/web/slug --external @getstrata/core/auth/accessControl --external @getstrata/core/auth/abilityChecker --external @getstrata/core/auth/authContext --external @getstrata/core/auth/emailVerification --external @getstrata/core/auth/guard --external @getstrata/core/auth/jwt --external @getstrata/core/auth/jwtGuard --external @getstrata/core/auth/basicAuthGuard --external @getstrata/core/auth/tokenAbilityChecker --external @getstrata/core/auth/membershipContext --external @getstrata/core/auth/membershipMiddleware --external @getstrata/core/auth/membershipScope --external @getstrata/core/auth/membershipService --external @getstrata/core/auth/oauth/oidcProvider --external @getstrata/core/auth/oauth/providers --external @getstrata/core/auth/oauth/samlProvider --external @getstrata/core/auth/oauth/types --external @getstrata/core/auth/password --external @getstrata/core/auth/intendedUrlCookie --external @getstrata/core/auth/passwordConfirmCookie --external @getstrata/core/auth/policy --external @getstrata/core/auth/scimAuthMiddleware --external @getstrata/core/auth/sessionCookie --external @getstrata/core/auth/sessionGuard --external @getstrata/core/auth/tokenHash --external @getstrata/core/audit/exportAuditLogs --external @getstrata/core/audit/siemFormatter --external @getstrata/core/admin/formatValue --external @getstrata/core/admin/registry --external @getstrata/core/admin/types --external @getstrata/core/cache/tags --external @getstrata/core/cache/createCacheStore --external @getstrata/core/cache/repository --external @getstrata/core/cache/simpleCache --external @getstrata/core/cache/simpleCacheStore --external @getstrata/core/config/envSchema --external @getstrata/core/contracts/serviceTokens --external @getstrata/core/contracts/authUserDirectory --external @getstrata/core/contracts/container --external @getstrata/core/contracts/di --external @getstrata/core/crypto/fieldEncryption --external @getstrata/core/crypto/mfaSecret --external @getstrata/core/database --external @getstrata/core/database/baseRepository --external @getstrata/core/database/bindConnection --external @getstrata/core/database/boundConnection --external @getstrata/core/database/bunSql --external @getstrata/core/database/connection --external @getstrata/core/database/connectionContext --external @getstrata/core/database/defaultConnection --external @getstrata/core/database/dialect --external @getstrata/core/database/mysqlConnection --external @getstrata/core/database/namedConnections --external @getstrata/core/database/errors --external @getstrata/core/database/factory --external @getstrata/core/database/migrations --external @getstrata/core/database/migrations/types --external @getstrata/core/database/model --external @getstrata/core/database/query --external @getstrata/core/database/relationships --external @getstrata/core/database/repositoryConnection --external @getstrata/core/database/repositoryQuery --external @getstrata/core/database/seeders --external @getstrata/core/database/seeders/types --external @getstrata/core/database/schema --external @getstrata/core/database/sqliteConnection --external @getstrata/core/database/table --external @getstrata/core/database/transaction --external @getstrata/core/database/types --external @getstrata/core/database/whereBuilder --external @getstrata/core/errors/http --external @getstrata/core/events --external @getstrata/core/facades --external @getstrata/core/http --external @getstrata/core/http/authMiddleware --external @getstrata/core/http/authorizeMiddleware --external @getstrata/core/http/bodySizeLimitMiddleware --external @getstrata/core/http/clientIp --external @getstrata/core/http/cookies --external @getstrata/core/http/contentNegotiation --external @getstrata/core/http/contentSecurityPolicy --external @getstrata/core/http/conditionalResponse --external @getstrata/core/http/corsMiddleware --external @getstrata/core/http/csrfMiddleware --external @getstrata/core/http/csrfProtection --external @getstrata/core/http/csrfToken --external @getstrata/core/http/etag --external @getstrata/core/http/flashSession --external @getstrata/core/http/flashMiddleware --external @getstrata/core/http/formRequest --external @getstrata/core/http/middleware --external @getstrata/core/http/metricsMiddleware --external @getstrata/core/http/loginThrottleMiddleware --external @getstrata/core/http/memoryThrottleMiddleware --external @getstrata/core/http/pagination --external @getstrata/core/http/parseFormBody --external @getstrata/core/http/parseMultipartUpload --external @getstrata/core/http/requireAbilityMiddleware --external @getstrata/core/http/requireAuthMiddleware --external @getstrata/core/http/requireGlobalAdminMiddleware --external @getstrata/core/http/requireWebAuthMiddleware --external @getstrata/core/http/requireVerifiedMiddleware --external @getstrata/core/http/requirePasswordConfirmMiddleware --external @getstrata/core/http/resources --external @getstrata/core/http/response --external @getstrata/core/http/route --external @getstrata/core/http/routeMiddleware --external @getstrata/core/http/routeModelBinding --external @getstrata/core/http/safeInternalPath --external @getstrata/core/http/signedUrl --external @getstrata/core/http/statelessAuth --external @getstrata/core/http/scimThrottleMiddleware --external @getstrata/core/http/securityHeadersMiddleware --external @getstrata/core/http/securedRouteModelBinding --external @getstrata/core/http/requestMetaContext --external @getstrata/core/http/webErrorResponse --external @getstrata/core/http/webFormRequest --external @getstrata/core/http/throttleMiddleware --external @getstrata/core/http/validation --external @getstrata/core/jobs/exportAuditLogsJob --external @getstrata/core/jobs/invalidateCacheTagsJob --external @getstrata/core/lifecycle/gracefulShutdown --external @getstrata/core/logging/logger --external @getstrata/core/logging/requestLoggingMiddleware --external @getstrata/core/mail/mailer --external @getstrata/core/mail/markdownMail --external @getstrata/core/mail/markdownMailable --external @getstrata/core/mail/sanitizeMailHtml --external @getstrata/core/media/imageTransform --external @getstrata/core/metrics/prometheus --external @getstrata/core/notifications --external @getstrata/core/openapi/generator --external @getstrata/core/openapi/registeredRoute --external @getstrata/core/openapi/validate --external @getstrata/core/pagination --external @getstrata/core/queue --external @getstrata/core/queue/createAppQueue --external @getstrata/core/queue/failedJobRepository --external @getstrata/core/queue/failedJobService --external @getstrata/core/queue/jobRegistry --external @getstrata/core/queue/jobRunner --external @getstrata/core/queue/queueMetrics --external @getstrata/core/queue/publicQueue --external @getstrata/core/queue/redisQueue --external @getstrata/core/queue/types --external @getstrata/core/runtime/appEnv --external @getstrata/core/runtime/appKeyPrefix --external @getstrata/core/runtime/frontendMode --external @getstrata/core/runtime/applicationRegistry --external @getstrata/core/runtime/asyncContextStore --external @getstrata/core/scheduler/schedule --external @getstrata/core/scheduler/osCron --external @getstrata/core/security/oauthState --external @getstrata/core/security/publicReads --external @getstrata/core/security/recoveryCodes --external @getstrata/core/security/safeFetch --external @getstrata/core/security/safeUrl --external @getstrata/core/security/scimTenantTokens --external @getstrata/core/security/securityEvents --external @getstrata/core/security/stripeWebhook --external @getstrata/core/security/timingSafeCompare --external @getstrata/core/security/tokenExpiry --external @getstrata/core/security/totp --external @getstrata/core/storage/storage --external @getstrata/core/tenant/tenancyConfig --external @getstrata/core/tenant/tenantContext --external @getstrata/core/tenant/tenantDatabaseScope --external @getstrata/core/tenant/databaseTenantContext --external @getstrata/core/tenant/tenantMiddleware --external @getstrata/core/terminal/runShell --external @getstrata/core/tracing/traceContext --external @getstrata/core/tracing/tracingMiddleware --external @getstrata/core/validation/rules --external @getstrata/core/view",
171
+ "build:subpaths": "bun build entries/applicationRegistry.ts entries/buildModuleRoutes.ts entries/buildWebModuleRoutes.ts entries/cache/modelCacheTags.ts entries/config.ts entries/context.ts entries/contracts.ts entries/createWebRoutes.ts entries/createSpaRoutes.ts entries/dependencies.ts entries/discoverModules.ts entries/health.ts entries/http/securedRouteModelBinding.ts entries/httpKernel.ts entries/listeners/invalidateCacheOnModelWrite.ts entries/membershipService.ts entries/metricsRoutes.ts entries/queue/defaultJobs.ts entries/providers.ts entries/providers/view.ts entries/routeRegistry.ts entries/schedule.ts entries/secretsGuard.ts entries/web/forms.ts entries/web/routing.ts entries/web/server.ts entries/web/session.ts entries/web/slug.ts --outdir dist --root . --target bun --external bun --external eta --external mysql2 --external @getstrata/core --external @getstrata/bootstrap --external @getstrata/bootstrap/applicationRegistry --external @getstrata/bootstrap/buildModuleRoutes --external @getstrata/bootstrap/buildWebModuleRoutes --external @getstrata/bootstrap/cache/modelCacheTags --external @getstrata/bootstrap/config --external @getstrata/bootstrap/context --external @getstrata/bootstrap/contracts --external @getstrata/bootstrap/createWebRoutes --external @getstrata/bootstrap/createSpaRoutes --external @getstrata/bootstrap/dependencies --external @getstrata/bootstrap/discoverModules --external @getstrata/bootstrap/health --external @getstrata/bootstrap/http/securedRouteModelBinding --external @getstrata/bootstrap/httpKernel --external @getstrata/bootstrap/listeners/invalidateCacheOnModelWrite --external @getstrata/bootstrap/membershipService --external @getstrata/bootstrap/metricsRoutes --external @getstrata/bootstrap/queue/defaultJobs --external @getstrata/bootstrap/providers --external @getstrata/bootstrap/providers/view --external @getstrata/bootstrap/routeRegistry --external @getstrata/bootstrap/schedule --external @getstrata/bootstrap/secretsGuard --external @getstrata/bootstrap/web/forms --external @getstrata/bootstrap/web/routing --external @getstrata/bootstrap/web/server --external @getstrata/bootstrap/web/session --external @getstrata/bootstrap/web/slug --external @getstrata/core/auth/accessControl --external @getstrata/core/auth/abilityChecker --external @getstrata/core/auth/authContext --external @getstrata/core/auth/emailVerification --external @getstrata/core/auth/guard --external @getstrata/core/auth/jwt --external @getstrata/core/auth/jwtGuard --external @getstrata/core/auth/basicAuthGuard --external @getstrata/core/auth/tokenAbilityChecker --external @getstrata/core/auth/membershipContext --external @getstrata/core/auth/membershipMiddleware --external @getstrata/core/auth/membershipScope --external @getstrata/core/auth/membershipService --external @getstrata/core/auth/oauth/oidcProvider --external @getstrata/core/auth/oauth/providers --external @getstrata/core/auth/oauth/samlProvider --external @getstrata/core/auth/oauth/types --external @getstrata/core/auth/saml/samlServiceProvider --external @getstrata/core/auth/password --external @getstrata/core/auth/passwordLogin --external @getstrata/core/auth/oneTimeToken --external @getstrata/core/auth/intendedUrlCookie --external @getstrata/core/auth/passwordConfirmCookie --external @getstrata/core/auth/policy --external @getstrata/core/auth/scimAuthMiddleware --external @getstrata/core/auth/sessionCookie --external @getstrata/core/auth/sessionGuard --external @getstrata/core/auth/tokenHash --external @getstrata/core/audit/exportAuditLogs --external @getstrata/core/audit/siemFormatter --external @getstrata/core/admin/formatValue --external @getstrata/core/admin/registry --external @getstrata/core/admin/types --external @getstrata/core/cache/tags --external @getstrata/core/cache/createCacheStore --external @getstrata/core/cache/repository --external @getstrata/core/cache/simpleCache --external @getstrata/core/cache/simpleCacheStore --external @getstrata/core/config/envSchema --external @getstrata/core/contracts/serviceTokens --external @getstrata/core/contracts/authUserDirectory --external @getstrata/core/contracts/container --external @getstrata/core/contracts/di --external @getstrata/core/crypto/fieldEncryption --external @getstrata/core/crypto/mfaSecret --external @getstrata/core/database --external @getstrata/core/database/baseRepository --external @getstrata/core/database/bindConnection --external @getstrata/core/database/boundConnection --external @getstrata/core/database/bunSql --external @getstrata/core/database/connection --external @getstrata/core/database/connectionContext --external @getstrata/core/database/defaultConnection --external @getstrata/core/database/dialect --external @getstrata/core/database/mysqlConnection --external @getstrata/core/database/namedConnections --external @getstrata/core/database/errors --external @getstrata/core/database/factory --external @getstrata/core/database/migrations --external @getstrata/core/database/migrations/types --external @getstrata/core/database/model --external @getstrata/core/database/query --external @getstrata/core/database/relationships --external @getstrata/core/database/repositoryConnection --external @getstrata/core/database/repositoryQuery --external @getstrata/core/database/seeders --external @getstrata/core/database/seeders/types --external @getstrata/core/database/schema --external @getstrata/core/database/sqliteConnection --external @getstrata/core/database/table --external @getstrata/core/database/transaction --external @getstrata/core/database/types --external @getstrata/core/database/whereBuilder --external @getstrata/core/errors/http --external @getstrata/core/events --external @getstrata/core/facades --external @getstrata/core/http --external @getstrata/core/http/authMiddleware --external @getstrata/core/http/authorizeMiddleware --external @getstrata/core/http/bodySizeLimitMiddleware --external @getstrata/core/http/clientIp --external @getstrata/core/http/cookies --external @getstrata/core/http/contentNegotiation --external @getstrata/core/http/contentSecurityPolicy --external @getstrata/core/http/conditionalResponse --external @getstrata/core/http/corsMiddleware --external @getstrata/core/http/csrfMiddleware --external @getstrata/core/http/csrfProtection --external @getstrata/core/http/csrfToken --external @getstrata/core/http/etag --external @getstrata/core/http/flashSession --external @getstrata/core/http/flashMiddleware --external @getstrata/core/http/formRequest --external @getstrata/core/http/middleware --external @getstrata/core/http/metricsMiddleware --external @getstrata/core/http/loginThrottleMiddleware --external @getstrata/core/http/memoryThrottleMiddleware --external @getstrata/core/http/pagination --external @getstrata/core/http/parseFormBody --external @getstrata/core/http/parseMultipartUpload --external @getstrata/core/http/requireAbilityMiddleware --external @getstrata/core/http/requireAuthMiddleware --external @getstrata/core/http/requireGlobalAdminMiddleware --external @getstrata/core/http/requireWebAuthMiddleware --external @getstrata/core/http/requireVerifiedMiddleware --external @getstrata/core/http/requirePasswordConfirmMiddleware --external @getstrata/core/http/resources --external @getstrata/core/http/response --external @getstrata/core/http/route --external @getstrata/core/http/routeMiddleware --external @getstrata/core/http/routeModelBinding --external @getstrata/core/http/safeInternalPath --external @getstrata/core/http/signedUrl --external @getstrata/core/http/statelessAuth --external @getstrata/core/http/scimThrottleMiddleware --external @getstrata/core/http/securityHeadersMiddleware --external @getstrata/core/http/securedRouteModelBinding --external @getstrata/core/http/requestMetaContext --external @getstrata/core/http/webErrorResponse --external @getstrata/core/http/webFormRequest --external @getstrata/core/http/throttleMiddleware --external @getstrata/core/http/validation --external @getstrata/core/jobs/exportAuditLogsJob --external @getstrata/core/jobs/invalidateCacheTagsJob --external @getstrata/core/lifecycle/gracefulShutdown --external @getstrata/core/logging/logger --external @getstrata/core/logging/requestLoggingMiddleware --external @getstrata/core/mail/mailer --external @getstrata/core/mail/markdownMail --external @getstrata/core/mail/markdownMailable --external @getstrata/core/mail/sanitizeMailHtml --external @getstrata/core/media/imageTransform --external @getstrata/core/metrics/prometheus --external @getstrata/core/notifications --external @getstrata/core/openapi/generator --external @getstrata/core/openapi/registeredRoute --external @getstrata/core/openapi/validate --external @getstrata/core/pagination --external @getstrata/core/queue --external @getstrata/core/queue/createAppQueue --external @getstrata/core/queue/failedJobRepository --external @getstrata/core/queue/failedJobService --external @getstrata/core/queue/jobRegistry --external @getstrata/core/queue/jobRunner --external @getstrata/core/queue/queueMetrics --external @getstrata/core/queue/publicQueue --external @getstrata/core/queue/redisQueue --external @getstrata/core/queue/types --external @getstrata/core/runtime/appEnv --external @getstrata/core/runtime/appKeyPrefix --external @getstrata/core/runtime/frontendMode --external @getstrata/core/runtime/applicationRegistry --external @getstrata/core/runtime/asyncContextStore --external @getstrata/core/scheduler/schedule --external @getstrata/core/scheduler/osCron --external @getstrata/core/security/oauthState --external @getstrata/core/security/publicReads --external @getstrata/core/security/recoveryCodes --external @getstrata/core/security/safeFetch --external @getstrata/core/security/safePath --external @getstrata/core/security/safeUrl --external @getstrata/core/security/scimTenantTokens --external @getstrata/core/security/securityEvents --external @getstrata/core/security/stripeWebhook --external @getstrata/core/security/timingSafeCompare --external @getstrata/core/security/tokenExpiry --external @getstrata/core/security/totp --external @getstrata/core/storage/storage --external @getstrata/core/tenant/tenancyConfig --external @getstrata/core/tenant/tenantContext --external @getstrata/core/tenant/tenantDatabaseScope --external @getstrata/core/tenant/databaseTenantContext --external @getstrata/core/tenant/tenantMiddleware --external @getstrata/core/tenant/enableTenantRls --external @getstrata/core/terminal/runShell --external @getstrata/core/tracing/traceContext --external @getstrata/core/tracing/tracingMiddleware --external @getstrata/core/validation/rules --external @getstrata/core/view",
172
172
  "build:shims": "true"
173
173
  },
174
174
  "publishConfig": {
175
175
  "access": "public"
176
176
  },
177
177
  "peerDependencies": {
178
- "@getstrata/core": "^1.0.8",
178
+ "@getstrata/core": "^1.1.0",
179
179
  "typescript": "^5.9.0"
180
180
  },
181
181
  "engines": {