@event-driven-io/dumbo 0.13.0-beta.46 → 0.13.0-beta.47

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.
Files changed (43) hide show
  1. package/dist/cloudflare.cjs +432 -69
  2. package/dist/cloudflare.cjs.map +1 -1
  3. package/dist/cloudflare.d.cts +2 -2
  4. package/dist/cloudflare.d.ts +2 -2
  5. package/dist/cloudflare.js +432 -69
  6. package/dist/cloudflare.js.map +1 -1
  7. package/dist/{index-B5krjjrh.d.ts → index-4hcCG4SA.d.ts} +66 -30
  8. package/dist/{index-BaLXbc2l.d.ts → index-B7366zWR.d.ts} +1 -1
  9. package/dist/{index-_dj3upBo.d.cts → index-BJ-dm7dG.d.cts} +66 -30
  10. package/dist/{index-kYttgYkC.d.cts → index-BkJ1pipM.d.cts} +1 -1
  11. package/dist/{index-C9B46a1u.d.ts → index-CQokD3za.d.cts} +2 -2
  12. package/dist/{index-BoLWBnxd.d.cts → index-DIJv-L6M.d.ts} +2 -2
  13. package/dist/index.cjs +523 -269
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +2 -2
  16. package/dist/index.d.ts +2 -2
  17. package/dist/index.js +522 -270
  18. package/dist/index.js.map +1 -1
  19. package/dist/pg.cjs +168 -28
  20. package/dist/pg.cjs.map +1 -1
  21. package/dist/pg.d.cts +2 -2
  22. package/dist/pg.d.ts +2 -2
  23. package/dist/pg.js +168 -28
  24. package/dist/pg.js.map +1 -1
  25. package/dist/postgresql.cjs +4 -0
  26. package/dist/postgresql.cjs.map +1 -1
  27. package/dist/postgresql.d.cts +2 -2
  28. package/dist/postgresql.d.ts +2 -2
  29. package/dist/postgresql.js +4 -0
  30. package/dist/postgresql.js.map +1 -1
  31. package/dist/sqlite.cjs +429 -68
  32. package/dist/sqlite.cjs.map +1 -1
  33. package/dist/sqlite.d.cts +2 -2
  34. package/dist/sqlite.d.ts +2 -2
  35. package/dist/sqlite.js +429 -68
  36. package/dist/sqlite.js.map +1 -1
  37. package/dist/sqlite3.cjs +639 -343
  38. package/dist/sqlite3.cjs.map +1 -1
  39. package/dist/sqlite3.d.cts +10 -10
  40. package/dist/sqlite3.d.ts +10 -10
  41. package/dist/sqlite3.js +639 -343
  42. package/dist/sqlite3.js.map +1 -1
  43. package/package.json +1 -1
@@ -172,6 +172,294 @@ var InvalidOperationError = class InvalidOperationError extends DumboError {
172
172
  }
173
173
  };
174
174
 
