@getstrata/bootstrap 0.2.49 → 0.2.51

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
@@ -20,6 +20,7 @@ import { applyMiddlewareToRoutes } from "@getstrata/core/http/middleware";
20
20
 
21
21
  // ../../src/bootstrap/httpKernel.ts
22
22
  import { createMembershipMiddleware } from "@getstrata/core/auth/membershipMiddleware";
23
+ import { resolveAbilityChecker } from "@getstrata/core/contracts/serviceTokens";
23
24
  import { createAuthMiddleware } from "@getstrata/core/http/authMiddleware";
24
25
  import { createAuthorizeMiddleware } from "@getstrata/core/http/authorizeMiddleware";
25
26
  import { createBodySizeLimitMiddleware } from "@getstrata/core/http/bodySizeLimitMiddleware";
@@ -77,24 +78,38 @@ function parsePositiveInt(value, fallback) {
77
78
  }
78
79
  return Math.trunc(parsed);
79
80
  }
81
+ function parseWindowSeconds(secondsValue, msValue, fallback) {
82
+ if (secondsValue !== undefined && secondsValue.trim() !== "") {
83
+ return parsePositiveInt(secondsValue, fallback);
84
+ }
85
+ if (msValue !== undefined && msValue.trim() !== "") {
86
+ const parsedMs = Number(msValue);
87
+ if (Number.isFinite(parsedMs) && parsedMs > 0) {
88
+ return Math.max(1, Math.trunc(parsedMs / 1000));
89
+ }
90
+ }
91
+ return fallback;
92
+ }
80
93
  function resolveLoginRateLimit() {
81
94
  const defaults = isLocalAppEnv() ? LOCAL_LOGIN_RATE_LIMIT : PRODUCTION_LOGIN_RATE_LIMIT;
82
95
  return {
83
96
  maxAttempts: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
84
- decaySeconds: parsePositiveInt(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, defaults.decaySeconds)
97
+ decaySeconds: parseWindowSeconds(process.env.LOGIN_RATE_LIMIT_WINDOW_SECONDS, process.env.LOGIN_RATE_LIMIT_WINDOW_MS, defaults.decaySeconds)
85
98
  };
86
99
  }
87
100
  function resolveRegisterRateLimit() {
88
101
  const defaults = isLocalAppEnv() ? { maxAttempts: 100, decaySeconds: 60 } : { maxAttempts: 10, decaySeconds: 60 };
89
102
  return {
90
103
  maxAttempts: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_PER_WINDOW, defaults.maxAttempts),
91
- decaySeconds: parsePositiveInt(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS ?? process.env.REGISTER_RATE_LIMIT_WINDOW_MS ? String(Number(process.env.REGISTER_RATE_LIMIT_WINDOW_MS) / 1000) : undefined, defaults.decaySeconds)
104
+ decaySeconds: parseWindowSeconds(process.env.REGISTER_RATE_LIMIT_WINDOW_SECONDS, process.env.REGISTER_RATE_LIMIT_WINDOW_MS, defaults.decaySeconds)
92
105
  };
93
106
  }
94
107
 
95
108
  // ../../src/bootstrap/config.ts
