@getstrata/core 0.5.55 → 0.5.56

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 (77) hide show
  1. package/README.md +2 -0
  2. package/dist/core/media/imageTransform.d.ts +13 -0
  3. package/dist/core/scheduler/osCron.d.ts +14 -0
  4. package/dist/core/terminal/runShell.d.ts +6 -0
  5. package/dist/core/view/assertEtaHtmlSource.d.ts +5 -0
  6. package/dist/core/view/etaViewEngine.d.ts +4 -0
  7. package/dist/entries/audit/exportAuditLogs.js +2 -2
  8. package/dist/entries/audit/siemFormatter.js +2 -2
  9. package/dist/entries/auth/accessControl.js +4 -4
  10. package/dist/entries/auth/membershipScope.js +6 -6
  11. package/dist/entries/auth/oauth/providers.js +2 -2
  12. package/dist/entries/auth/password.js +2 -2
  13. package/dist/entries/auth/policy.js +2 -2
  14. package/dist/entries/auth/sessionCookie.js +4 -4
  15. package/dist/entries/auth/sessionGuard.js +0 -91
  16. package/dist/entries/auth/tokenHash.js +2 -2
  17. package/dist/entries/config/envSchema.js +2 -2
  18. package/dist/entries/contracts/container.js +2 -2
  19. package/dist/entries/contracts/di.js +2 -2
  20. package/dist/entries/contracts/serviceTokens.js +6 -6
  21. package/dist/entries/crypto/fieldEncryption.js +8 -8
  22. package/dist/entries/crypto/mfaSecret.js +2 -2
  23. package/dist/entries/database/errors.js +2 -2
  24. package/dist/entries/database/migrations.js +8 -8
  25. package/dist/entries/database/model.js +5 -5
  26. package/dist/entries/database/query.js +19 -19
  27. package/dist/entries/database/relationships.js +13 -13
  28. package/dist/entries/database/schema.js +13 -13
  29. package/dist/entries/database/seeders.js +2 -2
  30. package/dist/entries/facades.js +8 -8
  31. package/dist/entries/http/authMiddleware.js +2 -2
  32. package/dist/entries/http/bodySizeLimitMiddleware.js +2 -2
  33. package/dist/entries/http/cookies.js +2 -2
  34. package/dist/entries/http/csrfProtection.js +2 -2
  35. package/dist/entries/http/csrfToken.js +6 -6
  36. package/dist/entries/http/etag.js +8 -8
  37. package/dist/entries/http/flashSession.js +4 -4
  38. package/dist/entries/http/formRequest.js +2 -2
  39. package/dist/entries/http/loginThrottleMiddleware.js +2 -2
  40. package/dist/entries/http/metricsMiddleware.js +2 -2
  41. package/dist/entries/http/pagination.js +4 -4
  42. package/dist/entries/http/parseFormBody.js +2 -2
  43. package/dist/entries/http/parseMultipartUpload.js +2 -2
  44. package/dist/entries/http/resources.js +2 -2
  45. package/dist/entries/http/response.js +186 -98
  46. package/dist/entries/http/securedRouteModelBinding.js +2 -2
  47. package/dist/entries/http/throttleMiddleware.js +2 -2
  48. package/dist/entries/http/validation.js +13 -13
  49. package/dist/entries/http/webErrorResponse.js +184 -96
  50. package/dist/entries/lifecycle/gracefulShutdown.js +3 -3
  51. package/dist/entries/logging/logger.js +2 -2
  52. package/dist/entries/mail/mailer.js +6 -6
  53. package/dist/entries/mail/markdownMail.js +3 -3
  54. package/dist/entries/mail/markdownMailable.js +2 -2
  55. package/dist/entries/media/imageTransform.js +40 -0
  56. package/dist/entries/metrics/prometheus.js +2 -2
  57. package/dist/entries/openapi/generator.js +2 -2
  58. package/dist/entries/queue/createAppQueue.js +4 -4
  59. package/dist/entries/queue/publicQueue.js +10 -10
  60. package/dist/entries/queue/queueMetrics.js +2 -2
  61. package/dist/entries/queue/redisQueue.js +7 -7
  62. package/dist/entries/queue.js +3 -3
  63. package/dist/entries/scheduler/osCron.js +27 -0
  64. package/dist/entries/scheduler/schedule.js +2 -2
  65. package/dist/entries/security/oauthState.js +3 -3
  66. package/dist/entries/security/publicReads.js +2 -2
  67. package/dist/entries/security/safeFetch.js +2 -2
  68. package/dist/entries/security/safeUrl.js +5 -5
  69. package/dist/entries/security/scimTenantTokens.js +2 -2
  70. package/dist/entries/security/totp.js +2 -2
  71. package/dist/entries/storage/storage.js +7 -7
  72. package/dist/entries/tenant/tenantDatabaseScope.js +2 -2
  73. package/dist/entries/terminal/runShell.js +18 -0
  74. package/dist/entries/validation/rules.js +12 -12
  75. package/dist/entries/view.js +186 -98
  76. package/dist/index.js +431 -469
  77. package/package.json +17 -2