175
+ //#endregion
176
+ //#region src/core/taskProcessing/abort.ts
177
+ const never = { signal: new AbortController().signal };
178
+ const from = (options) => options?.abort ?? never;
179
+ const getSignal = (abort) => "signal" in abort ? abort.signal : abort;
180
+ const reason = (abort) => {
181
+ const signal = getSignal(abort);
182
+ return signal.reason instanceof Error ? signal.reason : new DumboError(typeof signal.reason === "string" ? signal.reason : "Operation aborted");
183
+ };
184
+ const scope = (parent, onAbort) => {
185
+ const controller = new AbortController();
186
+ return {
187
+ abort: (reason) => controller.abort(reason),
188
+ dispose: onAbortSignal(parent, (reason) => {
189
+ controller.abort(reason);
190
+ onAbort?.(reason);
191
+ }),
192
+ signal: controller.signal
193
+ };
194
+ };
195
+ const execute = (operation, options) => {
196
+ const abort = options?.abort;
197
+ if (!abort) return operation();
198
+ const signal = abort.signal;
199
+ if (signal.aborted) return Promise.reject(reason(abort));
200
+ return new Promise((resolve, reject) => {
201
+ let finished = false;
202
+ const rejectOnAbort = () => {
203
+ if (finished) return;
204
+ finished = true;
205
+ reject(reason(abort));
206
+ };
207
+ signal.addEventListener("abort", rejectOnAbort, { once: true });
208
+ let operationPromise;
209
+ try {
210
+ operationPromise = operation();
211
+ } catch (error) {
212
+ finished = true;
213
+ signal.removeEventListener("abort", rejectOnAbort);
214
+ reject(error);
215
+ return;
216
+ }
217
+ operationPromise.then((result) => {
218
+ if (finished) return;
219
+ finished = true;
220
+ signal.removeEventListener("abort", rejectOnAbort);
221
+ resolve(result);
222
+ }).catch((error) => {
223
+ if (finished) return;
224
+ finished = true;
225
+ signal.removeEventListener("abort", rejectOnAbort);
226
+ reject(error);
227
+ });
228
+ });
229
+ };
230
+ const throwIfAborted = (options) => {
231
+ const abort = options?.abort;
232
+ if (abort?.signal.aborted) throw reason(abort);
233
+ };
234
+ const rejectIfAborted = (options) => {
235
+ const abort = options?.abort;
236
+ return abort?.signal.aborted ? Promise.reject(reason(abort)) : void 0;
237
+ };
238
+ const onAbortSignal = (abort, handle) => {
239
+ if (!abort) return () => {};
240
+ const signal = abort.signal;
241
+ if (signal.aborted) {
242
+ handle(reason(abort));
243
+ return () => {};
244
+ }
245
+ const abortListener = () => handle(reason(abort));
246
+ signal.addEventListener("abort", abortListener, { once: true });
247
+ return () => signal.removeEventListener("abort", abortListener);
248
+ };
249
+ const Abort = {
250
+ execute,
251
+ from,
252
+ never,
253
+ onAbort: onAbortSignal,
254
+ reason,
255
+ rejectIfAborted,
256
+ scope,
257
+ throwIfAborted
258
+ };
259
+
260
+ //#endregion
261
+ //#region src/core/taskProcessing/taskProcessor.ts
262
+ var TaskProcessor = class {
263
+ queue = [];
264
+ isProcessing = false;
265
+ activeTasks = 0;
266
+ activeGroups = /* @__PURE__ */ new Set();
267
+ options;
268
+ stopped = false;
269
+ idleWaiters = [];
270
+ activeTaskAbortCallbacks = /* @__PURE__ */ new Set();
271
+ constructor(options) {
272
+ this.options = options;
273
+ }
274
+ enqueue(task, options) {
275
+ if (options?.abort?.signal.aborted) return Promise.reject(Abort.reason(options.abort.signal));
276
+ if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
277
+ if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
278
+ return this.schedule(task, options);
279
+ }
280
+ waitForEndOfProcessing() {
281
+ if (this.activeTasks === 0 && this.queue.length === 0) return Promise.resolve();
282
+ return new Promise((resolve) => {
283
+ this.idleWaiters.push(resolve);
284
+ });
285
+ }
286
+ async stop(options) {
287
+ if (this.stopped) return;
288
+ this.stopped = true;
289
+ const stoppedError = new DumboError("TaskProcessor has been stopped");
290
+ for (const item of this.queue.splice(0)) {
291
+ item.abort(stoppedError);
292
+ item.reject(stoppedError);
293
+ }
294
+ this.activeGroups.clear();
295
+ if (options?.force) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
296
+ if (options?.force) return;
297
+ if (options?.closeDeadline === void 0) {
298
+ await this.waitForEndOfProcessing();
299
+ return;
300
+ }
301
+ if (!await waitForProcessingOrDeadline(this.waitForEndOfProcessing(), options.closeDeadline)) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
302
+ }
303
+ schedule(task, options) {
304
+ return new Promise((resolve, reject) => {
305
+ let didQueueTimeout = false;
306
+ let didAbortBeforeStart = false;
307
+ let didStart = false;
308
+ let queueWaitTimer = noopQueueWaitTimer;
309
+ const abortScope = Abort.scope(options?.abort, (reason) => {
310
+ queueWaitTimer.cancel();
311
+ didAbortBeforeStart = !didStart;
312
+ reject(reason);
313
+ });
314
+ queueWaitTimer = createQueueWaitTimer(this.options.maxTaskIdleTime, (reason) => {
315
+ didQueueTimeout = true;
316
+ abortScope.abort(reason);
317
+ abortScope.dispose();
318
+ reject(reason);
319
+ });
320
+ const taskWithContext = () => {
321
+ return new Promise((resolveTask) => {
322
+ if (didQueueTimeout || didAbortBeforeStart) {
323
+ resolveTask();
324
+ return;
325
+ }
326
+ let taskPromise;
327
+ try {
328
+ taskPromise = task({
329
+ ack: resolveTask,
330
+ abort: abortScope
331
+ });
332
+ } catch (err) {
333
+ abortScope.dispose();
334
+ resolveTask();
335
+ reject(err);
336
+ return;
337
+ }
338
+ taskPromise.then((result) => {
339
+ abortScope.dispose();
340
+ resolve(result);
341
+ }).catch((err) => {
342
+ abortScope.dispose();
343
+ resolveTask();
344
+ reject(err);
345
+ });
346
+ });
347
+ };
348
+ this.queue.push({
349
+ task: taskWithContext,
350
+ options,
351
+ reject: (reason) => {
352
+ queueWaitTimer.cancel();
353
+ abortScope.dispose();
354
+ reject(reason);
355
+ },
356
+ markStarted: () => {
357
+ didStart = true;
358
+ queueWaitTimer.cancel();
359
+ },
360
+ abort: (reason) => {
361
+ abortScope.dispose();
362
+ abortScope.abort(reason);
363
+ }
364
+ });
365
+ if (!this.isProcessing) this.ensureProcessing();
366
+ });
367
+ }
368
+ ensureProcessing() {
369
+ if (this.isProcessing) return;
370
+ this.isProcessing = true;
371
+ this.processQueue();
372
+ }
373
+ processQueue() {
374
+ try {
375
+ while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
376
+ const item = this.takeFirstAvailableItem();
377
+ if (item === null) return;
378
+ const groupId = item.options?.taskGroupId;
379
+ if (groupId) this.activeGroups.add(groupId);
380
+ this.activeTasks++;
381
+ this.executeItem(item);
382
+ }
383
+ } catch (error) {
384
+ console.error(error);
385
+ throw error;
386
+ } finally {
387
+ this.isProcessing = false;
388
+ if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
389
+ }
390
+ }
391
+ async executeItem({ task, options, markStarted, abort }) {
392
+ markStarted();
393
+ this.activeTaskAbortCallbacks.add(abort);
394
+ try {
395
+ await task();
396
+ } finally {
397
+ this.activeTaskAbortCallbacks.delete(abort);
398
+ this.activeTasks--;
399
+ if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
400
+ this.resolveIdleWaiters();
401
+ this.ensureProcessing();
402
+ }
403
+ }
404
+ takeFirstAvailableItem = () => {
405
+ const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
406
+ if (taskIndex === -1) return null;
407
+ const [item] = this.queue.splice(taskIndex, 1);
408
+ return item ?? null;
409
+ };
410
+ hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
411
+ resolveIdleWaiters = () => {
412
+ if (this.activeTasks > 0 || this.queue.length > 0) return;
413
+ const waiters = this.idleWaiters.splice(0);
414
+ for (const resolve of waiters) resolve();
415
+ };
416
+ };
417
+ const noopQueueWaitTimer = { cancel: () => {} };
418
+ const createQueueWaitTimer = (timeoutMs, reject) => {
419
+ if (timeoutMs === void 0) return noopQueueWaitTimer;
420
+ let timeoutId = setTimeout(() => {
421
+ reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
422
+ }, timeoutMs);
423
+ timeoutId.unref();
424
+ return { cancel: () => {
425
+ if (!timeoutId) return;
426
+ clearTimeout(timeoutId);
427
+ timeoutId = null;
428
+ } };
429
+ };
430
+ const waitForProcessingOrDeadline = async (processing, closeDeadline) => {
431
+ let timeoutId = null;
432
+ try {
433
+ return await Promise.race([processing.then(() => true), new Promise((resolve) => {
434
+ timeoutId = setTimeout(() => resolve(false), closeDeadline);
435
+ timeoutId.unref();
436
+ })]);
437
+ } finally {
438
+ if (timeoutId) clearTimeout(timeoutId);
439
+ }
440
+ };
441
+
442
+ //#endregion
443
+ //#region src/core/taskProcessing/executionGuards.ts
444
+ const guardConcurrentAccess = (options) => {
445
+ const taskProcessor = new TaskProcessor({
446
+ maxActiveTasks: options?.maxActiveTasks ?? Number.MAX_SAFE_INTEGER,
447
+ maxQueueSize: options?.maxQueueSize ?? Number.MAX_SAFE_INTEGER,
448
+ ...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
449
+ });
450
+ return {
451
+ execute: (operation, options) => taskProcessor.enqueue(async (context) => {
452
+ try {
453
+ return await operation({ abort: context.abort });
454
+ } finally {
455
+ context.ack();
456
+ }
457
+ }, options),
458
+ waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
459
+ stop: (options) => taskProcessor.stop(options)
460
+ };
461
+ };
462
+
175
463
  //#endregion
