@getstrata/core 2.2.21 → 2.2.23

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.
@@ -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) {
@@ -95,8 +95,8 @@ function resolveSmtpConfig() {
95
95
  if (!from) {
96
96
  throw new Error('MAIL_DRIVER="smtp" requires MAIL_FROM to be set.');
97
97
  }
98
- const port = Number.parseInt(process.env.MAIL_PORT ?? "587", 10);
99
- if (!Number.isInteger(port) || port <= 0) {
98
+ const port = Number(process.env.MAIL_PORT ?? "587");
99
+ if (!Number.isInteger(port) || port <= 0 || port > 65535) {
100
100
  throw new Error('Environment variable "MAIL_PORT" must be a positive integer.');
101
101
  }
102
102
  return {
@@ -104,6 +104,7 @@ function resolveSmtpConfig() {
104
104
  port,
105
105
  from,
106
106
  secure: (process.env.MAIL_SECURE ?? "false") === "true",
107
+ ...process.env.MAIL_TIMEOUT_MS !== undefined ? { timeoutMs: smtpTimeout(Number(process.env.MAIL_TIMEOUT_MS)) } : {},
107
108
  ...process.env.MAIL_USERNAME?.trim() ? { username: process.env.MAIL_USERNAME.trim() } : {},
108
109
  ...process.env.MAIL_PASSWORD?.trim() ? { password: process.env.MAIL_PASSWORD.trim() } : {}
109
110
  };
@@ -113,6 +114,12 @@ function assertSafeSmtpField(value, field) {
113
114
  if (/[\r\n]/u.test(value)) {
114
115
  throw new Error(`Mail ${field} must not contain CR or LF.`);
115
116
  }
117
+ for (const character of value) {
118
+ const code = character.charCodeAt(0);
119
+ if (code < 32 && code !== 9 || code === 127) {
120
+ throw new Error(`Mail ${field} must not contain control characters.`);
121
+ }
122
+ }
116
123
  return value;
117
124
  }
118
125
  function smtpEnvelopeAddress(value, field) {
@@ -133,148 +140,307 @@ function redactMailBody(body) {
133
140
  function encodeBase64(value) {
134
141
  return Buffer.from(value, "utf8").toString("base64");
135
142
  }
136
- function parseSmtpResponses(buffer) {
137
- const responses = [];
138
- let remainder = buffer;
139
- while (remainder.includes(`\r
140
- `)) {
141
- const index = remainder.indexOf(`\r
142
- `);
143
- const line = remainder.slice(0, index);
144
- remainder = remainder.slice(index + 2);
145
- if (line.length >= 4 && line[3] === "-") {
146
- continue;
147
- }
148
- responses.push(line);
143
+ function smtpTimeout(value = 1e4) {
144
+ if (!Number.isSafeInteger(value) || value < 1 || value > 2147483647) {
145
+ throw new RangeError("SMTP timeoutMs must be a positive integer within the timer range.");
149
146
  }
150
- return { responses, remainder };
147
+ return value;
151
148
  }
152
- async function waitForSmtpResponse(readResponse, expectedCodes) {
153
- const response = await readResponse();
154
- const code = response.slice(0, 3);
155
- if (!expectedCodes.includes(code)) {
156
- throw new Error(`Unexpected SMTP response: ${response}`);
149
+ async function defaultSmtpTransport(config, message) {
150
+ const timeoutMs = smtpTimeout(config.timeoutMs);
151
+ if (!config.host.trim() || !Number.isInteger(config.port) || config.port < 1 || config.port > 65535) {
152
+ throw new RangeError("SMTP requires a host and a port between 1 and 65535.");
157
153
  }
158
- return response;
159
- }
160
- async function openSmtpConnection(config) {
161
- let buffer = "";
154
+ if (Boolean(config.username) !== Boolean(config.password)) {
155
+ throw new Error("SMTP authentication requires both username and password.");
156
+ }
157
+ if (config.username)
158
+ assertSafeSmtpField(config.username, "username");
159
+ if (config.password)
160
+ assertSafeSmtpField(config.password, "password");
161
+ const fromHeader = assertSafeSmtpAddress(config.from, "from");
162
+ const toHeader = assertSafeSmtpAddress(message.to, "to");
163
+ assertSafeSmtpField(message.subject, "subject");
164
+ const fromEnvelope = smtpEnvelopeAddress(config.from, "from");
165
+ const toEnvelope = smtpEnvelopeAddress(message.to, "to");
166
+ if (/[^\x21-\x7e]/u.test(fromEnvelope + toEnvelope)) {
167
+ throw new Error("Default SMTP transport requires ASCII envelopes; SMTPUTF8 is not negotiated.");
168
+ }
169
+ for (const address of [fromEnvelope, toEnvelope]) {
170
+ const at = address.lastIndexOf("@");
171
+ if (Buffer.byteLength(address) > 254 || Buffer.byteLength(address.slice(0, at)) > 64) {
172
+ throw new RangeError("SMTP envelope address exceeds the path/local-part length limit.");
173
+ }
174
+ }
175
+ message.signal?.throwIfAborted();
176
+ let socket;
177
+ let finished = false;
178
+ let failed = false;
179
+ let failure;
180
+ let rejectFailure;
181
+ const failurePromise = new Promise((_, reject) => {
182
+ rejectFailure = reject;
183
+ });
184
+ failurePromise.catch(() => {});
185
+ const responses = [];
162
186
  const waiters = [];
163
- const readResponse = () => new Promise((resolve, reject) => {
164
- const parsed = parseSmtpResponses(buffer);
165
- if (parsed.responses.length > 0) {
166
- buffer = parsed.remainder;
167
- resolve(parsed.responses.shift());
187
+ let drain;
188
+ let drainVersion = 0;
189
+ let buffer = "";
190
+ let multilineCode;
191
+ let multilineLines = 0;
192
+ const decoder = new TextDecoder;
193
+ const fail = (reason) => {
194
+ if (finished || failed)
168
195
  return;
196
+ failed = true;
197
+ failure = reason;
198
+ rejectFailure(reason);
199
+ waiters.length = 0;
200
+ drain?.();
201
+ drain = undefined;
202
+ socket?.terminate();
203
+ };
204
+ const onAbort = () => fail(message.signal?.reason ?? new DOMException("Mail send aborted.", "AbortError"));
205
+ const timer = setTimeout(() => fail(new DOMException("SMTP send deadline exceeded.", "TimeoutError")), timeoutMs);
206
+ message.signal?.addEventListener("abort", onAbort, { once: true });
207
+ if (message.signal?.aborted)
208
+ onAbort();
209
+ const readResponse = async (expected) => {
210
+ const queued = responses.shift();
211
+ const response = queued ?? await Promise.race([
212
+ failed ? Promise.reject(failure) : new Promise((resolve) => waiters.push(resolve)),
213
+ failurePromise
214
+ ]);
215
+ const code = response.slice(0, 3);
216
+ if (!expected.includes(code))
217
+ throw new Error(`Unexpected SMTP response code: ${code}`);
218
+ };
219
+ const write = async (text) => {
220
+ const bytes = Buffer.from(text, "utf8");
221
+ let offset = 0;
222
+ while (offset < bytes.length) {
223
+ if (failed)
224
+ throw failure;
225
+ const version = drainVersion;
226
+ if (!socket)
227
+ throw new Error("SMTP socket is not connected.");
228
+ const written = socket.write(bytes.subarray(offset));
229
+ if (written < 0)
230
+ throw new Error("SMTP socket write failed.");
231
+ offset += written;
232
+ if (offset < bytes.length && version === drainVersion) {
233
+ await Promise.race([
234
+ new Promise((resolve) => {
235
+ drain = resolve;
236
+ }),
237
+ failurePromise
238
+ ]);
239
+ }
169
240
  }
170
- waiters.push({ resolve, reject });
171
- });
172
- const socket = await Bun.connect({
173
- hostname: config.host,
174
- port: config.port,
175
- socket: {
176
- open() {},
177
- data(_socket, chunk) {
178
- buffer += Buffer.from(chunk).toString("utf8");
179
- const parsed = parseSmtpResponses(buffer);
180
- buffer = parsed.remainder;
181
- while (parsed.responses.length > 0 && waiters.length > 0) {
182
- const response = parsed.responses.shift();
183
- waiters.shift()?.resolve(response);
241
+ };
242
+ try {
243
+ const connecting = Bun.connect({
244
+ hostname: config.host,
245
+ port: config.port,
246
+ ...config.secure ? {
247
+ tls: {
248
+ ...config.tls,
249
+ serverName: config.tls?.serverName ?? config.host,
250
+ rejectUnauthorized: true
184
251
  }
185
- },
186
- error(_socket, error) {
187
- const pending = waiters.splice(0);
188
- for (const waiter of pending) {
189
- waiter.reject(error instanceof Error ? error : new Error(String(error)));
252
+ } : {},
253
+ socket: {
254
+ open(value) {
255
+ socket = value;
256
+ if (failed || finished)
257
+ value.terminate();
258
+ },
259
+ data(_socket, chunk) {
260
+ if (failed || finished)
261
+ return;
262
+ buffer += decoder.decode(chunk, { stream: true });
263
+ if (Buffer.byteLength(buffer, "utf8") > 16384) {
264
+ fail(new Error("SMTP response exceeded the buffer limit."));
265
+ return;
266
+ }
267
+ while (buffer.includes(`\r
268
+ `)) {
269
+ const index = buffer.indexOf(`\r
270
+ `);
271
+ const line = buffer.slice(0, index);
272
+ buffer = buffer.slice(index + 2);
273
+ const match = /^([2-5]\d{2})([- ]|$)/.exec(line);
274
+ if (!match || multilineCode !== undefined && match[1] !== multilineCode) {
275
+ fail(new Error("Malformed SMTP reply."));
276
+ return;
277
+ }
278
+ if (++multilineLines > 128) {
279
+ fail(new Error("SMTP reply exceeded the line limit."));
280
+ return;
281
+ }
282
+ if (match[2] === "-") {
283
+ multilineCode = match[1];
284
+ continue;
285
+ }
286
+ multilineCode = undefined;
287
+ multilineLines = 0;
288
+ const waiter = waiters.shift();
289
+ if (waiter)
290
+ waiter(line);
291
+ else {
292
+ responses.push(line);
293
+ if (responses.length > 32) {
294
+ fail(new Error("Too many buffered SMTP replies."));
295
+ return;
296
+ }
297
+ }
298
+ }
299
+ },
300
+ drain() {
301
+ drainVersion++;
302
+ const waiting = drain;
303
+ drain = undefined;
304
+ waiting?.();
305
+ },
306
+ end() {
307
+ fail(new Error("SMTP peer closed before the send completed."));
308
+ },
309
+ close() {
310
+ fail(new Error("SMTP connection closed before the send completed."));
311
+ },
312
+ error() {
313
+ fail(new Error("SMTP socket failed."));
314
+ },
315
+ connectError() {
316
+ fail(new Error("SMTP connection failed."));
317
+ },
318
+ handshake(_socket, success) {
319
+ if (!success)
320
+ fail(new Error("SMTP TLS verification failed."));
190
321
  }
191
322
  }
192
- }
193
- });
194
- return { socket, readResponse };
195
- }
196
- async function defaultSmtpTransport(config, message) {
197
- if (config.username) {
198
- assertSafeSmtpField(config.username, "username");
199
- }
200
- if (config.password) {
201
- assertSafeSmtpField(config.password, "password");
202
- }
203
- const { socket, readResponse } = await openSmtpConnection(config);
204
- try {
205
- await waitForSmtpResponse(readResponse, ["220"]);
206
- await socket.write(`EHLO ${smtpEhloHost()}\r
323
+ });
324
+ connecting.then((value) => {
325
+ socket = value;
326
+ if (failed || finished)
327
+ value.terminate();
328
+ }, () => {});
329
+ socket = await Promise.race([connecting, failurePromise]);
330
+ await readResponse(["220"]);
331
+ await write(`EHLO ${smtpEhloHost()}\r
207
332
  `);
208
- await waitForSmtpResponse(readResponse, ["250"]);
333
+ await readResponse(["250"]);
209
334
  if (config.username && config.password) {
210
- await socket.write(`AUTH LOGIN\r
335
+ await write(`AUTH LOGIN\r
211
336
  `);
212
- await waitForSmtpResponse(readResponse, ["334"]);
213
- await socket.write(`${encodeBase64(config.username)}\r
337
+ await readResponse(["334"]);
338
+ await write(`${encodeBase64(config.username)}\r
214
339
  `);
215
- await waitForSmtpResponse(readResponse, ["334"]);
216
- await socket.write(`${encodeBase64(config.password)}\r
340
+ await readResponse(["334"]);
341
+ await write(`${encodeBase64(config.password)}\r
217
342
  `);
218
- await waitForSmtpResponse(readResponse, ["235"]);
343
+ await readResponse(["235"]);
219
344
  }
220
- const fromHeader = assertSafeSmtpAddress(config.from, "from");
221
- const toHeader = assertSafeSmtpAddress(message.to, "to");
222
- const from = smtpEnvelopeAddress(config.from, "from");
223
- const to = smtpEnvelopeAddress(message.to, "to");
224
- await socket.write(`MAIL FROM:<${from}>\r
345
+ await write(`MAIL FROM:<${fromEnvelope}>\r
225
346
  `);
226
- await waitForSmtpResponse(readResponse, ["250"]);
227
- await socket.write(`RCPT TO:<${to}>\r
347
+ await readResponse(["250"]);
348
+ await write(`RCPT TO:<${toEnvelope}>\r
228
349
  `);
229
- await waitForSmtpResponse(readResponse, ["250", "251"]);
230
- await socket.write(`DATA\r
350
+ await readResponse(["250", "251"]);
351
+ await write(`DATA\r
231
352
  `);
232
- await waitForSmtpResponse(readResponse, ["354"]);
233
- const payload = buildSmtpPayload(fromHeader, {
234
- ...message,
235
- to: toHeader,
236
- subject: assertSafeSmtpField(message.subject, "subject")
237
- });
238
- await socket.write(payload);
239
- await waitForSmtpResponse(readResponse, ["250"]);
240
- await socket.write(`QUIT\r
353
+ await readResponse(["354"]);
354
+ await write(buildSmtpPayload(fromHeader, { ...message, to: toHeader }));
355
+ await readResponse(["250"]);
356
+ finished = true;
357
+ try {
358
+ socket.write(`QUIT\r
241
359
  `);
242
- await waitForSmtpResponse(readResponse, ["221"]);
360
+ } catch {}
243
361
  } finally {
244
- socket.end();
362
+ finished = true;
363
+ clearTimeout(timer);
364
+ message.signal?.removeEventListener("abort", onAbort);
365
+ waiters.length = 0;
366
+ responses.length = 0;
367
+ drain = undefined;
368
+ socket?.end();
369
+ }
370
+ }
371
+ function encodeSmtpHeader(value) {
372
+ if (/^[\x20-\x7e]{0,60}$/u.test(value))
373
+ return value;
374
+ const words = [];
375
+ let part = "";
376
+ for (const character of value) {
377
+ if (Buffer.byteLength(part + character, "utf8") > 45) {
378
+ words.push(`=?UTF-8?B?${encodeBase64(part)}?=`);
379
+ part = "";
380
+ }
381
+ part += character;
245
382
  }
383
+ if (part)
384
+ words.push(`=?UTF-8?B?${encodeBase64(part)}?=`);
385
+ return words.join(`\r
386
+ `);
387
+ }
388
+ function smtpAddressHeader(value) {
389
+ if (!value.includes("<"))
390
+ return value;
391
+ const name = value.slice(0, value.lastIndexOf("<")).trim();
392
+ const address = smtpEnvelopeAddress(value, "address");
393
+ if (!name)
394
+ return address;
395
+ const encoded = encodeSmtpHeader(name);
396
+ const display = encoded.startsWith("=?UTF-8?B?") ? encoded : `"${encoded.replace(/[\\"]/gu, "\\$&")}"`;
397
+ return `${display} <${address}>`;
398
+ }
399
+ function smtpMimePart(type, value) {
400
+ const normalized = value.replace(/\r\n|\r|\n/gu, `\r
401
+ `);
402
+ const base64 = /[^\t\r\n\x20-\x7e]/u.test(normalized) || normalized.split(`\r
403
+ `).some((line) => line.length > 998);
404
+ return [
405
+ `Content-Type: ${type}; charset=utf-8`,
406
+ `Content-Transfer-Encoding: ${base64 ? "base64" : "7bit"}`,
407
+ "",
408
+ base64 ? (encodeBase64(normalized).match(/.{1,76}/gu) ?? []).join(`\r
409
+ `) : normalized
410
+ ];
246
411
  }
247
412
  function buildSmtpPayload(from, message) {
248
- const safeFrom = assertSafeSmtpAddress(from, "from");
249
- const safeTo = assertSafeSmtpAddress(message.to, "to");
250
- const safeSubject = assertSafeSmtpField(message.subject, "subject");
251
413
  const headers = [
252
- `From: ${safeFrom}`,
253
- `To: ${safeTo}`,
254
- `Subject: ${safeSubject}`,
414
+ `From: ${smtpAddressHeader(assertSafeSmtpAddress(from, "from"))}`,
415
+ `To: ${smtpAddressHeader(assertSafeSmtpAddress(message.to, "to"))}`,
416
+ `Subject: ${encodeSmtpHeader(assertSafeSmtpField(message.subject, "subject"))}`,
255
417
  "MIME-Version: 1.0"
256
418
  ];
419
+ let lines;
257
420
  if (message.html) {
258
- const boundary = `strata-${Date.now().toString(36)}`;
259
- headers.push(`Content-Type: multipart/alternative; boundary="${boundary}"`);
260
- const parts = [
261
- `--${boundary}`,
262
- "Content-Type: text/plain; charset=utf-8",
421
+ let boundary;
422
+ do {
423
+ boundary = `strata-${crypto.randomUUID()}`;
424
+ } while (message.body.includes(boundary) || message.html.includes(boundary));
425
+ lines = [
426
+ ...headers,
427
+ `Content-Type: multipart/alternative; boundary="${boundary}"`,
263
428
  "",
264
- message.body,
265
429
  `--${boundary}`,
266
- "Content-Type: text/html; charset=utf-8",
267
- "",
268
- message.html,
430
+ ...smtpMimePart("text/plain", message.body),
431
+ `--${boundary}`,
432
+ ...smtpMimePart("text/html", message.html),
269
433
  `--${boundary}--`,
270
434
  ""
271
435
  ];
272
- return [...headers, "", ...parts, ".", ""].join(`\r
273
- `);
274
- }
275
- headers.push("Content-Type: text/plain; charset=utf-8");
276
- return [...headers, "", message.body, ".", ""].join(`\r
277
- `);
436
+ } else
437
+ lines = [...headers, ...smtpMimePart("text/plain", message.body)];
438
+ const content = lines.join(`\r
439
+ `).replace(/(^|\r\n)\./gu, "$1..");
440
+ return `${content}${content.endsWith(`\r
441
+ `) ? "" : `\r
442
+ `}.\r
443
+ `;
278
444
  }
279
445
 
280
446
  class LogMailDriver {
@@ -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) {