96
109
  import {
110
+ CORE_ABILITY_CHECKER_TOKEN,
97
111
  CORE_AUTH_TOKEN,
112
+ CORE_AUTH_USER_DIRECTORY_TOKEN,
98
113
  CORE_CACHE_TOKEN,
99
114
  CORE_CONFIG_TOKEN,
100
115
  CORE_EVENT_BUS_TOKEN,
@@ -197,7 +212,7 @@ class HttpKernel {
197
212
  }
198
213
  wrapWebAbility(ability, handler) {
199
214
  const auth = this.dependencies.container.resolve(CORE_AUTH_TOKEN);
200
- const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
215
+ const abilityChecker = resolveAbilityChecker(this.dependencies.container);
201
216
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
202
217
  const middleware = [createRequireWebAuthMiddleware(auth), requireAbility(ability)];
203
218
  return this.wrapWeb(withMiddleware(...middleware)(handler));
@@ -221,7 +236,7 @@ class HttpKernel {
221
236
  return withMiddleware(...middleware)(handler);
222
237
  }
223
238
  wrapAbility(ability, handler) {
224
- const abilityChecker = this.dependencies.container.resolve(CORE_TOKEN_SERVICE_TOKEN);
239
+ const abilityChecker = resolveAbilityChecker(this.dependencies.container);
225
240
  const requireAbility = createRequireAbilityMiddleware(abilityChecker);
226
241
  const middleware = [...this.group("authenticated"), requireAbility(ability)];
227
242
  return withMiddleware(...middleware)(handler);
@@ -891,6 +906,101 @@ function mergeWebRoutes(dependencies, routes) {
891
906
  function createAppDependencies() {
892
907
  return createAppContext().dependencies;
893
908
  }
909
+ // ../../src/bootstrap/health.ts
910
+ import { getBoundDatabaseConnection } from "@getstrata/core/database/boundConnection";
911
+ import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
912
+ import { jsonResponse } from "@getstrata/core/http/response";
913
+ var {RedisClient } = globalThis.Bun;
914
+ function resolveRedisUrl(dependencies) {
915
+ if (!dependencies.container.has(CORE_CONFIG_TOKEN)) {
916
+ return process.env.REDIS_URL?.trim() || undefined;
917
+ }
918
+ const config = dependencies.container.resolve(CORE_CONFIG_TOKEN);
919
+ const redisUrl = config.get(REDIS_URL_CONFIG_KEY)?.trim();
920
+ return redisUrl || undefined;
921
+ }
922
+ function resolveHealthDatabase() {
923
+ const bound = getBoundDatabaseConnection();
924
+ if (bound) {
925
+ return bound;
926
+ }
927
+ try {
928
+ return getDefaultDatabasePool();
929
+ } catch {
930
+ return null;
931
+ }
932
+ }
933
+ async function pingDatabaseClient(connection) {
934
+ try {
935
+ await connection.unsafe("SELECT 1");
936
+ return true;
937
+ } catch {
938
+ return false;
939
+ }
940
+ }
941
+ async function checkDatabase() {
942
+ const connection = resolveHealthDatabase();
943
+ if (!connection) {
944
+ return false;
945
+ }
946
+ return await pingDatabaseClient(connection);
947
+ }
948
+ async function pingDatabase() {
949
+ return await checkDatabase();
950
+ }
951
+ async function checkRedis(redisUrl) {
952
+ try {
953
+ const client = new RedisClient(redisUrl);
954
+ const response = await client.ping();
955
+ return response === "PONG";
956
+ } catch {
957
+ return false;
958
+ }
959
+ }
960
+ async function resolveExtraFields(extra) {
961
+ if (!extra) {
962
+ return {};
963
+ }
964
+ return typeof extra === "function" ? await extra() : extra;
965
+ }
966
+ async function collectDependencyChecks(dependencies) {
967
+ const checks = {
968
+ database: await checkDatabase() ? "ok" : "error"
969
+ };
970
+ const redisUrl = resolveRedisUrl(dependencies);
971
+ if (redisUrl) {
972
+ checks.redis = await checkRedis(redisUrl) ? "ok" : "error";
973
+ } else {
974
+ checks.redis = "skipped";
975
+ }
976
+ const ready = checks.database === "ok" && (checks.redis === "ok" || checks.redis === "skipped");
977
+ return { checks, ready };
978
+ }
979
+ function createHealthRoutes(dependencies, options = {}) {
980
+ return {
981
+ "/health": async () => {
982
+ const extra = await resolveExtraFields(options.extra);
983
+ if (!options.pingOnHealth) {
984
+ return jsonResponse({ status: "ok", ...extra });
985
+ }
986
+ const { checks, ready } = await collectDependencyChecks(dependencies);
987
+ return jsonResponse({
988
+ status: ready ? "ok" : "error",
989
+ checks,
990
+ ...extra
991
+ }, { status: ready ? 200 : 503 });
992
+ },
993
+ "/ready": async () => {
994
+ const extra = await resolveExtraFields(options.extra);
995
+ const { checks, ready } = await collectDependencyChecks(dependencies);
996
+ return jsonResponse({
997
+ status: ready ? "ready" : "not_ready",
998
+ checks,
999
+ ...extra
1000
+ }, { status: ready ? 200 : 503 });
1001
+ }
1002
+ };
1003
+ }
894
1004
  // ../../src/bootstrap/http/securedRouteModelBinding.ts
895
1005
  import {
896
1006
  securedBindRouteModel,
@@ -902,6 +1012,7 @@ import { resolveMembershipService } from "@getstrata/core/auth/membershipService
902
1012
  var DEFAULT_ADMIN_API_TOKEN = "workhub-admin-test-token";
903
1013
  var DEFAULT_MEMBER_API_TOKEN = "workhub-member-test-token";
904
1014
  var DEFAULT_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
1015
+ var MIN_SESSION_SECRET_LENGTH = 32;
905
1016
  var DEFAULT_TOKENS = new Set([DEFAULT_ADMIN_API_TOKEN, DEFAULT_MEMBER_API_TOKEN]);
906
1017
  var DEFAULT_SCIM_TOKENS = new Set([DEFAULT_SCIM_BEARER_TOKEN]);
907
1018
  function isEnabled(value, defaultEnabled) {
@@ -910,19 +1021,31 @@ function isEnabled(value, defaultEnabled) {
910
1021
  }
911
1022
  return defaultEnabled ? value !== "false" : value === "true";
912
1023
  }
913
- function assertProductionSecrets(env = process.env) {
914
- const appEnv = env.APP_ENV ?? "local";
915
- if (appEnv !== "production") {
916
- return;
1024
+ function isTokenAuthEnabled(env) {
1025
+ return Boolean(env.ADMIN_API_TOKEN?.trim()) || Boolean(env.MEMBER_API_TOKEN?.trim()) || env.FEATURE_API_TOKENS === "true";
1026
+ }
1027
+ function isOAuthEnabled(env) {
1028
+ return isEnabled(env.FEATURE_OAUTH, false) || isEnabled(env.FEATURE_SAML, false) || Boolean(env.GITHUB_CLIENT_ID?.trim()) || Boolean(env.OIDC_ISSUER?.trim()) || Boolean(env.SAML_LOGIN_URL?.trim());
1029
+ }
1030
+ function isCorsConfigured(env) {
1031
+ return env.CORS_ALLOWED_ORIGINS !== undefined;
1032
+ }
1033
+ function assertAuthDevHeadersDisabled(env) {
1034
+ if (isEnabled(env.AUTH_DEV_HEADERS, true)) {
1035
+ throw new Error("Production startup blocked: set AUTH_DEV_HEADERS=false to disable development auth headers.");
917
1036
  }
1037
+ }
1038
+ function assertSessionSecret(env) {
1039
+ const secret = env.SESSION_SECRET?.trim() ?? "";
1040
+ if (secret.length < MIN_SESSION_SECRET_LENGTH) {
1041
+ throw new Error("Production startup blocked: set SESSION_SECRET when FRONTEND_MODE=server-htmx (32+ characters).");
1042
+ }
1043
+ }
1044
+ function assertWorkHubProductionSecrets(env) {
918
1045
  const adminToken = env.ADMIN_API_TOKEN ?? DEFAULT_ADMIN_API_TOKEN;
919
1046
  const memberToken = env.MEMBER_API_TOKEN ?? DEFAULT_MEMBER_API_TOKEN;
920
1047
  const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
921
1048
  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
1049
  if (DEFAULT_TOKENS.has(adminToken) || DEFAULT_TOKENS.has(memberToken)) {
927
1050
  throw new Error("Production startup blocked: rotate ADMIN_API_TOKEN and MEMBER_API_TOKEN away from default WorkHub test values.");
928
1051
  }
@@ -954,9 +1077,50 @@ function assertProductionSecrets(env = process.env) {
954
1077
  if (!env.API_TOKEN_DEFAULT_EXPIRY_DAYS?.trim()) {
955
1078
  throw new Error("Production startup blocked: set API_TOKEN_DEFAULT_EXPIRY_DAYS to enforce token rotation.");
956
1079
  }
1080
+ }
1081
+ function assertSiblingProductionSecrets(env) {
1082
+ if (isEnabled(env.FEATURE_SCIM, false)) {
1083
+ const scimToken = env.SCIM_BEARER_TOKEN ?? DEFAULT_SCIM_BEARER_TOKEN;
1084
+ if (DEFAULT_SCIM_TOKENS.has(scimToken) || !env.SCIM_BEARER_TOKEN?.trim()) {
1085
+ throw new Error("Production startup blocked: rotate SCIM_BEARER_TOKEN away from default WorkHub test values.");
1086
+ }
1087
+ }
1088
+ if (isEnabled(env.FEATURE_FIELD_ENCRYPTION, false) && !env.KMS_ENCRYPTION_KEY?.trim()) {
1089
+ throw new Error("Production startup blocked: set KMS_ENCRYPTION_KEY when field encryption is enabled.");
1090
+ }
1091
+ if (isEnabled(env.FEATURE_BILLING, false) && !env.STRIPE_WEBHOOK_SECRET?.trim()) {
1092
+ throw new Error("Production startup blocked: set STRIPE_WEBHOOK_SECRET when billing webhooks are enabled.");
1093
+ }
1094
+ if (isOAuthEnabled(env) && !env.OAUTH_STATE_SECRET?.trim()) {
1095
+ throw new Error("Production startup blocked: set OAUTH_STATE_SECRET for OAuth CSRF protection.");
1096
+ }
1097
+ if (isCorsConfigured(env)) {
1098
+ const corsOrigins = (env.CORS_ALLOWED_ORIGINS ?? "*").split(",").map((origin) => origin.trim());
1099
+ if (corsOrigins.includes("*")) {
1100
+ throw new Error("Production startup blocked: set explicit CORS_ALLOWED_ORIGINS instead of wildcard.");
1101
+ }
1102
+ }
1103
+ if (isEnabled(env.FEATURE_PUBLIC_READS, false)) {
1104
+ throw new Error("Production startup blocked: set FEATURE_PUBLIC_READS=false for authenticated-only reads.");
1105
+ }
1106
+ if (!env.SIEM_EXPORT_URL?.trim() && isEnabled(env.FEATURE_SIEM_EXPORT, false)) {
1107
+ console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
1108
+ }
1109
+ }
1110
+ function assertProductionSecrets(env = process.env) {
1111
+ const appEnv = env.APP_ENV ?? "local";
1112
+ if (appEnv !== "production") {
1113
+ return;
1114
+ }
1115
+ assertAuthDevHeadersDisabled(env);
1116
+ if (isTokenAuthEnabled(env)) {
1117
+ assertWorkHubProductionSecrets(env);
1118
+ } else {
1119
+ assertSiblingProductionSecrets(env);
1120
+ }
957
1121
  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.");
1122
+ if (frontendMode === "server-htmx") {
1123
+ assertSessionSecret(env);
960
1124
  }
961
1125
  }
962
1126
  // ../../src/bootstrap/web/forms.ts
@@ -1090,18 +1254,71 @@ function createWebServer(options) {
1090
1254
  }
1091
1255
  // ../../src/bootstrap/web/session.ts
1092
1256
  import { createHash, randomBytes } from "crypto";
1257
+ import { AuthManager as AuthManager2 } from "@getstrata/core/auth/guard";
1258
+ import { getBoundDatabaseConnection as getBoundDatabaseConnection2 } from "@getstrata/core/database/boundConnection";
1259
+ import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
1093
1260
  import { readRequestCookie } from "@getstrata/core/http/cookies";
1261
+ function isSqlClient(value) {
1262
+ return typeof value.unsafe === "function";
1263
+ }
1264
+ function resolveSql(source) {
1265
+ if (isSqlClient(source)) {
1266
+ return source;
1267
+ }
1268
+ return source();
1269
+ }
1270
+ function defaultSessionSql() {
1271
+ const bound = getBoundDatabaseConnection2();
1272
+ if (bound) {
1273
+ return bound;
1274
+ }
1275
+ return getDefaultDatabasePool2();
1276
+ }
1277
+ function defaultMapSessionUser(user) {
1278
+ return {
1279
+ id: user.id,
1280
+ role: user.is_admin ? "admin" : "member"
1281
+ };
1282
+ }
1283
+ async function defaultLoadSessionUser(sql, sessionId) {
1284
+ const rows = await sql.unsafe(`SELECT s.id, s.user_id, s.expires_at, u.name, u.email, u.learn_subscriber,
1285
+ COALESCE(u.is_admin, false) AS is_admin
1286
+ FROM sessions s
1287
+ INNER JOIN users u ON u.id = s.user_id
1288
+ WHERE s.id = $1 AND s.expires_at > NOW()`, [sessionId]);
1289
+ const row = rows[0];
1290
+ if (!row)
1291
+ return null;
1292
+ return {
1293
+ id: row.user_id,
1294
+ name: row.name,
1295
+ email: row.email,
1296
+ learn_subscriber: row.learn_subscriber,
1297
+ is_admin: row.is_admin
1298
+ };
1299
+ }
1300
+ function redirectWithCookie(location, setCookie, status) {
1301
+ return new Response(null, {
1302
+ status,
1303
+ headers: {
1304
+ Location: location,
1305
+ "Set-Cookie": setCookie
1306
+ }
1307
+ });
1308
+ }
1094
1309
 
1095
1310
  class CookieSessionStore {
1096
- sql;
1311
+ sqlSource;
1097
1312
  secret;
1098
1313
  cookieName;
1099
1314
  maxAgeSeconds;
1100
- constructor(sql, secret, cookieName = "strata_session", maxAgeSeconds = 60 * 60 * 24 * 14) {
1101
- this.sql = sql;
1315
+ loadSessionUser;
1316
+ constructor(sqlSource, secret, cookieName = "strata_session", maxAgeSeconds = 60 * 60 * 24 * 14, loadSessionUser = defaultLoadSessionUser) {
1317
+ this.sqlSource = sqlSource;
1102
1318
  this.secret = secret;
1103
1319
  this.cookieName = cookieName;
1104
1320
  this.maxAgeSeconds = maxAgeSeconds;
1321
+ this.loadSessionUser = loadSessionUser;
1105
1322
  }
1106
1323
  cookieHeader(_user, sessionId) {
1107
1324
  const payload = `${sessionId}.${this.sign(sessionId)}`;
@@ -1110,16 +1327,30 @@ class CookieSessionStore {
1110
1327
  clearCookieHeader() {
1111
1328
  return this.withSecureFlag(`${this.cookieName}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`);
1112
1329
  }
1330
+ sessionIdFromRequest(request) {
1331
+ const cookie = readRequestCookie(request, this.cookieName);
1332
+ const raw = cookie ?? null;
1333
+ if (!raw)
1334
+ return null;
1335
+ const [sessionId, signature] = raw.split(".");
1336
+ if (!sessionId || !signature || signature !== this.sign(sessionId)) {
1337
+ return null;
1338
+ }
1339
+ return sessionId;
1340
+ }
1113
1341
  withSecureFlag(header) {
1114
1342
  if (true) {
1115
1343
  return header;
1116
1344
  }
1117
1345
  return header.includes("Secure") ? header : `${header}; Secure`;
1118
1346
  }
1347
+ sql() {
1348
+ return resolveSql(this.sqlSource);
1349
+ }
1119
1350
  async create(user) {
1120
1351
  const id = randomBytes(32).toString("hex");
1121
1352
  const expires = new Date(Date.now() + this.maxAgeSeconds * 1000);
1122
- await this.sql.unsafe(`INSERT INTO sessions (id, user_id, expires_at) VALUES ($1, $2, $3)`, [
1353
+ await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at) VALUES ($1, $2, $3)`, [
1123
1354
  id,
1124
1355
  user.id,
1125
1356
  expires
@@ -1127,50 +1358,82 @@ class CookieSessionStore {
1127
1358
  return id;
1128
1359
  }
1129
1360
  async destroy(sessionId) {
1130
- await this.sql.unsafe(`DELETE FROM sessions WHERE id = $1`, [sessionId]);
1361
+ await this.sql().unsafe(`DELETE FROM sessions WHERE id = $1`, [sessionId]);
1131
1362
  }
1132
1363
  async read(request) {
1133
- const cookie = readRequestCookie(request, this.cookieName);
1134
- const raw = cookie ?? null;
1135
- if (!raw)
1136
- return null;
1137
- const [sessionId, signature] = raw.split(".");
1138
- if (!sessionId || !signature || signature !== this.sign(sessionId)) {
1139
- return null;
1140
- }
1141
- const rows = await this.sql.unsafe(`SELECT s.id, s.user_id, s.expires_at, u.name, u.email, u.learn_subscriber,
1142
- COALESCE(u.is_admin, false) AS is_admin
1143
- FROM sessions s
1144
- INNER JOIN users u ON u.id = s.user_id
1145
- WHERE s.id = $1 AND s.expires_at > NOW()`, [sessionId]);
1146
- const row = rows[0];
1147
- if (!row)
1364
+ const sessionId = this.sessionIdFromRequest(request);
1365
+ if (!sessionId)
1148
1366
  return null;
1149
- return {
1150
- id: row.user_id,
1151
- name: row.name,
1152
- email: row.email,
1153
- learn_subscriber: row.learn_subscriber,
1154
- is_admin: row.is_admin
1155
- };
1367
+ return this.loadSessionUser(this.sql(), sessionId);
1156
1368
  }
1157
1369
  sign(value) {
1158
1370
  return createHash("sha256").update(`${value}.${this.secret}`).digest("hex").slice(0, 32);
1159
1371
  }
1160
1372
  }
1373
+
1374
+ class CookieSessionGuard {
1375
+ store;
1376
+ mapUser;
1377
+ constructor(store, mapUser = defaultMapSessionUser) {
1378
+ this.store = store;
1379
+ this.mapUser = mapUser;
1380
+ }
1381
+ async resolve(request) {
1382
+ const user = await this.store.read(request);
1383
+ if (!user) {
1384
+ return null;
1385
+ }
1386
+ return this.mapUser(user);
1387
+ }
1388
+ }
1389
+
1390
+ class CookieSessionAuthManager extends AuthManager2 {
1391
+ store;
1392
+ constructor(store, mapUser = defaultMapSessionUser) {
1393
+ super(new CookieSessionGuard(store, mapUser));
1394
+ this.store = store;
1395
+ }
1396
+ async signIn(user) {
1397
+ const sessionId = await this.store.create(user);
1398
+ return { sessionId, setCookie: this.store.cookieHeader(user, sessionId) };
1399
+ }
1400
+ async signOut(request) {
1401
+ const sessionId = this.store.sessionIdFromRequest(request);
1402
+ if (sessionId) {
1403
+ await this.store.destroy(sessionId);
1404
+ }
1405
+ return { setCookie: this.store.clearCookieHeader() };
1406
+ }
1407
+ async signInRedirect(user, location, status = 302) {
1408
+ const { setCookie } = await this.signIn(user);
1409
+ return redirectWithCookie(location, setCookie, status);
1410
+ }
1411
+ async signOutRedirect(request, location, status = 302) {
1412
+ const { setCookie } = await this.signOut(request);
1413
+ return redirectWithCookie(location, setCookie, status);
1414
+ }
1415
+ }
1416
+ function createCookieSessionAuthManager(options = {}) {
1417
+ const store = options.store ?? new CookieSessionStore(options.sql ?? defaultSessionSql, options.secret ?? process.env.SESSION_SECRET?.trim() ?? "", options.cookieName, options.maxAgeSeconds, options.loadSessionUser);
1418
+ return new CookieSessionAuthManager(store, options.mapUser ?? defaultMapSessionUser);
1419
+ }
1161
1420
  // ../../src/bootstrap/web/slug.ts
1162
1421
  function slugify(value) {
1163
1422
  return value.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 80);
1164
1423
  }
1165
1424
  export {
1166
1425
  APP_PORT_CONFIG_KEY,
1426
+ CORE_ABILITY_CHECKER_TOKEN,
1167
1427
  CORE_AUTH_TOKEN,
1428
+ CORE_AUTH_USER_DIRECTORY_TOKEN,
1168
1429
  CORE_CACHE_TOKEN,
1169
1430
  CORE_CONFIG_TOKEN,
1170
1431
  CORE_EVENT_BUS_TOKEN,
1171
1432
  CORE_POLICY_GATE_TOKEN,
1172
1433
  CORE_QUEUE_TOKEN,
1173
1434
  CORE_TOKEN_SERVICE_TOKEN,
1435
+ CookieSessionAuthManager,
1436
+ CookieSessionGuard,
1174
1437
  CookieSessionStore,
1175
1438
  DATABASE_URL_CONFIG_KEY,
1176
1439
  DEFAULT_APP_PORT,
@@ -1184,12 +1447,16 @@ export {
1184
1447
  buildModuleRoutes,
1185
1448
  buildWebModuleRoutes,
1186
1449
  cacheTagsForModelWrite,
1450
+ checkDatabase,
1451
+ checkRedis,
1187
1452
  collectProviders,
1188
1453
  configureModulesDirectory,
1189
1454
  coreProviders,
1190
1455
  createAppContext,
1191
1456
  createAppDependencies,
1457
+ createCookieSessionAuthManager,
1192
1458
  createCsrfProtection,
1459
+ createHealthRoutes,
1193
1460
  createHttpKernel,
1194
1461
  createRouteKernel,
1195
1462
  createWebRoutes,
@@ -1200,6 +1467,7 @@ export {
1200
1467
  getRequiredDependency,
1201
1468
  mergeWebRoutes,
1202
1469
  parseFormBody,
1470
+ pingDatabase,
1203
1471
  prefixRouteMap,
1204
1472
  registerDefaultJobs,
1205
1473
  resolveApplicationAuth,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "0.2.49",
3
+ "version": "0.2.51",
4
4
  "description": "Strata application bootstrap — HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -116,7 +116,7 @@
116
116
  "default": "./dist/entries/providers.js"
117
117
  },
118
118
  "./providers/view": {
119
- "types": "./dist/bootstrap/providers/view/index.d.ts",
119
+ "types": "./dist/bootstrap/providers/view.d.ts",
120
120
  "import": "./dist/entries/providers/view.js",
121
121
  "default": "./dist/entries/providers/view.js"
122
122
  },
@@ -165,21 +165,22 @@
165
165
  "types": "./dist/bootstrap/public-api.d.ts",
166
166
  "files": [
167
167
  "dist",
168
- "README.md"
168
+ "README.md",
169
+ "CHANGELOG.md"
169
170
  ],
170
171
  "scripts": {
171
172
  "build": "bun run build:bundle && bun run build:shims && bun run build:subpaths && bun run build:types",
172
173
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta --external @getstrata/core",
173
174
  "build:types": "tsc -p tsconfig.types.json && bun ../../scripts/prune-bootstrap-dist-types.ts",
174
175
  "prepublishOnly": "bun run build",
175
- "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/createRoutes.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 @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/createRoutes --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/guard --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/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/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/connection --external @getstrata/core/database/defaultConnection --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/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/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/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/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/dispatchWebhookJob --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/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/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/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",
176
+ "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/createRoutes.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 @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/createRoutes --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/guard --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/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/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/defaultConnection --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/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/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/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/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/dispatchWebhookJob --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/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/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/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",
176
177
  "build:shims": "true"
177
178
  },
178
179
  "publishConfig": {
179
180
  "access": "public"
180
181
  },
181
182
  "peerDependencies": {
182
- "@getstrata/core": "^0.5.58",
183
+ "@getstrata/core": "^0.5.60",
183
184
  "typescript": "^5.9.0"
184
185
  }
185
186
  }