@getstrata/bootstrap 1.0.0 → 1.0.2

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/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # @getstrata/bootstrap changelog
2
2
 
3
+ ## 1.0.2
4
+
5
+ - Lockstep with `create-strata` 1.0.2. No runtime changes.
6
+
7
+ ## 1.0.1
8
+
9
+ - `assertProductionSecrets()` rejects secrets still holding a generated `change-me` placeholder. The shipped `SESSION_SECRET` placeholder was 38 characters, so it passed the length check and booted in production unchanged.
10
+ - Cookie sessions write timestamps through the active dialect, so `CookieSessionStore.create()` works on MySQL.
11
+ - Removed the `createRoutes` subpath. It was in-repo fixture HTTP, documented as not an app API, and it packed a stray `dist/_.._/_.._/index.html` asset and dogfood helpers into the tarball.
12
+ - Removed leftover third-party product token strings from the production blocklist.
13
+ - `assertProductionSecrets()` requires `APP_URL` to be a public `http(s)` origin (localhost rejected, plain http warns). Signed links and redirects are built from it.
14
+ - `createWebServer()` records the socket address (IPv4-mapped addresses normalized) in the request context, so throttles, request logs, and session rows see the real client.
15
+ - `CookieSessionAuthManager.signIn()` fills `ip_address` and `user_agent` from the request context when the caller passes no meta. The `Secure` cookie flag now keys on `APP_ENV=production` (or `NODE_ENV`), matching the rest of the framework; generated apps never set `NODE_ENV`, so production cookies were not marked `Secure`.
16
+ - `engines.bun` is declared.
17
+
3
18
  ## 1.0.0
4
19
 
5
20
  - First stable release of `@getstrata/bootstrap`.
@@ -23,7 +38,7 @@
23
38
 
24
39
  - Peer `@getstrata/core` `^0.7.0`.
25
40
  - Production `SESSION_SECRET` is required whenever views are on (`FRONTEND_MODE=server-htmx` or `hybrid`).
26
- - Hybrid SPA routes stay under `/app/*`. They do not redirect `/`, so staff HTML keeps the home path.
41
+ - Hybrid SPA routes stay under `/app/*`. They do not redirect `/`, so HTML at `/` keeps the home path.
27
42
  - `appEnvSchema` validates `FRONTEND_MODE` with the core pattern, including `hybrid`.
28
43
  - `HttpKernel.wrapApi` maps thrown HTTP errors to JSON the same way `wrapWeb` maps them to HTML.
29
44
 
@@ -31,7 +46,7 @@
31
46
 
32
47
  - Peer `@getstrata/core` `^0.6.0`.
33
48
  - Cookie session SQL uses the current SQL dialect for placeholders, `NOW()`, and `NULLS LAST`.
34
- - Kernel comments describe guest, verified-email, and password-confirm gates without referring to another framework.
49
+ - Kernel helpers cover guest, verified-email, and password-confirm gates.
35
50
 
36
51
  ## 0.2.68
37
52
 
@@ -46,17 +61,17 @@
46
61
 
47
62
  ## 0.2.66
48
63
 
49
- - `assertProductionSecrets()` is feature-gated for every app. API tokens no longer imply the previous in-repo app's encryption/CORS/OAuth/public-read checklist. Published test token strings are still denied. the previous in-repo app's extra production profile lives in the app (`src/config/productionSecrets.ts`).
50
- - **Breaking:** `HttpKernel.wrapWebGuest()` defaults `home` to `/` instead of `/organizations`. Apps with an org home should pass that path (the previous in-repo app already does).
64
+ - `assertProductionSecrets()` is feature-gated for every app. API tokens no longer imply HiroApp's encryption/CORS/OAuth/public-read checklist. Published test token strings are still denied. HiroApp's extra production profile lives in the app (`src/config/productionSecrets.ts`).
65
+ - **Breaking:** `HttpKernel.wrapWebGuest()` defaults `home` to `/` instead of `/organizations`. Apps with an org home should pass that path (HiroApp already does).
51
66
  - `createWebRoutes()` no longer seeds a `/` → `/organizations` redirect. The in-repo app owns `/` via its organization module.
52
67
 
53
68
  ## 0.2.65
54
69
 
