@event-driven-io/dumbo 0.13.0-beta.45 → 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.
- package/dist/cloudflare.cjs +432 -69
- package/dist/cloudflare.cjs.map +1 -1
- package/dist/cloudflare.d.cts +2 -2
- package/dist/cloudflare.d.ts +2 -2
- package/dist/cloudflare.js +432 -69
- package/dist/cloudflare.js.map +1 -1
- package/dist/{index-B5krjjrh.d.ts → index-4hcCG4SA.d.ts} +66 -30
- package/dist/{index-BaLXbc2l.d.ts → index-B7366zWR.d.ts} +1 -1
- package/dist/{index-_dj3upBo.d.cts → index-BJ-dm7dG.d.cts} +66 -30
- package/dist/{index-kYttgYkC.d.cts → index-BkJ1pipM.d.cts} +1 -1
- package/dist/{index-C9B46a1u.d.ts → index-CQokD3za.d.cts} +2 -2
- package/dist/{index-BoLWBnxd.d.cts → index-DIJv-L6M.d.ts} +2 -2
- package/dist/index.cjs +523 -269
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +522 -270
- package/dist/index.js.map +1 -1
- package/dist/pg.cjs +168 -28
- package/dist/pg.cjs.map +1 -1
- package/dist/pg.d.cts +2 -2
- package/dist/pg.d.ts +2 -2
- package/dist/pg.js +168 -28
- package/dist/pg.js.map +1 -1
- package/dist/postgresql.cjs +4 -0
- package/dist/postgresql.cjs.map +1 -1
- package/dist/postgresql.d.cts +2 -2
- package/dist/postgresql.d.ts +2 -2
- package/dist/postgresql.js +4 -0
- package/dist/postgresql.js.map +1 -1
- package/dist/sqlite.cjs +429 -68
- package/dist/sqlite.cjs.map +1 -1
- package/dist/sqlite.d.cts +2 -2
- package/dist/sqlite.d.ts +2 -2
- package/dist/sqlite.js +429 -68
- package/dist/sqlite.js.map +1 -1
- package/dist/sqlite3.cjs +639 -343
- package/dist/sqlite3.cjs.map +1 -1
- package/dist/sqlite3.d.cts +10 -10
- package/dist/sqlite3.d.ts +10 -10
- package/dist/sqlite3.js +639 -343
- package/dist/sqlite3.js.map +1 -1
- package/package.json +1 -1
package/dist/cloudflare.cjs
CHANGED
|
@@ -201,6 +201,294 @@ var InvalidOperationError = class InvalidOperationError extends DumboError {
|
|
|
201
201
|
}
|
|
202
202
|
};
|
|
203
203
|
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/core/taskProcessing/abort.ts
|
|
206
|
+
const never = { signal: new AbortController().signal };
|
|
207
|
+
const from = (options) => options?.abort ?? never;
|
|
208
|
+
const getSignal = (abort) => "signal" in abort ? abort.signal : abort;
|
|
209
|
+
const reason = (abort) => {
|
|
210
|
+
const signal = getSignal(abort);
|
|
211
|
+
return signal.reason instanceof Error ? signal.reason : new DumboError(typeof signal.reason === "string" ? signal.reason : "Operation aborted");
|
|
212
|
+
};
|
|
213
|
+
const scope = (parent, onAbort) => {
|
|
214
|
+
const controller = new AbortController();
|
|
215
|
+
return {
|
|
216
|
+
abort: (reason) => controller.abort(reason),
|
|
217
|
+
dispose: onAbortSignal(parent, (reason) => {
|
|
218
|
+
controller.abort(reason);
|
|
219
|
+
onAbort?.(reason);
|
|
220
|
+
}),
|
|
221
|
+
signal: controller.signal
|
|
222
|
+
};
|
|
223
|
+
};
|
|
224
|
+
const execute = (operation, options) => {
|
|
225
|
+
const abort = options?.abort;
|
|
226
|
+
if (!abort) return operation();
|
|
227
|
+
const signal = abort.signal;
|
|
228
|
+
if (signal.aborted) return Promise.reject(reason(abort));
|
|
229
|
+
return new Promise((resolve, reject) => {
|
|
230
|
+
let finished = false;
|
|
231
|
+
const rejectOnAbort = () => {
|
|
232
|
+
if (finished) return;
|
|
233
|
+
finished = true;
|
|
234
|
+
reject(reason(abort));
|
|
235
|
+
};
|
|
236
|
+
signal.addEventListener("abort", rejectOnAbort, { once: true });
|
|
237
|
+
let operationPromise;
|
|
238
|
+
try {
|
|
239
|
+
operationPromise = operation();
|
|
240
|
+
} catch (error) {
|
|
241
|
+
finished = true;
|
|
242
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
243
|
+
reject(error);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
operationPromise.then((result) => {
|
|
247
|
+
if (finished) return;
|
|
248
|
+
finished = true;
|
|
249
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
250
|
+
resolve(result);
|
|
251
|
+
}).catch((error) => {
|
|
252
|
+
if (finished) return;
|
|
253
|
+
finished = true;
|
|
254
|
+
signal.removeEventListener("abort", rejectOnAbort);
|
|
255
|
+
reject(error);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
};
|
|
259
|
+
const throwIfAborted = (options) => {
|
|
260
|
+
const abort = options?.abort;
|
|
261
|
+
if (abort?.signal.aborted) throw reason(abort);
|
|
262
|
+
};
|
|
263
|
+
const rejectIfAborted = (options) => {
|
|
264
|
+
const abort = options?.abort;
|
|
265
|
+
return abort?.signal.aborted ? Promise.reject(reason(abort)) : void 0;
|
|
266
|
+
};
|
|
267
|
+
const onAbortSignal = (abort, handle) => {
|
|
268
|
+
if (!abort) return () => {};
|
|
269
|
+
const signal = abort.signal;
|
|
270
|
+
if (signal.aborted) {
|
|
271
|
+
handle(reason(abort));
|
|
272
|
+
return () => {};
|
|
273
|
+
}
|
|
274
|
+
const abortListener = () => handle(reason(abort));
|
|
275
|
+
signal.addEventListener("abort", abortListener, { once: true });
|
|
276
|
+
return () => signal.removeEventListener("abort", abortListener);
|
|
277
|
+
};
|
|
278
|
+
const Abort = {
|
|
279
|
+
execute,
|
|
280
|
+
from,
|
|
281
|
+
never,
|
|
282
|
+
onAbort: onAbortSignal,
|
|
283
|
+
reason,
|
|
284
|
+
rejectIfAborted,
|
|
285
|
+
scope,
|
|
286
|
+
throwIfAborted
|
|
287
|
+
};
|
|
288
|
+
|
|
289
|
+
//#endregion
|
|
290
|
+
//#region src/core/taskProcessing/taskProcessor.ts
|
|
291
|
+
var TaskProcessor = class {
|
|
292
|
+
queue = [];
|
|
293
|
+
isProcessing = false;
|
|
294
|
+
activeTasks = 0;
|
|
295
|
+
activeGroups = /* @__PURE__ */ new Set();
|
|
296
|
+
options;
|
|
297
|
+
stopped = false;
|
|
298
|
+
idleWaiters = [];
|
|
299
|
+
activeTaskAbortCallbacks = /* @__PURE__ */ new Set();
|
|
300
|
+
constructor(options) {
|
|
301
|
+
this.options = options;
|
|
302
|
+
}
|
|
303
|
+
enqueue(task, options) {
|
|
304
|
+
if (options?.abort?.signal.aborted) return Promise.reject(Abort.reason(options.abort.signal));
|
|
305
|
+
if (this.stopped) return Promise.reject(new DumboError("TaskProcessor has been stopped"));
|
|
306
|
+
if (this.queue.length >= this.options.maxQueueSize) return Promise.reject(new TransientDatabaseError("Too many pending connections. Please try again later."));
|
|
307
|
+
return this.schedule(task, options);
|
|
308
|
+
}
|
|
309
|
+
waitForEndOfProcessing() {
|
|
310
|
+
if (this.activeTasks === 0 && this.queue.length === 0) return Promise.resolve();
|
|
311
|
+
return new Promise((resolve) => {
|
|
312
|
+
this.idleWaiters.push(resolve);
|
|
313
|
+
});
|
|
314
|
+
}
|
|
315
|
+
async stop(options) {
|
|
316
|
+
if (this.stopped) return;
|
|
317
|
+
this.stopped = true;
|
|
318
|
+
const stoppedError = new DumboError("TaskProcessor has been stopped");
|
|
319
|
+
for (const item of this.queue.splice(0)) {
|
|
320
|
+
item.abort(stoppedError);
|
|
321
|
+
item.reject(stoppedError);
|
|
322
|
+
}
|
|
323
|
+
this.activeGroups.clear();
|
|
324
|
+
if (options?.force) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
325
|
+
if (options?.force) return;
|
|
326
|
+
if (options?.closeDeadline === void 0) {
|
|
327
|
+
await this.waitForEndOfProcessing();
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (!await waitForProcessingOrDeadline(this.waitForEndOfProcessing(), options.closeDeadline)) for (const abort of this.activeTaskAbortCallbacks) abort(stoppedError);
|
|
331
|
+
}
|
|
332
|
+
schedule(task, options) {
|
|
333
|
+
return new Promise((resolve, reject) => {
|
|
334
|
+
let didQueueTimeout = false;
|
|
335
|
+
let didAbortBeforeStart = false;
|
|
336
|
+
let didStart = false;
|
|
337
|
+
let queueWaitTimer = noopQueueWaitTimer;
|
|
338
|
+
const abortScope = Abort.scope(options?.abort, (reason) => {
|
|
339
|
+
queueWaitTimer.cancel();
|
|
340
|
+
didAbortBeforeStart = !didStart;
|
|
341
|
+
reject(reason);
|
|
342
|
+
});
|
|
343
|
+
queueWaitTimer = createQueueWaitTimer(this.options.maxTaskIdleTime, (reason) => {
|
|
344
|
+
didQueueTimeout = true;
|
|
345
|
+
abortScope.abort(reason);
|
|
346
|
+
abortScope.dispose();
|
|
347
|
+
reject(reason);
|
|
348
|
+
});
|
|
349
|
+
const taskWithContext = () => {
|
|
350
|
+
return new Promise((resolveTask) => {
|
|
351
|
+
if (didQueueTimeout || didAbortBeforeStart) {
|
|
352
|
+
resolveTask();
|
|
353
|
+
return;
|
|
354
|
+
}
|
|
355
|
+
let taskPromise;
|
|
356
|
+
try {
|
|
357
|
+
taskPromise = task({
|
|
358
|
+
ack: resolveTask,
|
|
359
|
+
abort: abortScope
|
|
360
|
+
});
|
|
361
|
+
} catch (err) {
|
|
362
|
+
abortScope.dispose();
|
|
363
|
+
resolveTask();
|
|
364
|
+
reject(err);
|
|
365
|
+
return;
|
|
366
|
+
}
|
|
367
|
+
taskPromise.then((result) => {
|
|
368
|
+
abortScope.dispose();
|
|
369
|
+
resolve(result);
|
|
370
|
+
}).catch((err) => {
|
|
371
|
+
abortScope.dispose();
|
|
372
|
+
resolveTask();
|
|
373
|
+
reject(err);
|
|
374
|
+
});
|
|
375
|
+
});
|
|
376
|
+
};
|
|
377
|
+
this.queue.push({
|
|
378
|
+
task: taskWithContext,
|
|
379
|
+
options,
|
|
380
|
+
reject: (reason) => {
|
|
381
|
+
queueWaitTimer.cancel();
|
|
382
|
+
abortScope.dispose();
|
|
383
|
+
reject(reason);
|
|
384
|
+
},
|
|
385
|
+
markStarted: () => {
|
|
386
|
+
didStart = true;
|
|
387
|
+
queueWaitTimer.cancel();
|
|
388
|
+
},
|
|
389
|
+
abort: (reason) => {
|
|
390
|
+
abortScope.dispose();
|
|
391
|
+
abortScope.abort(reason);
|
|
392
|
+
}
|
|
393
|
+
});
|
|
394
|
+
if (!this.isProcessing) this.ensureProcessing();
|
|
395
|
+
});
|
|
396
|
+
}
|
|
397
|
+
ensureProcessing() {
|
|
398
|
+
if (this.isProcessing) return;
|
|
399
|
+
this.isProcessing = true;
|
|
400
|
+
this.processQueue();
|
|
401
|
+
}
|
|
402
|
+
processQueue() {
|
|
403
|
+
try {
|
|
404
|
+
while (this.activeTasks < this.options.maxActiveTasks && this.queue.length > 0) {
|
|
405
|
+
const item = this.takeFirstAvailableItem();
|
|
406
|
+
if (item === null) return;
|
|
407
|
+
const groupId = item.options?.taskGroupId;
|
|
408
|
+
if (groupId) this.activeGroups.add(groupId);
|
|
409
|
+
this.activeTasks++;
|
|
410
|
+
this.executeItem(item);
|
|
411
|
+
}
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.error(error);
|
|
414
|
+
throw error;
|
|
415
|
+
} finally {
|
|
416
|
+
this.isProcessing = false;
|
|
417
|
+
if (this.hasItemsToProcess() && this.activeTasks < this.options.maxActiveTasks) this.ensureProcessing();
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
async executeItem({ task, options, markStarted, abort }) {
|
|
421
|
+
markStarted();
|
|
422
|
+
this.activeTaskAbortCallbacks.add(abort);
|
|
423
|
+
try {
|
|
424
|
+
await task();
|
|
425
|
+
} finally {
|
|
426
|
+
this.activeTaskAbortCallbacks.delete(abort);
|
|
427
|
+
this.activeTasks--;
|
|
428
|
+
if (options && options.taskGroupId) this.activeGroups.delete(options.taskGroupId);
|
|
429
|
+
this.resolveIdleWaiters();
|
|
430
|
+
this.ensureProcessing();
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
takeFirstAvailableItem = () => {
|
|
434
|
+
const taskIndex = this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId));
|
|
435
|
+
if (taskIndex === -1) return null;
|
|
436
|
+
const [item] = this.queue.splice(taskIndex, 1);
|
|
437
|
+
return item ?? null;
|
|
438
|
+
};
|
|
439
|
+
hasItemsToProcess = () => this.queue.findIndex((item) => !item.options?.taskGroupId || !this.activeGroups.has(item.options.taskGroupId)) !== -1;
|
|
440
|
+
resolveIdleWaiters = () => {
|
|
441
|
+
if (this.activeTasks > 0 || this.queue.length > 0) return;
|
|
442
|
+
const waiters = this.idleWaiters.splice(0);
|
|
443
|
+
for (const resolve of waiters) resolve();
|
|
444
|
+
};
|
|
445
|
+
};
|
|
446
|
+
const noopQueueWaitTimer = { cancel: () => {} };
|
|
447
|
+
const createQueueWaitTimer = (timeoutMs, reject) => {
|
|
448
|
+
if (timeoutMs === void 0) return noopQueueWaitTimer;
|
|
449
|
+
let timeoutId = setTimeout(() => {
|
|
450
|
+
reject(/* @__PURE__ */ new Error("Task was not started within the maximum waiting time"));
|
|
451
|
+
}, timeoutMs);
|
|
452
|
+
timeoutId.unref();
|
|
453
|
+
return { cancel: () => {
|
|
454
|
+
if (!timeoutId) return;
|
|
455
|
+
clearTimeout(timeoutId);
|
|
456
|
+
timeoutId = null;
|
|
457
|
+
} };
|
|
458
|
+
};
|
|
459
|
+
const waitForProcessingOrDeadline = async (processing, closeDeadline) => {
|
|
460
|
+
let timeoutId = null;
|
|
461
|
+
try {
|
|
462
|
+
return await Promise.race([processing.then(() => true), new Promise((resolve) => {
|
|
463
|
+
timeoutId = setTimeout(() => resolve(false), closeDeadline);
|
|
464
|
+
timeoutId.unref();
|
|
465
|
+
})]);
|
|
466
|
+
} finally {
|
|
467
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
468
|
+
}
|
|
469
|
+
};
|
|
470
|
+
|
|
471
|
+
//#endregion
|
|
472
|
+
//#region src/core/taskProcessing/executionGuards.ts
|
|
473
|
+
const guardConcurrentAccess = (options) => {
|
|
474
|
+
const taskProcessor = new TaskProcessor({
|
|
475
|
+
maxActiveTasks: options?.maxActiveTasks ?? Number.MAX_SAFE_INTEGER,
|
|
476
|
+
maxQueueSize: options?.maxQueueSize ?? Number.MAX_SAFE_INTEGER,
|
|
477
|
+
...options?.maxTaskIdleTime !== void 0 ? { maxTaskIdleTime: options.maxTaskIdleTime } : {}
|
|
478
|
+
});
|
|
479
|
+
return {
|
|
480
|
+
execute: (operation, options) => taskProcessor.enqueue(async (context) => {
|
|
481
|
+
try {
|
|
482
|
+
return await operation({ abort: context.abort });
|
|
483
|
+
} finally {
|
|
484
|
+
context.ack();
|
|
485
|
+
}
|
|
486
|
+
}, options),
|
|
487
|
+
waitForIdle: () => taskProcessor.waitForEndOfProcessing(),
|
|
488
|
+
stop: (options) => taskProcessor.stop(options)
|
|
489
|
+
};
|
|
490
|
+
};
|
|
491
|
+
|
|
204
492
|
//#endregion
|
|
205
493
|
//#region src/core/execute/execute.ts
|
|
206
494
|
const mapSQLQueryResult = (result, mapping) => {
|
|
@@ -225,26 +513,63 @@ var BatchCommandNoChangesError = class BatchCommandNoChangesError extends DumboE
|
|
|
225
513
|
}
|
|
226
514
|
};
|
|
227
515
|
const sqlExecutor = (sqlExecutor, options) => ({
|
|
228
|
-
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions),
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
516
|
+
query: (sql, queryOptions) => executeInNewDbClient((client) => sqlExecutor.query(client, sql, queryOptions), {
|
|
517
|
+
...options,
|
|
518
|
+
...queryOptions
|
|
519
|
+
}),
|
|
520
|
+
batchQuery: (sqls, queryOptions) => executeInNewDbClient((client) => sqlExecutor.batchQuery(client, sqls, queryOptions), {
|
|
521
|
+
...options,
|
|
522
|
+
...queryOptions
|
|
523
|
+
}),
|
|
524
|
+
command: (sql, commandOptions) => executeInNewDbClient((client) => sqlExecutor.command(client, sql, commandOptions), {
|
|
525
|
+
...options,
|
|
526
|
+
...commandOptions
|
|
527
|
+
}),
|
|
528
|
+
batchCommand: (sqls, commandOptions) => executeInNewDbClient((client) => sqlExecutor.batchCommand(client, sqls, commandOptions), {
|
|
529
|
+
...options,
|
|
530
|
+
...commandOptions
|
|
531
|
+
})
|
|
232
532
|
});
|
|
233
533
|
const sqlExecutorInNewConnection = (options) => ({
|
|
234
|
-
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
534
|
+
query: (sql, queryOptions) => executeInNewConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
535
|
+
...options,
|
|
536
|
+
...queryOptions
|
|
537
|
+
}),
|
|
538
|
+
batchQuery: (sqls, queryOptions) => executeInNewConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
539
|
+
...options,
|
|
540
|
+
...queryOptions
|
|
541
|
+
}),
|
|
542
|
+
command: (sql, commandOptions) => executeInNewConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
543
|
+
...options,
|
|
544
|
+
...commandOptions
|
|
545
|
+
}),
|
|
546
|
+
batchCommand: (sqls, commandOptions) => executeInNewConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
547
|
+
...options,
|
|
548
|
+
...commandOptions
|
|
549
|
+
})
|
|
238
550
|
});
|
|
239
551
|
const sqlExecutorInAmbientConnection = (options) => ({
|
|
240
|
-
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions),
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
552
|
+
query: (sql, queryOptions) => executeInAmbientConnection((connection) => connection.execute.query(sql, queryOptions), {
|
|
553
|
+
...options,
|
|
554
|
+
...queryOptions
|
|
555
|
+
}),
|
|
556
|
+
batchQuery: (sqls, queryOptions) => executeInAmbientConnection((connection) => connection.execute.batchQuery(sqls, queryOptions), {
|
|
557
|
+
...options,
|
|
558
|
+
...queryOptions
|
|
559
|
+
}),
|
|
560
|
+
command: (sql, commandOptions) => executeInAmbientConnection((connection) => connection.execute.command(sql, commandOptions), {
|
|
561
|
+
...options,
|
|
562
|
+
...commandOptions
|
|
563
|
+
}),
|
|
564
|
+
batchCommand: (sqls, commandOptions) => executeInAmbientConnection((connection) => connection.execute.batchCommand(sqls, commandOptions), {
|
|
565
|
+
...options,
|
|
566
|
+
...commandOptions
|
|
567
|
+
})
|
|
244
568
|
});
|
|
245
569
|
const executeInNewDbClient = async (handle, options) => {
|
|
570
|
+
Abort.throwIfAborted(options);
|
|
246
571
|
const { connect, close } = options;
|
|
247
|
-
const client = await connect();
|
|
572
|
+
const client = await connect({ abort: Abort.from(options) });
|
|
248
573
|
try {
|
|
249
574
|
return await handle(client);
|
|
250
575
|
} catch (error) {
|
|
@@ -253,7 +578,8 @@ const executeInNewDbClient = async (handle, options) => {
|
|
|
253
578
|
}
|
|
254
579
|
};
|
|
255
580
|
const executeInNewConnection = async (handle, options) => {
|
|
256
|
-
|
|
581
|
+
Abort.throwIfAborted(options);
|
|
582
|
+
const connection = await options.connection({ abort: Abort.from(options) });
|
|
257
583
|
try {
|
|
258
584
|
return await handle(connection);
|
|
259
585
|
} finally {
|
|
@@ -261,10 +587,8 @@ const executeInNewConnection = async (handle, options) => {
|
|
|
261
587
|
}
|
|
262
588
|
};
|
|
263
589
|
const executeInAmbientConnection = async (handle, options) => {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return await handle(connection);
|
|
267
|
-
} finally {}
|
|
590
|
+
Abort.throwIfAborted(options);
|
|
591
|
+
return handle(await options.connection({ abort: Abort.from(options) }));
|
|
268
592
|
};
|
|
269
593
|
|
|
270
594
|
//#endregion
|
|
@@ -332,10 +656,10 @@ const toTransactionResult = (transactionResult) => transactionResult !== void 0
|
|
|
332
656
|
success: true,
|
|
333
657
|
result: transactionResult
|
|
334
658
|
};
|
|
335
|
-
const executeInTransaction = async (transaction, handle) => {
|
|
659
|
+
const executeInTransaction = async (transaction, handle, context = { abort: Abort.never }) => {
|
|
336
660
|
await transaction.begin();
|
|
337
661
|
try {
|
|
338
|
-
const { success, result } = toTransactionResult(await handle(transaction));
|
|
662
|
+
const { success, result } = toTransactionResult(await handle(transaction, context));
|
|
339
663
|
if (success) await transaction.commit();
|
|
340
664
|
else await transaction.rollback();
|
|
341
665
|
return result;
|
|
@@ -344,18 +668,32 @@ const executeInTransaction = async (transaction, handle) => {
|
|
|
344
668
|
throw e;
|
|
345
669
|
}
|
|
346
670
|
};
|
|
671
|
+
const executeInNestedTransaction = async (transaction, handle, options, context) => {
|
|
672
|
+
Abort.throwIfAborted(options);
|
|
673
|
+
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.");
|
|
674
|
+
return executeInTransaction(transaction, handle, context);
|
|
675
|
+
};
|
|
347
676
|
const transactionFactoryWithDbClient = (connect, initTransaction) => {
|
|
348
677
|
let currentTransaction = void 0;
|
|
349
|
-
const getOrInitCurrentTransaction = (options) =>
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
678
|
+
const getOrInitCurrentTransaction = (options) => {
|
|
679
|
+
Abort.throwIfAborted(options);
|
|
680
|
+
if (currentTransaction) return currentTransaction;
|
|
681
|
+
currentTransaction = initTransaction(connect({ abort: Abort.from(options) }), {
|
|
682
|
+
close: () => {
|
|
683
|
+
currentTransaction = void 0;
|
|
684
|
+
return Promise.resolve();
|
|
685
|
+
},
|
|
686
|
+
...options ?? {}
|
|
687
|
+
});
|
|
688
|
+
return currentTransaction;
|
|
689
|
+
};
|
|
356
690
|
return {
|
|
357
691
|
transaction: getOrInitCurrentTransaction,
|
|
358
|
-
withTransaction: (handle, options) =>
|
|
692
|
+
withTransaction: (handle, options) => {
|
|
693
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
694
|
+
if (abortRejection) return abortRejection;
|
|
695
|
+
return executeInTransaction(getOrInitCurrentTransaction(options), handle, { abort: Abort.from(options) });
|
|
696
|
+
}
|
|
359
697
|
};
|
|
360
698
|
};
|
|
361
699
|
const wrapInConnectionClosure = async (connection, handle) => {
|
|
@@ -367,7 +705,8 @@ const wrapInConnectionClosure = async (connection, handle) => {
|
|
|
367
705
|
};
|
|
368
706
|
const transactionFactoryWithNewConnection = (connect) => ({
|
|
369
707
|
transaction: (options) => {
|
|
370
|
-
|
|
708
|
+
Abort.throwIfAborted(options);
|
|
709
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
371
710
|
const transaction = connection.transaction(options);
|
|
372
711
|
return {
|
|
373
712
|
...transaction,
|
|
@@ -376,7 +715,9 @@ const transactionFactoryWithNewConnection = (connect) => ({
|
|
|
376
715
|
};
|
|
377
716
|
},
|
|
378
717
|
withTransaction: (handle, options) => {
|
|
379
|
-
const
|
|
718
|
+
const abortRejection = Abort.rejectIfAborted(options);
|
|
719
|
+
if (abortRejection) return abortRejection;
|
|
720
|
+
const connection = connect({ abort: Abort.from(options) });
|
|
380
721
|
const withTx = connection.withTransaction;
|
|
381
722
|
return wrapInConnectionClosure(connection, () => withTx(handle, options));
|
|
382
723
|
}
|
|
@@ -385,19 +726,21 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
385
726
|
close ??= () => Promise.resolve();
|
|
386
727
|
return {
|
|
387
728
|
transaction: (options) => {
|
|
729
|
+
Abort.throwIfAborted(options);
|
|
388
730
|
let conn = null;
|
|
389
731
|
let innerTx = null;
|
|
390
732
|
let connectingPromise = null;
|
|
391
733
|
const ensureConnection = async () => {
|
|
392
734
|
if (conn) return innerTx;
|
|
393
735
|
if (!connectingPromise) connectingPromise = (async () => {
|
|
394
|
-
|
|
736
|
+
Abort.throwIfAborted(options);
|
|
737
|
+
conn = await connect({ abort: Abort.from(options) });
|
|
395
738
|
innerTx = conn.transaction(options);
|
|
396
739
|
})();
|
|
397
740
|
await connectingPromise;
|
|
398
741
|
return innerTx;
|
|
399
742
|
};
|
|
400
|
-
|
|
743
|
+
const tx = {
|
|
401
744
|
driverType,
|
|
402
745
|
get connection() {
|
|
403
746
|
if (!conn) throw new Error("Transaction not started - call begin() first");
|
|
@@ -439,11 +782,14 @@ const transactionFactoryWithAsyncAmbientConnection = (driverType, connect, close
|
|
|
439
782
|
if (conn) await close(conn);
|
|
440
783
|
}
|
|
441
784
|
},
|
|
442
|
-
|
|
785
|
+
withTransaction: (handle, options) => executeInNestedTransaction(tx, handle, options, { abort: Abort.from(options) }),
|
|
786
|
+
_transactionOptions: options ?? {}
|
|
443
787
|
};
|
|
788
|
+
return tx;
|
|
444
789
|
},
|
|
445
790
|
withTransaction: async (handle, options) => {
|
|
446
|
-
|
|
791
|
+
Abort.throwIfAborted(options);
|
|
792
|
+
const conn = await connect({ abort: Abort.from(options) });
|
|
447
793
|
try {
|
|
448
794
|
const withTx = conn.withTransaction;
|
|
449
795
|
return await withTx(handle, options);
|
|
@@ -460,7 +806,10 @@ const createAmbientConnection = (options) => {
|
|
|
460
806
|
const { driverType, client, executor, initTransaction, serializer } = options;
|
|
461
807
|
const clientPromise = Promise.resolve(client);
|
|
462
808
|
const closePromise = Promise.resolve();
|
|
463
|
-
const open = () =>
|
|
809
|
+
const open = (context) => {
|
|
810
|
+
Abort.throwIfAborted(context);
|
|
811
|
+
return clientPromise;
|
|
812
|
+
};
|
|
464
813
|
const close = () => closePromise;
|
|
465
814
|
const typedConnection = {
|
|
466
815
|
driverType,
|
|
@@ -476,9 +825,10 @@ const createConnection = (options) => {
|
|
|
476
825
|
const { driverType, connect, close, initTransaction, executor, serializer } = options;
|
|
477
826
|
let client = null;
|
|
478
827
|
let connectPromise = null;
|
|
479
|
-
const getClient = async () => {
|
|
828
|
+
const getClient = async (context) => {
|
|
829
|
+
Abort.throwIfAborted(context);
|
|
480
830
|
if (client) return client;
|
|
481
|
-
if (!connectPromise) connectPromise = connect().then((c) => {
|
|
831
|
+
if (!connectPromise) connectPromise = connect(context ?? { abort: Abort.never }).then((c) => {
|
|
482
832
|
client = c;
|
|
483
833
|
return c;
|
|
484
834
|
});
|
|
@@ -508,7 +858,10 @@ const createAmbientConnectionPool = (options) => {
|
|
|
508
858
|
getConnection: () => connection,
|
|
509
859
|
execute: connection.execute,
|
|
510
860
|
transaction: (options) => connection.transaction(options),
|
|
511
|
-
withConnection: (handle,
|
|
861
|
+
withConnection: (handle, options) => {
|
|
862
|
+
Abort.throwIfAborted(options);
|
|
863
|
+
return handle(connection, { abort: Abort.from(options) });
|
|
864
|
+
},
|
|
512
865
|
withTransaction: (handle, options) => {
|
|
513
866
|
const withTx = connection.withTransaction;
|
|
514
867
|
return withTx(handle, options);
|
|
@@ -519,48 +872,45 @@ const createSingletonConnectionPool = (options) => {
|
|
|
519
872
|
const { driverType, getConnection } = options;
|
|
520
873
|
let connectionPromise = null;
|
|
521
874
|
let closed = false;
|
|
522
|
-
const
|
|
875
|
+
const operationGuard = guardConcurrentAccess();
|
|
523
876
|
const closedError = () => /* @__PURE__ */ new Error("Singleton connection pool has been closed");
|
|
524
|
-
const
|
|
877
|
+
const executeIfOpen = async (operation, operationOptions) => {
|
|
525
878
|
if (closed) throw closedError();
|
|
526
|
-
|
|
527
|
-
activeOperations.add(activeOperation);
|
|
528
|
-
try {
|
|
529
|
-
return await activeOperation;
|
|
530
|
-
} finally {
|
|
531
|
-
activeOperations.delete(activeOperation);
|
|
532
|
-
}
|
|
879
|
+
return operationGuard.execute(operation, operationOptions);
|
|
533
880
|
};
|
|
534
|
-
const getExistingOrNewConnection = () => {
|
|
535
|
-
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection());
|
|
881
|
+
const getExistingOrNewConnection = (context) => {
|
|
882
|
+
if (!connectionPromise) connectionPromise ??= Promise.resolve(getConnection(context));
|
|
536
883
|
return connectionPromise;
|
|
537
884
|
};
|
|
538
885
|
const innerTransactionFactory = transactionFactoryWithAsyncAmbientConnection(options.driverType, getExistingOrNewConnection, options.closeConnection);
|
|
539
886
|
return {
|
|
540
887
|
driverType,
|
|
541
|
-
connection: () =>
|
|
888
|
+
connection: (connectionOptions) => executeIfOpen((context) => getExistingOrNewConnection(context).then((conn) => wrapPooledConnection(conn, () => Promise.resolve())), connectionOptions),
|
|
542
889
|
execute: (() => {
|
|
543
890
|
const ambientExecutor = sqlExecutorInAmbientConnection({
|
|
544
891
|
driverType,
|
|
545
892
|
connection: getExistingOrNewConnection
|
|
546
893
|
});
|
|
547
894
|
return {
|
|
548
|
-
query: (sql, opts) =>
|
|
549
|
-
batchQuery: (sqls, opts) =>
|
|
550
|
-
command: (sql, opts) =>
|
|
551
|
-
batchCommand: (sqls, opts) =>
|
|
895
|
+
query: (sql, opts) => executeIfOpen(() => ambientExecutor.query(sql, opts), opts),
|
|
896
|
+
batchQuery: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchQuery(sqls, opts), opts),
|
|
897
|
+
command: (sql, opts) => executeIfOpen(() => ambientExecutor.command(sql, opts), opts),
|
|
898
|
+
batchCommand: (sqls, opts) => executeIfOpen(() => ambientExecutor.batchCommand(sqls, opts), opts)
|
|
552
899
|
};
|
|
553
900
|
})(),
|
|
554
|
-
withConnection: (handle,
|
|
901
|
+
withConnection: (handle, options) => executeIfOpen((context) => executeInAmbientConnection((connection) => handle(connection, context), {
|
|
902
|
+
connection: getExistingOrNewConnection,
|
|
903
|
+
...options
|
|
904
|
+
}), options),
|
|
555
905
|
transaction: (transactionOptions) => {
|
|
556
906
|
if (closed) throw closedError();
|
|
557
907
|
return innerTransactionFactory.transaction(transactionOptions);
|
|
558
908
|
},
|
|
559
|
-
withTransaction: (handle, transactionOptions) =>
|
|
909
|
+
withTransaction: (handle, transactionOptions) => executeIfOpen((context) => innerTransactionFactory.withTransaction((tx) => handle(tx, context), transactionOptions), transactionOptions),
|
|
560
910
|
close: async (closeOptions) => {
|
|
561
911
|
if (closed) return;
|
|
562
912
|
closed = true;
|
|
563
|
-
|
|
913
|
+
await operationGuard.stop(closeOptions);
|
|
564
914
|
if (!connectionPromise) return;
|
|
565
915
|
const connection = await connectionPromise;
|
|
566
916
|
connectionPromise = null;
|
|
@@ -572,16 +922,22 @@ const createAlwaysNewConnectionPool = (options) => {
|
|
|
572
922
|
const { driverType, getConnection, connectionOptions } = options;
|
|
573
923
|
return createConnectionPool({
|
|
574
924
|
driverType,
|
|
575
|
-
getConnection: () => connectionOptions ? getConnection(connectionOptions) : getConnection()
|
|
925
|
+
getConnection: (context) => connectionOptions ? getConnection(connectionOptions, context) : getConnection(context)
|
|
576
926
|
});
|
|
577
927
|
};
|
|
578
928
|
const createConnectionPool = (pool) => {
|
|
579
929
|
const { driverType, getConnection } = pool;
|
|
580
|
-
const connection = "connection" in pool ? pool.connection : () =>
|
|
930
|
+
const connection = "connection" in pool ? pool.connection : (options) => {
|
|
931
|
+
Abort.throwIfAborted(options);
|
|
932
|
+
return Promise.resolve(getConnection({ abort: Abort.from(options) }));
|
|
933
|
+
};
|
|
581
934
|
return {
|
|
582
935
|
driverType,
|
|
583
936
|
connection,
|
|
584
|
-
withConnection: "withConnection" in pool ? pool.withConnection : (handle,
|
|
937
|
+
withConnection: "withConnection" in pool ? pool.withConnection : (handle, options) => executeInNewConnection((connection) => handle(connection, { abort: Abort.from(options) }), {
|
|
938
|
+
connection,
|
|
939
|
+
...options
|
|
940
|
+
}),
|
|
585
941
|
close: "close" in pool ? pool.close : () => Promise.resolve(),
|
|
586
942
|
execute: "execute" in pool ? pool.execute : sqlExecutorInNewConnection({
|
|
587
943
|
driverType,
|
|
@@ -1916,9 +2272,9 @@ const sqliteSQLExecutor = (driverType, serializer, formatter, errorMapper) => ({
|
|
|
1916
2272
|
|
|
1917
2273
|
//#endregion
|
|
1918
2274
|
//#region src/storage/sqlite/core/transactions/index.ts
|
|
1919
|
-
const sqliteTransaction = (driverType, connection,
|
|
1920
|
-
|
|
1921
|
-
const
|
|
2275
|
+
const sqliteTransaction = (driverType, connection, defaultOptions, serializer, defaultTransactionMode) => (getClient, options) => {
|
|
2276
|
+
const defaultTransactionOptions = typeof defaultOptions === "boolean" ? { allowNestedTransactions: defaultOptions } : defaultOptions;
|
|
2277
|
+
const allowNestedTransactions = options?.allowNestedTransactions ?? defaultTransactionOptions.allowNestedTransactions ?? false;
|
|
1922
2278
|
const tx = databaseTransaction({
|
|
1923
2279
|
begin: async () => {
|
|
1924
2280
|
const client = await getClient;
|
|
@@ -1946,23 +2302,28 @@ const sqliteTransaction = (driverType, connection, allowNestedTransactions, seri
|
|
|
1946
2302
|
},
|
|
1947
2303
|
releaseSavepoint: async (level) => {
|
|
1948
2304
|
await (await getClient).command(SQL`RELEASE transaction${SQL.plain(level.toString())}`);
|
|
2305
|
+
},
|
|
2306
|
+
rollbackToSavepoint: async (level) => {
|
|
2307
|
+
await (await getClient).command(SQL`ROLLBACK TO transaction${SQL.plain(level.toString())}`);
|
|
1949
2308
|
}
|
|
1950
2309
|
}, {
|
|
1951
2310
|
allowNestedTransactions,
|
|
1952
|
-
useSavepoints
|
|
2311
|
+
useSavepoints: options?.useSavepoints ?? defaultTransactionOptions.useSavepoints ?? false
|
|
1953
2312
|
});
|
|
1954
|
-
|
|
2313
|
+
const transaction = {
|
|
1955
2314
|
connection: connection(),
|
|
1956
2315
|
driverType,
|
|
1957
2316
|
begin: tx.begin,
|
|
1958
2317
|
commit: tx.commit,
|
|
1959
2318
|
rollback: tx.rollback,
|
|
1960
2319
|
execute: sqlExecutor(sqliteSQLExecutor(driverType, serializer), { connect: () => getClient }),
|
|
2320
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
1961
2321
|
_transactionOptions: {
|
|
1962
2322
|
...options,
|
|
1963
2323
|
allowNestedTransactions
|
|
1964
2324
|
}
|
|
1965
2325
|
};
|
|
2326
|
+
return transaction;
|
|
1966
2327
|
};
|
|
1967
2328
|
|
|
1968
2329
|
//#endregion
|
|
@@ -2007,7 +2368,7 @@ const sqliteAmbientClientConnection = (options) => {
|
|
|
2007
2368
|
return createAmbientConnection({
|
|
2008
2369
|
driverType,
|
|
2009
2370
|
client,
|
|
2010
|
-
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, allowNestedTransactions ?? false, serializer, defaultTransactionMode)),
|
|
2371
|
+
initTransaction: initTransaction ?? ((connection) => sqliteTransaction(driverType, connection, { allowNestedTransactions: allowNestedTransactions ?? false }, serializer, defaultTransactionMode)),
|
|
2011
2372
|
executor: ({ serializer }) => sqliteSQLExecutor(driverType, serializer, void 0, errorMapper),
|
|
2012
2373
|
serializer
|
|
2013
2374
|
});
|
|
@@ -2032,7 +2393,7 @@ const sqliteClientConnection = (options) => {
|
|
|
2032
2393
|
if (client && "close" in client && typeof client.close === "function") await client.close();
|
|
2033
2394
|
else if (client && "release" in client && typeof client.release === "function") client.release();
|
|
2034
2395
|
},
|
|
2035
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2396
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2036
2397
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2037
2398
|
serializer
|
|
2038
2399
|
});
|
|
@@ -2054,7 +2415,7 @@ const sqlitePoolClientConnection = (options) => {
|
|
|
2054
2415
|
driverType: options.driverType,
|
|
2055
2416
|
connect,
|
|
2056
2417
|
close: () => client !== null ? Promise.resolve(client.release()) : Promise.resolve(),
|
|
2057
|
-
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions
|
|
2418
|
+
initTransaction: (connection) => sqliteTransaction(options.driverType, connection, connectionOptions.transactionOptions ?? {}, serializer, connectionOptions.defaultTransactionMode),
|
|
2058
2419
|
executor: ({ serializer }) => sqliteSQLExecutor(options.driverType, serializer),
|
|
2059
2420
|
serializer
|
|
2060
2421
|
});
|
|
@@ -2424,7 +2785,7 @@ const d1Transaction = (connection, serializer, defaultOptions) => (getClient, op
|
|
|
2424
2785
|
client = await getClient;
|
|
2425
2786
|
return client;
|
|
2426
2787
|
};
|
|
2427
|
-
|
|
2788
|
+
const transaction = {
|
|
2428
2789
|
connection: connection(),
|
|
2429
2790
|
driverType: D1DriverType,
|
|
2430
2791
|
begin: async function() {
|
|
@@ -2468,8 +2829,10 @@ const d1Transaction = (connection, serializer, defaultOptions) => (getClient, op
|
|
|
2468
2829
|
if (!sessionClient) throw new Error("Transaction has not been started. Call begin() first.");
|
|
2469
2830
|
return Promise.resolve(sessionClient);
|
|
2470
2831
|
} }),
|
|
2832
|
+
withTransaction: (handle, options) => executeInNestedTransaction(transaction, handle, options),
|
|
2471
2833
|
_transactionOptions: options ?? {}
|
|
2472
2834
|
};
|
|
2835
|
+
return transaction;
|
|
2473
2836
|
};
|
|
2474
2837
|
|
|
2475
2838
|
//#endregion
|