@getstrata/core 2.2.20 → 2.2.22
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.
- package/CHANGELOG.md +8 -0
- package/dist/entries/audit/exportAuditLogs.js +9 -6
- package/dist/entries/auth/scimAuthMiddleware.js +9 -6
- package/dist/entries/jobs/exportAuditLogsJob.js +9 -6
- package/dist/entries/queue/queueMetrics.js +9 -6
- package/dist/entries/tenant/databaseTenantContext.js +9 -6
- package/dist/entries/tenant/enableTenantRls.js +17 -17
- package/dist/entries/tenant/tenantDatabaseScope.js +9 -6
- package/dist/index.js +9 -6
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 2.2.22
|
|
4
|
+
|
|
5
|
+
Preserve caller auth, credential sources, request metadata and application async stores through cold/queued native transaction callbacks. Serialize Postgres privilege grants with full provisioning using atomic database-scoped batches, retaining delegated-admin permissions and rollback/retry safety. Add real constrained-pool JSON/HTML bearer/session-CSRF and concurrent grant/atomicity regressions; no automatic workers, auth bypass or production runtime provisioning.
|
|
6
|
+
|
|
7
|
+
## 2.2.21
|
|
8
|
+
|
|
9
|
+
Make registered queue/outbox workers discoverable in CLI help and generated startup guidance. Announce polling after boot and when it stops without claiming dependency health, printing secrets, or starting workers automatically. Preserve explicit process ownership, delivery semantics and graceful lifecycle; cover idle, boot/runtime/cleanup failures and real SIGTERM draining.
|
|
10
|
+
|
|
3
11
|
## 2.2.20
|
|
4
12
|
|
|
5
13
|
Validate complete migration definitions before callbacks, locks or database effects; reject malformed exports and duplicate names with source diagnostics. Refuse fresh with missing applied definitions and rollback with incomplete latest batches; preserve partial forward migration and remove blanket history deletion. Cover physical schema/data/history preservation and reset recovery on real Postgres, MySQL and SQLite, plus loader and preflight regressions; document safe missing-file recovery and unchanged atomicity limits.
|
|
@@ -221,6 +221,7 @@ var init_tenancyConfig = __esm(() => {
|
|
|
221
221
|
|
|
222
222
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
223
223
|
import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
|
|
224
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
224
225
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
225
226
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
226
227
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -243,12 +244,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
243
244
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
244
245
|
}
|
|
245
246
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
246
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
247
|
+
return await begin.bind(pool)(AsyncLocalStorage2.bind(async (transaction) => {
|
|
247
248
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
248
249
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
249
250
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
250
251
|
});
|
|
251
|
-
});
|
|
252
|
+
}));
|
|
252
253
|
}));
|
|
253
254
|
}
|
|
254
255
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
@@ -717,6 +718,7 @@ var init_repositoryConnection = __esm(() => {
|
|
|
717
718
|
});
|
|
718
719
|
|
|
719
720
|
// ../../src/core/database/transaction.ts
|
|
721
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
720
722
|
async function runInSavepoint(connection, operation) {
|
|
721
723
|
const inherited = transactionFrame.getStore();
|
|
722
724
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -776,9 +778,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
776
778
|
signal?.throwIfAborted();
|
|
777
779
|
if (!supportsTransactions(connection))
|
|
778
780
|
throw new Error("Reserved database connection does not support transactions.");
|
|
779
|
-
return await connection.begin(async (transaction) => {
|
|
781
|
+
return await connection.begin(AsyncLocalStorage3.bind(async (transaction) => {
|
|
780
782
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
781
|
-
});
|
|
783
|
+
}));
|
|
782
784
|
} finally {
|
|
783
785
|
await reserved?.release();
|
|
784
786
|
}
|
|
@@ -800,6 +802,7 @@ var init_transaction = __esm(() => {
|
|
|
800
802
|
|
|
801
803
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
802
804
|
import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
|
|
805
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
803
806
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
804
807
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
805
808
|
bypass ? "true" : "false"
|
|
@@ -851,10 +854,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
851
854
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
852
855
|
}
|
|
853
856
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
854
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
857
|
+
return await begin.bind(pool)(AsyncLocalStorage4.bind(async (transaction) => {
|
|
855
858
|
await apply(transaction);
|
|
856
859
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
857
|
-
});
|
|
860
|
+
}));
|
|
858
861
|
}));
|
|
859
862
|
}
|
|
860
863
|
async function runWithMigrationBypass(callback) {
|
|
@@ -673,6 +673,7 @@ var init_repositoryConnection = __esm(() => {
|
|
|
673
673
|
});
|
|
674
674
|
|
|
675
675
|
// ../../src/core/database/transaction.ts
|
|
676
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
676
677
|
async function runInSavepoint(connection, operation) {
|
|
677
678
|
const inherited = transactionFrame.getStore();
|
|
678
679
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -732,9 +733,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
732
733
|
signal?.throwIfAborted();
|
|
733
734
|
if (!supportsTransactions(connection))
|
|
734
735
|
throw new Error("Reserved database connection does not support transactions.");
|
|
735
|
-
return await connection.begin(async (transaction) => {
|
|
736
|
+
return await connection.begin(AsyncLocalStorage2.bind(async (transaction) => {
|
|
736
737
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
737
|
-
});
|
|
738
|
+
}));
|
|
738
739
|
} finally {
|
|
739
740
|
await reserved?.release();
|
|
740
741
|
}
|
|
@@ -756,6 +757,7 @@ var init_transaction = __esm(() => {
|
|
|
756
757
|
|
|
757
758
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
758
759
|
import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
|
|
760
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
759
761
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
760
762
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
761
763
|
bypass ? "true" : "false"
|
|
@@ -807,10 +809,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
807
809
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
808
810
|
}
|
|
809
811
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
810
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
812
|
+
return await begin.bind(pool)(AsyncLocalStorage3.bind(async (transaction) => {
|
|
811
813
|
await apply(transaction);
|
|
812
814
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
813
|
-
});
|
|
815
|
+
}));
|
|
814
816
|
}));
|
|
815
817
|
}
|
|
816
818
|
async function runWithMigrationBypass(callback) {
|
|
@@ -830,6 +832,7 @@ var init_databaseTenantContext = __esm(() => {
|
|
|
830
832
|
|
|
831
833
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
832
834
|
import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
|
|
835
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
833
836
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
834
837
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
835
838
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -852,12 +855,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
852
855
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
853
856
|
}
|
|
854
857
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
855
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
858
|
+
return await begin.bind(pool)(AsyncLocalStorage4.bind(async (transaction) => {
|
|
856
859
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
857
860
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
858
861
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
859
862
|
});
|
|
860
|
-
});
|
|
863
|
+
}));
|
|
861
864
|
}));
|
|
862
865
|
}
|
|
863
866
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
@@ -221,6 +221,7 @@ var init_tenancyConfig = __esm(() => {
|
|
|
221
221
|
|
|
222
222
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
223
223
|
import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
|
|
224
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
224
225
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
225
226
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
226
227
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -243,12 +244,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
243
244
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
244
245
|
}
|
|
245
246
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
246
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
247
|
+
return await begin.bind(pool)(AsyncLocalStorage2.bind(async (transaction) => {
|
|
247
248
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
248
249
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
249
250
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
250
251
|
});
|
|
251
|
-
});
|
|
252
|
+
}));
|
|
252
253
|
}));
|
|
253
254
|
}
|
|
254
255
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
@@ -717,6 +718,7 @@ var init_repositoryConnection = __esm(() => {
|
|
|
717
718
|
});
|
|
718
719
|
|
|
719
720
|
// ../../src/core/database/transaction.ts
|
|
721
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
720
722
|
async function runInSavepoint(connection, operation) {
|
|
721
723
|
const inherited = transactionFrame.getStore();
|
|
722
724
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -776,9 +778,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
776
778
|
signal?.throwIfAborted();
|
|
777
779
|
if (!supportsTransactions(connection))
|
|
778
780
|
throw new Error("Reserved database connection does not support transactions.");
|
|
779
|
-
return await connection.begin(async (transaction) => {
|
|
781
|
+
return await connection.begin(AsyncLocalStorage3.bind(async (transaction) => {
|
|
780
782
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
781
|
-
});
|
|
783
|
+
}));
|
|
782
784
|
} finally {
|
|
783
785
|
await reserved?.release();
|
|
784
786
|
}
|
|
@@ -800,6 +802,7 @@ var init_transaction = __esm(() => {
|
|
|
800
802
|
|
|
801
803
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
802
804
|
import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
|
|
805
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
803
806
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
804
807
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
805
808
|
bypass ? "true" : "false"
|
|
@@ -851,10 +854,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
851
854
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
852
855
|
}
|
|
853
856
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
854
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
857
|
+
return await begin.bind(pool)(AsyncLocalStorage4.bind(async (transaction) => {
|
|
855
858
|
await apply(transaction);
|
|
856
859
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
857
|
-
});
|
|
860
|
+
}));
|
|
858
861
|
}));
|
|
859
862
|
}
|
|
860
863
|
async function runWithMigrationBypass(callback) {
|
|
@@ -531,6 +531,7 @@ var init_tenancyConfig = __esm(() => {
|
|
|
531
531
|
|
|
532
532
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
533
533
|
import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
|
|
534
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
534
535
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
535
536
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
536
537
|
bypass ? "true" : "false"
|
|
@@ -582,10 +583,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
582
583
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
583
584
|
}
|
|
584
585
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
585
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
586
|
+
return await begin.bind(pool)(AsyncLocalStorage2.bind(async (transaction) => {
|
|
586
587
|
await apply(transaction);
|
|
587
588
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
588
|
-
});
|
|
589
|
+
}));
|
|
589
590
|
}));
|
|
590
591
|
}
|
|
591
592
|
async function runWithMigrationBypass(callback) {
|
|
@@ -605,6 +606,7 @@ var init_databaseTenantContext = __esm(() => {
|
|
|
605
606
|
|
|
606
607
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
607
608
|
import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
|
|
609
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
608
610
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
609
611
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
610
612
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -627,12 +629,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
627
629
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
628
630
|
}
|
|
629
631
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
630
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
632
|
+
return await begin.bind(pool)(AsyncLocalStorage3.bind(async (transaction) => {
|
|
631
633
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
632
634
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
633
635
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
634
636
|
});
|
|
635
|
-
});
|
|
637
|
+
}));
|
|
636
638
|
}));
|
|
637
639
|
}
|
|
638
640
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
@@ -791,6 +793,7 @@ function createDatabaseConnection(source) {
|
|
|
791
793
|
var init_connection = () => {};
|
|
792
794
|
|
|
793
795
|
// ../../src/core/database/transaction.ts
|
|
796
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
794
797
|
async function runInSavepoint(connection, operation) {
|
|
795
798
|
const inherited = transactionFrame.getStore();
|
|
796
799
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -850,9 +853,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
850
853
|
signal?.throwIfAborted();
|
|
851
854
|
if (!supportsTransactions(connection))
|
|
852
855
|
throw new Error("Reserved database connection does not support transactions.");
|
|
853
|
-
return await connection.begin(async (transaction) => {
|
|
856
|
+
return await connection.begin(AsyncLocalStorage4.bind(async (transaction) => {
|
|
854
857
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
855
|
-
});
|
|
858
|
+
}));
|
|
856
859
|
} finally {
|
|
857
860
|
await reserved?.release();
|
|
858
861
|
}
|
|
@@ -221,6 +221,7 @@ var init_tenancyConfig = __esm(() => {
|
|
|
221
221
|
|
|
222
222
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
223
223
|
import { getDefaultDatabasePool } from "@getstrata/core/database/defaultConnection";
|
|
224
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
224
225
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
225
226
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
226
227
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -243,12 +244,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
243
244
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
244
245
|
}
|
|
245
246
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
246
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
247
|
+
return await begin.bind(pool)(AsyncLocalStorage2.bind(async (transaction) => {
|
|
247
248
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
248
249
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
249
250
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
250
251
|
});
|
|
251
|
-
});
|
|
252
|
+
}));
|
|
252
253
|
}));
|
|
253
254
|
}
|
|
254
255
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
@@ -717,6 +718,7 @@ var init_repositoryConnection = __esm(() => {
|
|
|
717
718
|
});
|
|
718
719
|
|
|
719
720
|
// ../../src/core/database/transaction.ts
|
|
721
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
720
722
|
async function runInSavepoint(connection, operation) {
|
|
721
723
|
const inherited = transactionFrame.getStore();
|
|
722
724
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -776,9 +778,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
776
778
|
signal?.throwIfAborted();
|
|
777
779
|
if (!supportsTransactions(connection))
|
|
778
780
|
throw new Error("Reserved database connection does not support transactions.");
|
|
779
|
-
return await connection.begin(async (transaction) => {
|
|
781
|
+
return await connection.begin(AsyncLocalStorage3.bind(async (transaction) => {
|
|
780
782
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
781
|
-
});
|
|
783
|
+
}));
|
|
782
784
|
} finally {
|
|
783
785
|
await reserved?.release();
|
|
784
786
|
}
|
|
@@ -800,6 +802,7 @@ var init_transaction = __esm(() => {
|
|
|
800
802
|
|
|
801
803
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
802
804
|
import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
|
|
805
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
803
806
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
804
807
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
805
808
|
bypass ? "true" : "false"
|
|
@@ -851,10 +854,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
851
854
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
852
855
|
}
|
|
853
856
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
854
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
857
|
+
return await begin.bind(pool)(AsyncLocalStorage4.bind(async (transaction) => {
|
|
855
858
|
await apply(transaction);
|
|
856
859
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
857
|
-
});
|
|
860
|
+
}));
|
|
858
861
|
}));
|
|
859
862
|
}
|
|
860
863
|
async function runWithMigrationBypass(callback) {
|
|
@@ -191,15 +191,11 @@ BEGIN
|
|
|
191
191
|
END
|
|
192
192
|
$$`;
|
|
193
193
|
}
|
|
194
|
-
|
|
194
|
+
var POSTGRES_PRIVILEGE_LOCK_SQL = "SELECT pg_advisory_xact_lock(hashtext('strata:postgres-privileges'), hashtext(current_database()))";
|
|
195
|
+
function postgresAppRolePrivilegeSql(options) {
|
|
195
196
|
const database = assertSafeSqlIdentifier(options.database, "database name");
|
|
196
197
|
const role = assertSafeSqlIdentifier(options.role ?? POSTGRES_APP_ROLE, "role name");
|
|
197
|
-
return
|
|
198
|
-
-- Superuser remains for CREATE ROLE / CREATE DATABASE / GRANT / migrate / migrate:fresh DROP.
|
|
199
|
-
-- This script is repeatable on an already-existing volume (not only docker-entrypoint-initdb.d).
|
|
200
|
-
${postgresAppRoleCreateSql(options)};
|
|
201
|
-
|
|
202
|
-
GRANT CONNECT ON DATABASE ${database} TO ${role};
|
|
198
|
+
return `GRANT CONNECT ON DATABASE ${database} TO ${role};
|
|
203
199
|
GRANT USAGE, CREATE ON SCHEMA public TO ${role};
|
|
204
200
|
GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${role};
|
|
205
201
|
GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${role};
|
|
@@ -209,17 +205,21 @@ ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${role};
|
|
|
209
205
|
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO ${role};
|
|
210
206
|
`;
|
|
211
207
|
}
|
|
208
|
+
function postgresAppRoleSql(options) {
|
|
209
|
+
const privileges = postgresAppRolePrivilegeSql(options);
|
|
210
|
+
return `-- Application login role. FORCE RLS applies because this role is NOSUPERUSER and NOBYPASSRLS.
|
|
211
|
+
-- Superuser remains for CREATE ROLE / CREATE DATABASE / GRANT / migrate / migrate:fresh DROP.
|
|
212
|
+
-- This script is repeatable on an already-existing volume (not only docker-entrypoint-initdb.d).
|
|
213
|
+
${POSTGRES_PRIVILEGE_LOCK_SQL};
|
|
214
|
+
${postgresAppRoleCreateSql(options)};
|
|
215
|
+
|
|
216
|
+
${privileges}
|
|
217
|
+
`;
|
|
218
|
+
}
|
|
212
219
|
async function grantPostgresAppRolePrivileges(sql, options) {
|
|
213
|
-
const
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
await sql.unsafe(`GRANT USAGE, CREATE ON SCHEMA public TO ${role}`);
|
|
217
|
-
await sql.unsafe(`GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO ${role}`);
|
|
218
|
-
await sql.unsafe(`GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA public TO ${role}`);
|
|
219
|
-
await sql.unsafe(`GRANT EXECUTE ON ALL FUNCTIONS IN SCHEMA public TO ${role}`);
|
|
220
|
-
await sql.unsafe(`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO ${role}`);
|
|
221
|
-
await sql.unsafe(`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON SEQUENCES TO ${role}`);
|
|
222
|
-
await sql.unsafe(`ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT EXECUTE ON FUNCTIONS TO ${role}`);
|
|
220
|
+
const privileges = postgresAppRolePrivilegeSql(options);
|
|
221
|
+
await sql.unsafe(`${POSTGRES_PRIVILEGE_LOCK_SQL};
|
|
222
|
+
${privileges}`);
|
|
223
223
|
}
|
|
224
224
|
async function ensurePostgresAppRole(sql, options) {
|
|
225
225
|
await sql.unsafe(postgresAppRoleSql(options));
|
|
@@ -651,6 +651,7 @@ var init_repositoryConnection = __esm(() => {
|
|
|
651
651
|
});
|
|
652
652
|
|
|
653
653
|
// ../../src/core/database/transaction.ts
|
|
654
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
654
655
|
async function runInSavepoint(connection, operation) {
|
|
655
656
|
const inherited = transactionFrame.getStore();
|
|
656
657
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -710,9 +711,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
710
711
|
signal?.throwIfAborted();
|
|
711
712
|
if (!supportsTransactions(connection))
|
|
712
713
|
throw new Error("Reserved database connection does not support transactions.");
|
|
713
|
-
return await connection.begin(async (transaction) => {
|
|
714
|
+
return await connection.begin(AsyncLocalStorage2.bind(async (transaction) => {
|
|
714
715
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
715
|
-
});
|
|
716
|
+
}));
|
|
716
717
|
} finally {
|
|
717
718
|
await reserved?.release();
|
|
718
719
|
}
|
|
@@ -756,6 +757,7 @@ var init_tenancyConfig = __esm(() => {
|
|
|
756
757
|
|
|
757
758
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
758
759
|
import { getDefaultDatabasePool as getDefaultDatabasePool2 } from "@getstrata/core/database/defaultConnection";
|
|
760
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
759
761
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
760
762
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
761
763
|
bypass ? "true" : "false"
|
|
@@ -807,10 +809,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
807
809
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
808
810
|
}
|
|
809
811
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
810
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
812
|
+
return await begin.bind(pool)(AsyncLocalStorage3.bind(async (transaction) => {
|
|
811
813
|
await apply(transaction);
|
|
812
814
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
813
|
-
});
|
|
815
|
+
}));
|
|
814
816
|
}));
|
|
815
817
|
}
|
|
816
818
|
async function runWithMigrationBypass(callback) {
|
|
@@ -830,6 +832,7 @@ var init_databaseTenantContext = __esm(() => {
|
|
|
830
832
|
|
|
831
833
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
832
834
|
import { getDefaultDatabasePool as getDefaultDatabasePool3 } from "@getstrata/core/database/defaultConnection";
|
|
835
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
833
836
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
834
837
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
835
838
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -852,12 +855,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
852
855
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
853
856
|
}
|
|
854
857
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
855
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
858
|
+
return await begin.bind(pool)(AsyncLocalStorage4.bind(async (transaction) => {
|
|
856
859
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
857
860
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
858
861
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
859
862
|
});
|
|
860
|
-
});
|
|
863
|
+
}));
|
|
861
864
|
}));
|
|
862
865
|
}
|
|
863
866
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
package/dist/index.js
CHANGED
|
@@ -807,6 +807,7 @@ function createDatabaseConnection(source) {
|
|
|
807
807
|
var init_connection = () => {};
|
|
808
808
|
|
|
809
809
|
// ../../src/core/database/transaction.ts
|
|
810
|
+
import { AsyncLocalStorage as AsyncLocalStorage2 } from "async_hooks";
|
|
810
811
|
async function runInSavepoint(connection, operation) {
|
|
811
812
|
const inherited = transactionFrame.getStore();
|
|
812
813
|
let parent = inherited?.connection === connection ? inherited : outerFrames.get(connection);
|
|
@@ -866,9 +867,9 @@ async function runInTransaction(operation, options = {}) {
|
|
|
866
867
|
signal?.throwIfAborted();
|
|
867
868
|
if (!supportsTransactions(connection))
|
|
868
869
|
throw new Error("Reserved database connection does not support transactions.");
|
|
869
|
-
return await connection.begin(async (transaction) => {
|
|
870
|
+
return await connection.begin(AsyncLocalStorage2.bind(async (transaction) => {
|
|
870
871
|
return await runWithDatabaseConnection(transaction, () => transactionFrame.run({ connection: transaction, childActive: false }, () => runWithTransactionScope(async () => commitOrRollbackScope(await admittedOperation(createDatabaseConnection(transaction))))));
|
|
871
|
-
});
|
|
872
|
+
}));
|
|
872
873
|
} finally {
|
|
873
874
|
await reserved?.release();
|
|
874
875
|
}
|
|
@@ -889,6 +890,7 @@ var init_transaction = __esm(() => {
|
|
|
889
890
|
});
|
|
890
891
|
|
|
891
892
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
893
|
+
import { AsyncLocalStorage as AsyncLocalStorage3 } from "async_hooks";
|
|
892
894
|
async function applyBypassToTransaction(transaction, bypass) {
|
|
893
895
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, [
|
|
894
896
|
bypass ? "true" : "false"
|
|
@@ -940,10 +942,10 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
940
942
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
941
943
|
}
|
|
942
944
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
943
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
945
|
+
return await begin.bind(pool)(AsyncLocalStorage3.bind(async (transaction) => {
|
|
944
946
|
await apply(transaction);
|
|
945
947
|
return await runWithDatabaseConnection(transaction, () => runWithTransactionScope(async () => commitOrRollbackScope(await callback())));
|
|
946
|
-
});
|
|
948
|
+
}));
|
|
947
949
|
}));
|
|
948
950
|
}
|
|
949
951
|
async function runWithMigrationBypass(callback) {
|
|
@@ -963,6 +965,7 @@ var init_databaseTenantContext = __esm(() => {
|
|
|
963
965
|
});
|
|
964
966
|
|
|
965
967
|
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
968
|
+
import { AsyncLocalStorage as AsyncLocalStorage4 } from "async_hooks";
|
|
966
969
|
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
967
970
|
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
968
971
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
@@ -985,12 +988,12 @@ async function runWithTenantDatabase(tenant, callback) {
|
|
|
985
988
|
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
986
989
|
}
|
|
987
990
|
return await settleTransaction(() => runWithDeferredModelEvents(async () => {
|
|
988
|
-
return await begin.bind(pool)(async (transaction) => {
|
|
991
|
+
return await begin.bind(pool)(AsyncLocalStorage4.bind(async (transaction) => {
|
|
989
992
|
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
990
993
|
return await runWithDatabaseConnection(transaction, async () => {
|
|
991
994
|
return await runWithTransactionScope(async () => commitOrRollbackScope(await runWithTenant(tenant, callback)));
|
|
992
995
|
});
|
|
993
|
-
});
|
|
996
|
+
}));
|
|
994
997
|
}));
|
|
995
998
|
}
|
|
996
999
|
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|