@getstrata/core 0.5.65 → 0.5.67

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 (31) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README.md +1 -1
  3. package/dist/core/http/signedUrl.d.ts +3 -1
  4. package/dist/core/openapi/generator.d.ts +2 -2
  5. package/dist/core/runtime/appKeyPrefix.d.ts +5 -1
  6. package/dist/entries/audit/exportAuditLogs.js +50 -39
  7. package/dist/entries/audit/siemFormatter.js +20 -0
  8. package/dist/entries/auth/oauth/providers.js +20 -0
  9. package/dist/entries/auth/oauth/samlProvider.js +20 -0
  10. package/dist/entries/cache/createCacheStore.js +20 -0
  11. package/dist/entries/facades.js +20 -0
  12. package/dist/entries/http/loginThrottleMiddleware.js +20 -0
  13. package/dist/entries/http/memoryThrottleMiddleware.js +20 -0
  14. package/dist/entries/http/scimThrottleMiddleware.js +20 -0
  15. package/dist/entries/http/securityHeadersMiddleware.js +48 -9
  16. package/dist/entries/http/signedUrl.js +7 -0
  17. package/dist/entries/http/throttleMiddleware.js +20 -0
  18. package/dist/entries/jobs/exportAuditLogsJob.js +50 -39
  19. package/dist/entries/mail/mailer.js +20 -0
  20. package/dist/entries/openapi/generator.js +58 -19
  21. package/dist/entries/queue/createAppQueue.js +20 -0
  22. package/dist/entries/queue/publicQueue.js +20 -0
  23. package/dist/entries/queue/queueMetrics.js +20 -0
  24. package/dist/entries/queue/redisQueue.js +20 -0
  25. package/dist/entries/runtime/appKeyPrefix.js +24 -0
  26. package/dist/entries/security/safeFetch.js +48 -9
  27. package/dist/entries/tracing/tracingMiddleware.js +20 -0
  28. package/dist/framework/public-api.d.ts +1 -1
  29. package/dist/index.js +11 -10
  30. package/package.json +1 -1
  31. package/dist/config/app.d.ts +0 -10
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @getstrata/core changelog
2
2
 
3
+ ## 0.5.67
4
+
5
+ - OpenAPI title, server URLs, and generated SDK class name come from `APP_NAME` / `APP_URL` / `API_PREFIX` / `APP_SDK_CLASS` instead of importing WorkHub `src/config/app`.
6
+ - `appEnv()`, `appUrl()`, `apiPrefix()`, and `sdkClientClassName()` live on `@getstrata/core/runtime/appKeyPrefix`. SIEM export, HSTS, and `safeFetch` DNS resolve use `appEnv()` instead of WorkHub `appConfig`.
7
+
8
+ ## 0.5.66
9
+
10
+ - `createValidateSignatureMiddleware()` on `@getstrata/core/http/signedUrl` is Laravel’s `signed` / `ValidateSignature` middleware. Invalid or expired links throw `ForbiddenError`.
11
+
3
12
  ## 0.5.65
4
13
 
5
14
  - Identity helpers on `@getstrata/core/runtime/appKeyPrefix` (`smtpEhloHost`, `siemEventType`, `appUserAgent`, `otelServiceName`, `appDisplayName`, `webhookSignatureHeader`) so sibling apps are not stuck with WorkHub SMTP/SIEM/OTEL/OAuth names.
package/README.md CHANGED
@@ -82,7 +82,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
82
82
  Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
83
83
 
84
84
  1. Add `NPM_TOKEN` to GitHub repository secrets.
85
- 2. Tag a release: `git tag v0.5.67 && git push origin v0.5.67`
85
+ 2. Tag a release: `git tag v0.5.69 && git push origin v0.5.69`
86
86
  3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
87
87
 
88
88
  Previously published as `@eyk-workhub/framework@0.1.0`, deprecated in favor of this package.