55
- - App listener discovery reads `src/listeners` from `process.cwd()` so a built `@getstrata/bootstrap` bundle still finds the previous in-repo app registrars (`import.meta.dir` after `build:bootstrap` is the package dist). Listener boot calls each registrar every time (registrars are idempotent per `eventBus`).
70
+ - App listener discovery reads `src/listeners` from `process.cwd()` so a built `@getstrata/bootstrap` bundle still finds HiroApp registrars (`import.meta.dir` after `build:bootstrap` is the package dist). Listener boot calls each registrar every time (registrars are idempotent per `eventBus`).
56
71
 
57
72
  ## 0.2.64
58
73
 
59
- - `registerDefaultJobs()` no longer registers the previous in-repo app `webhook.dispatch`. Apps that dispatch model webhooks should call `registerWebhookJobs()` (the previous in-repo app's webhook provider and `queue:work` do).
74
+ - `registerDefaultJobs()` no longer registers HiroApp `webhook.dispatch`. Apps that dispatch model webhooks should call `registerWebhookJobs()` (HiroApp's webhook provider and `queue:work` do).
60
75
 
61
76
  ## 0.2.63
62
77
 
@@ -64,7 +79,7 @@
64
79
 
65
80
  ## 0.2.62
66
81
 
67
- - `HttpKernel.wrapWebPasswordConfirm()` is the previous PHP framework `password.confirm` for HTML routes. Peer `@getstrata/core` `^0.5.75`.
82
+ - `HttpKernel.wrapWebPasswordConfirm()` is the HTML password-confirm gate. Peer `@getstrata/core` `^0.5.75`.
68
83
 
69
84
  ## 0.2.61
70
85
 
@@ -76,7 +91,7 @@
76
91
 
77
92
  ## 0.2.59
78
93
 
79
- - `HttpKernel.wrapWebGuest()` is the previous PHP framework `guest` / `RedirectIfAuthenticated`. Signed-in HTML users redirect to `/organizations` (override the home path). Peer `@getstrata/core` `^0.5.72`.
94
+ - `HttpKernel.wrapWebGuest()` sends signed-in HTML users to `/organizations` (override the home path). Peer `@getstrata/core` `^0.5.72`.
80
95
 
81
96
  ## 0.2.58
82
97
 
@@ -92,7 +107,7 @@
92
107
 
93
108
  ## 0.2.55
94
109
 
95
- - HttpKernel and route builders read `isViewsEnabled` / `isSpaEnabled` from `@getstrata/core/runtime/frontendMode` instead of the previous in-repo app `src/config/frontend`.
110
+ - HttpKernel and route builders read `isViewsEnabled` / `isSpaEnabled` from `@getstrata/core/runtime/frontendMode` instead of HiroApp `src/config/frontend`.
96
111
 
97
112
  ## 0.2.54
98
113
 
@@ -116,7 +131,7 @@
116
131
 
117
132
  ## 0.2.50
118
133
 
119
- - `createCookieSessionAuthManager` / `CookieSessionGuard` turn `CookieSessionStore` into an `AuthGuard` / `AuthManager` for `CORE_AUTH_TOKEN`. Supply a `mapUser` mapper; the previous in-repo app abilities are not baked in. Omit `sql` to read the client from `bindDatabaseConnection()` on every call.
120
- - `checkDatabase()` / `createHealthRoutes()` ping the bound database client, not the previous in-repo app's private connection holder. `/health` stays `{ status: "ok" }` unless `{ pingOnHealth: true }`. Extra JSON fields are optional.
121
- - `assertProductionSecrets()` is feature-gated. A production HTMX app with `SESSION_SECRET` (32+), `AUTH_DEV_HEADERS=false`, and feature flags off does not need the previous in-repo app API tokens. the previous in-repo app's current production env (rotated tokens, CORS, pepper, expiry) still passes.
134
+ - `createCookieSessionAuthManager` / `CookieSessionGuard` turn `CookieSessionStore` into an `AuthGuard` / `AuthManager` for `CORE_AUTH_TOKEN`. Supply a `mapUser` mapper; HiroApp abilities are not baked in. Omit `sql` to read the client from `bindDatabaseConnection()` on every call.
135
+ - `checkDatabase()` / `createHealthRoutes()` ping the bound database client, not HiroApp's private connection holder. `/health` stays `{ status: "ok" }` unless `{ pingOnHealth: true }`. Extra JSON fields are optional.
136
+ - `assertProductionSecrets()` is feature-gated. A production HTMX app with `SESSION_SECRET` (32+), `AUTH_DEV_HEADERS=false`, and feature flags off does not need HiroApp API tokens. HiroApp's current production env (rotated tokens, CORS, pepper, expiry) still passes.
122
137
  - Re-exports `CORE_ABILITY_CHECKER_TOKEN` and `CORE_AUTH_USER_DIRECTORY_TOKEN`.
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # @getstrata/bootstrap
2
2
 
3
- Application bootstrap for Strata apps: HttpKernel, service container, and web utilities.
3
+ Application bootstrap for [Strata](https://github.com/EyK-26/strata) apps: HttpKernel, service container, and web session helpers. Requires Bun (tested on 1.4.x).
4
+
5
+ Generate an app rather than wiring this by hand:
6
+
7
+ ```bash
8
+ bunx create-strata my-app
9
+ ```
4
10
 
5
11
  ## Install
6
12
 
@@ -8,6 +14,16 @@ Application bootstrap for Strata apps: HttpKernel, service container, and web ut
8
14
  bun add @getstrata/bootstrap @getstrata/core
9
15
  ```
10
16
 
17
+ ## HttpKernel and providers
18
+
19
+ ```typescript
20
+ import { coreProviders, createAppContext, createHttpKernel } from "@getstrata/bootstrap";
21
+ ```
22
+
23
+ `createAppContext()` does not call `assertProductionSecrets`. Call that yourself when `APP_ENV=production`; it is feature-gated, so an HTML app with API tokens off is not asked for token secrets. It rejects empty secrets, short `SESSION_SECRET` values, wildcard `CORS_ALLOWED_ORIGINS`, `AUTH_DEV_HEADERS=true`, `FEATURE_PUBLIC_READS=true`, and any secret still holding a generated `change-me` placeholder.
24
+
25
+ Build routes with `buildWebModuleRoutes` and `buildModuleRoutes`.
26
+
11
27
  ## Web utilities
12
28
 
13
29
  ```typescript
@@ -20,22 +36,26 @@ import {
20
36
  } from "@getstrata/bootstrap";
21
37
  ```
22
38
 
23
- `CookieSessionStore` can read `is_admin` from the user row for `wrapWebGlobalAdmin`. Pass `loadSessionUser` when your `users` table does not match the default `SELECT u.*`.
39
+ `CookieSessionStore` reads `is_admin` from the user row for `wrapWebGlobalAdmin`. Pass `loadSessionUser` when your `users` table does not match the default `SELECT u.*`.
24
40
 
25
- ## HttpKernel
41
+ HTML apps should bind `createCookieSessionAuthManager` from `@getstrata/bootstrap/web/session` rather than the HMAC `SessionGuard`, then use `signIn` / `signOut` or the redirect helpers. Pass `mapUser` to map roles.
26
42
 
27
- ```typescript
28
- import { createHttpKernel, createAppContext, coreProviders } from "@getstrata/bootstrap";
29
- ```
43
+ `wrapWeb` applies the web group (CSRF plus flash) and `withErrorHandling`, so a CSRF `ForbiddenError` becomes an HTML 403 when views are on (`FRONTEND_MODE=server-htmx` or `hybrid`). `wrapWebGuest` sends signed-in users to `/` by default; pass a string or `(user) => path` to override. `wrapWebLogin` and `wrapWebRegister` already include the web group plus throttle, so do not wrap them with `wrapWeb` again. The throttle callback should return an HTML form at 429.
44
+
45
+ ## SPA routes
46
+
47
+ `createSpaRoutes` and `mergeSpaRoutes` serve `SPA_PREFIX` (default `/app`) as `prefix`, `prefix/`, and `prefix/*`. Hybrid keeps HTML at `/`. Pass `distDirectory` and `wrap` when the app is not the process cwd. Until the SPA is built, the prefix answers 503 telling you to run `bun run frontend:build`; generated apps ship that script.
30
48
 
31
- `createAppContext()` does not call `assertProductionSecrets`. That helper is feature-gated (tokens, CORS, OAuth, encryption only when those features are on). HTML apps can call it without API tokens when those flags are off. See [docs/BUILDING-APPS.md](../../docs/BUILDING-APPS.md) and [docs/PRODUCTION.md](../../docs/PRODUCTION.md).
49
+ ## Queue
32
50
 
33
- HTML apps should bind `createCookieSessionAuthManager` from `@getstrata/bootstrap/web/session` instead of HMAC `SessionGuard`. Use `signIn` / `signOut` (or the redirect helpers). Pass `mapUser` to map roles in the app.
51
+ `registerDefaultJobs()` registers `cache.invalidate-tags` and `audit.export` only. Apps that dispatch model webhooks call `registerWebhookJobs()` themselves.
34
52
 
35
- `wrapWeb` applies the web group (CSRF + flash) and `withErrorHandling`, so CSRF `ForbiddenError` becomes an HTML 403 when views are on (`FRONTEND_MODE=server-htmx` or `hybrid`). `wrapWebGuest` sends signed-in users to `/` by default (pass a string or `(user) => path` to override). `wrapWebLogin` / `wrapWebRegister` include that web group plus throttle. Do not wrap them with `wrapWeb` again. The throttle callback should return an HTML form at 429.
53
+ ## Not a starter API
36
54
 
37
- `createSpaRoutes` / `mergeSpaRoutes` serve `SPA_PREFIX` (default `/app`) as `prefix`, `prefix/`, and `prefix/*`. Hybrid does not redirect `/`. Pass `distDirectory` and `wrap` when the app is not the process cwd. Apps should not copy a second static-file server.
55
+ `@getstrata/bootstrap/createWebRoutes` and `@getstrata/bootstrap/schedule` assemble in-repo fixture HTTP for framework tests. They are not a general app API. New apps use `buildWebModuleRoutes` and `buildModuleRoutes`.
38
56
 
39
- `registerDefaultJobs()` registers `cache.invalidate-tags` and `audit.export` only. Apps that dispatch model webhooks should call `registerWebhookJobs()` themselves.
57
+ ## Docs
40
58
 
41
- These subpaths assemble leftover fixture HTTP for framework tests and are not a generic starter API: `@getstrata/bootstrap/createRoutes`, `@getstrata/bootstrap/schedule`, `@getstrata/bootstrap/createWebRoutes`. New apps should use `buildWebModuleRoutes` / `buildModuleRoutes`. Generated examples use `createApp()` in `apps/hiroapp`.
59
+ - [Building apps](https://github.com/EyK-26/strata/blob/main/docs/BUILDING-APPS.md)
60
+ - [Production](https://github.com/EyK-26/strata/blob/main/docs/PRODUCTION.md)
61
+ - [Auth choices](https://github.com/EyK-26/strata/blob/main/docs/AUTH.md)
@@ -1,4 +1,4 @@
1
- import type { BunRequest } from "bun";
1
+ import type { BunRequest, Server } from "bun";
2
2
  import type { AppRouteMap } from "../contracts.ts";
3
3
  export interface WebServerOptions {
4
4
  port: number;
@@ -7,7 +7,7 @@ export interface WebServerOptions {
7
7
  publicDir?: string;
8
8
  onRequest?: (request: Request) => Promise<void> | void;
9
9
  }
10
- type BunRouteHandler = (request: BunRequest) => Response | Promise<Response>;
10
+ type BunRouteHandler = (request: BunRequest, server: Server<unknown>) => Response | Promise<Response>;
11
11
  declare function convertAppRoutesToBunRoutes(routes: AppRouteMap): Record<string, Record<string, BunRouteHandler>>;
12
- export declare function createWebServer(options: WebServerOptions): Bun.Server<undefined>;
12
+ export declare function createWebServer(options: WebServerOptions): Server<undefined>;
13
13
  export { convertAppRoutesToBunRoutes };
@@ -7,14 +7,28 @@ var PUBLISHED_TEST_SCIM_BEARER_TOKEN = "strata-scim-test-token";
7
7
  var MIN_SESSION_SECRET_LENGTH = 32;
8
8
  var PUBLISHED_TEST_TOKENS = new Set([
9
9
  PUBLISHED_TEST_ADMIN_API_TOKEN,
10
- PUBLISHED_TEST_MEMBER_API_TOKEN,
11
- "workhub-admin-test-token",
12
- "workhub-member-test-token"
13
- ]);
14
- var PUBLISHED_TEST_SCIM_TOKENS = new Set([
15
- PUBLISHED_TEST_SCIM_BEARER_TOKEN,
16
- "workhub-scim-test-token"
10
+ PUBLISHED_TEST_MEMBER_API_TOKEN
17
11
  ]);
12
+ var PUBLISHED_TEST_SCIM_TOKENS = new Set([PUBLISHED_TEST_SCIM_BEARER_TOKEN]);
13
+ var PLACEHOLDER_SECRET_PATTERN = /change-me/i;
14
+ var SECRETS_TO_ROTATE = [
15
+ "SESSION_SECRET",
16
+ "TOKEN_HASH_PEPPER",
17
+ "METRICS_TOKEN",
18
+ "SCIM_BEARER_TOKEN",
19
+ "JWT_SECRET",
20
+ "OAUTH_STATE_SECRET",
21
+ "KMS_ENCRYPTION_KEY",
22
+ "STRIPE_WEBHOOK_SECRET",
23
+ "ADMIN_API_TOKEN",
24
+ "MEMBER_API_TOKEN"
25
+ ];
26
+ function assertNoPlaceholderSecrets(env) {
27
+ const unrotated = SECRETS_TO_ROTATE.filter((name) => PLACEHOLDER_SECRET_PATTERN.test(env[name]?.trim() ?? ""));
28
+ if (unrotated.length > 0) {
29
+ throw new Error(`Production startup blocked: replace the generated placeholder values for ${unrotated.join(", ")}.`);
30
+ }
31
+ }
18
32
  function isEnabled(value, defaultEnabled) {
19
33
  if (value === undefined) {
20
34
  return defaultEnabled;
@@ -90,12 +104,30 @@ function assertFeatureProductionSecrets(env) {
90
104
  console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
91
105
  }
92
106
  }
107
+ var LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]);
108
+ function assertPublicAppUrl(env) {
109
+ const raw = env.APP_URL?.trim() ?? "";
110
+ let url = null;
111
+ try {
112
+ url = raw ? new URL(raw) : null;
113
+ } catch {
114
+ url = null;
115
+ }
116
+ if (!url || !/^https?:$/.test(url.protocol) || LOCAL_HOSTS.has(url.hostname)) {
117
+ throw new Error("Production startup blocked: set APP_URL to the public origin of this app (for example https://app.example.com). Signed links and redirects are built from it.");
118
+ }
119
+ if (url.protocol === "http:") {
120
+ console.warn(`[secrets] APP_URL uses http; session cookies are marked Secure in production and will not be sent over ${raw}.`);
121
+ }
122
+ }
93
123
  function assertProductionSecrets(env = process.env) {
94
124
  const appEnv = env.APP_ENV ?? "local";
95
125
  if (appEnv !== "production") {
96
126
  return;
97
127
  }
128
+ assertNoPlaceholderSecrets(env);
98
129
  assertAuthDevHeadersDisabled(env);
130
+ assertPublicAppUrl(env);
99
131
  if (isTokenAuthEnabled(env)) {
100
132
  assertTokenAuthProductionSecrets(env);
101
133
  }
@@ -2,14 +2,19 @@
2
2
  // ../../src/bootstrap/web/server.ts
3
3
  import { currentRequestMeta, runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
4
4
  import { notFoundHtmlResponse } from "@getstrata/core/view";
5
+ function socketAddress(server, request) {
6
+ const address = server?.requestIP(request)?.address;
7
+ return address ? address.replace(/^::ffff:/i, "") : null;
8
+ }
5
9
  async function missingHtmlResponse() {
6
10
  return notFoundHtmlResponse();
7
11
  }
8
12
  function wrapRouteHandler(handler) {
9
- return async (request) => {
13
+ return async (request, server) => {
10
14
  return await runWithRequestMeta({
11
15
  ...currentRequestMeta(),
12
16
  request,
17
+ ipAddress: socketAddress(server, request),
13
18
  userAgent: request.headers.get("user-agent")
14
19
  }, async () => {
15
20
  const response = await handler(request);
@@ -45,10 +50,11 @@ function createWebServer(options) {
45
50
  return Bun.serve({
46
51
  port: options.port,
47
52
  ...bunRoutes ? { routes: bunRoutes } : {},
48
- async fetch(request) {
53
+ async fetch(request, server) {
49
54
  return await runWithRequestMeta({
50
55
  ...currentRequestMeta(),
51
56
  request,
57
+ ipAddress: socketAddress(server, request),
52
58
  userAgent: request.headers.get("user-agent")
53
59
  }, async () => {
54
60
  await options.onRequest?.(request);
@@ -4,8 +4,10 @@ import { createHmac, randomBytes } from "crypto";
4
4
  import { AuthManager } from "@getstrata/core/auth/guard";
5
5
  import { getBoundDatabaseConnection } from "@getstrata/core/database/boundConnection";
6
6
  import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
7
- import { currentSqlDialect } from "@getstrata/core/database/dialect";
7
+ import { currentSqlDialect, sqlTimestamp } from "@getstrata/core/database/dialect";
8
8
  import { readRequestCookie } from "@getstrata/core/http/cookies";
9
+ import { currentRequestMeta } from "@getstrata/core/http/requestMetaContext";
10
+ import { isProductionEnv } from "@getstrata/core/runtime/appEnv";
9
11
  import { timingSafeCompareString } from "@getstrata/core/security/timingSafeCompare";
10
12
  function sqlPlaceholder(index) {
11
13
  return currentSqlDialect().placeholder(index);
@@ -108,7 +110,7 @@ class CookieSessionStore {
108
110
  return sessionId;
109
111
  }
110
112
  withSecureFlag(header) {
111
- if (true) {
113
+ if (!isProductionEnv()) {
112
114
  return header;
113
115
  }
114
116
  return header.includes("Secure") ? header : `${header}; Secure`;
@@ -120,7 +122,7 @@ class CookieSessionStore {
120
122
  const id = randomBytes(32).toString("hex");
121
123
  const expires = new Date(Date.now() + this.maxAgeSeconds * 1000);
122
124
  await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at, user_agent, ip_address, last_active_at)
123
- VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()})`, [id, user.id, expires.toISOString(), meta.userAgent ?? null, meta.ipAddress ?? null]);
125
+ VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()})`, [id, user.id, sqlTimestamp(expires), meta.userAgent ?? null, meta.ipAddress ?? null]);
124
126
  return id;
125
127
  }
126
128
  async destroy(sessionId) {
@@ -176,7 +178,11 @@ class CookieSessionAuthManager extends AuthManager {
176
178
  this.registerGuard("session", guard);
177
179
  }
178
180
  async signIn(user, meta = {}) {
179
- const sessionId = await this.store.create(user, meta);
181
+ const current = currentRequestMeta();
182
+ const sessionId = await this.store.create(user, {
183
+ userAgent: meta.userAgent ?? current.userAgent ?? null,
184
+ ipAddress: meta.ipAddress ?? current.ipAddress ?? null
185
+ });
180
186
  return { sessionId, setCookie: this.store.cookieHeader(user, sessionId) };
181
187
  }
182
188
  async signOut(request) {
package/dist/index.js CHANGED
@@ -1146,14 +1146,28 @@ var PUBLISHED_TEST_SCIM_BEARER_TOKEN = "strata-scim-test-token";
1146
1146
  var MIN_SESSION_SECRET_LENGTH = 32;
1147
1147
  var PUBLISHED_TEST_TOKENS = new Set([
1148
1148
  PUBLISHED_TEST_ADMIN_API_TOKEN,
1149
- PUBLISHED_TEST_MEMBER_API_TOKEN,
1150
- "workhub-admin-test-token",
1151
- "workhub-member-test-token"
1152
- ]);
1153
- var PUBLISHED_TEST_SCIM_TOKENS = new Set([
1154
- PUBLISHED_TEST_SCIM_BEARER_TOKEN,
1155
- "workhub-scim-test-token"
1149
+ PUBLISHED_TEST_MEMBER_API_TOKEN
1156
1150
  ]);
1151
+ var PUBLISHED_TEST_SCIM_TOKENS = new Set([PUBLISHED_TEST_SCIM_BEARER_TOKEN]);
1152
+ var PLACEHOLDER_SECRET_PATTERN = /change-me/i;
1153
+ var SECRETS_TO_ROTATE = [
1154
+ "SESSION_SECRET",
1155
+ "TOKEN_HASH_PEPPER",
1156
+ "METRICS_TOKEN",
1157
+ "SCIM_BEARER_TOKEN",
1158
+ "JWT_SECRET",
1159
+ "OAUTH_STATE_SECRET",
1160
+ "KMS_ENCRYPTION_KEY",
1161
+ "STRIPE_WEBHOOK_SECRET",
1162
+ "ADMIN_API_TOKEN",
1163
+ "MEMBER_API_TOKEN"
1164
+ ];
1165
+ function assertNoPlaceholderSecrets(env) {
1166
+ const unrotated = SECRETS_TO_ROTATE.filter((name) => PLACEHOLDER_SECRET_PATTERN.test(env[name]?.trim() ?? ""));
1167
+ if (unrotated.length > 0) {
1168
+ throw new Error(`Production startup blocked: replace the generated placeholder values for ${unrotated.join(", ")}.`);
1169
+ }
1170
+ }
1157
1171
  function isEnabled(value, defaultEnabled) {
1158
1172
  if (value === undefined) {
1159
1173
  return defaultEnabled;
@@ -1229,12 +1243,30 @@ function assertFeatureProductionSecrets(env) {
1229
1243
  console.warn("[secrets] SIEM_EXPORT_URL is not configured; audit logs remain database-only.");
1230
1244
  }
1231
1245
  }
1246
+ var LOCAL_HOSTS = new Set(["localhost", "127.0.0.1", "0.0.0.0", "::1", "[::1]"]);
1247
+ function assertPublicAppUrl(env) {
1248
+ const raw = env.APP_URL?.trim() ?? "";
1249
+ let url = null;
1250
+ try {
1251
+ url = raw ? new URL(raw) : null;
1252
+ } catch {
1253
+ url = null;
1254
+ }
1255
+ if (!url || !/^https?:$/.test(url.protocol) || LOCAL_HOSTS.has(url.hostname)) {
1256
+ throw new Error("Production startup blocked: set APP_URL to the public origin of this app (for example https://app.example.com). Signed links and redirects are built from it.");
1257
+ }
1258
+ if (url.protocol === "http:") {
1259
+ console.warn(`[secrets] APP_URL uses http; session cookies are marked Secure in production and will not be sent over ${raw}.`);
1260
+ }
1261
+ }
1232
1262
  function assertProductionSecrets(env = process.env) {
1233
1263
  const appEnv = env.APP_ENV ?? "local";
1234
1264
  if (appEnv !== "production") {
1235
1265
  return;
1236
1266
  }
1267
+ assertNoPlaceholderSecrets(env);
1237
1268
  assertAuthDevHeadersDisabled(env);
1269
+ assertPublicAppUrl(env);
1238
1270
  if (isTokenAuthEnabled(env)) {
1239
1271
  assertTokenAuthProductionSecrets(env);
1240
1272
  }
@@ -1324,14 +1356,19 @@ function createRouteKernel(dependencies) {
1324
1356
  // ../../src/bootstrap/web/server.ts
1325
1357
  import { currentRequestMeta as currentRequestMeta2, runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
1326
1358
  import { notFoundHtmlResponse as notFoundHtmlResponse2 } from "@getstrata/core/view";
1359
+ function socketAddress(server, request) {
1360
+ const address = server?.requestIP(request)?.address;
1361
+ return address ? address.replace(/^::ffff:/i, "") : null;
1362
+ }
1327
1363
  async function missingHtmlResponse() {
1328
1364
  return notFoundHtmlResponse2();
1329
1365
  }
1330
1366
  function wrapRouteHandler(handler) {
1331
- return async (request) => {
1367
+ return async (request, server) => {
1332
1368
  return await runWithRequestMeta({
1333
1369
  ...currentRequestMeta2(),
1334
1370
  request,
1371
+ ipAddress: socketAddress(server, request),
1335
1372
  userAgent: request.headers.get("user-agent")
1336
1373
  }, async () => {
1337
1374
  const response = await handler(request);
@@ -1367,10 +1404,11 @@ function createWebServer(options) {
1367
1404
  return Bun.serve({
1368
1405
  port: options.port,
1369
1406
  ...bunRoutes ? { routes: bunRoutes } : {},
1370
- async fetch(request) {
1407
+ async fetch(request, server) {
1371
1408
  return await runWithRequestMeta({
1372
1409
  ...currentRequestMeta2(),
1373
1410
  request,
1411
+ ipAddress: socketAddress(server, request),
1374
1412
  userAgent: request.headers.get("user-agent")
1375
1413
  }, async () => {
1376
1414
  await options.onRequest?.(request);
@@ -1395,8 +1433,10 @@ import { createHmac, randomBytes } from "crypto";
1395
1433
  import { AuthManager as AuthManager2 } from "@getstrata/core/auth/guard";
1396
1434
  import { getBoundDatabaseConnection as getBoundDatabaseConnection2 } from "@getstrata/core/database/boundConnection";
1397
1435
  import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
1398
- import { currentSqlDialect } from "@getstrata/core/database/dialect";
1436
+ import { currentSqlDialect, sqlTimestamp } from "@getstrata/core/database/dialect";
1399
1437
  import { readRequestCookie } from "@getstrata/core/http/cookies";
1438
+ import { currentRequestMeta as currentRequestMeta3 } from "@getstrata/core/http/requestMetaContext";
1439
+ import { isProductionEnv } from "@getstrata/core/runtime/appEnv";
1400
1440
  import { timingSafeCompareString } from "@getstrata/core/security/timingSafeCompare";
1401
1441
  function sqlPlaceholder(index) {
1402
1442
  return currentSqlDialect().placeholder(index);
@@ -1499,7 +1539,7 @@ class CookieSessionStore {
1499
1539
  return sessionId;
1500
1540
  }
1501
1541
  withSecureFlag(header) {
1502
- if (true) {
1542
+ if (!isProductionEnv()) {
1503
1543
  return header;
1504
1544
  }
1505
1545
  return header.includes("Secure") ? header : `${header}; Secure`;
@@ -1511,7 +1551,7 @@ class CookieSessionStore {
1511
1551
  const id = randomBytes(32).toString("hex");
1512
1552
  const expires = new Date(Date.now() + this.maxAgeSeconds * 1000);
1513
1553
  await this.sql().unsafe(`INSERT INTO sessions (id, user_id, expires_at, user_agent, ip_address, last_active_at)
1514
- VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()})`, [id, user.id, expires.toISOString(), meta.userAgent ?? null, meta.ipAddress ?? null]);
1554
+ VALUES (${sqlPlaceholder(1)}, ${sqlPlaceholder(2)}, ${sqlPlaceholder(3)}, ${sqlPlaceholder(4)}, ${sqlPlaceholder(5)}, ${sqlNow()})`, [id, user.id, sqlTimestamp(expires), meta.userAgent ?? null, meta.ipAddress ?? null]);
1515
1555
  return id;
1516
1556
  }
1517
1557
  async destroy(sessionId) {
@@ -1567,7 +1607,11 @@ class CookieSessionAuthManager extends AuthManager2 {
1567
1607
  this.registerGuard("session", guard);
1568
1608
  }
1569
1609
  async signIn(user, meta = {}) {
1570
- const sessionId = await this.store.create(user, meta);
1610
+ const current = currentRequestMeta3();
1611
+ const sessionId = await this.store.create(user, {
1612
+ userAgent: meta.userAgent ?? current.userAgent ?? null,
1613
+ ipAddress: meta.ipAddress ?? current.ipAddress ?? null
1614
+ });
1571
1615
  return { sessionId, setCookie: this.store.cookieHeader(user, sessionId) };
1572
1616
  }
1573
1617
  async signOut(request) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/bootstrap",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Strata application bootstrap: HttpKernel, DI, web session helpers",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -55,11 +55,6 @@
55
55
  "import": "./dist/entries/createWebRoutes.js",
56
56
  "default": "./dist/entries/createWebRoutes.js"
57
57
  },
58
- "./createRoutes": {
59
- "types": "./dist/bootstrap/createRoutes.d.ts",
60
- "import": "./dist/entries/createRoutes.js",
61
- "default": "./dist/entries/createRoutes.js"
62
- },
63
58
  "./createSpaRoutes": {
64
59
  "types": "./dist/bootstrap/createSpaRoutes.d.ts",
65
60
  "import": "./dist/entries/createSpaRoutes.js",
@@ -169,18 +164,21 @@
169
164
  "CHANGELOG.md"
170
165
  ],
171
166
  "scripts": {
172
- "build": "bun run build:bundle && bun run build:shims && bun run build:subpaths && bun run build:types",
167
+ "build": "rm -rf dist && bun run build:bundle && bun run build:shims && bun run build:subpaths && bun run build:types",
173
168
  "build:bundle": "bun build index.ts --outdir dist --target bun --external bun --external eta --external mysql2 --external @getstrata/core",
174
169
  "build:types": "tsc -p tsconfig.types.json && bun ../../scripts/prune-bootstrap-dist-types.ts",
175
170
  "prepublishOnly": "bun run build",
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 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/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/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/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/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/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",
177
172
  "build:shims": "true"
178
173
  },
179
174
  "publishConfig": {
180
175
  "access": "public"
181
176
  },
182
177
  "peerDependencies": {
183
- "@getstrata/core": "^1.0.0",
178
+ "@getstrata/core": "^1.0.2",
184
179
  "typescript": "^5.9.0"
180
+ },
181
+ "engines": {
182
+ "bun": ">=1.4.0"
185
183
  }
186
184
  }