@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 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). Create and bind it with `createBunSqlPool()` / `bindBunSql()`, or call `bindDatabaseConnection()` yourself. Extra engines register with `registerNamedConnection` (`database/namedConnections`, `database/sqliteConnection`, `database/mysqlConnection`).
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
- HiroApp uses failed-job recovery in the staff dashboard. You do not have to use the admin registry.
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.4 && git push origin v0.7.4`
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
- return env.TENANCY_DRIVER === "none" ? "none" : "rls";
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
- return env.TENANCY_DRIVER === "none" ? "none" : "rls";
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
- return env.TENANCY_DRIVER === "none" ? "none" : "rls";
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
- return env.TENANCY_DRIVER === "none" ? "none" : "rls";
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,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@getstrata/core",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "Strata Bun framework public API",
5
5
  "type": "module",
6
6
  "license": "MIT",