@@ -1,3 +1,4 @@
1
+ import type { Middleware } from "./middleware";
1
2
  interface TemporarySignedUrlOptions {
2
3
  expiresInSeconds?: number;
3
4
  query?: Record<string, string | number>;
@@ -7,5 +8,6 @@ declare function temporarySignedUrl(path: string, expiresInSeconds: number, quer
7
8
  declare function absoluteTemporarySignedUrl(path: string, expiresInSeconds: number, query?: Record<string, string | number>, origin?: string): string;
8
9
  declare function hasValidSignature(input: Request | URL | string): boolean;
9
10
  declare function assertValidSignature(input: Request | URL | string): void;
11
+ declare function createValidateSignatureMiddleware(): Middleware;
10
12
  export type { TemporarySignedUrlOptions };
11
- export { absoluteTemporarySignedUrl, assertValidSignature, hasValidSignature, signedUrl, temporarySignedUrl, };
13
+ export { absoluteTemporarySignedUrl, assertValidSignature, createValidateSignatureMiddleware, hasValidSignature, signedUrl, temporarySignedUrl, };
@@ -1,4 +1,4 @@
1
- import type { RegisteredRoute } from "../openapi/registeredRoute";
1
+ import type { RegisteredRoute } from "./registeredRoute";
2
2
  interface OpenApiSpec {
3
3
  openapi: string;
4
4
  info: {
@@ -17,6 +17,6 @@ interface OpenApiSpec {
17
17
  }
18
18
  declare function generateOpenApiSpec(routes: RegisteredRoute[]): OpenApiSpec;
19
19
  declare function renderOpenApiDocument(spec: OpenApiSpec): string;
20
- declare function renderTypeScriptSdk(spec: OpenApiSpec, apiPrefix?: string): string;
20
+ declare function renderTypeScriptSdk(spec: OpenApiSpec, prefix?: string): string;
21
21
  export type { OpenApiSpec };
22
22
  export { generateOpenApiSpec, renderOpenApiDocument, renderTypeScriptSdk };
@@ -6,4 +6,8 @@ declare function appUserAgent(): string;
6
6
  declare function otelServiceName(): string;
7
7
  declare function webhookSignatureHeader(): string;
8
8
  declare function appDisplayName(): string;
9
- export { appDisplayName, appKeyPrefix, appUserAgent, namespacedRedisKey, otelServiceName, siemEventType, smtpEhloHost, webhookSignatureHeader, };
9
+ declare function appEnv(): string;
10
+ declare function appUrl(): string;
11
+ declare function apiPrefix(): string;
12
+ declare function sdkClientClassName(): string;
13
+ export { apiPrefix, appDisplayName, appEnv, appKeyPrefix, appUrl, appUserAgent, namespacedRedisKey, otelServiceName, sdkClientClassName, siemEventType, smtpEhloHost, webhookSignatureHeader, };
@@ -2,14 +2,53 @@
2
2
  // ../../src/core/audit/exportAuditLogs.ts
3
3
  import { repositoryConnection as db2 } from "@getstrata/core/database/repositoryConnection";
4
4
 
5
- // ../../src/config/app.ts
6
- var appConfig = {
7
- name: process.env.APP_NAME?.trim() || "WorkHub",
8
- env: process.env.APP_ENV ?? "local",
9
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
10
- url: process.env.APP_URL ?? "http://localhost:3000",
11
- apiPrefix: process.env.API_PREFIX ?? "/api/v1"
12
- };
5
+ // ../../src/core/runtime/appKeyPrefix.ts
6
+ function appKeyPrefix() {
7
+ return process.env.APP_KEY_PREFIX?.trim() || "workhub";
8
+ }
9
+ function namespacedRedisKey(kind) {
10
+ return `${appKeyPrefix()}:${kind}`;
11
+ }
12
+ function smtpEhloHost() {
13
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
14
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
15
+ return safe || "strata.local";
16
+ }
17
+ function siemEventType() {
18
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
19
+ }
20
+ function appUserAgent() {
21
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
22
+ }
23
+ function otelServiceName() {
24
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
25
+ }
26
+ function webhookSignatureHeader() {
27
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
28
+ }
29
+ function appDisplayName() {
30
+ return process.env.APP_NAME?.trim() || "WorkHub";
31
+ }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
13
52
 
14
53
  // ../../src/core/security/safeUrl.ts
15
54
  import { lookup as dnsLookupImpl } from "dns/promises";
@@ -113,7 +152,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
113
152
  async function safeFetch(input, init = {}, options = {}) {
114
153
  const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
115
154
  const maxRedirects = options.maxRedirects ?? 0;
116
- const resolveDns = options.resolveDns ?? appConfig.env === "production";
155
+ const resolveDns = options.resolveDns ?? appEnv() === "production";
117
156
  const urlOptions = { allowHttp: options.allowHttp, resolveDns };
118
157
  const controller = new AbortController;
119
158
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
@@ -153,34 +192,6 @@ async function runWithMigrationBypass(callback) {
153
192
  }
154
193
  }
155
194
 
156
- // ../../src/core/runtime/appKeyPrefix.ts
157
- function appKeyPrefix() {
158
- return process.env.APP_KEY_PREFIX?.trim() || "workhub";
159
- }
160
- function namespacedRedisKey(kind) {
161
- return `${appKeyPrefix()}:${kind}`;
162
- }
163
- function smtpEhloHost() {
164
- const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
165
- const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
166
- return safe || "strata.local";
167
- }
168
- function siemEventType() {
169
- return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
170
- }
171
- function appUserAgent() {
172
- return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
173
- }
174
- function otelServiceName() {
175
- return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
176
- }
177
- function webhookSignatureHeader() {
178
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
179
- }
180
- function appDisplayName() {
181
- return process.env.APP_NAME?.trim() || "WorkHub";
182
- }
183
-
184
195
  // ../../src/core/audit/siemFormatter.ts
185
196
  function formatSiemAuditEvent(input) {
186
197
  return {
@@ -220,7 +231,7 @@ function resolveAuditExportConfig() {
220
231
  if (!endpoint) {
221
232
  return null;
222
233
  }
223
- assertSafeOutboundUrl(endpoint, { allowHttp: appConfig.env !== "production" });
234
+ assertSafeOutboundUrl(endpoint, { allowHttp: appEnv() !== "production" });
224
235
  const batchSize = Number(process.env.SIEM_EXPORT_BATCH_SIZE ?? "100");
225
236
  return {
226
237
  endpoint,
@@ -278,7 +289,7 @@ async function exportPendingAuditLogs() {
278
289
  ...process.env.SIEM_EXPORT_TOKEN ? { authorization: `Bearer ${process.env.SIEM_EXPORT_TOKEN}` } : {}
279
290
  },
280
291
  body
281
- }, { allowHttp: appConfig.env !== "production" });
292
+ }, { allowHttp: appEnv() !== "production" });
282
293
  if (!response.ok) {
283
294
  throw new Error(`SIEM export failed with status ${response.status}.`);
284
295
  }
@@ -26,6 +26,26 @@ function webhookSignatureHeader() {
26
26
  function appDisplayName() {
27
27
  return process.env.APP_NAME?.trim() || "WorkHub";
28
28
  }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
29
49
 
30
50
  // ../../src/core/audit/siemFormatter.ts
31
51
  function formatSiemAuditEvent(input) {
@@ -26,6 +26,26 @@ function webhookSignatureHeader() {
26
26
  function appDisplayName() {
27
27
  return process.env.APP_NAME?.trim() || "WorkHub";
28
28
  }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
29
49
 
30
50
  // ../../src/core/auth/oauth/providers.ts
31
51
  class GitHubOAuthProvider {
@@ -26,6 +26,26 @@ function webhookSignatureHeader() {
26
26
  function appDisplayName() {
27
27
  return process.env.APP_NAME?.trim() || "WorkHub";
28
28
  }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
29
49
 
30
50
  // ../../src/core/auth/oauth/samlProvider.ts
31
51
  class SamlProvider {
@@ -29,6 +29,26 @@ function webhookSignatureHeader() {
29
29
  function appDisplayName() {
30
30
  return process.env.APP_NAME?.trim() || "WorkHub";
31
31
  }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
32
52
 
33
53
  // ../../src/core/cache/redisCacheStore.ts
34
54
  function cacheKeyPrefix() {
@@ -38,6 +38,26 @@ function webhookSignatureHeader() {
38
38
  function appDisplayName() {
39
39
  return process.env.APP_NAME?.trim() || "WorkHub";
40
40
  }
41
+ function appEnv() {
42
+ return process.env.APP_ENV?.trim() || "local";
43
+ }
44
+ function appUrl() {
45
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
46
+ }
47
+ function apiPrefix() {
48
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
49
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
50
+ const trimmed = withSlash.replace(/\/+$/, "");
51
+ return trimmed || "/api/v1";
52
+ }
53
+ function sdkClientClassName() {
54
+ const override = process.env.APP_SDK_CLASS?.trim();
55
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
56
+ return override;
57
+ }
58
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
59
+ return fromName ? `${fromName}Client` : "AppClient";
60
+ }
41
61
 
42
62
  // ../../src/core/mail/mailer.ts
43
63
  function resolveSmtpConfig() {
@@ -29,6 +29,26 @@ function webhookSignatureHeader() {
29
29
  function appDisplayName() {
30
30
  return process.env.APP_NAME?.trim() || "WorkHub";
31
31
  }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
32
52
 
33
53
  // ../../src/core/http/clientIp.ts
34
54
  function trustForwardedFor(env = process.env) {
@@ -26,6 +26,26 @@ function webhookSignatureHeader() {
26
26
  function appDisplayName() {
27
27
  return process.env.APP_NAME?.trim() || "WorkHub";
28
28
  }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
29
49
 
30
50
  // ../../src/core/http/clientIp.ts
31
51
  function trustForwardedFor(env = process.env) {
@@ -29,6 +29,26 @@ function webhookSignatureHeader() {
29
29
  function appDisplayName() {
30
30
  return process.env.APP_NAME?.trim() || "WorkHub";
31
31
  }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
32
52
 
33
53
  // ../../src/core/http/clientIp.ts
34
54
  function trustForwardedFor(env = process.env) {
@@ -7,14 +7,53 @@ import {
7
7
  } from "@getstrata/core/http/contentSecurityPolicy";
8
8
  import { currentRequestMeta, runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
9
9
 
10
- // ../../src/config/app.ts
11
- var appConfig = {
12
- name: process.env.APP_NAME?.trim() || "WorkHub",
13
- env: process.env.APP_ENV ?? "local",
14
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
15
- url: process.env.APP_URL ?? "http://localhost:3000",
16
- apiPrefix: process.env.API_PREFIX ?? "/api/v1"
17
- };
10
+ // ../../src/core/runtime/appKeyPrefix.ts
11
+ function appKeyPrefix() {
12
+ return process.env.APP_KEY_PREFIX?.trim() || "workhub";
13
+ }
14
+ function namespacedRedisKey(kind) {
15
+ return `${appKeyPrefix()}:${kind}`;
16
+ }
17
+ function smtpEhloHost() {
18
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
19
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
20
+ return safe || "strata.local";
21
+ }
22
+ function siemEventType() {
23
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
24
+ }
25
+ function appUserAgent() {
26
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
27
+ }
28
+ function otelServiceName() {
29
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
30
+ }
31
+ function webhookSignatureHeader() {
32
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
33
+ }
34
+ function appDisplayName() {
35
+ return process.env.APP_NAME?.trim() || "WorkHub";
36
+ }
37
+ function appEnv() {
38
+ return process.env.APP_ENV?.trim() || "local";
39
+ }
40
+ function appUrl() {
41
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
42
+ }
43
+ function apiPrefix() {
44
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
45
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
46
+ const trimmed = withSlash.replace(/\/+$/, "");
47
+ return trimmed || "/api/v1";
48
+ }
49
+ function sdkClientClassName() {
50
+ const override = process.env.APP_SDK_CLASS?.trim();
51
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
52
+ return override;
53
+ }
54
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
55
+ return fromName ? `${fromName}Client` : "AppClient";
56
+ }
18
57
 
19
58
  // ../../src/core/http/securityHeadersMiddleware.ts
20
59
  function createSecurityHeadersMiddleware(options = {}) {
@@ -33,7 +72,7 @@ function createSecurityHeadersMiddleware(options = {}) {
33
72
  headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
34
73
  headers.set("X-XSS-Protection", "0");
35
74
  headers.set("Content-Security-Policy", resolveContentSecurityPolicy(response, { ...options, nonce }));
36
- if (appConfig.env === "production") {
75
+ if (appEnv() === "production") {
37
76
  headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
38
77
  }
39
78
  return new Response(response.body, {
@@ -88,9 +88,16 @@ function assertValidSignature(input) {
88
88
  throw new ForbiddenError("Invalid or expired signed URL.");
89
89
  }
90
90
  }
91
+ function createValidateSignatureMiddleware() {
92
+ return async (request, next) => {
93
+ assertValidSignature(request);
94
+ return await next();
95
+ };
96
+ }
91
97
  export {
92
98
  absoluteTemporarySignedUrl,
93
99
  assertValidSignature,
100
+ createValidateSignatureMiddleware,
94
101
  hasValidSignature,
95
102
  signedUrl,
96
103
  temporarySignedUrl
@@ -31,6 +31,26 @@ function webhookSignatureHeader() {
31
31
  function appDisplayName() {
32
32
  return process.env.APP_NAME?.trim() || "WorkHub";
33
33
  }
34
+ function appEnv() {
35
+ return process.env.APP_ENV?.trim() || "local";
36
+ }
37
+ function appUrl() {
38
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
39
+ }
40
+ function apiPrefix() {
41
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
42
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
43
+ const trimmed = withSlash.replace(/\/+$/, "");
44
+ return trimmed || "/api/v1";
45
+ }
46
+ function sdkClientClassName() {
47
+ const override = process.env.APP_SDK_CLASS?.trim();
48
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
49
+ return override;
50
+ }
51
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
52
+ return fromName ? `${fromName}Client` : "AppClient";
53
+ }
34
54
 
35
55
  // ../../src/core/http/clientIp.ts
36
56
  function trustForwardedFor(env = process.env) {
@@ -2,14 +2,53 @@
2
2
  // ../../src/core/audit/exportAuditLogs.ts
3
3
  import { repositoryConnection as db2 } from "@getstrata/core/database/repositoryConnection";
4
4
 
5
- // ../../src/config/app.ts
6
- var appConfig = {
7
- name: process.env.APP_NAME?.trim() || "WorkHub",
8
- env: process.env.APP_ENV ?? "local",
9
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
10
- url: process.env.APP_URL ?? "http://localhost:3000",
11
- apiPrefix: process.env.API_PREFIX ?? "/api/v1"
12
- };
5
+ // ../../src/core/runtime/appKeyPrefix.ts
6
+ function appKeyPrefix() {
7
+ return process.env.APP_KEY_PREFIX?.trim() || "workhub";
8
+ }
9
+ function namespacedRedisKey(kind) {
10
+ return `${appKeyPrefix()}:${kind}`;
11
+ }
12
+ function smtpEhloHost() {
13
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
14
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
15
+ return safe || "strata.local";
16
+ }
17
+ function siemEventType() {
18
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
19
+ }
20
+ function appUserAgent() {
21
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
22
+ }
23
+ function otelServiceName() {
24
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
25
+ }
26
+ function webhookSignatureHeader() {
27
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
28
+ }
29
+ function appDisplayName() {
30
+ return process.env.APP_NAME?.trim() || "WorkHub";
31
+ }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
13
52
 
14
53
  // ../../src/core/security/safeUrl.ts
15
54
  import { lookup as dnsLookupImpl } from "dns/promises";
@@ -113,7 +152,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
113
152
  async function safeFetch(input, init = {}, options = {}) {
114
153
  const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
115
154
  const maxRedirects = options.maxRedirects ?? 0;
116
- const resolveDns = options.resolveDns ?? appConfig.env === "production";
155
+ const resolveDns = options.resolveDns ?? appEnv() === "production";
117
156
  const urlOptions = { allowHttp: options.allowHttp, resolveDns };
118
157
  const controller = new AbortController;
119
158
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
@@ -153,34 +192,6 @@ async function runWithMigrationBypass(callback) {
153
192
  }
154
193
  }
155
194
 
156
- // ../../src/core/runtime/appKeyPrefix.ts
157
- function appKeyPrefix() {
158
- return process.env.APP_KEY_PREFIX?.trim() || "workhub";
159
- }
160
- function namespacedRedisKey(kind) {
161
- return `${appKeyPrefix()}:${kind}`;
162
- }
163
- function smtpEhloHost() {
164
- const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
165
- const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
166
- return safe || "strata.local";
167
- }
168
- function siemEventType() {
169
- return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
170
- }
171
- function appUserAgent() {
172
- return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
173
- }
174
- function otelServiceName() {
175
- return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
176
- }
177
- function webhookSignatureHeader() {
178
- return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
179
- }
180
- function appDisplayName() {
181
- return process.env.APP_NAME?.trim() || "WorkHub";
182
- }
183
-
184
195
  // ../../src/core/audit/siemFormatter.ts
185
196
  function formatSiemAuditEvent(input) {
186
197
  return {
@@ -220,7 +231,7 @@ function resolveAuditExportConfig() {
220
231
  if (!endpoint) {
221
232
  return null;
222
233
  }
223
- assertSafeOutboundUrl(endpoint, { allowHttp: appConfig.env !== "production" });
234
+ assertSafeOutboundUrl(endpoint, { allowHttp: appEnv() !== "production" });
224
235
  const batchSize = Number(process.env.SIEM_EXPORT_BATCH_SIZE ?? "100");
225
236
  return {
226
237
  endpoint,
@@ -278,7 +289,7 @@ async function exportPendingAuditLogs() {
278
289
  ...process.env.SIEM_EXPORT_TOKEN ? { authorization: `Bearer ${process.env.SIEM_EXPORT_TOKEN}` } : {}
279
290
  },
280
291
  body
281
- }, { allowHttp: appConfig.env !== "production" });
292
+ }, { allowHttp: appEnv() !== "production" });
282
293
  if (!response.ok) {
283
294
  throw new Error(`SIEM export failed with status ${response.status}.`);
284
295
  }
@@ -26,6 +26,26 @@ function webhookSignatureHeader() {
26
26
  function appDisplayName() {
27
27
  return process.env.APP_NAME?.trim() || "WorkHub";
28
28
  }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
29
49
 
30
50
  // ../../src/core/mail/mailer.ts
31
51
  function resolveSmtpConfig() {
@@ -1,12 +1,51 @@
1
1
  // @bun
2
- // ../../src/config/app.ts
3
- var appConfig = {
4
- name: process.env.APP_NAME?.trim() || "WorkHub",
5
- env: process.env.APP_ENV ?? "local",
6
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
7
- url: process.env.APP_URL ?? "http://localhost:3000",
8
- apiPrefix: process.env.API_PREFIX ?? "/api/v1"
9
- };
2
+ // ../../src/core/runtime/appKeyPrefix.ts
3
+ function appKeyPrefix() {
4
+ return process.env.APP_KEY_PREFIX?.trim() || "workhub";
5
+ }
6
+ function namespacedRedisKey(kind) {
7
+ return `${appKeyPrefix()}:${kind}`;
8
+ }
9
+ function smtpEhloHost() {
10
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
11
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
12
+ return safe || "strata.local";
13
+ }
14
+ function siemEventType() {
15
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
16
+ }
17
+ function appUserAgent() {
18
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
19
+ }
20
+ function otelServiceName() {
21
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
22
+ }
23
+ function webhookSignatureHeader() {
24
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
25
+ }
26
+ function appDisplayName() {
27
+ return process.env.APP_NAME?.trim() || "WorkHub";
28
+ }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
10
49
 
11
50
  // ../../src/core/openapi/generator.ts
12
51
  var PUBLIC_ROUTE_DESCRIPTIONS = {
@@ -84,12 +123,12 @@ function generateOpenApiSpec(routes) {
84
123
  return {
85
124
  openapi: "3.1.0",
86
125
  info: {
87
- title: "WorkHub API",
126
+ title: `${appDisplayName()} API`,
88
127
  version: "1.0.0"
89
128
  },
90
129
  servers: [
91
- { url: `${appConfig.url}${appConfig.apiPrefix}`, description: "WorkHub API" },
92
- { url: appConfig.url, description: "Root (health, metrics, SCIM)" }
130
+ { url: `${appUrl()}${apiPrefix()}`, description: `${appDisplayName()} API` },
131
+ { url: appUrl(), description: "Root (health, metrics, SCIM)" }
93
132
  ],
94
133
  paths,
95
134
  components: {
@@ -142,17 +181,17 @@ function renderOpenApiDocument(spec) {
142
181
  return `${JSON.stringify(spec, null, 2)}
143
182
  `;
144
183
  }
145
- function toMethodName(method, path, apiPrefix) {
146
- const relativePath = path.startsWith(apiPrefix) ? path.slice(apiPrefix.length) || "/" : path;
184
+ function toMethodName(method, path, apiPrefix2) {
185
+ const relativePath = path.startsWith(apiPrefix2) ? path.slice(apiPrefix2.length) || "/" : path;
147
186
  const segments = relativePath.replace(/\{|\}/g, "").split("/").filter(Boolean).flatMap((segment) => segment.split("-")).map((segment) => segment.replace(/[^a-zA-Z0-9]/g, "")).filter(Boolean).map((segment) => segment.charAt(0).toUpperCase() + segment.slice(1));
148
187
  return `${method.toLowerCase()}${segments.join("")}`;
149
188
  }
150
- function toRequestPath(path, apiPrefix) {
151
- return path.startsWith(apiPrefix) ? path.slice(apiPrefix.length) || "/" : path;
189
+ function toRequestPath(path, apiPrefix2) {
190
+ return path.startsWith(apiPrefix2) ? path.slice(apiPrefix2.length) || "/" : path;
152
191
  }
153
- function renderTypeScriptSdk(spec, apiPrefix = "/api/v1") {
192
+ function renderTypeScriptSdk(spec, prefix = apiPrefix()) {
154
193
  const lines = [
155
- "export class WorkHubClient {",
194
+ `export class ${sdkClientClassName()} {`,
156
195
  ` constructor(private readonly baseUrl = "${spec.servers[0]?.url ?? ""}") {}`,
157
196
  "",
158
197
  " private async request(path: string, init: RequestInit = {}): Promise<Response> {",
@@ -161,9 +200,9 @@ function renderTypeScriptSdk(spec, apiPrefix = "/api/v1") {
161
200
  ""
162
201
  ];
163
202
  for (const [path, methods] of Object.entries(spec.paths)) {
164
- const requestPath = toRequestPath(path, apiPrefix);
203
+ const requestPath = toRequestPath(path, prefix);
165
204
  for (const method of Object.keys(methods)) {
166
- const functionName = toMethodName(method, path, apiPrefix);
205
+ const functionName = toMethodName(method, path, prefix);
167
206
  lines.push(` async ${functionName}(init: RequestInit = {}): Promise<Response> {`, ` return await this.request("${requestPath}", { ...init, method: "${method.toUpperCase()}" });`, " }", "");
168
207
  }
169
208
  }
@@ -181,6 +181,26 @@ function webhookSignatureHeader() {
181
181
  function appDisplayName() {
182
182
  return process.env.APP_NAME?.trim() || "WorkHub";
183
183
  }
184
+ function appEnv() {
185
+ return process.env.APP_ENV?.trim() || "local";
186
+ }
187
+ function appUrl() {
188
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
189
+ }
190
+ function apiPrefix() {
191
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
192
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
193
+ const trimmed = withSlash.replace(/\/+$/, "");
194
+ return trimmed || "/api/v1";
195
+ }
196
+ function sdkClientClassName() {
197
+ const override = process.env.APP_SDK_CLASS?.trim();
198
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
199
+ return override;
200
+ }
201
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
202
+ return fromName ? `${fromName}Client` : "AppClient";
203
+ }
184
204
 
185
205
  // ../../src/core/queue/redisQueue.ts
186
206
  function queueListKey() {
@@ -181,6 +181,26 @@ function webhookSignatureHeader() {
181
181
  function appDisplayName() {
182
182
  return process.env.APP_NAME?.trim() || "WorkHub";
183
183
  }
184
+ function appEnv() {
185
+ return process.env.APP_ENV?.trim() || "local";
186
+ }
187
+ function appUrl() {
188
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
189
+ }
190
+ function apiPrefix() {
191
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
192
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
193
+ const trimmed = withSlash.replace(/\/+$/, "");
194
+ return trimmed || "/api/v1";
195
+ }
196
+ function sdkClientClassName() {
197
+ const override = process.env.APP_SDK_CLASS?.trim();
198
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
199
+ return override;
200
+ }
201
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
202
+ return fromName ? `${fromName}Client` : "AppClient";
203
+ }
184
204
 
185
205
  // ../../src/core/queue/redisQueue.ts
186
206
  function queueListKey() {
@@ -184,6 +184,26 @@ function webhookSignatureHeader() {
184
184
  function appDisplayName() {
185
185
  return process.env.APP_NAME?.trim() || "WorkHub";
186
186
  }
187
+ function appEnv() {
188
+ return process.env.APP_ENV?.trim() || "local";
189
+ }
190
+ function appUrl() {
191
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
192
+ }
193
+ function apiPrefix() {
194
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
195
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
196
+ const trimmed = withSlash.replace(/\/+$/, "");
197
+ return trimmed || "/api/v1";
198
+ }
199
+ function sdkClientClassName() {
200
+ const override = process.env.APP_SDK_CLASS?.trim();
201
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
202
+ return override;
203
+ }
204
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
205
+ return fromName ? `${fromName}Client` : "AppClient";
206
+ }
187
207
 
188
208
  // ../../src/core/queue/redisQueue.ts
189
209
  function queueListKey() {
@@ -29,6 +29,26 @@ function webhookSignatureHeader() {
29
29
  function appDisplayName() {
30
30
  return process.env.APP_NAME?.trim() || "WorkHub";
31
31
  }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
32
52
 
33
53
  // ../../src/core/queue/jobRegistry.ts
34
54
  class JobRegistry {
@@ -26,12 +26,36 @@ function webhookSignatureHeader() {
26
26
  function appDisplayName() {
27
27
  return process.env.APP_NAME?.trim() || "WorkHub";
28
28
  }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
29
49
  export {
50
+ apiPrefix,
30
51
  appDisplayName,
52
+ appEnv,
31
53
  appKeyPrefix,
54
+ appUrl,
32
55
  appUserAgent,
33
56
  namespacedRedisKey,
34
57
  otelServiceName,
58
+ sdkClientClassName,
35
59
  siemEventType,
36
60
  smtpEhloHost,
37
61
  webhookSignatureHeader
@@ -1,12 +1,51 @@
1
1
  // @bun
2
- // ../../src/config/app.ts
3
- var appConfig = {
4
- name: process.env.APP_NAME?.trim() || "WorkHub",
5
- env: process.env.APP_ENV ?? "local",
6
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
7
- url: process.env.APP_URL ?? "http://localhost:3000",
8
- apiPrefix: process.env.API_PREFIX ?? "/api/v1"
9
- };
2
+ // ../../src/core/runtime/appKeyPrefix.ts
3
+ function appKeyPrefix() {
4
+ return process.env.APP_KEY_PREFIX?.trim() || "workhub";
5
+ }
6
+ function namespacedRedisKey(kind) {
7
+ return `${appKeyPrefix()}:${kind}`;
8
+ }
9
+ function smtpEhloHost() {
10
+ const raw = process.env.MAIL_EHLO?.trim() || `${appKeyPrefix()}.local`;
11
+ const safe = raw.replace(/[^a-zA-Z0-9.-]/g, "");
12
+ return safe || "strata.local";
13
+ }
14
+ function siemEventType() {
15
+ return process.env.SIEM_EVENT_TYPE?.trim() || `${appKeyPrefix()}.audit`;
16
+ }
17
+ function appUserAgent() {
18
+ return process.env.APP_USER_AGENT?.trim() || appKeyPrefix();
19
+ }
20
+ function otelServiceName() {
21
+ return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
22
+ }
23
+ function webhookSignatureHeader() {
24
+ return process.env.WEBHOOK_SIGNATURE_HEADER?.trim() || `x-${appKeyPrefix()}-signature`;
25
+ }
26
+ function appDisplayName() {
27
+ return process.env.APP_NAME?.trim() || "WorkHub";
28
+ }
29
+ function appEnv() {
30
+ return process.env.APP_ENV?.trim() || "local";
31
+ }
32
+ function appUrl() {
33
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
34
+ }
35
+ function apiPrefix() {
36
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
37
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
38
+ const trimmed = withSlash.replace(/\/+$/, "");
39
+ return trimmed || "/api/v1";
40
+ }
41
+ function sdkClientClassName() {
42
+ const override = process.env.APP_SDK_CLASS?.trim();
43
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
44
+ return override;
45
+ }
46
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
47
+ return fromName ? `${fromName}Client` : "AppClient";
48
+ }
10
49
 
11
50
  // ../../src/core/security/safeUrl.ts
12
51
  import { lookup as dnsLookupImpl } from "dns/promises";
@@ -110,7 +149,7 @@ var DEFAULT_FETCH_TIMEOUT_MS = 1e4;
110
149
  async function safeFetch(input, init = {}, options = {}) {
111
150
  const timeoutMs = options.timeoutMs ?? DEFAULT_FETCH_TIMEOUT_MS;
112
151
  const maxRedirects = options.maxRedirects ?? 0;
113
- const resolveDns = options.resolveDns ?? appConfig.env === "production";
152
+ const resolveDns = options.resolveDns ?? appEnv() === "production";
114
153
  const urlOptions = { allowHttp: options.allowHttp, resolveDns };
115
154
  const controller = new AbortController;
116
155
  const timeout = setTimeout(() => controller.abort(), timeoutMs);
@@ -29,6 +29,26 @@ function webhookSignatureHeader() {
29
29
  function appDisplayName() {
30
30
  return process.env.APP_NAME?.trim() || "WorkHub";
31
31
  }
32
+ function appEnv() {
33
+ return process.env.APP_ENV?.trim() || "local";
34
+ }
35
+ function appUrl() {
36
+ return (process.env.APP_URL?.trim() || "http://localhost:3000").replace(/\/$/, "");
37
+ }
38
+ function apiPrefix() {
39
+ const raw = process.env.API_PREFIX?.trim() || "/api/v1";
40
+ const withSlash = raw.startsWith("/") ? raw : `/${raw}`;
41
+ const trimmed = withSlash.replace(/\/+$/, "");
42
+ return trimmed || "/api/v1";
43
+ }
44
+ function sdkClientClassName() {
45
+ const override = process.env.APP_SDK_CLASS?.trim();
46
+ if (override && /^[A-Za-z_][A-Za-z0-9_]*$/.test(override)) {
47
+ return override;
48
+ }
49
+ const fromName = appDisplayName().replace(/[^A-Za-z0-9]/g, "");
50
+ return fromName ? `${fromName}Client` : "AppClient";
51
+ }
32
52
 
33
53
  // ../../src/core/tracing/otel.ts
34
54
  function randomHex(bytes) {
@@ -83,7 +83,7 @@ export type { RouteRequest } from "../core/http/route.ts";
83
83
  export { loginRedirectLocation, safeInternalRedirectPath, sanitizeInternalPath, } from "../core/http/safeInternalPath.ts";
84
84
  export { createScimThrottleMiddleware } from "../core/http/scimThrottleMiddleware.ts";
85
85
  export { createSecurityHeadersMiddleware } from "../core/http/securityHeadersMiddleware.ts";
86
- export { absoluteTemporarySignedUrl, assertValidSignature, hasValidSignature, signedUrl, temporarySignedUrl, } from "../core/http/signedUrl.ts";
86
+ export { absoluteTemporarySignedUrl, assertValidSignature, createValidateSignatureMiddleware, hasValidSignature, signedUrl, temporarySignedUrl, } from "../core/http/signedUrl.ts";
87
87
  export { createThrottleMiddleware } from "../core/http/throttleMiddleware.ts";
88
88
  export { WebFormRequest } from "../core/http/webFormRequest.ts";
89
89
  export { installGracefulShutdownSignals, registerShutdownHandler, runGracefulShutdown, } from "../core/lifecycle/gracefulShutdown.ts";
package/dist/index.js CHANGED
@@ -982,6 +982,9 @@ function smtpEhloHost() {
982
982
  function otelServiceName() {
983
983
  return process.env.OTEL_SERVICE_NAME?.trim() || `${appKeyPrefix()}-api`;
984
984
  }
985
+ function appEnv() {
986
+ return process.env.APP_ENV?.trim() || "local";
987
+ }
985
988
 
986
989
  // ../../src/core/cache/redisCacheStore.ts
987
990
  function cacheKeyPrefix() {
@@ -5666,15 +5669,6 @@ function createScimThrottleMiddleware(options) {
5666
5669
  return await next();
5667
5670
  };
5668
5671
  }
5669
- // ../../src/config/app.ts
5670
- var appConfig = {
5671
- name: process.env.APP_NAME?.trim() || "WorkHub",
5672
- env: process.env.APP_ENV ?? "local",
5673
- debug: (process.env.APP_DEBUG ?? "true") !== "false",
5674
- url: process.env.APP_URL ?? "http://localhost:3000",
5675
- apiPrefix: process.env.API_PREFIX ?? "/api/v1"
5676
- };
5677
-
5678
5672
  // ../../src/core/http/securityHeadersMiddleware.ts
5679
5673
  function createSecurityHeadersMiddleware(options = {}) {
5680
5674
  return async (request, next) => {
@@ -5692,7 +5686,7 @@ function createSecurityHeadersMiddleware(options = {}) {
5692
5686
  headers.set("Referrer-Policy", "strict-origin-when-cross-origin");
5693
5687
  headers.set("X-XSS-Protection", "0");
5694
5688
  headers.set("Content-Security-Policy", resolveContentSecurityPolicy(response, { ...options, nonce }));
5695
- if (appConfig.env === "production") {
5689
+ if (appEnv() === "production") {
5696
5690
  headers.set("Strict-Transport-Security", "max-age=31536000; includeSubDomains");
5697
5691
  }
5698
5692
  return new Response(response.body, {
@@ -5790,6 +5784,12 @@ function assertValidSignature(input) {
5790
5784
  throw new ForbiddenError("Invalid or expired signed URL.");
5791
5785
  }
5792
5786
  }
5787
+ function createValidateSignatureMiddleware() {
5788
+ return async (request, next) => {
5789
+ assertValidSignature(request);
5790
+ return await next();
5791
+ };
5792
+ }
5793
5793
  // ../../src/core/http/throttleMiddleware.ts
5794
5794
  var {RedisClient: RedisClient4 } = globalThis.Bun;
5795
5795
  function resolveThrottleIdentity(request) {
@@ -7223,6 +7223,7 @@ export {
7223
7223
  createThrottleMiddleware,
7224
7224
  createTracingMiddleware,
7225
7225
  createTrackedJob,
7226
+ createValidateSignatureMiddleware,
7226
7227
  createdResponse,
7227
7228
  currentAuthUser,
7228
7229
  currentOrgRole,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "0.5.65",
3
+ "version": "0.5.67",
4
4
  "description": "Strata — Laravel-inspired Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,10 +0,0 @@
1
- interface AppConfig {
2
- name: string;
3
- env: string;
4
- debug: boolean;
5
- url: string;
6
- apiPrefix: string;
7
- }
8
- declare const appConfig: AppConfig;
9
- export type { AppConfig };
10
- export { appConfig };