@getstrata/core 0.5.55 → 0.5.57

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 (118) hide show
  1. package/README.md +3 -1
  2. package/dist/{domain/abilities.d.ts → core/auth/abilityCatalog.d.ts} +9 -1
  3. package/dist/core/auth/accessControl.d.ts +1 -1
  4. package/dist/core/auth/membershipContext.d.ts +6 -4
  5. package/dist/core/auth/membershipService.d.ts +5 -6
  6. package/dist/core/contracts/authUserDirectory.d.ts +11 -0
  7. package/dist/core/contracts/membership.d.ts +20 -0
  8. package/dist/core/http/clientIp.d.ts +3 -0
  9. package/dist/core/jobs/dispatchWebhookJob.d.ts +2 -0
  10. package/dist/core/media/imageTransform.d.ts +13 -0
  11. package/dist/core/scheduler/osCron.d.ts +14 -0
  12. package/dist/core/tenant/tenancyConfig.d.ts +5 -0
  13. package/dist/core/tenant/tenantMiddleware.d.ts +1 -1
  14. package/dist/core/terminal/runShell.d.ts +6 -0
  15. package/dist/core/view/assertEtaHtmlSource.d.ts +5 -0
  16. package/dist/core/view/etaViewEngine.d.ts +4 -0
  17. package/dist/entries/audit/exportAuditLogs.js +2 -2
  18. package/dist/entries/audit/siemFormatter.js +2 -2
  19. package/dist/entries/auth/accessControl.js +4 -4
  20. package/dist/entries/auth/membershipMiddleware.js +21 -161
  21. package/dist/entries/auth/membershipScope.js +26 -166
  22. package/dist/entries/auth/membershipService.js +21 -161
  23. package/dist/entries/auth/oauth/providers.js +2 -2
  24. package/dist/entries/auth/password.js +2 -2
  25. package/dist/entries/auth/policy.js +2 -2
  26. package/dist/entries/auth/scimAuthMiddleware.js +22 -4
  27. package/dist/entries/auth/sessionCookie.js +4 -4
  28. package/dist/entries/auth/sessionGuard.js +12 -349
  29. package/dist/entries/auth/tokenHash.js +2 -2
  30. package/dist/entries/config/envSchema.js +2 -2
  31. package/dist/entries/contracts/container.js +2 -2
  32. package/dist/entries/contracts/di.js +2 -2
  33. package/dist/entries/contracts/serviceTokens.js +6 -6
  34. package/dist/entries/crypto/fieldEncryption.js +8 -8
  35. package/dist/entries/crypto/mfaSecret.js +2 -2
  36. package/dist/entries/database/errors.js +2 -2
  37. package/dist/entries/database/migrations.js +8 -8
  38. package/dist/entries/database/model.js +5 -5
  39. package/dist/entries/database/query.js +19 -19
  40. package/dist/entries/database/relationships.js +13 -13
  41. package/dist/entries/database/schema.js +13 -13
  42. package/dist/entries/database/seeders.js +2 -2
  43. package/dist/entries/facades.js +8 -8
  44. package/dist/entries/http/authMiddleware.js +2 -2
  45. package/dist/entries/http/bodySizeLimitMiddleware.js +2 -2
  46. package/dist/entries/http/clientIp.js +19 -0
  47. package/dist/entries/http/cookies.js +2 -2
  48. package/dist/entries/http/csrfMiddleware.js +2 -1
  49. package/dist/entries/http/csrfProtection.js +2 -2
  50. package/dist/entries/http/csrfToken.js +8 -7
  51. package/dist/entries/http/etag.js +8 -8
  52. package/dist/entries/http/flashSession.js +4 -4
  53. package/dist/entries/http/formRequest.js +2 -2
  54. package/dist/entries/http/loginThrottleMiddleware.js +20 -3
  55. package/dist/entries/http/memoryThrottleMiddleware.js +16 -1
  56. package/dist/entries/http/metricsMiddleware.js +2 -2
  57. package/dist/entries/http/pagination.js +4 -4
  58. package/dist/entries/http/parseFormBody.js +2 -2
  59. package/dist/entries/http/parseMultipartUpload.js +2 -2
  60. package/dist/entries/http/resources.js +2 -2
  61. package/dist/entries/http/response.js +214 -395
  62. package/dist/entries/http/scimThrottleMiddleware.js +40 -1
  63. package/dist/entries/http/securedRouteModelBinding.js +2 -2
  64. package/dist/entries/http/throttleMiddleware.js +20 -3
  65. package/dist/entries/http/validation.js +13 -13
  66. package/dist/entries/http/webErrorResponse.js +212 -393
  67. package/dist/entries/jobs/dispatchWebhookJob.js +111 -5
  68. package/dist/entries/lifecycle/gracefulShutdown.js +3 -3
  69. package/dist/entries/logging/logger.js +2 -2
  70. package/dist/entries/logging/requestLoggingMiddleware.js +16 -1
  71. package/dist/entries/mail/mailer.js +6 -6
  72. package/dist/entries/mail/markdownMail.js +3 -3
  73. package/dist/entries/mail/markdownMailable.js +2 -2
  74. package/dist/entries/media/imageTransform.js +40 -0
  75. package/dist/entries/metrics/prometheus.js +2 -2
  76. package/dist/entries/openapi/generator.js +2 -2
  77. package/dist/entries/queue/createAppQueue.js +4 -4
  78. package/dist/entries/queue/publicQueue.js +10 -10
  79. package/dist/entries/queue/queueMetrics.js +2 -2
  80. package/dist/entries/queue/redisQueue.js +7 -7
  81. package/dist/entries/queue.js +3 -3
  82. package/dist/entries/scheduler/osCron.js +27 -0
  83. package/dist/entries/scheduler/schedule.js +2 -2
  84. package/dist/entries/security/oauthState.js +3 -3
  85. package/dist/entries/security/publicReads.js +2 -2
  86. package/dist/entries/security/safeFetch.js +2 -2
  87. package/dist/entries/security/safeUrl.js +5 -5
  88. package/dist/entries/security/scimTenantTokens.js +3 -6
  89. package/dist/entries/security/totp.js +2 -2
  90. package/dist/entries/storage/storage.js +7 -7
  91. package/dist/entries/tenant/tenancyConfig.js +12 -0
  92. package/dist/entries/tenant/tenantDatabaseScope.js +13 -2
  93. package/dist/entries/terminal/runShell.js +18 -0
  94. package/dist/entries/validation/rules.js +12 -12
  95. package/dist/entries/view.js +212 -393
  96. package/dist/framework/public-api.d.ts +4 -1
  97. package/dist/index.js +2591 -2686
  98. package/package.json +27 -2
  99. package/dist/config/database.d.ts +0 -10
  100. package/dist/db/connection/createConnection.d.ts +0 -6
  101. package/dist/db/connection/index.d.ts +0 -11
  102. package/dist/domain/auth.d.ts +0 -4
  103. package/dist/domain/scim.d.ts +0 -9
  104. package/dist/modules/organization/memberRepository.d.ts +0 -14
  105. package/dist/modules/organization/memberTypes.d.ts +0 -9
  106. package/dist/modules/user/apiTokenRepository.d.ts +0 -8
  107. package/dist/modules/user/apiTokenTable.d.ts +0 -3
  108. package/dist/modules/user/authService.d.ts +0 -23
  109. package/dist/modules/user/notificationRepository.d.ts +0 -6
  110. package/dist/modules/user/notificationService.d.ts +0 -25
  111. package/dist/modules/user/notificationTable.d.ts +0 -3
  112. package/dist/modules/user/notificationTypes.d.ts +0 -12
  113. package/dist/modules/user/oauthIdentityRepository.d.ts +0 -9
  114. package/dist/modules/user/provider.d.ts +0 -11
  115. package/dist/modules/user/repository.d.ts +0 -13
  116. package/dist/modules/user/table.d.ts +0 -3
  117. package/dist/modules/user/tokenService.d.ts +0 -29
  118. package/dist/modules/user/types.d.ts +0 -46
