@getstrata/core 1.1.9 → 2.0.1

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 (36) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/dist/core/database/transaction.d.ts +2 -1
  3. package/dist/core/database/transactionControl.d.ts +5 -0
  4. package/dist/core/http/loginThrottleMiddleware.d.ts +3 -0
  5. package/dist/core/http/metricsMiddleware.d.ts +1 -2
  6. package/dist/core/http/requestMetaContext.d.ts +2 -0
  7. package/dist/core/http/throttleMiddleware.d.ts +11 -2
  8. package/dist/core/metrics/prometheus.d.ts +11 -1
  9. package/dist/core/queue/jobRunner.d.ts +3 -1
  10. package/dist/core/queue/redisQueue.d.ts +20 -3
  11. package/dist/core/tenant/databaseTenantContext.d.ts +5 -1
  12. package/dist/entries/audit/exportAuditLogs.js +522 -62
  13. package/dist/entries/auth/basicAuthGuard.js +245 -219
  14. package/dist/entries/auth/oauth/samlProvider.js +185 -161
  15. package/dist/entries/auth/oneTimeToken.js +130 -127
  16. package/dist/entries/auth/passwordLogin.js +185 -180
  17. package/dist/entries/auth/saml/samlServiceProvider.js +185 -161
  18. package/dist/entries/auth/scimAuthMiddleware.js +525 -11
  19. package/dist/entries/database/migrations.js +132 -129
  20. package/dist/entries/database/schema.js +58 -39
  21. package/dist/entries/http/metricsMiddleware.js +124 -27
  22. package/dist/entries/http/response.js +107 -0
  23. package/dist/entries/http/routeMiddleware.js +40 -0
  24. package/dist/entries/http/throttleMiddleware.js +124 -11
  25. package/dist/entries/jobs/exportAuditLogsJob.js +522 -62
  26. package/dist/entries/metrics/prometheus.js +71 -11
  27. package/dist/entries/queue/createAppQueue.js +215 -21
  28. package/dist/entries/queue/jobRunner.js +3 -2
  29. package/dist/entries/queue/publicQueue.js +215 -21
  30. package/dist/entries/queue/queueMetrics.js +215 -21
  31. package/dist/entries/queue/redisQueue.js +223 -22
  32. package/dist/entries/tenant/databaseTenantContext.js +477 -16
  33. package/dist/entries/tenant/tenantDatabaseScope.js +523 -9
  34. package/dist/framework/public-api.d.ts +2 -2
  35. package/dist/index.js +954 -457
  36. package/package.json +1 -1
@@ -101,6 +101,80 @@ var init_eventBus = __esm(() => {
101
101
  eventBus = readSharedEventBus();
102
102
  });
103
103
 
104
+ // ../../src/core/database/transactionControl.ts
105
+ function unwrapTransactionRollback(error, depth = 0) {
106
+ if (error instanceof TransactionRollback) {
107
+ return error;
108
+ }
109
+ if (depth >= 5 || !(error instanceof Error) || error.cause === undefined) {
110
+ return null;
111
+ }
112
+ return unwrapTransactionRollback(error.cause, depth + 1);
113
+ }
114
+ function requestTransactionRollback() {
115
+ const state = transactionScopes.getStore();
116
+ const index = state ? state.rollback.length - 1 : -1;
117
+ if (!state || index < 0) {
118
+ return;
119
+ }
120
+ const flags = state.rollback;
121
+ const current = flags[index];
122
+ if (current === undefined) {
123
+ return;
124
+ }
125
+ flags[index] = true;
126
+ }
127
+ function scopeRequestsRollback() {
128
+ const state = transactionScopes.getStore();
129
+ if (!state || state.rollback.length === 0) {
130
+ return false;
131
+ }
132
+ return state.rollback[state.rollback.length - 1] === true;
133
+ }
134
+ async function commitOrRollbackScope(result) {
135
+ if (scopeRequestsRollback()) {
136
+ throw new TransactionRollback(result);
137
+ }
138
+ return result;
139
+ }
140
+ async function settleTransaction(work) {
141
+ try {
142
+ return await work();
143
+ } catch (error) {
144
+ const rollback = unwrapTransactionRollback(error);
145
+ if (rollback) {
146
+ return rollback.result;
147
+ }
148
+ throw error;
149
+ }
150
+ }
151
+ async function runWithTransactionScope(callback) {
152
+ const existing = transactionScopes.getStore();
153
+ if (existing) {
154
+ existing.rollback.push(false);
155
+ try {
156
+ return await callback();
157
+ } finally {
158
+ existing.rollback.pop();
159
+ }
160
+ }
161
+ const state = { rollback: [false] };
162
+ return await transactionScopes.run(state, callback);
163
+ }
164
+ var TransactionRollback, transactionScopes;
165
+ var init_transactionControl = __esm(() => {
166
+ init_asyncContextStore();
167
+ TransactionRollback = class TransactionRollback extends Error {
168
+ result;
169
+ constructor(result) {
170
+ super("Transaction rolled back");
171
+ this.name = "TransactionRollback";
172
+ this.result = result;
173
+ }
174
+ };
175
+ transactionScopes = createAsyncContextStore("@getstrata/transactionScopes");
176
+ });
177
+
104
178
  // ../../src/core/tenant/tenancyConfig.ts