176
464
  //#region src/core/execute/execute.ts
177
465
  const mapSQLQueryResult = (result, mapping) => {
@@ -196,26 +484,63 @@ var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboE
196
484
  }
197
485
  };
198
486
  const sqlExecutor = (sqlExecutor, options) => ({
199
- query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), options),
200
- batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), options),
201
- command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), options),
202
- batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), options)
487
+ query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
488
+ ...options,
489
+ ...queryOptions
490
+ }),
491
+ batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
492
+ ...options,
493
+ ...queryOptions
494
+ }),
495
+ command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
496
+ ...options,
497
+ ...commandOptions
498
+ }),
499
+ batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
500
+ ...options,
501
+ ...commandOptions
502
+ })
203
503
  });
204
504
  const sqlExecutorInNewConnection = (options) => ({
205
- query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), options),
206
- batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), options),
207
- command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), options),
208
- batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), options)
505
+ query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
506
+ ...options,
507
+ ...queryOptions
508
+ }),
509
+ batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
510
+ ...options,
511
+ ...queryOptions
512
+ }),
513
+ command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
514
+ ...options,
515
+ ...commandOptions
516
+ }),
517
+ batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
518
+ ...options,
519
+ ...commandOptions
520
+ })
209
521
  });
210
522
  const sqlExecutorInAmbientConnection = (options) => ({
211
- query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), options),
212
- batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), options),
213
- command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), options),
214
- batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), options)
523
+ query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
524
+ ...options,
525
+ ...queryOptions
526
+ }),
527
+ batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
528
+ ...options,
529
+ ...queryOptions
530
+ }),
531
+ command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
532
+ ...options,
533
+ ...commandOptions
534
+ }),
535
+ batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
536
+ ...options,
537
+ ...commandOptions
538
+ })
215
539
  });