@@ -1,7 +1,7 @@
1
1
  // @bun
2
2
  // ../../src/core/jobs/dispatchWebhookJob.ts
3
3
  import { createHmac } from "crypto";
4
- import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
4
+ import { repositoryConnection as db2 } from "@getstrata/core/database/repositoryConnection";
5
5
 
6
6
  // ../../src/config/app.ts
7
7
  var appConfig = {
@@ -169,12 +169,118 @@ async function safeFetch(input, init = {}, options = {}) {
169
169
  }
170
170
  }
171
171
 
172
+ // ../../src/core/tenant/resolveTenant.ts
173
+ import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
174
+
175
+ // ../../src/core/tenant/tenancyConfig.ts
176
+ function readTenancyDriver(env = process.env) {
177
+ return env.TENANCY_DRIVER === "none" ? "none" : "rls";
178
+ }
179
+ function isTenancyEnabled(env = process.env) {
180
+ return readTenancyDriver(env) !== "none";
181
+ }
182
+
183
+ // ../../src/core/tenant/resolveTenant.ts
184
+ async function resolveTenant(tenantId) {
185
+ if (!isTenancyEnabled()) {
186
+ return {
187
+ id: tenantId,
188
+ slug: "default",
189
+ plan: "free",
190
+ region: "eu"
191
+ };
192
+ }
193
+ const rows = await db`
194
+ SELECT id, slug, plan, region
195
+ FROM tenant
196
+ WHERE id = ${tenantId}
197
+ LIMIT 1
198
+ `;
199
+ const row = rows[0];
200
+ return row ? { id: row.id, slug: row.slug, plan: row.plan, region: row.region ?? "eu" } : null;
201
+ }
202
+
203
+ // ../../src/core/tenant/tenantDatabaseScope.ts
204
+ import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
205
+
206
+ // ../../src/core/runtime/asyncContextStore.ts
207
+ import { AsyncLocalStorage } from "async_hooks";
208
+ function createAsyncContextStore(key) {
209
+ const symbol = Symbol.for(key);
210
+ const globalRecord = globalThis;
211
+ const existing = globalRecord[symbol];
212
+ if (existing) {
213
+ return existing;
214
+ }
215
+ const store = new AsyncLocalStorage;
216
+ globalRecord[symbol] = store;
217
+ return store;
218
+ }
219
+
220
+ // ../../src/core/database/connectionContext.ts
221
+ var activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
222
+ function runWithDatabaseConnection(connection, callback) {
223
+ return activeConnection.run(connection, callback);
224
+ }
225
+ function getActiveDatabaseConnection(fallback) {
226
+ return activeConnection.getStore() ?? fallback;
227
+ }
228
+ function hasActiveDatabaseConnection() {
229
+ return activeConnection.getStore() !== undefined;
230
+ }
231
+
232
+ // ../../src/core/tenant/tenantContext.ts
233
+ var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
234
+ function runWithTenant(tenant, callback) {
235
+ return tenantContext.run(tenant, callback);
236
+ }
237
+ function currentTenant() {
238
+ return tenantContext.getStore() ?? null;
239
+ }
240
+
241
+ // ../../src/core/tenant/tenantDatabaseScope.ts
242
+ async function applyTenantContextToTransaction(transaction, tenantId) {
243
+ await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
244
+ await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
245
+ }
246
+ async function runWithTenantDatabase(tenant, callback) {
247
+ if (!isTenancyEnabled()) {
248
+ return await runWithTenant(tenant, callback);
249
+ }
250
+ if (hasActiveDatabaseConnection()) {
251
+ const activeConnection2 = getActiveDatabaseConnection(getDefaultDatabasePool());
252
+ await applyTenantContextToTransaction(activeConnection2, tenant.id);
253
+ return await runWithTenant(tenant, callback);
254
+ }
255
+ return await getDefaultDatabasePool().begin(async (transaction) => {
256
+ await applyTenantContextToTransaction(transaction, tenant.id);
257
+ return await runWithDatabaseConnection(transaction, async () => {
258
+ return await runWithTenant(tenant, callback);
259
+ });
260
+ });
261
+ }
262
+ function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
263
+ return hasActiveDatabaseConnection() && currentTenant()?.id === tenantId;
264
+ }
265
+
172
266
  // ../../src/core/jobs/dispatchWebhookJob.ts
