@getstrata/core 0.7.4 → 0.7.5
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 +5 -0
- package/README.md +3 -3
- package/dist/core/contracts/authUserDirectory.d.ts +4 -0
- package/dist/core/tenant/tenancyConfig.d.ts +4 -2
- package/dist/entries/audit/exportAuditLogs.js +22 -0
- package/dist/entries/auth/scimAuthMiddleware.js +11 -2
- package/dist/entries/jobs/exportAuditLogsJob.js +22 -0
- package/dist/entries/tenant/databaseTenantContext.js +22 -0
- package/dist/entries/tenant/tenancyConfig.js +11 -1
- package/dist/entries/tenant/tenantDatabaseScope.js +11 -2
- package/dist/framework/public-api.d.ts +1 -1
- package/dist/index.js +15 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# @getstrata/core changelog
|
|
2
2
|
|
|
3
|
+
## 0.7.5
|
|
4
|
+
|
|
5
|
+
- `TENANCY_DRIVER=column`: tenant ALS without Postgres `SET LOCAL` / `set_config`. `isRlsTenancy()` is the RLS-only check.
|
|
6
|
+
- `AuthUserRecord` may include `name` and optional MFA columns used by generated starters.
|
|
7
|
+
|
|
3
8
|
## 0.7.4
|
|
4
9
|
|
|
5
10
|
- Default database pool and query handles live on `globalThis`, so the published `@getstrata/core` bundle and `src/core` share one pool in the same process.
|
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ import { EtaViewEngine } from "@getstrata/core/view";
|
|
|
23
23
|
|
|
24
24
|
`.eta` files are **HTML + Eta tags** (`<% %>`, `<%= %>`, `<%~ include() %>`), not another template language. Class/attribute shorthand such as `section.section` or `a href=` fails at render time with the template name.
|
|
25
25
|
|
|
26
|
-
**Dependency:** `eta` is a direct dependency of `@getstrata/core`. Apps do not need to list it separately. The database **engine** is your choice. HiroApp uses **Bun's built-in `Bun.sql`** (Postgres)
|
|
26
|
+
**Dependency:** `eta` is a direct dependency of `@getstrata/core`. Apps do not need to list it separately. The database **engine** is your choice. Generated HiroApp uses **Bun's built-in `Bun.sql`** (Postgres): `createBunSqlPool()`, then `registerDefaultDatabasePool()` and `bindDatabaseConnection()`. `bindBunSql()` is a helper that registers both. Extra engines register with `registerNamedConnection` (`database/namedConnections`, `database/sqliteConnection`, `database/mysqlConnection`).
|
|
27
27
|
|
|
28
28
|
`orderBy` accepts `{ column, direction }` objects or column shorthand such as `{ published_at: "desc" }`. `{ ilike }` uses the value as-is. Pass `%term%` yourself.
|
|
29
29
|
|
|
@@ -33,7 +33,7 @@ import { EtaViewEngine } from "@getstrata/core/view";
|
|
|
33
33
|
- `createFailedJobService`, `FailedJobService.delete()`: failed job persistence and cleanup
|
|
34
34
|
- `runQueueJob`, `jobRegistry`: dispatch retried jobs from admin UIs
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
Core ships failed-job helpers and an optional admin resource registry. Generated HiroApp does not include an admin dashboard. You do not have to use the registry.
|
|
37
37
|
|
|
38
38
|
## Build and verify (monorepo root)
|
|
39
39
|
|
|
@@ -65,7 +65,7 @@ import type { Migration } from "@getstrata/core/database/migrations/types";
|
|
|
65
65
|
Package name: **`@getstrata/core`** (npm org [`@getstrata`](https://www.npmjs.com/org/getstrata)).
|
|
66
66
|
|
|
67
67
|
1. Add `NPM_TOKEN` to GitHub repository secrets.
|
|
68
|
-
2. Tag a release: `git tag v0.7.
|
|
68
|
+
2. Tag a release: `git tag v0.7.5 && git push origin v0.7.5`
|
|
69
69
|
3. [Release workflow](../../.github/workflows/release.yml) builds and runs `npm publish --access public`.
|
|
70
70
|
|
|
71
71
|
See [docs/PACKAGING.md](../../docs/PACKAGING.md).
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
import type { AuthUser } from "../auth/authContext";
|
|
2
2
|
interface AuthUserRecord {
|
|
3
3
|
id: number;
|
|
4
|
+
name?: string | null;
|
|
4
5
|
email?: string | null;
|
|
5
6
|
role: string;
|
|
6
7
|
email_verified_at?: Date | string | null;
|
|
7
8
|
session_valid_after?: Date | string | null;
|
|
9
|
+
mfa_enabled?: boolean;
|
|
10
|
+
mfa_secret?: string | null;
|
|
11
|
+
mfa_recovery_codes?: string | null;
|
|
8
12
|
}
|
|
9
13
|
interface AuthUserDirectory {
|
|
10
14
|
resolveUserFromToken(token: string): Promise<AuthUser | null>;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
type TenancyDriver = "rls" | "none";
|
|
1
|
+
type TenancyDriver = "rls" | "column" | "none";
|
|
2
2
|
declare function readTenancyDriver(env?: Record<string, string | undefined>): TenancyDriver;
|
|
3
3
|
declare function isTenancyEnabled(env?: Record<string, string | undefined>): boolean;
|
|
4
|
+
/** Postgres `SET LOCAL` / `set_config` for row-level security. SQLite and MySQL cannot do this. */
|
|
5
|
+
declare function isRlsTenancy(env?: Record<string, string | undefined>): boolean;
|
|
4
6
|
export type { TenancyDriver };
|
|
5
|
-
export { isTenancyEnabled, readTenancyDriver };
|
|
7
|
+
export { isRlsTenancy, isTenancyEnabled, readTenancyDriver };
|
|
@@ -189,7 +189,29 @@ async function safeFetch(input, init = {}, options = {}) {
|
|
|
189
189
|
|
|
190
190
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
191
191
|
import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
|
|
192
|
+
|
|
193
|
+
// ../../src/core/tenant/tenancyConfig.ts
|
|
194
|
+
function readTenancyDriver(env = process.env) {
|
|
195
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
196
|
+
return "none";
|
|
197
|
+
}
|
|
198
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
199
|
+
return "column";
|
|
200
|
+
}
|
|
201
|
+
return "rls";
|
|
202
|
+
}
|
|
203
|
+
function isTenancyEnabled(env = process.env) {
|
|
204
|
+
return readTenancyDriver(env) !== "none";
|
|
205
|
+
}
|
|
206
|
+
function isRlsTenancy(env = process.env) {
|
|
207
|
+
return readTenancyDriver(env) === "rls";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../../src/core/tenant/databaseTenantContext.ts
|
|
192
211
|
async function runWithMigrationBypass(callback) {
|
|
212
|
+
if (!isRlsTenancy()) {
|
|
213
|
+
return await callback();
|
|
214
|
+
}
|
|
193
215
|
await db`SELECT set_config('app.bypass_rls', 'true', false)`;
|
|
194
216
|
try {
|
|
195
217
|
return await callback();
|
|
@@ -79,11 +79,20 @@ import { repositoryConnection as db } from "@getstrata/core/database/repositoryC
|
|
|
79
79
|
|
|
80
80
|
// ../../src/core/tenant/tenancyConfig.ts
|
|
81
81
|
function readTenancyDriver(env = process.env) {
|
|
82
|
-
|
|
82
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
83
|
+
return "none";
|
|
84
|
+
}
|
|
85
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
86
|
+
return "column";
|
|
87
|
+
}
|
|
88
|
+
return "rls";
|
|
83
89
|
}
|
|
84
90
|
function isTenancyEnabled(env = process.env) {
|
|
85
91
|
return readTenancyDriver(env) !== "none";
|
|
86
92
|
}
|
|
93
|
+
function isRlsTenancy(env = process.env) {
|
|
94
|
+
return readTenancyDriver(env) === "rls";
|
|
95
|
+
}
|
|
87
96
|
|
|
88
97
|
// ../../src/core/tenant/resolveTenant.ts
|
|
89
98
|
async function resolveTenant(tenantId) {
|
|
@@ -123,7 +132,7 @@ async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
|
123
132
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
124
133
|
}
|
|
125
134
|
async function runWithTenantDatabase(tenant, callback) {
|
|
126
|
-
if (!isTenancyEnabled()) {
|
|
135
|
+
if (!isTenancyEnabled() || !isRlsTenancy()) {
|
|
127
136
|
return await runWithTenant(tenant, callback);
|
|
128
137
|
}
|
|
129
138
|
if (hasActiveDatabaseConnection()) {
|
|
@@ -189,7 +189,29 @@ async function safeFetch(input, init = {}, options = {}) {
|
|
|
189
189
|
|
|
190
190
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
191
191
|
import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
|
|
192
|
+
|
|
193
|
+
// ../../src/core/tenant/tenancyConfig.ts
|
|
194
|
+
function readTenancyDriver(env = process.env) {
|
|
195
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
196
|
+
return "none";
|
|
197
|
+
}
|
|
198
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
199
|
+
return "column";
|
|
200
|
+
}
|
|
201
|
+
return "rls";
|
|
202
|
+
}
|
|
203
|
+
function isTenancyEnabled(env = process.env) {
|
|
204
|
+
return readTenancyDriver(env) !== "none";
|
|
205
|
+
}
|
|
206
|
+
function isRlsTenancy(env = process.env) {
|
|
207
|
+
return readTenancyDriver(env) === "rls";
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ../../src/core/tenant/databaseTenantContext.ts
|
|
192
211
|
async function runWithMigrationBypass(callback) {
|
|
212
|
+
if (!isRlsTenancy()) {
|
|
213
|
+
return await callback();
|
|
214
|
+
}
|
|
193
215
|
await db`SELECT set_config('app.bypass_rls', 'true', false)`;
|
|
194
216
|
try {
|
|
195
217
|
return await callback();
|
|
@@ -1,7 +1,29 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
3
3
|
import { repositoryConnection as db } from "@getstrata/core/database/repositoryConnection";
|
|
4
|
+
|
|
5
|
+
// ../../src/core/tenant/tenancyConfig.ts
|
|
6
|
+
function readTenancyDriver(env = process.env) {
|
|
7
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
8
|
+
return "none";
|
|
9
|
+
}
|
|
10
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
11
|
+
return "column";
|
|
12
|
+
}
|
|
13
|
+
return "rls";
|
|
14
|
+
}
|
|
15
|
+
function isTenancyEnabled(env = process.env) {
|
|
16
|
+
return readTenancyDriver(env) !== "none";
|
|
17
|
+
}
|
|
18
|
+
function isRlsTenancy(env = process.env) {
|
|
19
|
+
return readTenancyDriver(env) === "rls";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// ../../src/core/tenant/databaseTenantContext.ts
|
|
4
23
|
async function runWithMigrationBypass(callback) {
|
|
24
|
+
if (!isRlsTenancy()) {
|
|
25
|
+
return await callback();
|
|
26
|
+
}
|
|
5
27
|
await db`SELECT set_config('app.bypass_rls', 'true', false)`;
|
|
6
28
|
try {
|
|
7
29
|
return await callback();
|
|
@@ -1,12 +1,22 @@
|
|
|
1
1
|
// @bun
|
|
2
2
|
// ../../src/core/tenant/tenancyConfig.ts
|
|
3
3
|
function readTenancyDriver(env = process.env) {
|
|
4
|
-
|
|
4
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
5
|
+
return "none";
|
|
6
|
+
}
|
|
7
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
8
|
+
return "column";
|
|
9
|
+
}
|
|
10
|
+
return "rls";
|
|
5
11
|
}
|
|
6
12
|
function isTenancyEnabled(env = process.env) {
|
|
7
13
|
return readTenancyDriver(env) !== "none";
|
|
8
14
|
}
|
|
15
|
+
function isRlsTenancy(env = process.env) {
|
|
16
|
+
return readTenancyDriver(env) === "rls";
|
|
17
|
+
}
|
|
9
18
|
export {
|
|
19
|
+
isRlsTenancy,
|
|
10
20
|
isTenancyEnabled,
|
|
11
21
|
readTenancyDriver
|
|
12
22
|
};
|
|
@@ -30,11 +30,20 @@ function hasActiveDatabaseConnection() {
|
|
|
30
30
|
|
|
31
31
|
// ../../src/core/tenant/tenancyConfig.ts
|
|
32
32
|
function readTenancyDriver(env = process.env) {
|
|
33
|
-
|
|
33
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
34
|
+
return "none";
|
|
35
|
+
}
|
|
36
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
37
|
+
return "column";
|
|
38
|
+
}
|
|
39
|
+
return "rls";
|
|
34
40
|
}
|
|
35
41
|
function isTenancyEnabled(env = process.env) {
|
|
36
42
|
return readTenancyDriver(env) !== "none";
|
|
37
43
|
}
|
|
44
|
+
function isRlsTenancy(env = process.env) {
|
|
45
|
+
return readTenancyDriver(env) === "rls";
|
|
46
|
+
}
|
|
38
47
|
|
|
39
48
|
// ../../src/core/tenant/tenantContext.ts
|
|
40
49
|
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
@@ -51,7 +60,7 @@ async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
|
51
60
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
52
61
|
}
|
|
53
62
|
async function runWithTenantDatabase(tenant, callback) {
|
|
54
|
-
if (!isTenancyEnabled()) {
|
|
63
|
+
if (!isTenancyEnabled() || !isRlsTenancy()) {
|
|
55
64
|
return await runWithTenant(tenant, callback);
|
|
56
65
|
}
|
|
57
66
|
if (hasActiveDatabaseConnection()) {
|
|
@@ -126,7 +126,7 @@ export { logSecurityEvent } from "../core/security/securityEvents.ts";
|
|
|
126
126
|
export type { StorageDriver } from "../core/storage/storage.ts";
|
|
127
127
|
export { createStorageDriver, LocalStorageDriver, resetDefaultStorage, StorageManager, } from "../core/storage/storage.ts";
|
|
128
128
|
export type { TenancyDriver } from "../core/tenant/tenancyConfig.ts";
|
|
129
|
-
export { isTenancyEnabled, readTenancyDriver } from "../core/tenant/tenancyConfig.ts";
|
|
129
|
+
export { isRlsTenancy, isTenancyEnabled, readTenancyDriver, } from "../core/tenant/tenancyConfig.ts";
|
|
130
130
|
export type { TenantContext } from "../core/tenant/tenantContext.ts";
|
|
131
131
|
export { currentTenant, currentTenantId, rateLimitMultiplierForPlan, runWithTenant, } from "../core/tenant/tenantContext.ts";
|
|
132
132
|
export { isInsideTenantDatabaseScope, runWithTenantDatabase, } from "../core/tenant/tenantDatabaseScope.ts";
|
package/dist/index.js
CHANGED
|
@@ -1157,11 +1157,20 @@ var repositoryConnection = new Proxy(function repositoryConnection2() {}, {
|
|
|
1157
1157
|
|
|
1158
1158
|
// ../../src/core/tenant/tenancyConfig.ts
|
|
1159
1159
|
function readTenancyDriver(env = process.env) {
|
|
1160
|
-
|
|
1160
|
+
if (env.TENANCY_DRIVER === "none") {
|
|
1161
|
+
return "none";
|
|
1162
|
+
}
|
|
1163
|
+
if (env.TENANCY_DRIVER === "column") {
|
|
1164
|
+
return "column";
|
|
1165
|
+
}
|
|
1166
|
+
return "rls";
|
|
1161
1167
|
}
|
|
1162
1168
|
function isTenancyEnabled(env = process.env) {
|
|
1163
1169
|
return readTenancyDriver(env) !== "none";
|
|
1164
1170
|
}
|
|
1171
|
+
function isRlsTenancy(env = process.env) {
|
|
1172
|
+
return readTenancyDriver(env) === "rls";
|
|
1173
|
+
}
|
|
1165
1174
|
|
|
1166
1175
|
// ../../src/core/tenant/resolveTenant.ts
|
|
1167
1176
|
async function resolveTenant(tenantId) {
|
|
@@ -1189,7 +1198,7 @@ async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
|
1189
1198
|
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
1190
1199
|
}
|
|
1191
1200
|
async function runWithTenantDatabase(tenant, callback) {
|
|
1192
|
-
if (!isTenancyEnabled()) {
|
|
1201
|
+
if (!isTenancyEnabled() || !isRlsTenancy()) {
|
|
1193
1202
|
return await runWithTenant(tenant, callback);
|
|
1194
1203
|
}
|
|
1195
1204
|
if (hasActiveDatabaseConnection()) {
|
|
@@ -8829,6 +8838,9 @@ import { createHash } from "crypto";
|
|
|
8829
8838
|
|
|
8830
8839
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
8831
8840
|
async function runWithMigrationBypass(callback) {
|
|
8841
|
+
if (!isRlsTenancy()) {
|
|
8842
|
+
return await callback();
|
|
8843
|
+
}
|
|
8832
8844
|
await repositoryConnection`SELECT set_config('app.bypass_rls', 'true', false)`;
|
|
8833
8845
|
try {
|
|
8834
8846
|
return await callback();
|
|
@@ -9561,6 +9573,7 @@ export {
|
|
|
9561
9573
|
isHttpErrorLike,
|
|
9562
9574
|
isInsideTenantDatabaseScope,
|
|
9563
9575
|
isPublicReadsEnabled,
|
|
9576
|
+
isRlsTenancy,
|
|
9564
9577
|
isTenancyEnabled,
|
|
9565
9578
|
jobRegistry,
|
|
9566
9579
|
jsonResponse2 as jsonResponse,
|