216
540
  const executeInNewDbClient = async (handle, options) => {
541
+ Abort.throwIfAborted(options);
217
542
  const { connect, close } = options;
218
- const client = await connect();
543
+ const client = await connect({ abort: Abort.from(options) });
219
544
  try {
220
545
  return await handle(client);
221
546
  } catch (error) {
@@ -224,7 +549,8 @@ const executeInNewDbClient = async (handle, options) => {
224
549
  }
225
550
  };
226
551
  const executeInNewConnection = async (handle, options) => {
227
- const connection = await options.connection();
552
+ Abort.throwIfAborted(options);
553
+ const connection = await options.connection({ abort: Abort.from(options) });
228
554
  try {
229
555
  return await handle(connection);
230
556
  } finally {
@@ -232,10 +558,8 @@ const executeInNewConnection = async (handle, options) => {
232
558
  }
233
559
  };
234
560
  const executeInAmbientConnection = async (handle, options) => {
235
- const connection = await options.connection();
236
- try {
237
- return await handle(connection);
238
- } finally {}
561
+ Abort.throwIfAborted(options);
562
+ return handle(await options.connection({ abort: Abort.from(options) }));
239
563
  };
240
564
 
241
565
  //#endregion
@@ -303,10 +627,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
303
627
  success: true,
304
628
  result: transactionResult
305
629
  };
306
- const executeInTransaction = async (transaction, handle) => {
630
+ const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
307
631
  await transaction.begin();
308
632
  try {
309
- const { success, result } = toTransactionResult(await handle(transaction));
633
+ const { success, result } = toTransactionResult(await handle(transaction, context));
310
634
  if (success) await transaction.commit();
311
635
  else await transaction.rollback();
312
636
  return result;
@@ -315,18 +639,32 @@ const executeInTransaction = async (transaction, handle) => {
315
639
  throw e;
316
640
  }
317
641
  };
642
+ const executeInNestedTransaction = async (transaction, handle, options, context) => {
643
+ Abort.throwIfAborted(options);
644
+ if (!(options?.allowNestedTransactions ?? transaction._transactionOptions.allowNestedTransactions ?? false)) throw new InvalidOperationError("Cannot start a nested transaction: allowNestedTransactions is false. Set transactionOptions: { allowNestedTransactions: true } on your pool or connection.");
645
+ return executeInTransaction(transaction, handle, context);
646
+ };
318
647
  const transactionFactoryWithDbClient = (connect, initTransaction) => {
319
648
  let currentTransaction = void 0;
320
- const getOrInitCurrentTransaction = (options) => currentTransaction ?? (currentTransaction = initTransaction(connect(), {
321
- close: () => {
322
- currentTransaction = void 0;
323
- return Promise.resolve();
324
- },
325
- ...options ?? {}
326
- }));
649
+ const getOrInitCurrentTransaction = (options) => {
650
+ Abort.throwIfAborted(options);
651
+ if (currentTransaction) return currentTransaction;
652
+ currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
653
+ close: () => {
654
+ currentTransaction = void 0;
655
+ return Promise.resolve();
656
+ },
657
+ ...options ?? {}
658
+ });
659
+ return currentTransaction;
660
+ };
327
661
  return {
328
662
  transaction: getOrInitCurrentTransaction,
329
- withTransaction: (handle, options) => executeInTransaction(getOrInitCurrentTransaction(options), handle)
663
+ withTransaction: (handle, options) => {
664
+ const abortRejection = Abort.rejectIfAborted(options);
665
+ if (abortRejection) return abortRejection;
666
+ return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
667
+ }
330
668
  };
331
669
  };