173
267
  class DispatchWebhookJob extends Job {
174
268
  maxAttempts = 3;
175
269
  backoffMs = 2000;
176
270
  async handle(payload) {
177
- const rows = await db`
271
+ const tenant = await resolveTenant(payload.tenantId) ?? {
272
+ id: payload.tenantId,
273
+ slug: "job",
274
+ plan: "free",
275
+ region: "eu"
276
+ };
277
+ const failure = await runWithTenantDatabase(tenant, async () => await this.deliver(payload));
278
+ if (failure) {
279
+ throw failure;
280
+ }
281
+ }
282
+ async deliver(payload) {
283
+ const rows = await db2`
178
284
  SELECT id, url, secret
179
285
  FROM webhook
180
286
  WHERE id = ${payload.webhookId} AND active = TRUE
@@ -204,7 +310,7 @@ class DispatchWebhookJob extends Job {
204
310
  }
205
311
  } catch (error) {
206
312
  errorMessage = error instanceof Error ? error.message : String(error);
207
- await db`
313
+ await db2`
208
314
  INSERT INTO webhook_delivery (webhook_id, event, payload, response_status, error)
209
315
  VALUES (
210
316
  ${webhook.id},
@@ -214,9 +320,9 @@ class DispatchWebhookJob extends Job {
214
320
  ${errorMessage}
215
321
  )
216
322
  `;
217
- throw error instanceof Error ? error : new Error(errorMessage);
323
+ return error instanceof Error ? error : new Error(errorMessage);
218
324
  }
219
- await db`
325
+ await db2`
220
326
  INSERT INTO webhook_delivery (webhook_id, event, payload, response_status)
221
327
  VALUES (
222
328
  ${webhook.id},
@@ -43,8 +43,8 @@ function resetGracefulShutdownForTests() {
43
43
  shuttingDown = false;
44
44
  }
45
45
  export {
46
- runGracefulShutdown,
47
- resetGracefulShutdownForTests,
46
+ installGracefulShutdownSignals,
48
47
  registerShutdownHandler,
49
- installGracefulShutdownSignals
48
+ resetGracefulShutdownForTests,
49
+ runGracefulShutdown
50
50
  };
@@ -35,6 +35,6 @@ class Logger {
35
35
  }
36
36
  var appLogger = new Logger("app");
37
37
  export {
38
- appLogger,
39
- Logger
38
+ Logger,
39
+ appLogger
40
40
  };
@@ -2,6 +2,21 @@
2
2
  // ../../src/core/logging/requestLoggingMiddleware.ts
3
3
  import { runWithRequestMeta } from "@getstrata/core/http/requestMetaContext";
4
4
 
5
+ // ../../src/core/http/clientIp.ts
6
+ function trustForwardedFor(env = process.env) {
7
+ return (env.TRUST_FORWARDED_FOR ?? "false") === "true";
8
+ }
9
+ function readClientIp(request, env = process.env) {
10
+ if (!trustForwardedFor(env)) {
11
+ return;
12
+ }
13
+ const forwarded = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim();
14
+ if (forwarded) {
15
+ return forwarded;
16
+ }
17
+ return request.headers.get("x-real-ip")?.trim() || undefined;
18
+ }
19
+
5
20
  // ../../src/core/logging/logger.ts
6
21
  class Logger {
7
22
  channel;
@@ -42,7 +57,7 @@ var appLogger = new Logger("app");
42
57
  function createRequestLoggingMiddleware() {
43
58
  return async (request, next) => {
44
59
  return await runWithRequestMeta({
45
- ipAddress: request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip"),
60
+ ipAddress: readClientIp(request) ?? null,
46
61
  userAgent: request.headers.get("user-agent"),
47
62
  request
48
63
  }, async () => {
@@ -198,11 +198,11 @@ function mailer() {
198
198
  return appMailer;
199
199
  }
200
200
  export {
201
- resolveSmtpConfig,
202
- mailer,
203
- createMailDriver,
204
- buildSmtpPayload,
205
- SmtpMailDriver,
201
+ LogMailDriver,
206
202
  Mailer,
207
- LogMailDriver
203
+ SmtpMailDriver,
204
+ buildSmtpPayload,
205
+ createMailDriver,
206
+ mailer,
207
+ resolveSmtpConfig
208
208
  };
@@ -56,8 +56,8 @@ function renderMarkdownMail(markdown, options = {}) {
56
56
  return { html, text };
57
57
  }
58
58
  export {
59
- wrapMarkdownMailLayout,
60
- stripMarkdown,
59
+ markdownToHtml,
61
60
  renderMarkdownMail,
62
- markdownToHtml
61
+ stripMarkdown,
62
+ wrapMarkdownMailLayout
63
63
  };
@@ -73,6 +73,6 @@ async function sendMarkdownMail(mailer, input) {
73
73
  await mailer.send(buildMarkdownMailMessage(input));
74
74
  }
75
75
  export {
76
- sendMarkdownMail,
77
- buildMarkdownMailMessage
76
+ buildMarkdownMailMessage,
77
+ sendMarkdownMail
78
78
  };
@@ -0,0 +1,40 @@
1
+ // @bun
2
+ // ../../src/core/media/imageTransform.ts
3
+ var IMAGE_MIME_TYPES = new Set(["image/jpeg", "image/png", "image/gif", "image/webp"]);
4
+ function normalizeMimeType(mimeType) {
5
+ return mimeType.split(";")[0]?.trim().toLowerCase() ?? "";
6
+ }
7
+ function isImageMimeType(mimeType) {
8
+ return IMAGE_MIME_TYPES.has(normalizeMimeType(mimeType));
9
+ }
10
+ function parseThumbnailWidth(raw, options = {}) {
11
+ const defaultWidth = options.defaultWidth ?? 200;
12
+ const minWidth = options.minWidth ?? 16;
13
+ const maxWidth = options.maxWidth ?? 800;
14
+ if (!raw?.trim()) {
15
+ return defaultWidth;
16
+ }
17
+ const parsed = Number.parseInt(raw, 10);
18
+ if (!Number.isInteger(parsed)) {
19
+ return defaultWidth;
20
+ }
21
+ return Math.min(maxWidth, Math.max(minWidth, parsed));
22
+ }
23
+ async function resizeImageContents(contents, width, mimeType) {
24
+ const normalized = normalizeMimeType(mimeType);
25
+ const pipeline = new Bun.Image(contents).resize(width);
26
+ if (normalized === "image/png") {
27
+ return { body: await pipeline.png().bytes(), contentType: "image/png" };
28
+ }
29
+ if (normalized === "image/webp") {
30
+ return { body: await pipeline.webp().bytes(), contentType: "image/webp" };
31
+ }
32
+ return { body: await pipeline.jpeg().bytes(), contentType: "image/jpeg" };
33
+ }
34
+ export {
35
+ IMAGE_MIME_TYPES,
36
+ isImageMimeType,
37
+ normalizeMimeType,
38
+ parseThumbnailWidth,
39
+ resizeImageContents
40
+ };
@@ -65,6 +65,6 @@ class PrometheusRegistry {
65
65
  }
66
66
  var prometheusRegistry = new PrometheusRegistry;
67
67
  export {
68
- prometheusRegistry,
69
- PrometheusRegistry
68
+ PrometheusRegistry,
69
+ prometheusRegistry
70
70
  };
@@ -172,7 +172,7 @@ function renderTypeScriptSdk(spec, apiPrefix = "/api/v1") {
172
172
  `);
173
173
  }
174
174
  export {
175
- renderTypeScriptSdk,
175
+ generateOpenApiSpec,
176
176
  renderOpenApiDocument,
177
- generateOpenApiSpec
177
+ renderTypeScriptSdk
178
178
  };
@@ -346,9 +346,9 @@ function createAppQueue(driver, redisUrl, failedJobs = createFailedJobService(),
346
346
  });