105
179
  function readTenancyDriver(env = process.env) {
106
180
  const raw = env.TENANCY_DRIVER?.trim();
@@ -130,34 +204,39 @@ async function applyTenantContextToTransaction(transaction, tenantId) {
130
204
  await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
131
205
  }
132
206
  async function runWithTenantDatabase(tenant, callback) {
207
+ const outerTenant = currentTenant();
208
+ if (hasActiveDatabaseConnection() && outerTenant && outerTenant.id !== tenant.id) {
209
+ throw new Error("Cannot switch tenants inside an active database transaction.");
210
+ }
133
211
  if (!isTenancyEnabled() || !isRlsTenancy()) {
134
212
  return await runWithTenant(tenant, callback);
135
213
  }
136
214
  if (hasActiveDatabaseConnection()) {
137
215
  const activeConnection = getActiveDatabaseConnection(getDefaultDatabasePool());
138
- await applyTenantContextToTransaction(activeConnection, tenant.id);
139
- return await runWithTenant(tenant, callback);
216
+ return await runWithScopedTenantTransaction(() => applyTenantContextToTransaction(activeConnection, tenant.id), () => runWithTenant(tenant, callback));
140
217
  }
141
218
  const pool = getDefaultDatabasePool();
142
219
  const begin = pool.begin;
143
220
  if (typeof begin !== "function") {
144
221
  throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
145
222
  }
146
- return await runWithDeferredModelEvents(async () => {
147
- return await begin(async (transaction) => {
223
+ return await settleTransaction(() => runWithDeferredModelEvents(async () => {
224
+ return await begin.bind(pool)(async (transaction) => {
148
225
  await applyTenantContextToTransaction(transaction, tenant.id);
149
226
  return await runWithDatabaseConnection(transaction, async () => {
150
- return await runWithTenant(tenant, callback);
227
+ return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
151
228
  });
152
229
  });
153
- });
230
+ }));
154
231
  }
155
232
  function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
156
233
  return hasActiveDatabaseConnection() && currentTenant()?.id === tenantId;
157
234
  }
158
235
  var init_tenantDatabaseScope = __esm(() => {
159
236
  init_connectionContext();
237
+ init_transactionControl();
160
238
  init_deferredModelEvents();
239
+ init_databaseTenantContext();
161
240
  init_tenancyConfig();
162
241
  init_tenantContext();
163
242
  });
@@ -234,11 +313,369 @@ var init_deferredModelEvents = __esm(() => {
234
313
  deferredModelEvents = createAsyncContextStore("@getstrata/deferredModelEvents");
235
314
  });
236
315
 
316
+ // ../../src/core/database/connection.ts
317
+ function createDatabaseConnection(source) {
318
+ return {
319
+ async unsafe(query, params = []) {
320
+ return await source.unsafe(query, params);
321
+ }
322
+ };
323
+ }
324
+ var init_connection = () => {};
325
+
326
+ // ../../src/core/database/boundConnection.ts
327
+ function boundConnectionState() {
328
+ const globalRecord = globalThis;
329
+ const existing = globalRecord[BOUND_CONNECTION_KEY];
330
+ if (existing) {
331
+ return existing;
332
+ }
333
+ const created = { connection: null };
334
+ globalRecord[BOUND_CONNECTION_KEY] = created;
335
+ return created;
336
+ }
337
+ function getBoundDatabaseConnection() {
338
+ return boundConnectionState().connection;
339
+ }
340
+ var BOUND_CONNECTION_KEY;
341
+ var init_boundConnection = __esm(() => {
342
+ BOUND_CONNECTION_KEY = Symbol.for("@getstrata/boundDatabaseConnection");
343
+ });
344
+
345
+ // ../../src/core/database/queryProxy.ts
346
+ var POOL_CONNECTION_METHODS;
347
+ var init_queryProxy = __esm(() => {
348
+ init_connectionContext();
349
+ POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
350
+ });
351
+
352
+ // ../../src/core/database/defaultConnection.ts
353
+ function defaultConnectionState() {
354
+ const globalRecord = globalThis;
355
+ const existing = globalRecord[DEFAULT_CONNECTION_KEY];
356
+ if (existing) {
357
+ return existing;
358
+ }
359
+ const created = { pool: null, query: null };
360
+ globalRecord[DEFAULT_CONNECTION_KEY] = created;
361
+ return created;
362
+ }
363
+ function getDefaultDatabasePool2() {
364
+ const pool = defaultConnectionState().pool;
365
+ if (!pool) {
366
+ throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
367
+ }
368
+ return pool;
369
+ }
370
+ function getDefaultDatabaseQuery() {
371
+ const query = defaultConnectionState().query;
372
+ if (!query) {
373
+ throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
374
+ }
375
+ return query;
376
+ }
377
+ var DEFAULT_CONNECTION_KEY;
378
+ var init_defaultConnection = __esm(() => {
379
+ init_queryProxy();
380
+ DEFAULT_CONNECTION_KEY = Symbol.for("@getstrata/defaultDatabaseConnection");
381
+ });
382
+
383
+ // ../../src/core/database/schema/driver.ts
384
+ function normalizeConnectionName(connection) {
385
+ const normalized = connection.trim().toLowerCase();
386
+ if (normalized === "pgsql" || normalized === "postgres" || normalized === "postgresql") {
387
+ return "pgsql";
388
+ }
389
+ if (normalized === "mysql" || normalized === "mariadb") {
390
+ return "mysql";
391
+ }
392
+ if (normalized === "sqlite") {
393
+ return "sqlite";
394
+ }
395
+ throw new Error(`Unsupported DB_CONNECTION: ${connection}`);
396
+ }
397
+ function resolveDriverFromUrl(url) {
398
+ const normalized = url.trim().toLowerCase();
399
+ if (normalized.startsWith("postgres://") || normalized.startsWith("postgresql://") || normalized.startsWith("postgres:")) {
400
+ return "pgsql";
401
+ }
402
+ if (normalized.startsWith("mysql://") || normalized.startsWith("mysql:")) {
403
+ return "mysql";
404
+ }
405
+ if (normalized.startsWith("sqlite:")) {
406
+ return "sqlite";
407
+ }
408
+ return null;
409
+ }
410
+ function resolveDatabaseDriver(options = {}) {
411
+ const connection = options.connection ?? process.env.DB_CONNECTION;
412
+ if (connection) {
413
+ return normalizeConnectionName(connection);
414
+ }
415
+ const url = options.url ?? process.env.DATABASE_URL ?? "";
416
+ const fromUrl = resolveDriverFromUrl(url);
417
+ if (fromUrl) {
418
+ return fromUrl;
419
+ }
420
+ return "pgsql";
421
+ }
422
+ var init_driver = () => {};
423
+
424
+ // ../../src/core/database/dialect.ts
425
+ function assertSafeIdentifier(identifier) {
426
+ if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(identifier)) {
427
+ throw new Error(`Invalid SQL identifier: ${identifier}`);
428
+ }
429
+ return identifier;
430
+ }
431
+ function quoteIdentifierFor(dialect, column) {
432
+ return dialect.quoteIdentifier(column);
433
+ }
434
+ function dialectFor(driver) {
435
+ return dialects[driver];
436
+ }
437
+ function currentSqlDialect() {
438
+ return dialectContext.getStore() ?? dialectOverride ?? dialectFor(resolveDatabaseDriver());
439
+ }
440
+ var postgresDialect, mysqlDialect, sqliteDialect, dialects, dialectContext, dialectOverride = null;
441
+ var init_dialect = __esm(() => {
442
+ init_asyncContextStore();
443
+ init_driver();
444
+ postgresDialect = {
445
+ driver: "pgsql",
446
+ placeholder(index) {
447
+ return `$${index}`;
448
+ },
449
+ quoteIdentifier(identifier) {
450
+ return `"${assertSafeIdentifier(identifier)}"`;
451
+ },
452
+ nowExpression() {
453
+ return "NOW()";
454
+ },
455
+ timestampValue(value) {
456
+ return value.toISOString();
457
+ },
458
+ returningClause(columns) {
459
+ return ` RETURNING ${columns}`;
460
+ },
461
+ ilikeOperator() {
462
+ return "ILIKE";
463
+ },
464
+ nullsLastSuffix() {
465
+ return " NULLS LAST";
466
+ },
467
+ castToText(expression) {
468
+ return `${expression}::text`;
469
+ },
470
+ upsertSuffix(conflictColumns, updateColumns) {
471
+ const target = conflictColumns.map((column) => quoteIdentifierFor(this, column)).join(", ");
472
+ if (updateColumns.length === 0) {
473
+ return ` ON CONFLICT (${target}) DO NOTHING`;
474
+ }
475
+ const assignments = updateColumns.map((column) => {
476
+ const quoted = quoteIdentifierFor(this, column);
477
+ return `${quoted} = excluded.${quoted}`;
478
+ }).join(", ");
479
+ return ` ON CONFLICT (${target}) DO UPDATE SET ${assignments}`;
480
+ }
481
+ };
482
+ mysqlDialect = {
483
+ driver: "mysql",
484
+ placeholder() {
485
+ return "?";
486
+ },
487
+ quoteIdentifier(identifier) {
488
+ return `\`${assertSafeIdentifier(identifier)}\``;
489
+ },
490
+ nowExpression() {
491
+ return "CURRENT_TIMESTAMP";
492
+ },
493
+ timestampValue(value) {
494
+ return value.toISOString().slice(0, 19).replace("T", " ");
495
+ },
496
+ returningClause() {
497
+ return "";
498
+ },
499
+ ilikeOperator() {
500
+ return "LIKE";
501
+ },
502
+ nullsLastSuffix() {
503
+ return "";
504
+ },
505
+ castToText(expression) {
506
+ return `CAST(${expression} AS CHAR)`;
507
+ },
508
+ upsertSuffix(conflictColumns, updateColumns) {
509
+ if (updateColumns.length === 0) {
510
+ const anchor = quoteIdentifierFor(this, conflictColumns[0] ?? "");
511
+ return ` ON DUPLICATE KEY UPDATE ${anchor} = ${anchor}`;
512
+ }
513
+ const assignments = updateColumns.map((column) => {
514
+ const quoted = quoteIdentifierFor(this, column);
515
+ return `${quoted} = VALUES(${quoted})`;
516
+ }).join(", ");
517
+ return ` ON DUPLICATE KEY UPDATE ${assignments}`;
518
+ }
519
+ };
520
+ sqliteDialect = {
521
+ driver: "sqlite",
522
+ placeholder() {
523
+ return "?";
524
+ },
525
+ quoteIdentifier(identifier) {
526
+ return `"${assertSafeIdentifier(identifier)}"`;
527
+ },
528
+ nowExpression() {
529
+ return "strftime('%Y-%m-%dT%H:%M:%fZ', 'now')";
530
+ },
531
+ timestampValue(value) {
532
+ return value.toISOString();
533
+ },
534
+ returningClause(columns) {
535
+ return ` RETURNING ${columns}`;
536
+ },
537
+ ilikeOperator() {
538
+ return "LIKE";
539
+ },
540
+ nullsLastSuffix() {
541
+ return "";
542
+ },
543
+ castToText(expression) {
544
+ return `CAST(${expression} AS TEXT)`;
545
+ },
546
+ upsertSuffix(conflictColumns, updateColumns) {
547
+ const target = conflictColumns.map((column) => quoteIdentifierFor(this, column)).join(", ");
548
+ if (updateColumns.length === 0) {
549
+ return ` ON CONFLICT (${target}) DO NOTHING`;
550
+ }
551
+ const assignments = updateColumns.map((column) => {
552
+ const quoted = quoteIdentifierFor(this, column);
553
+ return `${quoted} = excluded.${quoted}`;
554
+ }).join(", ");
555
+ return ` ON CONFLICT (${target}) DO UPDATE SET ${assignments}`;
556
+ }
557
+ };
558
+ dialects = {
559
+ pgsql: postgresDialect,
560
+ mysql: mysqlDialect,
561
+ sqlite: sqliteDialect
562
+ };
563
+ dialectContext = createAsyncContextStore("@getstrata/sqlDialect");
564
+ });
565
+
566
+ // ../../src/core/database/repositoryConnection.ts
567
+ function resolveRepositoryConnection() {
568
+ return getActiveDatabaseConnection(getBoundDatabaseConnection() ?? getDefaultDatabaseQuery());
569
+ }
570
+ function compileTaggedSql(strings, values) {
571
+ const dialect = currentSqlDialect();
572
+ let query = strings[0] ?? "";
573
+ const params = [];
574
+ for (let index = 0;index < values.length; index += 1) {
575
+ params.push(values[index]);
576
+ query += `${dialect.placeholder(params.length)}${strings[index + 1] ?? ""}`;
577
+ }
578
+ return { query, params };
579
+ }
580
+ function isTaggedStrings(value) {
581
+ if (!Array.isArray(value) || typeof value[0] !== "string") {
582
+ return false;
583
+ }
584
+ return typeof value.raw !== "undefined";
585
+ }
586
+ function runOnConnection(connection, args) {
587
+ if (typeof connection === "function") {
588
+ return connection(...args);
589
+ }
590
+ if (typeof connection.unsafe !== "function") {
591
+ throw new Error("Database connection cannot run SQL.");
592
+ }
593
+ const strings = args[0];
594
+ if (!isTaggedStrings(strings)) {
595
+ throw new Error("Database connection cannot run tagged SQL.");
596
+ }
597
+ const compiled = compileTaggedSql(strings, args.slice(1));
598
+ return connection.unsafe(compiled.query, compiled.params);
599
+ }
600
+ var repositoryConnection;
601
+ var init_repositoryConnection = __esm(() => {
602
+ init_boundConnection();
603
+ init_connectionContext();
604
+ init_defaultConnection();
605
+ init_dialect();
606
+ repositoryConnection = new Proxy(function repositoryConnection() {}, {
607
+ apply(_target, _thisArg, args) {
608
+ return runOnConnection(resolveRepositoryConnection(), args);
609
+ },
610
+ get(_target, property) {
611
+ const connection = resolveRepositoryConnection();
612
+ const value = connection[property];
613
+ return typeof value === "function" ? value.bind(connection) : value;
614
+ }
615
+ });
616
+ });
617
+
618
+ // ../../src/core/database/transaction.ts
619
+ async function runInSavepoint(connection, operation) {
620
+ const inherited = transactionFrame.getStore();
621
+ let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
622
+ if (!parent) {
623
+ parent = { connection, childActive: false };
624
+ outerFrames.set(connection, parent);
625
+ }
626
+ if (parent.childActive) {
627
+ throw new Error("Concurrent nested transactions on one connection are not supported. Await each transaction before starting the next.");
628
+ }
629
+ parent.childActive = true;
630
+ const name = `strata_${crypto.randomUUID().replaceAll("-", "")}`;
631
+ try {
632
+ return await settleTransaction(() => runWithDeferredModelEvents(async () => {
633
+ await connection.unsafe(`SAVEPOINT ${name}`);
634
+ try {
635
+ const result = await transactionFrame.run({ connection, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(createDatabaseConnection(connection)))));
636
+ await connection.unsafe(`RELEASE SAVEPOINT ${name}`);
637
+ return result;
638
+ } catch (error) {
639
+ await connection.unsafe(`ROLLBACK TO SAVEPOINT ${name}`);
640
+ await connection.unsafe(`RELEASE SAVEPOINT ${name}`);
641
+ throw error;
642
+ }
643
+ }));
644
+ } finally {
645
+ parent.childActive = false;
646
+ }
647
+ }
648
+ function supportsTransactions(connection) {
649
+ return typeof connection.begin === "function";
650
+ }
651
+ async function runInTransaction(operation) {
652
+ const pool = resolveRepositoryConnection();
653
+ if (hasActiveDatabaseConnection()) {
654
+ return await runInSavepoint(getActiveDatabaseConnection(pool), operation);
655
+ }
656
+ if (!supportsTransactions(pool)) {
657
+ throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
658
+ }
659
+ return await settleTransaction(() => runWithDeferredModelEvents(async () => {
660
+ return await pool.begin(async (transaction) => {
661
+ return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await operation(createDatabaseConnection(transaction))))));
662
+ });
663
+ }));
664
+ }
665
+ var transactionFrame, outerFrames;
666
+ var init_transaction = __esm(() => {
667
+ init_deferredModelEvents();
668
+ init_asyncContextStore();
669
+ init_connection();
670
+ init_connectionContext();
671
+ init_repositoryConnection();
672
+ init_transactionControl();
673
+ transactionFrame = createAsyncContextStore("@getstrata/transactionFrame");
674
+ outerFrames = new WeakMap;
675
+ });
676
+
237
677
  // ../../src/core/tenant/databaseTenantContext.ts