@@ -81,8 +81,183 @@ async function withDatabaseErrorHandling(operation) {
81
81
  }
82
82
 
83
83
  // ../../src/core/view/etaViewEngine.ts
84
- import { join } from "path";
84
+ import { join, relative } from "path";
85
85
  import { Eta } from "eta";
86
+
87
+ // ../../src/core/view/assertEtaHtmlSource.ts
88
+ var HTML_TAGS = new Set([
89
+ "a",
90
+ "abbr",
91
+ "address",
92
+ "article",
93
+ "aside",
94
+ "audio",
95
+ "b",
96
+ "blockquote",
97
+ "body",
98
+ "button",
99
+ "canvas",
100
+ "caption",
101
+ "cite",
102
+ "code",
103
+ "col",
104
+ "colgroup",
105
+ "data",
106
+ "datalist",
107
+ "dd",
108
+ "del",
109
+ "details",
110
+ "dfn",
111
+ "dialog",
112
+ "div",
113
+ "dl",
114
+ "dt",
115
+ "em",
116
+ "fieldset",
117
+ "figcaption",
118
+ "figure",
119
+ "footer",
120
+ "form",
121
+ "h1",
122
+ "h2",
123
+ "h3",
124
+ "h4",
125
+ "h5",
126
+ "h6",
127
+ "header",
128
+ "hgroup",
129
+ "hr",
130
+ "html",
131
+ "i",
132
+ "iframe",
133
+ "img",
134
+ "input",
135
+ "ins",
136
+ "kbd",
137
+ "label",
138
+ "legend",
139
+ "li",
140
+ "main",
141
+ "map",
142
+ "mark",
143
+ "menu",
144
+ "meter",
145
+ "nav",
146
+ "object",
147
+ "ol",
148
+ "optgroup",
149
+ "option",
150
+ "output",
151
+ "p",
152
+ "picture",
153
+ "pre",
154
+ "progress",
155
+ "q",
156
+ "rp",
157
+ "rt",
158
+ "ruby",
159
+ "s",
160
+ "samp",
161
+ "script",
162
+ "search",
163
+ "section",
164
+ "select",
165
+ "slot",
166
+ "small",
167
+ "source",
168
+ "span",
169
+ "strong",
170
+ "style",
171
+ "sub",
172
+ "summary",
173
+ "sup",
174
+ "table",
175
+ "tbody",
176
+ "td",
177
+ "template",
178
+ "textarea",
179
+ "tfoot",
180
+ "th",
181
+ "thead",
182
+ "time",
183
+ "title",
184
+ "tr",
185
+ "track",
186
+ "u",
187
+ "ul",
188
+ "var",
189
+ "video",
190
+ "wbr"
191
+ ]);
192
+ var TAG_PATTERN = "([a-z][a-z0-9]*)";
193
+ var CLASS_OR_ID_PATTERN = "[.#][\\w-]+";
194
+ var CLASS_ID_SHORTHAND = new RegExp(`^${TAG_PATTERN}(?:${CLASS_OR_ID_PATTERN})+`);
195
+ var ATTRIBUTE_SHORTHAND = new RegExp(`^${TAG_PATTERN}(?:${CLASS_OR_ID_PATTERN})*\\s+[\\w:-]+=`);
196
+ var TAG_SHORTHAND = new RegExp(`^${TAG_PATTERN}(?:>|\\s+\\S|$)`);
197
+ function htmlTagName(match) {
198
+ const tag = match?.[1];
199
+ return tag && HTML_TAGS.has(tag) ? tag : undefined;
200
+ }
201
+ function describePugLikeLine(line) {
202
+ if (/^\|(?:\s|$)/.test(line)) {
203
+ return "piped text";
204
+ }
205
+ if (htmlTagName(line.match(CLASS_ID_SHORTHAND))) {
206
+ return "class/id shorthand";
207
+ }
208
+ if (htmlTagName(line.match(ATTRIBUTE_SHORTHAND))) {
209
+ return "attribute shorthand";
210
+ }
211
+ if (htmlTagName(line.match(TAG_SHORTHAND))) {
212
+ return "tag shorthand";
213
+ }
214
+ return;
215
+ }
216
+ function updateSkipBlock(lower, skipBlock) {
217
+ if (skipBlock) {
218
+ if (skipBlock === "comment" && lower.includes("-->")) {
219
+ return null;
220
+ }
221
+ if (skipBlock !== "comment" && lower.includes(`</${skipBlock}`)) {
222
+ return null;
223
+ }
224
+ return skipBlock;
225
+ }
226
+ if (lower.startsWith("<!--") && !lower.includes("-->")) {
227
+ return "comment";
228
+ }
229
+ if (lower.startsWith("<script") && !lower.includes("</script")) {
230
+ return "script";
231
+ }
232
+ if (lower.startsWith("<style") && !lower.includes("</style")) {
233
+ return "style";
234
+ }
235
+ return null;
236
+ }
237
+ function assertEtaHtmlSource(templateName, source) {
238
+ let skipBlock = null;
239
+ for (const rawLine of source.split(/\r?\n/)) {
240
+ const trimmed = rawLine.trim();
241
+ if (!trimmed) {
242
+ continue;
243
+ }
244
+ const lower = trimmed.toLowerCase();
245
+ const nextSkipBlock = updateSkipBlock(lower, skipBlock);
246
+ if (skipBlock || nextSkipBlock) {
247
+ skipBlock = nextSkipBlock;
248
+ continue;
249
+ }
250
+ if (trimmed.startsWith("<") || trimmed.startsWith("<%")) {
251
+ continue;
252
+ }
253
+ const kind = describePugLikeLine(trimmed);
254
+ if (kind) {
255
+ throw new Error(`Eta view "${templateName}" contains Pug-like markup (${kind}: ${trimmed}). Eta views must be HTML + <% %> tags, not Pug.`);
256
+ }
257
+ }
258
+ }
259
+
260
+ // ../../src/core/view/etaViewEngine.ts
86
261
  var DEFAULT_VIEWS_DIRECTORY = join(process.cwd(), "resources/views");