347
347
  }
348
348
  export {
349
- createTrackedJob,
350
- createQueueWorker,
351
- createFailedJobService,
349
+ FAILED_JOB_SERVICE_TOKEN,
352
350
  createAppQueue,
353
- FAILED_JOB_SERVICE_TOKEN
351
+ createFailedJobService,
352
+ createQueueWorker,
353
+ createTrackedJob
354
354
  };
@@ -336,15 +336,15 @@ function createQueueWorker(redisUrl, failedJobs = createFailedJobService()) {
336
336
  return new QueueWorker(redisUrl, failedJobs);
337
337
  }
338
338
  export {
339
- runQueueJob,
340
- jobRegistry,
341
- createTrackedJob,
342
- createQueueWorker,
343
- createProductionQueue,
344
- createFailedJobService,
345
- ResilientQueue,
346
- RedisQueue,
347
- QueueWorker,
339
+ failedJobRepository_default as FailedJobRepository,
348
340
  failedJobService_default as FailedJobService,
349
- failedJobRepository_default as FailedJobRepository
341
+ QueueWorker,
342
+ RedisQueue,
343
+ ResilientQueue,
344
+ createFailedJobService,
345
+ createProductionQueue,
346
+ createQueueWorker,
347
+ createTrackedJob,
348
+ jobRegistry,
349
+ runQueueJob
350
350
  };
