@getstrata/core 1.0.3 → 1.0.4

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.
Files changed (49) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/core/runtime/appEnv.d.ts +16 -2
  3. package/dist/core/runtime/appKeyPrefix.d.ts +8 -1
  4. package/dist/entries/audit/exportAuditLogs.js +83 -18
  5. package/dist/entries/audit/siemFormatter.js +32 -3
  6. package/dist/entries/auth/intendedUrlCookie.js +33 -4
  7. package/dist/entries/auth/jwt.js +36 -4
  8. package/dist/entries/auth/jwtGuard.js +36 -4
  9. package/dist/entries/auth/oauth/providers.js +32 -3
  10. package/dist/entries/auth/oauth/samlProvider.js +32 -3
  11. package/dist/entries/auth/passwordConfirmCookie.js +35 -6
  12. package/dist/entries/auth/sessionCookie.js +35 -6
  13. package/dist/entries/auth/sessionGuard.js +35 -6
  14. package/dist/entries/auth/tokenHash.js +33 -4
  15. package/dist/entries/cache/createCacheStore.js +32 -3
  16. package/dist/entries/facades.js +32 -3
  17. package/dist/entries/http/corsMiddleware.js +16 -1
  18. package/dist/entries/http/csrfMiddleware.js +34 -5
  19. package/dist/entries/http/csrfToken.js +34 -5
  20. package/dist/entries/http/flashMiddleware.js +33 -4
  21. package/dist/entries/http/flashSession.js +33 -4
  22. package/dist/entries/http/loginThrottleMiddleware.js +1 -373
  23. package/dist/entries/http/memoryThrottleMiddleware.js +32 -58
  24. package/dist/entries/http/response.js +16 -56
  25. package/dist/entries/http/scimThrottleMiddleware.js +32 -3
  26. package/dist/entries/http/securityHeadersMiddleware.js +32 -3
  27. package/dist/entries/http/signedUrl.js +33 -4
  28. package/dist/entries/http/throttleMiddleware.js +32 -58
  29. package/dist/entries/http/webErrorResponse.js +1 -401
  30. package/dist/entries/jobs/exportAuditLogsJob.js +83 -18
  31. package/dist/entries/lifecycle/gracefulShutdown.js +1 -50
  32. package/dist/entries/mail/mailer.js +32 -3
  33. package/dist/entries/openapi/generator.js +32 -3
  34. package/dist/entries/queue/createAppQueue.js +32 -3
  35. package/dist/entries/queue/publicQueue.js +32 -3
  36. package/dist/entries/queue/queueMetrics.js +32 -3
  37. package/dist/entries/queue/redisQueue.js +32 -3
  38. package/dist/entries/runtime/appEnv.js +18 -1
  39. package/dist/entries/runtime/appKeyPrefix.js +1 -70
  40. package/dist/entries/security/oauthState.js +33 -4
  41. package/dist/entries/security/safeFetch.js +32 -9
  42. package/dist/entries/security/safeUrl.js +1 -105
  43. package/dist/entries/security/totp.js +32 -3
  44. package/dist/entries/tenant/databaseTenantContext.js +48 -6
  45. package/dist/entries/tracing/tracingMiddleware.js +32 -3
  46. package/dist/entries/view.js +1 -778
  47. package/dist/framework/public-api.d.ts +27 -18
  48. package/dist/index.js +442 -53
  49. package/package.json +2 -2
@@ -2,6 +2,26 @@
2
2
  // ../../src/core/auth/sessionCookie.ts
3
3
  import { createHmac, timingSafeEqual } from "crypto";
4
4
 
5
+ // ../../src/core/runtime/appEnv.ts
6
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
7
+ function normalizeEnvValue(value) {
8
+ return (value ?? "").trim().toLowerCase();
9
+ }
10
+ function isProductionEnv(env = process.env) {
11
+ const appEnv = normalizeEnvValue(env.APP_ENV);
12
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
13
+ if (appEnv === "production" || nodeEnv === "production") {
14
+ return true;
15
+ }
16
+ if (appEnv === "") {
17
+ return false;
18
+ }
19
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
20
+ }
21
+ function envFlagEnabled(value) {
22
+ return value === "true";
23
+ }
24
+
5
25
  // ../../src/core/runtime/appKeyPrefix.ts