87
262
  var DEFAULT_LAYOUT = "layouts/app.eta";
88
263
 
@@ -95,6 +270,12 @@ class EtaViewEngine {
95
270
  autoTrim: false
96
271
  });
97
272
  this.resolveLayoutData = resolveLayoutData;
273
+ const readFile = this.eta.readFile?.bind(this.eta);
274
+ this.eta.readFile = (path) => {
275
+ const source = readFile ? readFile(path) : "";
276
+ assertEtaHtmlSource(relative(viewsDirectory, path) || path, source);
277
+ return source;
278
+ };
98
279
  }
99
280
  async render(name, data = {}, options = {}) {
100
281
  const template = name.endsWith(".eta") ? name : `${name}.eta`;
@@ -358,99 +539,6 @@ function resolveAbilitiesForRole(role) {
358
539
  return [...MEMBER_ABILITIES];
359
540
  }
360
541
 
361
- // ../../src/modules/user/authService.ts
362
- class AuthService {
363
- users;
364
- tokens;
365
- oauthIdentities;
366
- oauthProviders = new Map;
367
- constructor(users, tokens, oauthIdentities) {
368
- this.users = users;
369
- this.tokens = tokens;
370
- this.oauthIdentities = oauthIdentities;
371
- }
372
- registerOAuthProvider(provider) {
373
- this.oauthProviders.set(provider.name, provider);
374
- }
375
- getOAuthProvider(name) {
376
- return this.oauthProviders.get(name);
377
- }
378
- async loginWithPassword(email, password, options = {}) {
379
- const user = await this.users.findByEmail(email);
380
- if (!user?.password_hash) {
381
- logSecurityEvent("auth_login_failed", { reason: "unknown_user", email });
382
- throw new UnauthorizedError("Invalid credentials.");
383
- }
384
- const valid = await verifyPassword(password, user.password_hash);
385
- if (!valid) {
386
- logSecurityEvent("auth_login_failed", { reason: "invalid_password", user_id: user.id });
387
- throw new UnauthorizedError("Invalid credentials.");
388
- }
389
- if (isFeatureEnabled("emailVerification") && !user.email_verified_at) {
390
- logSecurityEvent("auth_login_blocked", { reason: "email_unverified", user_id: user.id });
391
- throw new UnauthorizedError("Email address is not verified.");
392
- }
393
- if (isFeatureEnabled("mfa") && user.mfa_enabled) {
394
- const mfaSecret = revealMfaSecret(user.mfa_secret);
395
- if (!mfaSecret || !options.mfaCode || !verifyTotp(mfaSecret, options.mfaCode)) {
396
- logSecurityEvent("auth_login_failed", { reason: "invalid_mfa", user_id: user.id });
397
- throw new UnauthorizedError("Invalid MFA code.");
398
- }
399
- }
400
- logSecurityEvent("auth_login_success", { user_id: user.id, method: "password" });
401
- return await this.tokens.createToken(user.id, {
402
- name: "password-login",
403
- abilities: resolveAbilitiesForRole(user.role),
404
- expiresInDays: resolveDefaultTokenExpiryDays() ?? undefined
405
- });
406
- }
407
- async loginWithOAuth(providerName, code) {
408
- const provider = this.oauthProviders.get(providerName);
409
- if (!provider) {
410
- throw new UnauthorizedError("Unsupported OAuth provider.");
411
- }
412
- const profile = await provider.exchangeCode(code);
413
- const user = await this.findOrCreateOAuthUser(providerName, profile);
414
- logSecurityEvent("auth_login_success", { user_id: user.id, method: `oauth:${providerName}` });
415
- return await this.tokens.createToken(user.id, {
416
- name: `${providerName}-oauth`,
417
- abilities: resolveAbilitiesForRole(user.role),
418
- expiresInDays: resolveDefaultTokenExpiryDays() ?? undefined
419
- });
420
- }
421
- buildOAuthAuthorizationUrl(providerName, state) {
422
- const provider = this.oauthProviders.get(providerName);
423
- if (!provider) {
424
- throw new UnauthorizedError("Unsupported OAuth provider.");
425
- }
426
- return provider.getAuthorizationUrl(state);
427
- }
428
- async findOrCreateOAuthUser(providerName, profile) {
429
- const existingIdentity = await this.oauthIdentities.findByProviderUser(providerName, profile.providerUserId);
430
- if (existingIdentity) {
431
- return await this.users.findByIdOrThrow(existingIdentity.user_id);
432
- }
433
- const existingUser = await this.users.findByEmail(profile.email);
434
- const user = existingUser ?? await this.users.create({
435
- name: profile.name,
436
- email: profile.email,
437
- role: "member",
438
- tenant_id: currentTenantId(),
439
- email_verified_at: new Date,
440
- created_at: new Date,
441
- updated_at: new Date
442
- });
443
- await this.oauthIdentities.create({
444
- user_id: user.id,
445
- provider: providerName,
446
- provider_user_id: profile.providerUserId,
447
- email: profile.email,
448
- created_at: new Date
449
- });
450
- return user;
451
- }
452
- }
453
-
454
542
  // ../../src/modules/user/notificationRepository.ts
455
543
  import { BaseRepository as BaseRepository2 } from "@getstrata/core/database/baseRepository";
456
544
 
@@ -834,6 +922,6 @@ function webErrorResponse(error, request) {
834
922
  });
835
923
  }
836
924
  export {
837
- webErrorResponse,
838
- normalizeFieldErrors
925
+ normalizeFieldErrors,
926
+ webErrorResponse
839
927
  };
@@ -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
  };
@@ -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
  };
@@ -46,6 +46,6 @@ function resolveScimTenantFromToken(token) {
46
46
  return null;
47
47
  }
48
48
  export {
49
- resolveScimTenantFromToken,
50
- parseScimTenantTokens
49
+ parseScimTenantTokens,
50
+ resolveScimTenantFromToken
51
51
  };
@@ -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
  };
@@ -59,7 +59,7 @@ function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
59
59
  return hasActiveDatabaseConnection() && currentTenant()?.id === tenantId;
60
60
  }
61
61
  export {
62
- runWithTenantDatabase,
62
+ applyTenantContextToTransaction,
63
63
  isInsideTenantDatabaseScope,
64
- applyTenantContextToTransaction
64
+ runWithTenantDatabase
65
65
  };
@@ -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
+ };