@@ -400,6 +400,6 @@ async function collectQueueMetrics() {
400
400
  };
401
401
  }
402
402
  export {
403
- readRedisQueueDepth,
404
- collectQueueMetrics
403
+ collectQueueMetrics,
404
+ readRedisQueueDepth
405
405
  };
@@ -222,12 +222,12 @@ async function countPendingQueueJobs(redisUrl) {
222
222
  }
223
223
  }
224
224
  export {
225
- queueKeyForPriority,
226
- parseQueueJobEnvelope,
227
- countPendingQueueJobs,
228
- RedisQueue,
229
- QueueWorker,
230
- QUEUE_LOW_KEY,
225
+ QUEUE_HIGH_KEY,
231
226
  QUEUE_LIST_KEY,
232
- QUEUE_HIGH_KEY
227
+ QUEUE_LOW_KEY,
228
+ QueueWorker,
229
+ RedisQueue,
230
+ countPendingQueueJobs,
231
+ parseQueueJobEnvelope,
232
+ queueKeyForPriority
233
233
  };
@@ -25,8 +25,8 @@ function createQueue(driver) {
25
25
  return driver === "async" ? new AsyncQueue : new SyncQueue;
26
26
  }
27
27
  export {
28
- createQueue,
29
- SyncQueue,
28
+ AsyncQueue,
30
29
  Job,
31
- AsyncQueue
30
+ SyncQueue,
31
+ createQueue
32
32
  };