238
- init_connectionContext();
239
- init_deferredModelEvents();
240
- init_tenancyConfig();
241
- import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
678
+ import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
242
679
  async function applyBypassToTransaction(transaction, bypass) {
243
680
  await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
244
681
  bypass ? "true" : "false"
@@ -271,17 +708,30 @@ async function runWithScopedTenantTransaction(apply, callback) {
271
708
  if (!isRlsTenancy()) {
272
709
  return await callback();
273
710
  }
274
- const pool = getDefaultDatabasePool2();
711
+ if (hasActiveDatabaseConnection()) {
712
+ const connection = getActiveDatabaseConnection(getDefaultDatabasePool3());
713
+ return await runInTransaction(async () => {
714
+ const [previous] = await connection.unsafe(`SELECT current_setting('app.tenant_id', true) AS tenant_id,
715
+ current_setting('app.bypass_rls', true) AS bypass_rls,
716
+ current_setting('app.bypass_identifier', true) AS bypass_identifier`);
717
+ await apply(connection);
718
+ const result = await callback();
719
+ await connection.unsafe(`SELECT set_config('app.tenant_id', $1, true),
720
+ set_config('app.bypass_rls', $2, true), set_config('app.bypass_identifier', $3, true)`, [previous?.tenant_id ?? "", previous?.bypass_rls ?? "", previous?.bypass_identifier ?? ""]);
721
+ return result;
722
+ });
723
+ }
724
+ const pool = getDefaultDatabasePool3();
275
725
  const begin = pool.begin;
276
726
  if (typeof begin !== "function") {
277
727
  throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
278
728
  }
279
- return await runWithDeferredModelEvents(async () => {
280
- return await begin(async (transaction) => {
729
+ return await settleTransaction(() => runWithDeferredModelEvents(async () => {
730
+ return await begin.bind(pool)(async (transaction) => {
281
731
  await apply(transaction);
282
- return await runWithDatabaseConnection(transaction, callback);
732
+ return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
283
733
  });
284
- });
734
+ }));
285
735
  }
286
736
  async function runWithMigrationBypass(callback) {
287
737
  return await runWithScopedTenantTransaction((transaction) => applyBypassToTransaction(transaction, true), callback);
@@ -290,7 +740,18 @@ async function runWithMigrationBypassForIdentifier(identifier, callback) {
290
740
  assertBypassIdentifier(identifier);
291
741
  return await runWithScopedTenantTransaction((transaction) => applyIdentifierToTransaction(transaction, identifier), callback);
292
742
  }
743
+ var init_databaseTenantContext = __esm(() => {
744
+ init_connectionContext();
745
+ init_transaction();
746
+ init_transactionControl();
747
+ init_deferredModelEvents();
748
+ init_tenancyConfig();
749
+ });
750
+
751
+ // entries/tenant/databaseTenantContext.ts
752
+ init_databaseTenantContext();
293
753
  export {
294
754
  runWithMigrationBypass,
295
- runWithMigrationBypassForIdentifier
755
+ runWithMigrationBypassForIdentifier,
756
+ runWithScopedTenantTransaction
296
757
  };