6
26
  function appKeyPrefix() {
7
27
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -12,6 +32,18 @@ function appCookieName(kind) {
12
32
  function appDevSecret(kind) {
13
33
  return `${appKeyPrefix()}-dev-${kind}`;
14
34
  }
35
+ function requireConfiguredSecret(names, devKind, env = process.env) {
36
+ for (const name of names) {
37
+ const value = env[name]?.trim();
38
+ if (value) {
39
+ return value;
40
+ }
41
+ }
42
+ if (isProductionEnv(env)) {
43
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
44
+ }
45
+ return appDevSecret(devKind);
46
+ }
15
47
  function namespacedRedisKey(kind) {
16
48
  return `${appKeyPrefix()}:${kind}`;
17
49
  }
@@ -29,9 +61,6 @@ function appUserAgent() {
29
61
  function otelServiceName() {
30
62
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
63
  }
32
- function webhookSignatureHeader() {
33
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
- }
35
64
  function appDisplayName() {
36
65
  return process.env.APP_NAME?.trim() || "Strata";
37
66
  }
@@ -74,7 +103,7 @@ function sessionRememberTtlSeconds() {
74
103
  return parsePositiveSeconds(process.env.SESSION_REMEMBER_TTL_SECONDS, SESSION_REMEMBER_TTL_SECONDS);
75
104
  }
76
105
  function resolveSessionSecret() {
77
- return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("session-secret");
106
+ return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "session-secret");
78
107
  }
79
108
  function signSession(userId, issuedAt, ttlSeconds) {
80
109
  const payload = ttlSeconds === undefined ? `${userId}.${issuedAt}` : `${userId}.${issuedAt}.${ttlSeconds}`;
@@ -153,7 +182,7 @@ function createSessionCookieDetails(userId, options = {}) {
153
182
  const issuedAt = Date.now();
154
183
  const ttlSeconds = options.remember ? sessionRememberTtlSeconds() : sessionTtlSeconds();
155
184
  const value = options.remember ? signSession(userId, issuedAt, ttlSeconds) : signSession(userId, issuedAt);
156
- const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
185
+ const secure = isProductionEnv() ? "; Secure" : "";
157
186
  return {
158
187
  header: `${sessionCookieName()}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ttlSeconds}${secure}`,
159
188
  userId,
@@ -165,7 +194,7 @@ function createSessionCookie(userId, options = {}) {
165
194
  return createSessionCookieDetails(userId, options).header;
166
195
  }
167
196
  function clearSessionCookie() {
168
- const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
197
+ const secure = isProductionEnv() ? "; Secure" : "";
169
198
  return `${sessionCookieName()}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure}`;
170
199
  }
171
200
  export {
@@ -69,6 +69,26 @@ function resolveAbilitiesForRole(role) {
69
69
  // ../../src/core/auth/sessionCookie.ts
70
70
  import { createHmac, timingSafeEqual } from "crypto";
71
71
 
72
+ // ../../src/core/runtime/appEnv.ts
73
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
74
+ function normalizeEnvValue(value) {
75
+ return (value ?? "").trim().toLowerCase();
76
+ }
77
+ function isProductionEnv(env = process.env) {
78
+ const appEnv = normalizeEnvValue(env.APP_ENV);
79
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
80
+ if (appEnv === "production" || nodeEnv === "production") {
81
+ return true;
82
+ }
83
+ if (appEnv === "") {
84
+ return false;
85
+ }
86
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
87
+ }
88
+ function envFlagEnabled(value) {
89
+ return value === "true";
90
+ }
91
+
72
92
  // ../../src/core/runtime/appKeyPrefix.ts
73
93
  function appKeyPrefix() {
74
94
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -79,6 +99,18 @@ function appCookieName(kind) {
79
99
  function appDevSecret(kind) {
80
100
  return `${appKeyPrefix()}-dev-${kind}`;
81
101
  }
102
+ function requireConfiguredSecret(names, devKind, env = process.env) {
103
+ for (const name of names) {
104
+ const value = env[name]?.trim();
105
+ if (value) {
106
+ return value;
107
+ }
108
+ }
109
+ if (isProductionEnv(env)) {
110
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
111
+ }
112
+ return appDevSecret(devKind);
113
+ }
82
114
  function namespacedRedisKey(kind) {
83
115
  return `${appKeyPrefix()}:${kind}`;
84
116
  }
@@ -96,9 +128,6 @@ function appUserAgent() {
96
128
  function otelServiceName() {
97
129
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
98
130
  }
99
- function webhookSignatureHeader() {
100
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
101
- }
102
131
  function appDisplayName() {
103
132
  return process.env.APP_NAME?.trim() || "Strata";
104
133
  }
@@ -141,7 +170,7 @@ function sessionRememberTtlSeconds() {
141
170
  return parsePositiveSeconds(process.env.SESSION_REMEMBER_TTL_SECONDS, SESSION_REMEMBER_TTL_SECONDS);
142
171
  }
143
172
  function resolveSessionSecret() {
144
- return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("session-secret");
173
+ return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "session-secret");
145
174
  }
146
175
  function signSession(userId, issuedAt, ttlSeconds) {
147
176
  const payload = ttlSeconds === undefined ? `${userId}.${issuedAt}` : `${userId}.${issuedAt}.${ttlSeconds}`;
@@ -220,7 +249,7 @@ function createSessionCookieDetails(userId, options = {}) {
220
249
  const issuedAt = Date.now();
221
250
  const ttlSeconds = options.remember ? sessionRememberTtlSeconds() : sessionTtlSeconds();
222
251
  const value = options.remember ? signSession(userId, issuedAt, ttlSeconds) : signSession(userId, issuedAt);
223
- const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
252
+ const secure = isProductionEnv() ? "; Secure" : "";
224
253
  return {
225
254
  header: `${sessionCookieName()}=${encodeURIComponent(value)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${ttlSeconds}${secure}`,
226
255
  userId,
@@ -232,7 +261,7 @@ function createSessionCookie(userId, options = {}) {
232
261
  return createSessionCookieDetails(userId, options).header;
233
262
  }
234
263
  function clearSessionCookie() {
235
- const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
264
+ const secure = isProductionEnv() ? "; Secure" : "";
236
265
  return `${sessionCookieName()}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0${secure}`;
237
266
  }
238
267
 
@@ -2,6 +2,26 @@
2
2
  // ../../src/core/auth/tokenHash.ts
3
3
  import { createHash, createHmac } from "crypto";
4
4
 
5
+ // ../../src/core/runtime/appEnv.ts
6
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
7
+ function normalizeEnvValue(value) {
8
+ return (value ?? "").trim().toLowerCase();
9
+ }
10
+ function isProductionEnv(env = process.env) {
11
+ const appEnv = normalizeEnvValue(env.APP_ENV);
12
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
13
+ if (appEnv === "production" || nodeEnv === "production") {
14
+ return true;
15
+ }
16
+ if (appEnv === "") {
17
+ return false;
18
+ }
19
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
20
+ }
21
+ function envFlagEnabled(value) {
22
+ return value === "true";
23
+ }
24
+
5
25
  // ../../src/core/runtime/appKeyPrefix.ts
6
26
  function appKeyPrefix() {
7
27
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -12,6 +32,18 @@ function appCookieName(kind) {
12
32
  function appDevSecret(kind) {
13
33
  return `${appKeyPrefix()}-dev-${kind}`;
14
34
  }
35
+ function requireConfiguredSecret(names, devKind, env = process.env) {
36
+ for (const name of names) {
37
+ const value = env[name]?.trim();
38
+ if (value) {
39
+ return value;
40
+ }
41
+ }
42
+ if (isProductionEnv(env)) {
43
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
44
+ }
45
+ return appDevSecret(devKind);
46
+ }
15
47
  function namespacedRedisKey(kind) {
16
48
  return `${appKeyPrefix()}:${kind}`;
17
49
  }
@@ -29,9 +61,6 @@ function appUserAgent() {
29
61
  function otelServiceName() {
30
62
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
63
  }
32
- function webhookSignatureHeader() {
33
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
- }
35
64
  function appDisplayName() {
36
65
  return process.env.APP_NAME?.trim() || "Strata";
37
66
  }
@@ -58,7 +87,7 @@ function sdkClientClassName() {
58
87
 
59
88
  // ../../src/core/auth/tokenHash.ts
60
89
  function resolveTokenPepper() {
61
- return process.env.TOKEN_HASH_PEPPER?.trim() ?? appDevSecret("token-pepper");
90
+ return requireConfiguredSecret(["TOKEN_HASH_PEPPER"], "token-pepper");
62
91
  }
63
92
  function hashApiToken(token) {
64
93
  const pepper = resolveTokenPepper();
@@ -2,6 +2,26 @@
2
2
  // ../../src/core/cache/redisCacheStore.ts
3
3
  var {RedisClient } = globalThis.Bun;
4
4
 
5
+ // ../../src/core/runtime/appEnv.ts
6
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
7
+ function normalizeEnvValue(value) {
8
+ return (value ?? "").trim().toLowerCase();
9
+ }
10
+ function isProductionEnv(env = process.env) {
11
+ const appEnv = normalizeEnvValue(env.APP_ENV);
12
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
13
+ if (appEnv === "production" || nodeEnv === "production") {
14
+ return true;
15
+ }
16
+ if (appEnv === "") {
17
+ return false;
18
+ }
19
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
20
+ }
21
+ function envFlagEnabled(value) {
22
+ return value === "true";
23
+ }
24
+
5
25
  // ../../src/core/runtime/appKeyPrefix.ts
6
26
  function appKeyPrefix() {
7
27
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -12,6 +32,18 @@ function appCookieName(kind) {
12
32
  function appDevSecret(kind) {
13
33
  return `${appKeyPrefix()}-dev-${kind}`;
14
34
  }
35
+ function requireConfiguredSecret(names, devKind, env = process.env) {
36
+ for (const name of names) {
37
+ const value = env[name]?.trim();
38
+ if (value) {
39
+ return value;
40
+ }
41
+ }
42
+ if (isProductionEnv(env)) {
43
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
44
+ }
45
+ return appDevSecret(devKind);
46
+ }
15
47
  function namespacedRedisKey(kind) {
16
48
  return `${appKeyPrefix()}:${kind}`;
17
49
  }
@@ -29,9 +61,6 @@ function appUserAgent() {
29
61
  function otelServiceName() {
30
62
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
63
  }
32
- function webhookSignatureHeader() {
33
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
- }
35
64
  function appDisplayName() {
36
65
  return process.env.APP_NAME?.trim() || "Strata";
37
66
  }
@@ -11,6 +11,26 @@ import {
11
11
  resolveApplicationQueue
12
12
  } from "@getstrata/core/runtime/applicationRegistry";
13
13
 
14
+ // ../../src/core/runtime/appEnv.ts
15
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
16
+ function normalizeEnvValue(value) {
17
+ return (value ?? "").trim().toLowerCase();
18
+ }
19
+ function isProductionEnv(env = process.env) {
20
+ const appEnv = normalizeEnvValue(env.APP_ENV);
21
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
22
+ if (appEnv === "production" || nodeEnv === "production") {
23
+ return true;
24
+ }
25
+ if (appEnv === "") {
26
+ return false;
27
+ }
28
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
29
+ }
30
+ function envFlagEnabled(value) {
31
+ return value === "true";
32
+ }
33
+
14
34
  // ../../src/core/runtime/appKeyPrefix.ts
15
35
  function appKeyPrefix() {
16
36
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -21,6 +41,18 @@ function appCookieName(kind) {
21
41
  function appDevSecret(kind) {
22
42
  return `${appKeyPrefix()}-dev-${kind}`;
23
43
  }
44
+ function requireConfiguredSecret(names, devKind, env = process.env) {
45
+ for (const name of names) {
46
+ const value = env[name]?.trim();
47
+ if (value) {
48
+ return value;
49
+ }
50
+ }
51
+ if (isProductionEnv(env)) {
52
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
53
+ }
54
+ return appDevSecret(devKind);
55
+ }
24
56
  function namespacedRedisKey(kind) {
25
57
  return `${appKeyPrefix()}:${kind}`;
26
58
  }
@@ -38,9 +70,6 @@ function appUserAgent() {
38
70
  function otelServiceName() {
39
71
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
40
72
  }
41
- function webhookSignatureHeader() {
42
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
43
- }
44
73
  function appDisplayName() {
45
74
  return process.env.APP_NAME?.trim() || "Strata";
46
75
  }
@@ -1,7 +1,22 @@
1
1
  // @bun
2
2
  // ../../src/core/runtime/appEnv.ts
3
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
4
+ function normalizeEnvValue(value) {
5
+ return (value ?? "").trim().toLowerCase();
6
+ }
3
7
  function isProductionEnv(env = process.env) {
4
- return env.APP_ENV === "production" || env.NODE_ENV === "production";
8
+ const appEnv = normalizeEnvValue(env.APP_ENV);
9
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
10
+ if (appEnv === "production" || nodeEnv === "production") {
11
+ return true;
12
+ }
13
+ if (appEnv === "") {
14
+ return false;
15
+ }
16
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
17
+ }
18
+ function envFlagEnabled(value) {
19
+ return value === "true";
5
20
  }
6
21
 
7
22
  // ../../src/core/http/corsMiddleware.ts
@@ -5,6 +5,26 @@ import { ForbiddenError } from "@getstrata/core/errors/http";
5
5
  // ../../src/core/http/csrfToken.ts
6
6
  import { timingSafeEqual } from "crypto";
7
7
 
8
+ // ../../src/core/runtime/appEnv.ts
9
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
10
+ function normalizeEnvValue(value) {
11
+ return (value ?? "").trim().toLowerCase();
12
+ }
13
+ function isProductionEnv(env = process.env) {
14
+ const appEnv = normalizeEnvValue(env.APP_ENV);
15
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
16
+ if (appEnv === "production" || nodeEnv === "production") {
17
+ return true;
18
+ }
19
+ if (appEnv === "") {
20
+ return false;
21
+ }
22
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
23
+ }
24
+ function envFlagEnabled(value) {
25
+ return value === "true";
26
+ }
27
+
8
28
  // ../../src/core/runtime/appKeyPrefix.ts
9
29
  function appKeyPrefix() {
10
30
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -15,6 +35,18 @@ function appCookieName(kind) {
15
35
  function appDevSecret(kind) {
16
36
  return `${appKeyPrefix()}-dev-${kind}`;
17
37
  }
38
+ function requireConfiguredSecret(names, devKind, env = process.env) {
39
+ for (const name of names) {
40
+ const value = env[name]?.trim();
41
+ if (value) {
42
+ return value;
43
+ }
44
+ }
45
+ if (isProductionEnv(env)) {
46
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
47
+ }
48
+ return appDevSecret(devKind);
49
+ }
18
50
  function namespacedRedisKey(kind) {
19
51
  return `${appKeyPrefix()}:${kind}`;
20
52
  }
@@ -32,9 +64,6 @@ function appUserAgent() {
32
64
  function otelServiceName() {
33
65
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
34
66
  }
35
- function webhookSignatureHeader() {
36
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
37
- }
38
67
  function appDisplayName() {
39
68
  return process.env.APP_NAME?.trim() || "Strata";
40
69
  }
@@ -120,7 +149,7 @@ function csrfCookieName() {
120
149
  return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
121
150
  }
122
151
  function resolveCsrfSecret() {
123
- return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("csrf-secret");
152
+ return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "csrf-secret");
124
153
  }
125
154
  function csrfVerifyOptions() {
126
155
  return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
@@ -135,7 +164,7 @@ function tokensMatch(left, right) {
135
164
  }
136
165
  function createCsrfTokenCookie() {
137
166
  const token = Bun.CSRF.generate(resolveCsrfSecret(), { expiresIn: CSRF_TTL_MS });
138
- const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
167
+ const secure = isProductionEnv() ? "; Secure" : "";
139
168
  return {
140
169
  token,
141
170
  cookie: `${csrfCookieName()}=${encodeURIComponent(token)}; Path=/; SameSite=Lax; Max-Age=${Math.floor(CSRF_TTL_MS / 1000)}${secure}`
@@ -2,6 +2,26 @@
2
2
  // ../../src/core/http/csrfToken.ts
3
3
  import { timingSafeEqual } from "crypto";
4
4
 
5
+ // ../../src/core/runtime/appEnv.ts
6
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
7
+ function normalizeEnvValue(value) {
8
+ return (value ?? "").trim().toLowerCase();
9
+ }
10
+ function isProductionEnv(env = process.env) {
11
+ const appEnv = normalizeEnvValue(env.APP_ENV);
12
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
13
+ if (appEnv === "production" || nodeEnv === "production") {
14
+ return true;
15
+ }
16
+ if (appEnv === "") {
17
+ return false;
18
+ }
19
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
20
+ }
21
+ function envFlagEnabled(value) {
22
+ return value === "true";
23
+ }
24
+
5
25
  // ../../src/core/runtime/appKeyPrefix.ts
6
26
  function appKeyPrefix() {
7
27
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -12,6 +32,18 @@ function appCookieName(kind) {
12
32
  function appDevSecret(kind) {
13
33
  return `${appKeyPrefix()}-dev-${kind}`;
14
34
  }
35
+ function requireConfiguredSecret(names, devKind, env = process.env) {
36
+ for (const name of names) {
37
+ const value = env[name]?.trim();
38
+ if (value) {
39
+ return value;
40
+ }
41
+ }
42
+ if (isProductionEnv(env)) {
43
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
44
+ }
45
+ return appDevSecret(devKind);
46
+ }
15
47
  function namespacedRedisKey(kind) {
16
48
  return `${appKeyPrefix()}:${kind}`;
17
49
  }
@@ -29,9 +61,6 @@ function appUserAgent() {
29
61
  function otelServiceName() {
30
62
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
63
  }
32
- function webhookSignatureHeader() {
33
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
- }
35
64
  function appDisplayName() {
36
65
  return process.env.APP_NAME?.trim() || "Strata";
37
66
  }
@@ -117,7 +146,7 @@ function csrfCookieName() {
117
146
  return process.env.CSRF_COOKIE_NAME?.trim() || appCookieName("csrf");
118
147
  }
119
148
  function resolveCsrfSecret() {
120
- return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || process.env.ADMIN_API_TOKEN?.trim() || appDevSecret("csrf-secret");
149
+ return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET", "ADMIN_API_TOKEN"], "csrf-secret");
121
150
  }
122
151
  function csrfVerifyOptions() {
123
152
  return { secret: resolveCsrfSecret(), maxAge: CSRF_TTL_MS };
@@ -132,7 +161,7 @@ function tokensMatch(left, right) {
132
161
  }
133
162
  function createCsrfTokenCookie() {
134
163
  const token = Bun.CSRF.generate(resolveCsrfSecret(), { expiresIn: CSRF_TTL_MS });
135
- const secure = process.env.APP_ENV === "production" ? "; Secure" : "";
164
+ const secure = isProductionEnv() ? "; Secure" : "";
136
165
  return {
137
166
  token,
138
167
  cookie: `${csrfCookieName()}=${encodeURIComponent(token)}; Path=/; SameSite=Lax; Max-Age=${Math.floor(CSRF_TTL_MS / 1000)}${secure}`
@@ -2,6 +2,26 @@
2
2
  // ../../src/core/http/flashSession.ts
3
3
  import { createHmac, timingSafeEqual } from "crypto";
4
4
 
5
+ // ../../src/core/runtime/appEnv.ts
6
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
7
+ function normalizeEnvValue(value) {
8
+ return (value ?? "").trim().toLowerCase();
9
+ }
10
+ function isProductionEnv(env = process.env) {
11
+ const appEnv = normalizeEnvValue(env.APP_ENV);
12
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
13
+ if (appEnv === "production" || nodeEnv === "production") {
14
+ return true;
15
+ }
16
+ if (appEnv === "") {
17
+ return false;
18
+ }
19
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
20
+ }
21
+ function envFlagEnabled(value) {
22
+ return value === "true";
23
+ }
24
+
5
25
  // ../../src/core/runtime/appKeyPrefix.ts
6
26
  function appKeyPrefix() {
7
27
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -12,6 +32,18 @@ function appCookieName(kind) {
12
32
  function appDevSecret(kind) {
13
33
  return `${appKeyPrefix()}-dev-${kind}`;
14
34
  }
35
+ function requireConfiguredSecret(names, devKind, env = process.env) {
36
+ for (const name of names) {
37
+ const value = env[name]?.trim();
38
+ if (value) {
39
+ return value;
40
+ }
41
+ }
42
+ if (isProductionEnv(env)) {
43
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
44
+ }
45
+ return appDevSecret(devKind);
46
+ }
15
47
  function namespacedRedisKey(kind) {
16
48
  return `${appKeyPrefix()}:${kind}`;
17
49
  }
@@ -29,9 +61,6 @@ function appUserAgent() {
29
61
  function otelServiceName() {
30
62
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
63
  }
32
- function webhookSignatureHeader() {
33
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
- }
35
64
  function appDisplayName() {
36
65
  return process.env.APP_NAME?.trim() || "Strata";
37
66
  }
@@ -63,7 +92,7 @@ function flashCookieName() {
63
92
  return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
64
93
  }
65
94
  function resolveFlashSecret() {
66
- return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || appDevSecret("flash-secret");
95
+ return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET"], "flash-secret");
67
96
  }
68
97
  function signFlashPayload(payload, issuedAt) {
69
98
  const signature = createHmac("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");
@@ -2,6 +2,26 @@
2
2
  // ../../src/core/http/flashSession.ts
3
3
  import { createHmac, timingSafeEqual } from "crypto";
4
4
 
5
+ // ../../src/core/runtime/appEnv.ts
6
+ var NON_PRODUCTION_APP_ENVS = new Set(["local", "development", "dev", "test", "testing", "ci"]);
7
+ function normalizeEnvValue(value) {
8
+ return (value ?? "").trim().toLowerCase();
9
+ }
10
+ function isProductionEnv(env = process.env) {
11
+ const appEnv = normalizeEnvValue(env.APP_ENV);
12
+ const nodeEnv = normalizeEnvValue(env.NODE_ENV);
13
+ if (appEnv === "production" || nodeEnv === "production") {
14
+ return true;
15
+ }
16
+ if (appEnv === "") {
17
+ return false;
18
+ }
19
+ return !NON_PRODUCTION_APP_ENVS.has(appEnv);
20
+ }
21
+ function envFlagEnabled(value) {
22
+ return value === "true";
23
+ }
24
+
5
25
  // ../../src/core/runtime/appKeyPrefix.ts
6
26
  function appKeyPrefix() {
7
27
  return process.env.APP_KEY_PREFIX?.trim() || "strata";
@@ -12,6 +32,18 @@ function appCookieName(kind) {
12
32
  function appDevSecret(kind) {
13
33
  return `${appKeyPrefix()}-dev-${kind}`;
14
34
  }
35
+ function requireConfiguredSecret(names, devKind, env = process.env) {
36
+ for (const name of names) {
37
+ const value = env[name]?.trim();
38
+ if (value) {
39
+ return value;
40
+ }
41
+ }
42
+ if (isProductionEnv(env)) {
43
+ throw new Error(`${names[0]} must be set outside development. Do not derive secrets from the app name.`);
44
+ }
45
+ return appDevSecret(devKind);
46
+ }
15
47
  function namespacedRedisKey(kind) {
16
48
  return `${appKeyPrefix()}:${kind}`;
17
49
  }
@@ -29,9 +61,6 @@ function appUserAgent() {
29
61
  function otelServiceName() {
30
62
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
31
63
  }
32
- function webhookSignatureHeader() {
33
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
34
- }
35
64
  function appDisplayName() {
36
65
  return process.env.APP_NAME?.trim() || "Strata";
37
66
  }
@@ -63,7 +92,7 @@ function flashCookieName() {
63
92
  return process.env.FLASH_COOKIE_NAME?.trim() || appCookieName("flash");
64
93
  }
65
94
  function resolveFlashSecret() {
66
- return process.env.SESSION_SECRET?.trim() || process.env.OAUTH_STATE_SECRET?.trim() || appDevSecret("flash-secret");
95
+ return requireConfiguredSecret(["SESSION_SECRET", "OAUTH_STATE_SECRET"], "flash-secret");
67
96
  }
68
97
  function signFlashPayload(payload, issuedAt) {
69
98
  const signature = createHmac("sha256", resolveFlashSecret()).update(`${payload}.${issuedAt}`).digest("hex");