@@ -0,0 +1,27 @@
1
+ // @bun
2
+ // ../../src/core/scheduler/osCron.ts
3
+ var DEFAULT_SCHEDULE_RUN_EXPRESSION = "* * * * *";
4
+ var OS_CRON_JOB_TITLE = "getstrata-schedule-run";
5
+ function parseScheduleExpression(expression, from = new Date) {
6
+ return Bun.cron.parse(expression, from);
7
+ }
8
+ function registerInProcessScheduleRunner(run, expression = DEFAULT_SCHEDULE_RUN_EXPRESSION) {
9
+ const register = Bun.cron;
10
+ return register(expression, async () => {
11
+ await run();
12
+ }).unref();
13
+ }
14
+ async function installOsScheduleRunner(workerScriptPath, expression = DEFAULT_SCHEDULE_RUN_EXPRESSION, title = OS_CRON_JOB_TITLE) {
15
+ await Bun.cron(workerScriptPath, expression, title);
16
+ }
17
+ async function uninstallOsScheduleRunner(title = OS_CRON_JOB_TITLE) {
18
+ await Bun.cron.remove(title);
19
+ }
20
+ export {
21
+ DEFAULT_SCHEDULE_RUN_EXPRESSION,
22
+ OS_CRON_JOB_TITLE,
23
+ installOsScheduleRunner,
24
+ parseScheduleExpression,
25
+ registerInProcessScheduleRunner,
26
+ uninstallOsScheduleRunner
27
+ };
@@ -32,7 +32,7 @@ async function runDueScheduledTasks(schedule = appSchedule, now = new Date) {
32
32
  return due.length;
33
33
  }
