@getstrata/core 1.1.8 → 1.1.9
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/core/events/deferredModelEvents.d.ts +3 -0
- package/dist/core/events/index.d.ts +1 -0
- package/dist/entries/audit/exportAuditLogs.js +247 -53
- package/dist/entries/auth/accessControl.js +16 -2
- package/dist/entries/auth/basicAuthGuard.js +47 -26
- package/dist/entries/auth/membershipMiddleware.js +17 -0
- package/dist/entries/auth/membershipScope.js +21 -6
- package/dist/entries/auth/membershipService.js +20 -5
- package/dist/entries/auth/oauth/samlProvider.js +47 -26
- package/dist/entries/auth/oneTimeToken.js +18 -2
- package/dist/entries/auth/passwordLogin.js +29 -13
- package/dist/entries/auth/policy.js +16 -2
- package/dist/entries/auth/saml/samlServiceProvider.js +47 -26
- package/dist/entries/auth/scimAuthMiddleware.js +217 -63
- package/dist/entries/database/migrations.js +20 -4
- package/dist/entries/http/csrfMiddleware.js +28 -14
- package/dist/entries/http/csrfToken.js +28 -14
- package/dist/entries/http/flashMiddleware.js +28 -14
- package/dist/entries/jobs/exportAuditLogsJob.js +247 -53
- package/dist/entries/runtime/asyncContextStore.js +16 -0
- package/dist/entries/tenant/databaseTenantContext.js +203 -9
- package/dist/entries/tenant/tenancyConfig.js +19 -1
- package/dist/entries/tenant/tenantDatabaseScope.js +173 -17
- package/dist/entries/tracing/tracingMiddleware.js +28 -14
- package/dist/framework/public-api.d.ts +1 -1
- package/dist/index.js +406 -210
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,4 +1,338 @@
|
|
|
1
1
|
// @bun
|
|
2
|
+
var __esm = (fn, res, err) => () => {
|
|
3
|
+
if (fn)
|
|
4
|
+
try {
|
|
5
|
+
res = fn(fn = 0);
|
|
6
|
+
} catch (e) {
|
|
7
|
+
err = [e];
|
|
8
|
+
}
|
|
9
|
+
if (err)
|
|
10
|
+
throw err[0];
|
|
11
|
+
return res;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
// ../../src/core/runtime/asyncContextStore.ts
|
|
15
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
16
|
+
function createAsyncContextStore(key) {
|
|
17
|
+
const symbol = Symbol.for(key);
|
|
18
|
+
const globalRecord = globalThis;
|
|
19
|
+
const existing = globalRecord[symbol];
|
|
20
|
+
if (existing) {
|
|
21
|
+
return existing;
|
|
22
|
+
}
|
|
23
|
+
const store = new AsyncLocalStorage;
|
|
24
|
+
globalRecord[symbol] = store;
|
|
25
|
+
return store;
|
|
26
|
+
}
|
|
27
|
+
var init_asyncContextStore = () => {};
|
|
28
|
+
|
|
29
|
+
// ../../src/core/database/connectionContext.ts
|
|
30
|
+
function runWithDatabaseConnection(connection, callback) {
|
|
31
|
+
return activeConnection.run(connection, callback);
|
|
32
|
+
}
|
|
33
|
+
function getActiveDatabaseConnection(fallback) {
|
|
34
|
+
return activeConnection.getStore() ?? fallback;
|
|
35
|
+
}
|
|
36
|
+
function hasActiveDatabaseConnection() {
|
|
37
|
+
return activeConnection.getStore() !== undefined;
|
|
38
|
+
}
|
|
39
|
+
var activeConnection;
|
|
40
|
+
var init_connectionContext = __esm(() => {
|
|
41
|
+
init_asyncContextStore();
|
|
42
|
+
activeConnection = createAsyncContextStore("@getstrata/databaseConnectionContext");
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
// ../../src/core/database/queryProxy.ts
|
|
46
|
+
function createDatabaseQueryProxy(pool) {
|
|
47
|
+
function resolveDatabase() {
|
|
48
|
+
return getActiveDatabaseConnection(pool);
|
|
49
|
+
}
|
|
50
|
+
function resolveDatabaseForProperty(property) {
|
|
51
|
+
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
52
|
+
return pool;
|
|
53
|
+
}
|
|
54
|
+
return resolveDatabase();
|
|
55
|
+
}
|
|
56
|
+
return new Proxy(function database() {}, {
|
|
57
|
+
apply(_target, _thisArg, args) {
|
|
58
|
+
return resolveDatabase()(...args);
|
|
59
|
+
},
|
|
60
|
+
get(_target, property) {
|
|
61
|
+
const connection = resolveDatabaseForProperty(property);
|
|
62
|
+
const value = connection[property];
|
|
63
|
+
return typeof value === "function" ? value.bind(connection) : value;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
var POOL_CONNECTION_METHODS;
|
|
68
|
+
var init_queryProxy = __esm(() => {
|
|
69
|
+
init_connectionContext();
|
|
70
|
+
POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// ../../src/core/database/defaultConnection.ts
|
|
74
|
+
function defaultConnectionState() {
|
|
75
|
+
const globalRecord = globalThis;
|
|
76
|
+
const existing = globalRecord[DEFAULT_CONNECTION_KEY];
|
|
77
|
+
if (existing) {
|
|
78
|
+
return existing;
|
|
79
|
+
}
|
|
80
|
+
const created = { pool: null, query: null };
|
|
81
|
+
globalRecord[DEFAULT_CONNECTION_KEY] = created;
|
|
82
|
+
return created;
|
|
83
|
+
}
|
|
84
|
+
function registerDefaultDatabasePool(connection) {
|
|
85
|
+
const holder = defaultConnectionState();
|
|
86
|
+
holder.pool = connection;
|
|
87
|
+
holder.query = createDatabaseQueryProxy(connection);
|
|
88
|
+
}
|
|
89
|
+
function getDefaultDatabasePool() {
|
|
90
|
+
const pool = defaultConnectionState().pool;
|
|
91
|
+
if (!pool) {
|
|
92
|
+
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
93
|
+
}
|
|
94
|
+
return pool;
|
|
95
|
+
}
|
|
96
|
+
function getDefaultDatabaseQuery() {
|
|
97
|
+
const query = defaultConnectionState().query;
|
|
98
|
+
if (!query) {
|
|
99
|
+
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
100
|
+
}
|
|
101
|
+
return query;
|
|
102
|
+
}
|
|
103
|
+
function resetDefaultDatabasePoolForTests() {
|
|
104
|
+
const holder = defaultConnectionState();
|
|
105
|
+
holder.pool = null;
|
|
106
|
+
holder.query = null;
|
|
107
|
+
}
|
|
108
|
+
var DEFAULT_CONNECTION_KEY;
|
|
109
|
+
var init_defaultConnection = __esm(() => {
|
|
110
|
+
init_queryProxy();
|
|
111
|
+
DEFAULT_CONNECTION_KEY = Symbol.for("@getstrata/defaultDatabaseConnection");
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
// ../../src/core/tenant/tenantContext.ts
|
|
115
|
+
function runWithTenant(tenant, callback) {
|
|
116
|
+
return tenantContext.run(tenant, callback);
|
|
117
|
+
}
|
|
118
|
+
function currentTenant() {
|
|
119
|
+
return tenantContext.getStore() ?? null;
|
|
120
|
+
}
|
|
121
|
+
function currentTenantId() {
|
|
122
|
+
const tenant = currentTenant();
|
|
123
|
+
if (!tenant) {
|
|
124
|
+
throw new Error("Tenant context is required.");
|
|
125
|
+
}
|
|
126
|
+
return tenant.id;
|
|
127
|
+
}
|
|
128
|
+
function rateLimitMultiplierForPlan(plan) {
|
|
129
|
+
switch (plan) {
|
|
130
|
+
case "enterprise":
|
|
131
|
+
return 4;
|
|
132
|
+
case "pro":
|
|
133
|
+
return 2;
|
|
134
|
+
default:
|
|
135
|
+
return 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
var tenantContext;
|
|
139
|
+
var init_tenantContext = __esm(() => {
|
|
140
|
+
init_asyncContextStore();
|
|
141
|
+
tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// ../../src/core/tenant/tenancyConfig.ts
|
|
145
|
+
function readTenancyDriver(env = process.env) {
|
|
146
|
+
const raw = env.TENANCY_DRIVER?.trim();
|
|
147
|
+
if (raw === undefined || raw === "") {
|
|
148
|
+
return "rls";
|
|
149
|
+
}
|
|
150
|
+
if (TENANCY_DRIVERS.includes(raw)) {
|
|
151
|
+
return raw;
|
|
152
|
+
}
|
|
153
|
+
throw new Error(`TENANCY_DRIVER must be one of ${TENANCY_DRIVERS.join(", ")}; received "${raw}".`);
|
|
154
|
+
}
|
|
155
|
+
function isTenancyEnabled(env = process.env) {
|
|
156
|
+
return readTenancyDriver(env) !== "none";
|
|
157
|
+
}
|
|
158
|
+
function isRlsTenancy(env = process.env) {
|
|
159
|
+
return readTenancyDriver(env) === "rls";
|
|
160
|
+
}
|
|
161
|
+
var TENANCY_DRIVERS;
|
|
162
|
+
var init_tenancyConfig = __esm(() => {
|
|
163
|
+
TENANCY_DRIVERS = ["rls", "column", "none"];
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// ../../src/core/events/eventBus.ts
|
|
167
|
+
class EventBus {
|
|
168
|
+
constructor() {}
|
|
169
|
+
listeners = new Map;
|
|
170
|
+
on(event, listener) {
|
|
171
|
+
return this.listen(event, listener);
|
|
172
|
+
}
|
|
173
|
+
listen(event, listener) {
|
|
174
|
+
const handlers = this.listeners.get(event) ?? new Set;
|
|
175
|
+
handlers.add(listener);
|
|
176
|
+
this.listeners.set(event, handlers);
|
|
177
|
+
return () => {
|
|
178
|
+
handlers.delete(listener);
|
|
179
|
+
if (handlers.size === 0) {
|
|
180
|
+
this.listeners.delete(event);
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
async emit(event, payload) {
|
|
185
|
+
await this.dispatch(event, payload);
|
|
186
|
+
}
|
|
187
|
+
async dispatch(event, payload) {
|
|
188
|
+
const handlers = this.listeners.get(event);
|
|
189
|
+
if (!handlers || handlers.size === 0) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
for (const handler of handlers) {
|
|
193
|
+
await handler(payload);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
function readSharedEventBus() {
|
|
198
|
+
const globalBus = globalThis[EVENT_BUS_KEY];
|
|
199
|
+
if (globalBus) {
|
|
200
|
+
return globalBus;
|
|
201
|
+
}
|
|
202
|
+
const bus = new EventBus;
|
|
203
|
+
globalThis[EVENT_BUS_KEY] = bus;
|
|
204
|
+
return bus;
|
|
205
|
+
}
|
|
206
|
+
var EVENT_BUS_KEY, eventBus;
|
|
207
|
+
var init_eventBus = __esm(() => {
|
|
208
|
+
EVENT_BUS_KEY = Symbol.for("@getstrata/eventBus");
|
|
209
|
+
eventBus = readSharedEventBus();
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
// ../../src/core/events/deferredModelEvents.ts
|
|
213
|
+
function sameTenant(left, right) {
|
|
214
|
+
if (left === null && right === null) {
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
if (left === null || right === null) {
|
|
218
|
+
return false;
|
|
219
|
+
}
|
|
220
|
+
return left.id === right.id;
|
|
221
|
+
}
|
|
222
|
+
function groupQueuedModelEvents(queued) {
|
|
223
|
+
const groups = [];
|
|
224
|
+
for (const item of queued) {
|
|
225
|
+
const lastGroup = groups[groups.length - 1];
|
|
226
|
+
const last = lastGroup?.[lastGroup.length - 1];
|
|
227
|
+
if (!lastGroup || !last || !sameTenant(last.tenant, item.tenant)) {
|
|
228
|
+
groups.push([item]);
|
|
229
|
+
} else {
|
|
230
|
+
lastGroup.push(item);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return groups;
|
|
234
|
+
}
|
|
235
|
+
async function dispatchEventBatch(batch) {
|
|
236
|
+
for (const item of batch) {
|
|
237
|
+
await eventBus.dispatch(item.event, item.payload);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
async function dispatchQueuedModelEvents(queued) {
|
|
241
|
+
if (queued.length === 0) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
await Promise.resolve().then(() => init_tenantDatabaseScope());
|
|
245
|
+
for (const batch of groupQueuedModelEvents(queued)) {
|
|
246
|
+
const tenant = batch[0]?.tenant ?? null;
|
|
247
|
+
if (tenant) {
|
|
248
|
+
await runWithTenantDatabase(tenant, () => dispatchEventBatch(batch));
|
|
249
|
+
} else {
|
|
250
|
+
await dispatchEventBatch(batch);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
async function dispatchModelEvent(event, payload) {
|
|
255
|
+
const state = deferredModelEvents.getStore();
|
|
256
|
+
const frame = state?.frames[state.frames.length - 1];
|
|
257
|
+
if (frame) {
|
|
258
|
+
frame.push({
|
|
259
|
+
event,
|
|
260
|
+
payload,
|
|
261
|
+
tenant: currentTenant()
|
|
262
|
+
});
|
|
263
|
+
return;
|
|
264
|
+
}
|
|
265
|
+
await eventBus.dispatch(event, payload);
|
|
266
|
+
}
|
|
267
|
+
async function runWithDeferredModelEvents(callback) {
|
|
268
|
+
const existing = deferredModelEvents.getStore();
|
|
269
|
+
if (existing) {
|
|
270
|
+
existing.frames.push([]);
|
|
271
|
+
try {
|
|
272
|
+
const result = await callback();
|
|
273
|
+
const nested = existing.frames.pop() ?? [];
|
|
274
|
+
const parent = existing.frames[existing.frames.length - 1];
|
|
275
|
+
if (parent) {
|
|
276
|
+
parent.push(...nested);
|
|
277
|
+
}
|
|
278
|
+
return result;
|
|
279
|
+
} catch (error) {
|
|
280
|
+
existing.frames.pop();
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const state = { frames: [[]] };
|
|
285
|
+
const result = await deferredModelEvents.run(state, callback);
|
|
286
|
+
await dispatchQueuedModelEvents(state.frames[0] ?? []);
|
|
287
|
+
return result;
|
|
288
|
+
}
|
|
289
|
+
var deferredModelEvents;
|
|
290
|
+
var init_deferredModelEvents = __esm(() => {
|
|
291
|
+
init_asyncContextStore();
|
|
292
|
+
init_tenantContext();
|
|
293
|
+
init_eventBus();
|
|
294
|
+
deferredModelEvents = createAsyncContextStore("@getstrata/deferredModelEvents");
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
298
|
+
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
299
|
+
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
300
|
+
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
301
|
+
}
|
|
302
|
+
async function runWithTenantDatabase(tenant, callback) {
|
|
303
|
+
if (!isTenancyEnabled() || !isRlsTenancy()) {
|
|
304
|
+
return await runWithTenant(tenant, callback);
|
|
305
|
+
}
|
|
306
|
+
if (hasActiveDatabaseConnection()) {
|
|
307
|
+
const activeConnection = getActiveDatabaseConnection(getDefaultDatabasePool());
|
|
308
|
+
await applyTenantContextToTransaction(activeConnection, tenant.id);
|
|
309
|
+
return await runWithTenant(tenant, callback);
|
|
310
|
+
}
|
|
311
|
+
const pool = getDefaultDatabasePool();
|
|
312
|
+
const begin = pool.begin;
|
|
313
|
+
if (typeof begin !== "function") {
|
|
314
|
+
throw new Error("RLS tenant scope requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
315
|
+
}
|
|
316
|
+
return await runWithDeferredModelEvents(async () => {
|
|
317
|
+
return await begin(async (transaction) => {
|
|
318
|
+
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
319
|
+
return await runWithDatabaseConnection(transaction, async () => {
|
|
320
|
+
return await runWithTenant(tenant, callback);
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
326
|
+
return hasActiveDatabaseConnection() && currentTenant()?.id === tenantId;
|
|
327
|
+
}
|
|
328
|
+
var init_tenantDatabaseScope = __esm(() => {
|
|
329
|
+
init_defaultConnection();
|
|
330
|
+
init_connectionContext();
|
|
331
|
+
init_deferredModelEvents();
|
|
332
|
+
init_tenancyConfig();
|
|
333
|
+
init_tenantContext();
|
|
334
|
+
});
|
|
335
|
+
|
|
2
336
|
// ../../src/core/admin/formatValue.ts
|
|
3
337
|
function formatAdminValue(value, type = "text") {
|
|
4
338
|
if (value === null || value === undefined) {
|
|
@@ -143,21 +477,8 @@ function toHttpError(error) {
|
|
|
143
477
|
return new HttpError(error.status, error.message, error.details);
|
|
144
478
|
}
|
|
145
479
|
|
|
146
|
-
// ../../src/core/runtime/asyncContextStore.ts
|
|
147
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
148
|
-
function createAsyncContextStore(key) {
|
|
149
|
-
const symbol = Symbol.for(key);
|
|
150
|
-
const globalRecord = globalThis;
|
|
151
|
-
const existing = globalRecord[symbol];
|
|
152
|
-
if (existing) {
|
|
153
|
-
return existing;
|
|
154
|
-
}
|
|
155
|
-
const store = new AsyncLocalStorage;
|
|
156
|
-
globalRecord[symbol] = store;
|
|
157
|
-
return store;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
480
|
// ../../src/core/auth/authContext.ts
|
|
481
|
+
init_asyncContextStore();
|
|
161
482
|
var authContext = createAsyncContextStore("@getstrata/authContext");
|
|
162
483
|
var credentialSourceContext = createAsyncContextStore("@getstrata/credentialSource");
|
|
163
484
|
function runWithAuthUser(user, callback) {
|
|
@@ -246,78 +567,8 @@ function resetBoundDatabaseConnection() {
|
|
|
246
567
|
boundConnectionState().connection = null;
|
|
247
568
|
}
|
|
248
569
|
|
|
249
|
-
// ../../src/core/
|
|
250
|
-
|
|
251
|
-
function runWithDatabaseConnection(connection, callback) {
|
|
252
|
-
return activeConnection.run(connection, callback);
|
|
253
|
-
}
|
|
254
|
-
function getActiveDatabaseConnection(fallback) {
|
|
255
|
-
return activeConnection.getStore() ?? fallback;
|
|
256
|
-
}
|
|
257
|
-
function hasActiveDatabaseConnection() {
|
|
258
|
-
return activeConnection.getStore() !== undefined;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ../../src/core/database/queryProxy.ts
|
|
262
|
-
var POOL_CONNECTION_METHODS = new Set(["begin", "close", "connect"]);
|
|
263
|
-
function createDatabaseQueryProxy(pool) {
|
|
264
|
-
function resolveDatabase() {
|
|
265
|
-
return getActiveDatabaseConnection(pool);
|
|
266
|
-
}
|
|
267
|
-
function resolveDatabaseForProperty(property) {
|
|
268
|
-
if (typeof property === "string" && POOL_CONNECTION_METHODS.has(property)) {
|
|
269
|
-
return pool;
|
|
270
|
-
}
|
|
271
|
-
return resolveDatabase();
|
|
272
|
-
}
|
|
273
|
-
return new Proxy(function database() {}, {
|
|
274
|
-
apply(_target, _thisArg, args) {
|
|
275
|
-
return resolveDatabase()(...args);
|
|
276
|
-
},
|
|
277
|
-
get(_target, property) {
|
|
278
|
-
const connection = resolveDatabaseForProperty(property);
|
|
279
|
-
const value = connection[property];
|
|
280
|
-
return typeof value === "function" ? value.bind(connection) : value;
|
|
281
|
-
}
|
|
282
|
-
});
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
// ../../src/core/database/defaultConnection.ts
|
|
286
|
-
var DEFAULT_CONNECTION_KEY = Symbol.for("@getstrata/defaultDatabaseConnection");
|
|
287
|
-
function defaultConnectionState() {
|
|
288
|
-
const globalRecord = globalThis;
|
|
289
|
-
const existing = globalRecord[DEFAULT_CONNECTION_KEY];
|
|
290
|
-
if (existing) {
|
|
291
|
-
return existing;
|
|
292
|
-
}
|
|
293
|
-
const created = { pool: null, query: null };
|
|
294
|
-
globalRecord[DEFAULT_CONNECTION_KEY] = created;
|
|
295
|
-
return created;
|
|
296
|
-
}
|
|
297
|
-
function registerDefaultDatabasePool(connection) {
|
|
298
|
-
const holder = defaultConnectionState();
|
|
299
|
-
holder.pool = connection;
|
|
300
|
-
holder.query = createDatabaseQueryProxy(connection);
|
|
301
|
-
}
|
|
302
|
-
function getDefaultDatabasePool() {
|
|
303
|
-
const pool = defaultConnectionState().pool;
|
|
304
|
-
if (!pool) {
|
|
305
|
-
throw new Error("Default database pool is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
306
|
-
}
|
|
307
|
-
return pool;
|
|
308
|
-
}
|
|
309
|
-
function getDefaultDatabaseQuery() {
|
|
310
|
-
const query = defaultConnectionState().query;
|
|
311
|
-
if (!query) {
|
|
312
|
-
throw new Error("Default database query handle is not registered. Call registerDefaultDatabasePool() during app bootstrap.");
|
|
313
|
-
}
|
|
314
|
-
return query;
|
|
315
|
-
}
|
|
316
|
-
function resetDefaultDatabasePoolForTests() {
|
|
317
|
-
const holder = defaultConnectionState();
|
|
318
|
-
holder.pool = null;
|
|
319
|
-
holder.query = null;
|
|
320
|
-
}
|
|
570
|
+
// ../../src/core/auth/basicAuthGuard.ts
|
|
571
|
+
init_defaultConnection();
|
|
321
572
|
|
|
322
573
|
// ../../src/core/http/statelessAuth.ts
|
|
323
574
|
function authorizationScheme(request) {
|
|
@@ -432,6 +683,9 @@ function revealMfaSecret(stored) {
|
|
|
432
683
|
return decryptField(stored, key);
|
|
433
684
|
}
|
|
434
685
|
|
|
686
|
+
// ../../src/core/database/dialect.ts
|
|
687
|
+
init_asyncContextStore();
|
|
688
|
+
|
|
435
689
|
// ../../src/core/database/schema/driver.ts
|
|
436
690
|
function normalizeConnectionName(connection) {
|
|
437
691
|
const normalized = connection.trim().toLowerCase();
|
|
@@ -1240,6 +1494,7 @@ function isBearerCapableGuard(guard) {
|
|
|
1240
1494
|
return guard instanceof ApiTokenGuard || guard instanceof DatabaseTokenGuard || guard instanceof JwtGuard;
|
|
1241
1495
|
}
|
|
1242
1496
|
// ../../src/core/auth/membershipContext.ts
|
|
1497
|
+
init_asyncContextStore();
|
|
1243
1498
|
var membershipContext = createAsyncContextStore("@getstrata/membershipContext");
|
|
1244
1499
|
var uninitializedMembershipLookup = {
|
|
1245
1500
|
async listForUser() {
|
|
@@ -1315,33 +1570,8 @@ function createMembershipContextMiddleware() {
|
|
|
1315
1570
|
function createMembershipMiddleware() {
|
|
1316
1571
|
return createMembershipContextMiddleware();
|
|
1317
1572
|
}
|
|
1318
|
-
// ../../src/core/tenant/tenantContext.ts
|
|
1319
|
-
var tenantContext = createAsyncContextStore("@getstrata/tenantContext");
|
|
1320
|
-
function runWithTenant(tenant, callback) {
|
|
1321
|
-
return tenantContext.run(tenant, callback);
|
|
1322
|
-
}
|
|
1323
|
-
function currentTenant() {
|
|
1324
|
-
return tenantContext.getStore() ?? null;
|
|
1325
|
-
}
|
|
1326
|
-
function currentTenantId() {
|
|
1327
|
-
const tenant = currentTenant();
|
|
1328
|
-
if (!tenant) {
|
|
1329
|
-
throw new Error("Tenant context is required.");
|
|
1330
|
-
}
|
|
1331
|
-
return tenant.id;
|
|
1332
|
-
}
|
|
1333
|
-
function rateLimitMultiplierForPlan(plan) {
|
|
1334
|
-
switch (plan) {
|
|
1335
|
-
case "enterprise":
|
|
1336
|
-
return 4;
|
|
1337
|
-
case "pro":
|
|
1338
|
-
return 2;
|
|
1339
|
-
default:
|
|
1340
|
-
return 1;
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
1573
|
// ../../src/core/auth/membershipScope.ts
|
|
1574
|
+
init_tenantContext();
|
|
1345
1575
|
function resolveOrganizationScope() {
|
|
1346
1576
|
const user = currentAuthUser();
|
|
1347
1577
|
if (!user) {
|
|
@@ -2162,6 +2392,9 @@ function missingOptionalPeer(packageName, reason, error) {
|
|
|
2162
2392
|
});
|
|
2163
2393
|
}
|
|
2164
2394
|
|
|
2395
|
+
// ../../src/core/auth/saml/samlAssertionReplay.ts
|
|
2396
|
+
init_defaultConnection();
|
|
2397
|
+
|
|
2165
2398
|
// ../../src/core/database/errors.ts
|
|
2166
2399
|
function isPostgresError(error) {
|
|
2167
2400
|
return typeof error === "object" && error !== null && (("errno" in error) || ("code" in error));
|
|
@@ -2543,6 +2776,10 @@ class PolicyGate {
|
|
|
2543
2776
|
}
|
|
2544
2777
|
}
|
|
2545
2778
|
}
|
|
2779
|
+
// ../../src/core/auth/scimAuthMiddleware.ts
|
|
2780
|
+
init_defaultConnection();
|
|
2781
|
+
init_connectionContext();
|
|
2782
|
+
|
|
2546
2783
|
// ../../src/core/security/scimTenantTokens.ts
|
|
2547
2784
|
function parseScimTenantTokens(raw) {
|
|
2548
2785
|
const tokens = new Map;
|
|
@@ -2584,6 +2821,7 @@ function resolveScimTenantFromToken(token) {
|
|
|
2584
2821
|
}
|
|
2585
2822
|
|
|
2586
2823
|
// ../../src/core/database/repositoryConnection.ts
|
|
2824
|
+
init_defaultConnection();
|
|
2587
2825
|
function resolveRepositoryConnection() {
|
|
2588
2826
|
return getBoundDatabaseConnection() ?? getDefaultDatabaseQuery();
|
|
2589
2827
|
}
|
|
@@ -2628,26 +2866,8 @@ var repositoryConnection = new Proxy(function repositoryConnection() {}, {
|
|
|
2628
2866
|
}
|
|
2629
2867
|
});
|
|
2630
2868
|
|
|
2631
|
-
// ../../src/core/tenant/tenancyConfig.ts
|
|
2632
|
-
var TENANCY_DRIVERS = ["rls", "column", "none"];
|
|
2633
|
-
function readTenancyDriver(env = process.env) {
|
|
2634
|
-
const raw = env.TENANCY_DRIVER?.trim();
|
|
2635
|
-
if (raw === undefined || raw === "") {
|
|
2636
|
-
return "rls";
|
|
2637
|
-
}
|
|
2638
|
-
if (TENANCY_DRIVERS.includes(raw)) {
|
|
2639
|
-
return raw;
|
|
2640
|
-
}
|
|
2641
|
-
throw new Error(`TENANCY_DRIVER must be one of ${TENANCY_DRIVERS.join(", ")}; received "${raw}".`);
|
|
2642
|
-
}
|
|
2643
|
-
function isTenancyEnabled(env = process.env) {
|
|
2644
|
-
return readTenancyDriver(env) !== "none";
|
|
2645
|
-
}
|
|
2646
|
-
function isRlsTenancy(env = process.env) {
|
|
2647
|
-
return readTenancyDriver(env) === "rls";
|
|
2648
|
-
}
|
|
2649
|
-
|
|
2650
2869
|
// ../../src/core/tenant/resolveTenant.ts
|
|
2870
|
+
init_tenancyConfig();
|
|
2651
2871
|
async function resolveTenant(tenantId) {
|
|
2652
2872
|
if (!isTenancyEnabled()) {
|
|
2653
2873
|
return {
|
|
@@ -2667,32 +2887,8 @@ async function resolveTenant(tenantId) {
|
|
|
2667
2887
|
return row ? { id: row.id, slug: row.slug, plan: row.plan, region: row.region ?? "eu" } : null;
|
|
2668
2888
|
}
|
|
2669
2889
|
|
|
2670
|
-
// ../../src/core/tenant/tenantDatabaseScope.ts
|
|
2671
|
-
async function applyTenantContextToTransaction(transaction, tenantId) {
|
|
2672
|
-
await transaction.unsafe(`SELECT set_config('app.tenant_id', $1, true)`, [String(tenantId)]);
|
|
2673
|
-
await transaction.unsafe(`SELECT set_config('app.bypass_rls', $1, true)`, ["false"]);
|
|
2674
|
-
}
|
|
2675
|
-
async function runWithTenantDatabase(tenant, callback) {
|
|
2676
|
-
if (!isTenancyEnabled() || !isRlsTenancy()) {
|
|
2677
|
-
return await runWithTenant(tenant, callback);
|
|
2678
|
-
}
|
|
2679
|
-
if (hasActiveDatabaseConnection()) {
|
|
2680
|
-
const activeConnection = getActiveDatabaseConnection(getDefaultDatabasePool());
|
|
2681
|
-
await applyTenantContextToTransaction(activeConnection, tenant.id);
|
|
2682
|
-
return await runWithTenant(tenant, callback);
|
|
2683
|
-
}
|
|
2684
|
-
return await getDefaultDatabasePool().begin(async (transaction) => {
|
|
2685
|
-
await applyTenantContextToTransaction(transaction, tenant.id);
|
|
2686
|
-
return await runWithDatabaseConnection(transaction, async () => {
|
|
2687
|
-
return await runWithTenant(tenant, callback);
|
|
2688
|
-
});
|
|
2689
|
-
});
|
|
2690
|
-
}
|
|
2691
|
-
function isInsideTenantDatabaseScope(tenantId = currentTenant()?.id) {
|
|
2692
|
-
return hasActiveDatabaseConnection() && currentTenant()?.id === tenantId;
|
|
2693
|
-
}
|
|
2694
|
-
|
|
2695
2890
|
// ../../src/core/auth/scimAuthMiddleware.ts
|
|
2891
|
+
init_tenantDatabaseScope();
|
|
2696
2892
|
function createScimAuthMiddleware() {
|
|
2697
2893
|
return async (request, next) => {
|
|
2698
2894
|
const authorization = request.headers.get("authorization");
|
|
@@ -3264,50 +3460,9 @@ class ConfigStore {
|
|
|
3264
3460
|
return this.values.has(key);
|
|
3265
3461
|
}
|
|
3266
3462
|
}
|
|
3267
|
-
// ../../src/core/events/eventBus.ts
|
|
3268
|
-
class EventBus {
|
|
3269
|
-
constructor() {}
|
|
3270
|
-
listeners = new Map;
|
|
3271
|
-
on(event, listener) {
|
|
3272
|
-
return this.listen(event, listener);
|
|
3273
|
-
}
|
|
3274
|
-
listen(event, listener) {
|
|
3275
|
-
const handlers = this.listeners.get(event) ?? new Set;
|
|
3276
|
-
handlers.add(listener);
|
|
3277
|
-
this.listeners.set(event, handlers);
|
|
3278
|
-
return () => {
|
|
3279
|
-
handlers.delete(listener);
|
|
3280
|
-
if (handlers.size === 0) {
|
|
3281
|
-
this.listeners.delete(event);
|
|
3282
|
-
}
|
|
3283
|
-
};
|
|
3284
|
-
}
|
|
3285
|
-
async emit(event, payload) {
|
|
3286
|
-
await this.dispatch(event, payload);
|
|
3287
|
-
}
|
|
3288
|
-
async dispatch(event, payload) {
|
|
3289
|
-
const handlers = this.listeners.get(event);
|
|
3290
|
-
if (!handlers || handlers.size === 0) {
|
|
3291
|
-
return;
|
|
3292
|
-
}
|
|
3293
|
-
for (const handler of handlers) {
|
|
3294
|
-
await handler(payload);
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
}
|
|
3298
|
-
var EVENT_BUS_KEY = Symbol.for("@getstrata/eventBus");
|
|
3299
|
-
function readSharedEventBus() {
|
|
3300
|
-
const globalBus = globalThis[EVENT_BUS_KEY];
|
|
3301
|
-
if (globalBus) {
|
|
3302
|
-
return globalBus;
|
|
3303
|
-
}
|
|
3304
|
-
const bus = new EventBus;
|
|
3305
|
-
globalThis[EVENT_BUS_KEY] = bus;
|
|
3306
|
-
return bus;
|
|
3307
|
-
}
|
|
3308
|
-
var eventBus = readSharedEventBus();
|
|
3309
|
-
|
|
3310
3463
|
// ../../src/core/events/index.ts
|
|
3464
|
+
init_deferredModelEvents();
|
|
3465
|
+
init_eventBus();
|
|
3311
3466
|
function modelEventName(tableName, action) {
|
|
3312
3467
|
return `${tableName}.${action}`;
|
|
3313
3468
|
}
|
|
@@ -4614,7 +4769,7 @@ class BaseRepository {
|
|
|
4614
4769
|
throw new Error(`Insert into ${this.table.name} did not return a record.`);
|
|
4615
4770
|
}
|
|
4616
4771
|
const entity = record;
|
|
4617
|
-
await
|
|
4772
|
+
await dispatchModelEvent(modelEventName(this.table.name, "created"), entity);
|
|
4618
4773
|
return entity;
|
|
4619
4774
|
});
|
|
4620
4775
|
}
|
|
@@ -4624,7 +4779,7 @@ class BaseRepository {
|
|
|
4624
4779
|
const [record] = await this.connection.unsafe(text, params);
|
|
4625
4780
|
const entity = record ?? null;
|
|
4626
4781
|
if (entity) {
|
|
4627
|
-
await
|
|
4782
|
+
await dispatchModelEvent(modelEventName(this.table.name, "updated"), entity);
|
|
4628
4783
|
}
|
|
4629
4784
|
return entity;
|
|
4630
4785
|
});
|
|
@@ -4649,7 +4804,7 @@ class BaseRepository {
|
|
|
4649
4804
|
if (!record) {
|
|
4650
4805
|
return false;
|
|
4651
4806
|
}
|
|
4652
|
-
await
|
|
4807
|
+
await dispatchModelEvent(modelEventName(this.table.name, "deleted"), record);
|
|
4653
4808
|
return true;
|
|
4654
4809
|
});
|
|
4655
4810
|
}
|
|
@@ -4660,7 +4815,7 @@ class BaseRepository {
|
|
|
4660
4815
|
if (!row) {
|
|
4661
4816
|
return false;
|
|
4662
4817
|
}
|
|
4663
|
-
await
|
|
4818
|
+
await dispatchModelEvent(modelEventName(this.table.name, "force-deleted"), {
|
|
4664
4819
|
id
|
|
4665
4820
|
});
|
|
4666
4821
|
return true;
|
|
@@ -4674,7 +4829,7 @@ class BaseRepository {
|
|
|
4674
4829
|
return null;
|
|
4675
4830
|
}
|
|
4676
4831
|
const entity = record;
|
|
4677
|
-
await
|
|
4832
|
+
await dispatchModelEvent(modelEventName(this.table.name, "restored"), entity);
|
|
4678
4833
|
return entity;
|
|
4679
4834
|
});
|
|
4680
4835
|
}
|
|
@@ -4888,6 +5043,7 @@ class BaseRepository {
|
|
|
4888
5043
|
var baseRepository_default = BaseRepository;
|
|
4889
5044
|
// ../../src/core/database/bunSql.ts
|
|
4890
5045
|
var {SQL } = globalThis.Bun;
|
|
5046
|
+
init_defaultConnection();
|
|
4891
5047
|
function createBunSqlPool(options) {
|
|
4892
5048
|
if (!options.url) {
|
|
4893
5049
|
throw new Error("DATABASE_URL is not configured. Set url before creating a Bun SQL pool.");
|
|
@@ -4913,6 +5069,11 @@ function createDatabaseConnection(source) {
|
|
|
4913
5069
|
}
|
|
4914
5070
|
};
|
|
4915
5071
|
}
|
|
5072
|
+
|
|
5073
|
+
// ../../src/framework/public-api.ts
|
|
5074
|
+
init_connectionContext();
|
|
5075
|
+
init_defaultConnection();
|
|
5076
|
+
|
|
4916
5077
|
// ../../src/core/database/inflection.ts
|
|
4917
5078
|
function singularize(word) {
|
|
4918
5079
|
if (word.endsWith("ies") && word.length > 3) {
|
|
@@ -6989,6 +7150,7 @@ function createMysqlConnection(url) {
|
|
|
6989
7150
|
};
|
|
6990
7151
|
}
|
|
6991
7152
|
// ../../src/core/database/namedConnections.ts
|
|
7153
|
+
init_connectionContext();
|
|
6992
7154
|
var REGISTRY_KEY = Symbol.for("@getstrata/namedConnections");
|
|
6993
7155
|
function registry() {
|
|
6994
7156
|
const globalRecord = globalThis;
|
|
@@ -7026,6 +7188,10 @@ function runOnNamedConnection(name, callback) {
|
|
|
7026
7188
|
const entry = getNamedConnection(name);
|
|
7027
7189
|
return runWithSqlDialect(entry.driver, () => runWithDatabaseConnection(entry.connection, callback));
|
|
7028
7190
|
}
|
|
7191
|
+
|
|
7192
|
+
// ../../src/framework/public-api.ts
|
|
7193
|
+
init_queryProxy();
|
|
7194
|
+
|
|
7029
7195
|
// ../../src/core/database/schema/columnDefinition.ts
|
|
7030
7196
|
class ColumnDefinition {
|
|
7031
7197
|
name;
|
|
@@ -7675,6 +7841,7 @@ function defineTable(definition) {
|
|
|
7675
7841
|
return definition;
|
|
7676
7842
|
}
|
|
7677
7843
|
// ../../src/core/database/transaction.ts
|
|
7844
|
+
init_deferredModelEvents();
|
|
7678
7845
|
function supportsTransactions(connection) {
|
|
7679
7846
|
return typeof connection.begin === "function";
|
|
7680
7847
|
}
|
|
@@ -7683,10 +7850,17 @@ async function runInTransaction(operation) {
|
|
|
7683
7850
|
if (!supportsTransactions(pool)) {
|
|
7684
7851
|
throw new Error("Active database connection does not support transactions. Bind a client with begin() via bindDatabaseConnection.");
|
|
7685
7852
|
}
|
|
7686
|
-
|
|
7687
|
-
|
|
7853
|
+
const begin = pool.begin;
|
|
7854
|
+
return await runWithDeferredModelEvents(async () => {
|
|
7855
|
+
return await begin(async (transaction) => {
|
|
7856
|
+
return await operation(createDatabaseConnection(transaction));
|
|
7857
|
+
});
|
|
7688
7858
|
});
|
|
7689
7859
|
}
|
|
7860
|
+
|
|
7861
|
+
// ../../src/framework/public-api.ts
|
|
7862
|
+
init_eventBus();
|
|
7863
|
+
|
|
7690
7864
|
// ../../src/core/mail/mailer.ts
|
|
7691
7865
|
function resolveSmtpConfig() {
|
|
7692
7866
|
const host = process.env.MAIL_HOST?.trim();
|
|
@@ -8131,6 +8305,7 @@ function createBodySizeLimitMiddleware(maxBytes = resolveMaxBodyBytes()) {
|
|
|
8131
8305
|
};
|
|
8132
8306
|
}
|
|
8133
8307
|
// ../../src/core/http/requestMetaContext.ts
|
|
8308
|
+
init_asyncContextStore();
|
|
8134
8309
|
var requestMetaContext = createAsyncContextStore("@getstrata/requestMetaContext");
|
|
8135
8310
|
function runWithRequestMeta(meta, callback) {
|
|
8136
8311
|
return requestMetaContext.run(meta, callback);
|
|
@@ -8806,6 +8981,7 @@ function createFlashMiddleware() {
|
|
|
8806
8981
|
};
|
|
8807
8982
|
}
|
|
8808
8983
|
// ../../src/core/http/validation.ts
|
|
8984
|
+
init_tenantContext();
|
|
8809
8985
|
function buildRequestCacheKey(fallbackPath, request) {
|
|
8810
8986
|
if (!request) {
|
|
8811
8987
|
return fallbackPath;
|
|
@@ -10128,6 +10304,7 @@ function createValidateSignatureMiddleware() {
|
|
|
10128
10304
|
};
|
|
10129
10305
|
}
|
|
10130
10306
|
// ../../src/core/http/throttleMiddleware.ts
|
|
10307
|
+
init_tenantContext();
|
|
10131
10308
|
var {RedisClient: RedisClient4 } = globalThis.Bun;
|
|
10132
10309
|
function resolveThrottleIdentity(request) {
|
|
10133
10310
|
const user = currentAuthUser();
|
|
@@ -11041,10 +11218,20 @@ function generatedRlsBootstrapSql(tables, userOwnedTables = []) {
|
|
|
11041
11218
|
].join(`
|
|
11042
11219
|
`);
|
|
11043
11220
|
}
|
|
11221
|
+
|
|
11222
|
+
// ../../src/framework/public-api.ts
|
|
11223
|
+
init_tenancyConfig();
|
|
11224
|
+
init_tenantContext();
|
|
11225
|
+
init_tenantDatabaseScope();
|
|
11226
|
+
|
|
11044
11227
|
// ../../src/core/tenant/tenantMiddleware.ts
|
|
11045
11228
|
import { createHash as createHash4 } from "crypto";
|
|
11046
11229
|
|
|
11047
11230
|
// ../../src/core/tenant/databaseTenantContext.ts
|
|
11231
|
+
init_defaultConnection();
|
|
11232
|
+
init_connectionContext();
|
|
11233
|
+
init_deferredModelEvents();
|
|
11234
|
+
init_tenancyConfig();
|
|
11048
11235
|
function stringifyBypassIdentifier(identifier) {
|
|
11049
11236
|
if (typeof identifier === "number") {
|
|
11050
11237
|
return String(identifier);
|
|
@@ -11073,12 +11260,15 @@ async function runWithScopedTenantTransaction(apply, callback) {
|
|
|
11073
11260
|
return await callback();
|
|
11074
11261
|
}
|
|
11075
11262
|
const pool = getDefaultDatabasePool();
|
|
11076
|
-
|
|
11263
|
+
const begin = pool.begin;
|
|
11264
|
+
if (typeof begin !== "function") {
|
|
11077
11265
|
throw new Error("RLS migration bypass requires a pool that supports begin(). Session-scoped set_config is not used on pooled connections.");
|
|
11078
11266
|
}
|
|
11079
|
-
return await
|
|
11080
|
-
await
|
|
11081
|
-
|
|
11267
|
+
return await runWithDeferredModelEvents(async () => {
|
|
11268
|
+
return await begin(async (transaction) => {
|
|
11269
|
+
await apply(transaction);
|
|
11270
|
+
return await runWithDatabaseConnection(transaction, callback);
|
|
11271
|
+
});
|
|
11082
11272
|
});
|
|
11083
11273
|
}
|
|
11084
11274
|
async function runWithMigrationBypassForIdentifier(identifier, callback) {
|
|
@@ -11087,6 +11277,9 @@ async function runWithMigrationBypassForIdentifier(identifier, callback) {
|
|
|
11087
11277
|
}
|
|
11088
11278
|
|
|
11089
11279
|
// ../../src/core/tenant/tenantMiddleware.ts
|
|
11280
|
+
init_tenancyConfig();
|
|
11281
|
+
init_tenantContext();
|
|
11282
|
+
init_tenantDatabaseScope();
|
|
11090
11283
|
var DEFAULT_TENANT = {
|
|
11091
11284
|
id: 1,
|
|
11092
11285
|
slug: "default",
|
|
@@ -11174,6 +11367,7 @@ function createTenantMiddleware() {
|
|
|
11174
11367
|
};
|
|
11175
11368
|
}
|
|
11176
11369
|
// ../../src/core/tracing/traceContext.ts
|
|
11370
|
+
init_asyncContextStore();
|
|
11177
11371
|
var traceContextStorage = createAsyncContextStore("@getstrata/traceContext");
|
|
11178
11372
|
function runWithTraceContext(context, callback) {
|
|
11179
11373
|
return traceContextStorage.run(context, callback);
|
|
@@ -11845,6 +12039,7 @@ export {
|
|
|
11845
12039
|
defineTable,
|
|
11846
12040
|
dehydrateValue,
|
|
11847
12041
|
dialectFor,
|
|
12042
|
+
dispatchModelEvent,
|
|
11848
12043
|
emailRule,
|
|
11849
12044
|
emptyPaginateResult,
|
|
11850
12045
|
enableTenantRlsSql,
|
|
@@ -12034,6 +12229,7 @@ export {
|
|
|
12034
12229
|
runWithAuthContext,
|
|
12035
12230
|
runWithAuthUser,
|
|
12036
12231
|
runWithDatabaseConnection,
|
|
12232
|
+
runWithDeferredModelEvents,
|
|
12037
12233
|
runWithMembershipContext,
|
|
12038
12234
|
runWithRequestMeta,
|
|
12039
12235
|
runWithSqlDialect,
|