332
670
  const wrapInConnectionClosure = async (connection, handle) => {
@@ -338,7 +676,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
338
676
  };
339
677
  const transactionFactoryWithNewConnection = (connect) => ({
340
678
  transaction: (options) => {
341
- const connection = connect();
679
+ Abort.throwIfAborted(options);
680
+ const connection = connect({ abort: Abort.from(options) });
342
681
  const transaction = connection.transaction(options);
343
682
  return {
344
683
  ...transaction,
@@ -347,7 +686,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
347
686
  };
348
687
  },
349
688
  withTransaction: (handle, options) => {
350
- const connection = connect();
689
+ const abortRejection = Abort.rejectIfAborted(options);
690
+ if (abortRejection) return abortRejection;
691
+ const connection = connect({ abort: Abort.from(options) });
351
692
  const withTx = connection.withTransaction;
352
693
  return wrapInConnectionClosure(connection, () => withTx(handle, options));
353
694
  }
@@ -356,19 +697,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
356
697
  close ??= () => Promise.resolve();
357
698
  return {
358
699
  transaction: (options) => {
700
+ Abort.throwIfAborted(options);
359
701
  let conn = null;
360
702
  let innerTx = null;
361
703
  let connectingPromise = null;
362
704
  const ensureConnection = async () => {
363
705
  if (conn) return innerTx;
364
706
  if (!connectingPromise) connectingPromise = (async () => {
365
- conn = await connect();
707
+ Abort.throwIfAborted(options);
708
+ conn = await connect({ abort: Abort.from(options) });
366
709
  innerTx = conn.transaction(options);
367
710
  })();
368
711
  await connectingPromise;
369
712
  return innerTx;
370
713
  };
371
- return {
714
+ const tx = {
372
715
  driverType,
373
716
  get connection() {
374
717
  if (!conn) throw new Error("Transaction not started - call begin() first");
@@ -410,11 +753,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
410
753
  if (conn) await close(conn);
411
754
  }
412
755
  },
413
- _transactionOptions: void 0
756
+ withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
757
+ _transactionOptions: options ?? {}
414
758
  };
759
+ return tx;
415
760
  },
416
761
  withTransaction: async (handle, options) => {
417
- const conn = await connect();
762
+ Abort.throwIfAborted(options);
763
+ const conn = await connect({ abort: Abort.from(options) });
418
764
  try {
419
765
  const withTx = conn.withTransaction;
420
766
  return await withTx(handle, options);
@@ -431,7 +777,10 @@ const createAmbientConnection = (options) => {
431
777
  const { driverType, client, executor, initTransaction, serializer } = options;
432
778
  const clientPromise = Promise.resolve(client);
433
779
  const closePromise = Promise.resolve();
434
- const open = () => clientPromise;
780
+ const open = (context) => {
781
+ Abort.throwIfAborted(context);
782
+ return clientPromise;
783
+ };
435
784
  const close = () => closePromise;
436
785
  const typedConnection = {
437
786
  driverType,
@@ -447,9 +796,10 @@ const createConnection = (options) => {
447
796
  const { driverType, connect, close, initTransaction, executor, serializer } = options;
448
797
  let client = null;
449
798
  let connectPromise = null;
450
- const getClient = async () => {
799
+ const getClient = async (context) => {
800
+ Abort.throwIfAborted(context);
451
801
  if (client) return client;
452
- if (!connectPromise) connectPromise = connect().then((c) => {
802
+ if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
453
803
  client = c;
454
804
  return c;
455
805
  });
@@ -479,7 +829,10 @@ const createAmbientConnectionPool = (options) => {
479
829
  getConnection: () => connection,
480
830
  execute: connection.execute,
481
831
  transaction: (options) => connection.transaction(options),
482
- withConnection: (handle, _options) => handle(connection),
832
+ withConnection: (handle, options) => {
833
+ Abort.throwIfAborted(options);
834
+ return handle(connection, { abort: Abort.from(options) });
835
+ },
483
836
  withTransaction: (handle, options) => {
484
837
  const withTx = connection.withTransaction;
485
838
  return withTx(handle, options);
@@ -490,48 +843,45 @@ const createSingletonConnectionPool = (options) => {
490
843
  const { driverType, getConnection } = options;
491
844
  let connectionPromise = null;
492
845
  let closed = false;
493
- const activeOperations = /* @__PURE__ */ new Set();
846
+ const operationGuard = guardConcurrentAccess();
494
847
  const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
495
- const run = async (operation) => {
848
+ const executeIfOpen = async (operation, operationOptions) => {
496
849
  if (closed) throw closedError();
497
- const activeOperation = operation();
498
- activeOperations.add(activeOperation);
499
- try {
500
- return await activeOperation;
501
- } finally {
502
- activeOperations.delete(activeOperation);
503
- }
850
+ return operationGuard.execute(operation, operationOptions);
504
851
  };
505
- const getExistingOrNewConnection = () => {
506
- if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
852
+ const getExistingOrNewConnection = (context) => {
853
+ if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
507
854
  return connectionPromise;
508
855
  };
509
856
  const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
510
857
  return {
511
858
  driverType,
512
- connection: () => run(() => getExistingOrNewConnection().then((conn) => wrapPooledConnection(conn, () => Promise.resolve()))),
859
+ connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
513
860
  execute: (() => {
514
861
  const ambientExecutor = sqlExecutorInAmbientConnection({
515
862
  driverType,
516
863
  connection: getExistingOrNewConnection
517
864
  });
518
865
  return {
519
- query: (sql, opts) => run(() => ambientExecutor.query(sql, opts)),
520
- batchQuery: (sqls, opts) => run(() => ambientExecutor.batchQuery(sqls, opts)),
521
- command: (sql, opts) => run(() => ambientExecutor.command(sql, opts)),
522
- batchCommand: (sqls, opts) => run(() => ambientExecutor.batchCommand(sqls, opts))
866
+ query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
867
+ batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
868
+ command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
869
+ batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
523
870
  };
524
871
  })(),
525
- withConnection: (handle, _options) => run(() => executeInAmbientConnection(handle, { connection: getExistingOrNewConnection })),
872
+ withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
873
+ connection: getExistingOrNewConnection,
874
+ ...options
875
+ }), options),
526
876
  transaction: (transactionOptions) => {
527
877
  if (closed) throw closedError();
528
878
  return innerTransactionFactory.transaction(transactionOptions);
529
879
  },
530
- withTransaction: (handle, transactionOptions) => run(() => innerTransactionFactory.withTransaction(handle, transactionOptions)),
880
+ withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
531
881
  close: async (closeOptions) => {
532
882
  if (closed) return;
533
883
  closed = true;
534
- if (!closeOptions?.force) await Promise.allSettled([...activeOperations]);
884
+ await operationGuard.stop(closeOptions);
535
885
  if (!connectionPromise) return;
536
886
  const connection = await connectionPromise;
537
887
  connectionPromise = null;
@@ -543,16 +893,22 @@ const createAlwaysNewConnectionPool = (options) => {
543
893
  const { driverType, getConnection, connectionOptions } = options;
544
894
  return createConnectionPool({
545
895
  driverType,
546
- getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
896
+ getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
547
897
  });
548
898
  };
549
899
  const createConnectionPool = (pool) => {
550
900
  const { driverType, getConnection } = pool;
551
- const connection = "connection" in pool ? pool.connection : () => Promise.resolve(getConnection());
901
+ const connection = "connection" in pool ? pool.connection : (options) => {
902
+ Abort.throwIfAborted(options);
903
+ return Promise.resolve(getConnection({ abort: Abort.from(options) }));
904
+ };
552
905
  return {
553
906
  driverType,
554
907
  connection,
555
- withConnection: "withConnection" in pool ? pool.withConnection : (handle, _options) => executeInNewConnection(handle, { connection }),
908
+ withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
909
+ connection,
910
+ ...options
911
+ }),
556
912
  close: "close" in pool ? pool.close : () => Promise.resolve(),
557
913
  execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
558
914
  driverType,
@@ -1887,9 +2243,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
1887
2243
 
1888
2244
  //#endregion
1889
2245
  //#region src/storage/sqlite/core/transactions/index.ts
1890
- const sqliteTransaction = (driverType, connection, allowNestedTransactions, serializer, defaultTransactionMode) => (getClient, options) => {
1891
- allowNestedTransactions = options?.allowNestedTransactions ?? allowNestedTransactions;
1892
- const useSavepoints = options?.useSavepoints ?? false;
2246
+ const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
2247
+ const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
2248
+ const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
1893
2249
  const tx = databaseTransaction({
1894
2250
  begin: async () => {
1895
2251
  const client = await getClient;
@@ -1917,23 +2273,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
1917
2273
  },
1918
2274
  releaseSavepoint: async (level) => {
1919
2275
  await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
2276
+ },
2277
+ rollbackToSavepoint: async (level) => {
2278
+ await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
1920
2279
  }
1921
2280
  }, {
1922
2281
  allowNestedTransactions,
1923
- useSavepoints
2282
+ useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
1924
2283
  });
1925
- return {
2284
+ const transaction = {
1926
2285
  connection: connection(),
1927
2286
  driverType,
1928
2287
  begin: tx.begin,
1929
2288
  commit: tx.commit,
1930
2289
  rollback: tx.rollback,
1931
2290
  execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
2291
+ withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
1932
2292
  _transactionOptions: {
1933
2293
  ...options,
1934
2294
  allowNestedTransactions
1935
2295
  }
1936
2296
  };
2297
+ return transaction;
1937
2298
  };
1938
2299
 
1939
2300
  //#endregion
@@ -1978,7 +2339,7 @@ const sqliteAmbientClientConnection = (options) => {
1978
2339
  return createAmbientConnection({
1979
2340
  driverType,
1980
2341
  client,
1981
- initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
2342
+ initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
1982
2343
  executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
1983
2344
  serializer
1984
2345
  });
@@ -2003,7 +2364,7 @@ const sqliteClientConnection = (options) => {
2003
2364
  if (client && "close" in client && typeof client.close === "function") await client.close();
2004
2365
  else if (client && "release" in client && typeof client.release === "function") client.release();
2005
2366
  },
2006
- initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions?.allowNestedTransactions ?? false, serializer, connectionOptions.defaultTransactionMode),
2367
+ initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
2007
2368
  executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
2008
2369
  serializer
2009
2370
  });
@@ -2025,7 +2386,7 @@ const sqlitePoolClientConnection = (options) => {
2025
2386
  driverType: options.driverType,
2026
2387
  connect,
2027
2388
  close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
2028
- initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions?.allowNestedTransactions ?? false, serializer, connectionOptions.defaultTransactionMode),
2389
+ initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
2029
2390
  executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
2030
2391
  serializer
2031
2392
  });
@@ -2395,7 +2756,7 @@ const d1Transaction = (connection, serializer, defaultOptions) => (getClient, op
2395
2756
  client = await getClient;
2396
2757
  return client;
2397
2758
  };
2398
- return {
2759
+ const transaction = {
2399
2760
  connection: connection(),
2400
2761
  driverType: D1DriverType,
2401
2762
  begin: async function() {
@@ -2439,8 +2800,10 @@ const d1Transaction = (connection, serializer, defaultOptions) => (getClient, op
2439
2800
  if (!sessionClient) throw new Error("Transaction has not been started. Call begin() first.");
2440
2801
  return Promise.resolve(sessionClient);
2441
2802
  } }),
2803
+ withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
2442
2804
  _transactionOptions: options ?? {}
2443
2805
  };
2806
+ return transaction;
2444
2807
  };
2445
2808
 
2446
2809
  //#endregion