34
34
  export {
35
- runDueScheduledTasks,
35
+ Schedule,
36
36
  appSchedule,
37
- Schedule
37
+ runDueScheduledTasks
38
38
  };
@@ -68,8 +68,8 @@ function clearOAuthStateCookie() {
68
68
  return `${OAUTH_STATE_COOKIE}=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0`;
69
69
  }
70
70
  export {
71
- verifyOAuthState,
72
- createOAuthStateCookie,
71
+ OAUTH_STATE_COOKIE,
73
72
  clearOAuthStateCookie,
74
- OAUTH_STATE_COOKIE
73
+ createOAuthStateCookie,
74
+ verifyOAuthState
75
75
  };
@@ -28,6 +28,6 @@ function guestCanViewResource() {
28
28
  return isPublicReadsEnabled();
29
29
  }
30
30
  export {
31
- isPublicReadsEnabled,
32
- guestCanViewResource
31
+ guestCanViewResource,
32
+ isPublicReadsEnabled
33
33
  };
@@ -139,6 +139,6 @@ async function safeFetch(input, init = {}, options = {}) {
139
139
  }
140
140
  }
141
141
  export {
142
- safeFetch,
143
- DEFAULT_FETCH_TIMEOUT_MS
142
+ DEFAULT_FETCH_TIMEOUT_MS,
143
+ safeFetch
144
144
  };
@@ -96,10 +96,10 @@ function resetDnsLookupForTests() {
96
96
  dnsLookup = dnsLookupImpl;
97
97
  }
98
98
  export {
99
- setDnsLookupForTests,
100
- resetDnsLookupForTests,
101
- isBlockedIpAddress,
102
- isBlockedHostname,
99
+ assertSafeOutboundUrl,
103
100
  assertSafeOutboundUrlResolved,
104
- assertSafeOutboundUrl
101
+ isBlockedHostname,
102
+ isBlockedIpAddress,
103
+ resetDnsLookupForTests,
104
+ setDnsLookupForTests
105
105
  };
@@ -1,7 +1,4 @@
1
1
  // @bun
2
- // ../../src/domain/scim.ts
3
- var TEST_SCIM_BEARER_TOKEN = "workhub-scim-test-token";
4
-
5
2
  // ../../src/core/security/timingSafeCompare.ts
6
3
  import { timingSafeEqual } from "crypto";
7
4
  function timingSafeCompareString(left, right) {
@@ -39,13 +36,13 @@ function resolveScimTenantFromToken(token) {
39
36
  return tenantId;
40
37
  }
41
38
  }
42
- const fallbackToken = process.env.SCIM_BEARER_TOKEN ?? TEST_SCIM_BEARER_TOKEN;
39
+ const fallbackToken = process.env.SCIM_BEARER_TOKEN ?? "";
43
40
  if (timingSafeCompareString(token, fallbackToken)) {
44
41
  return 1;
45
42
  }
46
43
  return null;
47
44
  }
48
45
  export {
49
- resolveScimTenantFromToken,
50
- parseScimTenantTokens
46
+ parseScimTenantTokens,
47
+ resolveScimTenantFromToken
51
48
  };
@@ -46,6 +46,6 @@ function verifyTotp(secret, token, window = 1) {
46
46
  return false;
47
47
  }
48
48
  export {
49
- verifyTotp,
50
- generateTotp
49
+ generateTotp,
50
+ verifyTotp
51
51
  };
@@ -122,12 +122,12 @@ function resetDefaultStorage() {
122
122
  defaultStorage.current = null;
123
123
  }
124
124
  export {
125
- storage,
126
- resolveS3Config,
127
- resetDefaultStorage,
128
- createStorageDriver,
129
- createS3Client,
130
- StorageManager,
125
+ LocalStorageDriver,
131
126
  S3StorageDriver,
132
- LocalStorageDriver
127
+ StorageManager,
128
+ createS3Client,
129
+ createStorageDriver,
130
+ resetDefaultStorage,
131
+ resolveS3Config,
132
+ storage
133
133
  };
@@ -0,0 +1,12 @@
1
+ // @bun
2
+ // ../../src/core/tenant/tenancyConfig.ts
3
+ function readTenancyDriver(env = process.env) {
4
+ return env.TENANCY_DRIVER === "none" ? "none" : "rls";
5
+ }
6
+ function isTenancyEnabled(env = process.env) {
7
+ return readTenancyDriver(env) !== "none";
8
+ }
9
+ export {
10
+ isTenancyEnabled,
11
+ readTenancyDriver
12
+ };
@@ -28,6 +28,14 @@ function hasActiveDatabaseConnection() {
28
28
  return activeConnection.getStore() !== undefined;
29
29
  }
30
30
 
31
+ // ../../src/core/tenant/tenancyConfig.ts
32
+ function readTenancyDriver(env = process.env) {
33
+ return env.TENANCY_DRIVER === "none" ? "none" : "rls";
34
+ }
35
+ function isTenancyEnabled(env = process.env) {
36
+ return readTenancyDriver(env) !== "none";
37
+ }
38
+
31
39
  // ../../src/core/tenant/tenantContext.ts
32
40
  var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
33
41
  function runWithTenant(tenant, callback) {
@@ -43,6 +51,9 @@ async function applyTenantContextToTransaction(transaction, tenantId) {
43
51
  await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
44
52
  }
45
53
  async function runWithTenantDatabase(tenant, callback) {
54
+ if (!isTenancyEnabled()) {
55
+ return await runWithTenant(tenant, callback);
56
+ }
46
57
  if (hasActiveDatabaseConnection()) {
47
58
  const activeConnection2 = getActiveDatabaseConnection(getDefaultDatabasePool());
48
59
  await applyTenantContextToTransaction(activeConnection2, tenant.id);
@@ -59,7 +70,7 @@ function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
59
70
  return hasActiveDatabaseConnection() && currentTenant()?.id === tenantId;
60
71
  }
61
72
  export {
62
- runWithTenantDatabase,
73
+ applyTenantContextToTransaction,
63
74
  isInsideTenantDatabaseScope,
64
- applyTenantContextToTransaction
75
+ runWithTenantDatabase
65
76
  };
@@ -0,0 +1,18 @@
1
+ // @bun
2
+ // ../../src/core/terminal/runShell.ts
3
+ async function runInteractiveShell(command, options = {}) {
4
+ await using terminal = new Bun.Terminal({
5
+ cols: options.cols ?? 120,
6
+ rows: options.rows ?? 30
7
+ });
8
+ const proc = Bun.spawn(command, {
9
+ terminal,
10
+ cwd: options.cwd,
11
+ stdin: "inherit"
12
+ });
13
+ await proc.exited;
14
+ return proc.exitCode ?? 1;
15
+ }
16
+ export {
17
+ runInteractiveShell
18
+ };
@@ -151,17 +151,17 @@ function validateObject(payload, schema) {
151
151
  return output;
152
152
  }
153
153
  export {
154
- validateObject,
155
- stringRule,
156
- required,
157
- positiveIntegerRule,
158
- pattern,
159
- optional,
160
- minLength,
161
- maxLength,
162
- integerRule,
163
- integerRange,
164
- enumRule,
154
+ confirmed,
165
155
  emailRule,
166
- confirmed
156
+ enumRule,
157
+ integerRange,
158
+ integerRule,
159
+ maxLength,
160
+ minLength,
161
+ optional,
162
+ pattern,
163
+ positiveIntegerRule,
164
+ required,
165
+ stringRule,
166
+ validateObject
167
167
  };