@andrew_l/mongo-transaction 0.3.22 → 0.4.0

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/index.mjs CHANGED
@@ -1,464 +1,365 @@
1
- import { defer, isPromise, logger, assert, catchError, asyncForEach, env, noop, isFunction, isEqual, has, deepDefaults, retryOnError } from '@andrew_l/toolkit';
2
- import { createContext, hasInjectionContext, withContext } from '@andrew_l/context';
3
- import { MongoTransactionError } from 'mongodb';
4
-
1
+ import { assert, asyncForEach, catchError, deepDefaults, defer, env, has, isEqual, isFunction, isPromise, logger, noop, retryOnError } from "@andrew_l/toolkit";
2
+ import { createContext, hasInjectionContext, withContext } from "@andrew_l/context";
3
+ import { MongoTransactionError } from "mongodb";
5
4
  const [injectMongoSession, provideMongoSession] = createContext("withMongoTransaction");
6
5
  function useMongoSession() {
7
- return hasInjectionContext() ? injectMongoSession(null) : null;
6
+ return hasInjectionContext() ? injectMongoSession(null) : null;
8
7
  }
9
-
10
8
  function onMongoSessionCommitted(...args) {
11
- let session;
12
- let fn;
13
- if (args.length === 2) {
14
- [session, fn] = args;
15
- } else {
16
- session = injectMongoSession();
17
- fn = args[0];
18
- }
19
- const q = defer();
20
- const onEnded = () => {
21
- if (!session.transaction.isCommitted) {
22
- return q.resolve(void 0);
23
- }
24
- try {
25
- const result = fn();
26
- if (isPromise(result)) {
27
- result.then((r) => q.resolve(r)).catch(q.reject);
28
- } else {
29
- q.resolve(result);
30
- }
31
- } catch (err) {
32
- q.reject(err);
33
- }
34
- };
35
- session.once("ended", onEnded);
36
- const cancel = () => {
37
- session.off("ended", onEnded);
38
- };
39
- return {
40
- promise: q.promise,
41
- cancel
42
- };
9
+ let session;
10
+ let fn;
11
+ if (args.length === 2) [session, fn] = args;
12
+ else {
13
+ session = injectMongoSession();
14
+ fn = args[0];
15
+ }
16
+ const q = defer();
17
+ const onEnded = () => {
18
+ if (!session.transaction.isCommitted) return q.resolve(void 0);
19
+ try {
20
+ const result = fn();
21
+ if (isPromise(result)) result.then((r) => q.resolve(r)).catch(q.reject);
22
+ else q.resolve(result);
23
+ } catch (err) {
24
+ q.reject(err);
25
+ }
26
+ };
27
+ session.once("ended", onEnded);
28
+ const cancel = () => {
29
+ session.off("ended", onEnded);
30
+ };
31
+ return {
32
+ promise: q.promise,
33
+ cancel
34
+ };
43
35
  }
44
-
45
36
  const [injectTransactionScope, provideTransactionScope] = createContext([
46
- "withTransaction",
47
- "withTransactionControlled",
48
- "withMongoTransaction"
37
+ "withTransaction",
38
+ "withTransactionControlled",
39
+ "withMongoTransaction"
49
40
  ]);
50
-
51
- class TransactionScope {
52
- constructor(fn) {
53
- this.fn = fn;
54
- const scope = this;
55
- this.run = function(...args) {
56
- const self = this === scope ? void 0 : this;
57
- return scope._run(self, ...args);
58
- };
59
- }
60
- /**
61
- * @internal
62
- */
63
- log = logger("TransactionScope");
64
- /**
65
- * @internal
66
- * Indicates currently ran scope
67
- */
68
- _active = false;
69
- /**
70
- * Last run error
71
- */
72
- error;
73
- /**
74
- * Last run result
75
- */
76
- result;
77
- run;
78
- /**
79
- * @internal
80
- */
81
- hooks = {
82
- committed: { byCursor: [], cursor: 0 },
83
- effects: { byCursor: [], cursor: 0 },
84
- rollbacks: { byCursor: [], cursor: 0 }
85
- };
86
- get active() {
87
- return this._active;
88
- }
89
- /**
90
- * @internal
91
- */
92
- async _run(self, ...args) {
93
- assert.ok(!this._active, "Cannot run while transaction active.");
94
- this.reset();
95
- this._active = true;
96
- const [cbError, cbResult] = await withContext(() => {
97
- provideTransactionScope(this);
98
- return catchError(this.fn.bind(self, ...args));
99
- })();
100
- if (cbError) {
101
- this.error = cbError;
102
- } else {
103
- const applyError = await effectsApply(this, "post");
104
- if (applyError) {
105
- this.error = applyError;
106
- }
107
- this.result = cbResult;
108
- }
109
- this._active = false;
110
- }
111
- async commit() {
112
- assert.ok(!this._active, "Cannot commit while transaction active.");
113
- assert.ok(!this.error, this.error);
114
- await asyncForEach(
115
- this.hooks.committed.byCursor,
116
- (h) => catchError(h.callback),
117
- { concurrency: 4 }
118
- );
119
- this.reset();
120
- this.clean();
121
- }
122
- async rollback() {
123
- assert.ok(!this._active, "Cannot rollback while transaction active.");
124
- const error = await effectsCleanup(this);
125
- if (error) {
126
- return Promise.reject(error);
127
- }
128
- await asyncForEach(
129
- this.hooks.rollbacks.byCursor,
130
- (h) => catchError(h.callback),
131
- { concurrency: 4 }
132
- );
133
- this.reset();
134
- this.clean();
135
- }
136
- reset() {
137
- assert.ok(!this._active, "Cannot reset while transaction active.");
138
- this.hooks.effects.cursor = 0;
139
- this.hooks.committed.cursor = 0;
140
- this.hooks.rollbacks.cursor = 0;
141
- this.result = void 0;
142
- this.error = void 0;
143
- }
144
- clean() {
145
- assert.ok(!this._active, "Cannot clean while transaction active.");
146
- this.hooks.effects = { byCursor: [], cursor: 0 };
147
- this.hooks.committed = { byCursor: [], cursor: 0 };
148
- this.hooks.rollbacks = { byCursor: [], cursor: 0 };
149
- }
150
- }
41
+ var TransactionScope = class {
42
+ fn;
43
+ log = logger("TransactionScope");
44
+ _active = false;
45
+ error;
46
+ result;
47
+ run;
48
+ hooks = {
49
+ committed: {
50
+ byCursor: [],
51
+ cursor: 0
52
+ },
53
+ effects: {
54
+ byCursor: [],
55
+ cursor: 0
56
+ },
57
+ rollbacks: {
58
+ byCursor: [],
59
+ cursor: 0
60
+ }
61
+ };
62
+ constructor(fn) {
63
+ this.fn = fn;
64
+ const scope = this;
65
+ this.run = function(...args) {
66
+ const self = this === scope ? void 0 : this;
67
+ return scope._run(self, ...args);
68
+ };
69
+ }
70
+ get active() {
71
+ return this._active;
72
+ }
73
+ _run(self, ...args) {
74
+ if (this._active) return Promise.reject(/* @__PURE__ */ new Error("Cannot commit while transaction active."));
75
+ this.reset();
76
+ this._active = true;
77
+ return Promise.resolve().then(() => withContext(() => {
78
+ provideTransactionScope(this);
79
+ return catchError(() => this.fn.call(self, ...args));
80
+ })()).then(({ 0: cbError, 1: cbResult }) => {
81
+ if (cbError) this.error = cbError;
82
+ else return effectsApply(this, "post").then((applyError) => {
83
+ if (applyError) this.error = applyError;
84
+ this.result = cbResult;
85
+ });
86
+ }).finally(() => {
87
+ this._active = false;
88
+ });
89
+ }
90
+ commit() {
91
+ if (this._active) return Promise.reject(/* @__PURE__ */ new Error("Cannot commit while transaction active."));
92
+ if (this.error) return Promise.reject(this.error);
93
+ return asyncForEach(this.hooks.committed.byCursor, (h) => catchError(h.callback), { concurrency: 4 }).then(() => {
94
+ this.reset();
95
+ this.clean();
96
+ });
97
+ }
98
+ rollback() {
99
+ if (this._active) return Promise.reject(/* @__PURE__ */ new Error("Cannot rollback while transaction active."));
100
+ return effectsCleanup(this).then((error) => {
101
+ if (error) return Promise.reject(error);
102
+ return asyncForEach(this.hooks.rollbacks.byCursor, (h) => catchError(h.callback), { concurrency: 4 }).then(() => {
103
+ this.reset();
104
+ this.clean();
105
+ });
106
+ });
107
+ }
108
+ reset() {
109
+ assert.ok(!this._active, "Cannot reset while transaction active.");
110
+ this.hooks.effects.cursor = 0;
111
+ this.hooks.committed.cursor = 0;
112
+ this.hooks.rollbacks.cursor = 0;
113
+ this.result = void 0;
114
+ this.error = void 0;
115
+ }
116
+ clean() {
117
+ assert.ok(!this._active, "Cannot clean while transaction active.");
118
+ this.hooks.effects = {
119
+ byCursor: [],
120
+ cursor: 0
121
+ };
122
+ this.hooks.committed = {
123
+ byCursor: [],
124
+ cursor: 0
125
+ };
126
+ this.hooks.rollbacks = {
127
+ byCursor: [],
128
+ cursor: 0
129
+ };
130
+ }
131
+ };
151
132
  function createTransactionScope(fn) {
152
- return new TransactionScope(fn);
133
+ return new TransactionScope(fn);
153
134
  }
154
- async function effectsApply(scope, reason = "no reason") {
155
- let error;
156
- const onComplete = (err) => void (error = err || error);
157
- await asyncForEach(
158
- scope.hooks.effects.byCursor,
159
- (effect) => applyEffect(scope, effect, reason).then(onComplete),
160
- {
161
- concurrency: 4
162
- }
163
- );
164
- return error;
135
+ function effectsApply(scope, reason = "no reason") {
136
+ let error;
137
+ const onComplete = (err) => void (error = err || error);
138
+ return asyncForEach(scope.hooks.effects.byCursor, (effect) => applyEffect(scope, effect, reason).then(onComplete), { concurrency: 4 }).then(() => error);
165
139
  }
166
- async function effectsCleanup(scope, reason = "no reason") {
167
- let error;
168
- const onComplete = (err) => void (error = err || error);
169
- await asyncForEach(
170
- scope.hooks.effects.byCursor,
171
- (effect) => cleanupEffect(scope, effect, reason).then(onComplete),
172
- {
173
- concurrency: 4
174
- }
175
- );
176
- return error;
140
+ function effectsCleanup(scope, reason = "no reason") {
141
+ let error;
142
+ const onComplete = (err) => void (error = err || error);
143
+ return asyncForEach(scope.hooks.effects.byCursor, (effect) => cleanupEffect(scope, effect, reason).then(onComplete), { concurrency: 4 }).then(() => error);
177
144
  }
178
- async function applyEffect(scope, effect, reason = "no reason") {
179
- if (effect.cleanup) return;
180
- scope.log.debug(
181
- "Effect name = %s, flush = %s, apply by %s",
182
- effect.name,
183
- effect.flush,
184
- reason
185
- );
186
- const [err, effectResult] = await catchError(effect.setup);
187
- if (err) {
188
- !env.isTest && scope.log.error(
189
- "Effect name = %s, flush = %s apply error",
190
- effect.name,
191
- effect.flush,
192
- err
193
- );
194
- return err;
195
- }
196
- effect.cleanup = isFunction(effectResult) ? effectResult : noop;
197
- return;
145
+ function applyEffect(scope, effect, reason = "no reason") {
146
+ if (effect.cleanup) return Promise.resolve(void 0);
147
+ scope.log.debug("Effect name = %s, flush = %s, apply by %s", effect.name, effect.flush, reason);
148
+ return Promise.resolve().then(() => catchError(effect.setup)).then(({ 0: err, 1: effectResult }) => {
149
+ if (err) {
150
+ !env.isTest && scope.log.error("Effect name = %s, flush = %s apply error", effect.name, effect.flush, err);
151
+ return err;
152
+ }
153
+ effect.cleanup = isFunction(effectResult) ? effectResult : noop;
154
+ });
198
155
  }
199
- async function cleanupEffect(scope, effect, reason = "no reason") {
200
- if (!effect.cleanup) return;
201
- scope.log.debug(
202
- "Effect name = %s, flush = %s, cleanup by %s",
203
- effect.name,
204
- effect.flush,
205
- reason
206
- );
207
- const [err] = await catchError(effect.cleanup);
208
- if (err) {
209
- !env.isTest && scope.log.error(
210
- "Effect name = %s, flush = %s, cleanup error",
211
- effect.name,
212
- effect.flush,
213
- err
214
- );
215
- return err;
216
- }
217
- effect.cleanup = void 0;
156
+ function cleanupEffect(scope, effect, reason = "no reason") {
157
+ if (!effect.cleanup) return Promise.resolve(void 0);
158
+ scope.log.debug("Effect name = %s, flush = %s, cleanup by %s", effect.name, effect.flush, reason);
159
+ return Promise.resolve().then(() => catchError(effect.cleanup)).then(({ 0: err }) => {
160
+ if (err) {
161
+ !env.isTest && scope.log.error("Effect name = %s, flush = %s, cleanup error", effect.name, effect.flush, err);
162
+ return err;
163
+ }
164
+ effect.cleanup = void 0;
165
+ });
218
166
  }
219
-
220
- async function useTransactionEffect(setup, options = {}) {
221
- const scope = injectTransactionScope();
222
- const { cursor, byCursor } = scope.hooks.effects;
223
- const effectConfig = byCursor[cursor] ?? {};
224
- const flush = options?.flush || "pre";
225
- const name = options?.name || `Effect #${cursor + 1}`;
226
- const dependencies = options.dependencies ?? [];
227
- const prevDependencies = effectConfig.dependencies;
228
- byCursor[cursor] = {
229
- ...effectConfig,
230
- dependencies,
231
- flush,
232
- name,
233
- setup
234
- };
235
- if (dependencies && prevDependencies) {
236
- if (!isEqual(dependencies, prevDependencies)) {
237
- scope.log.debug(
238
- "Effect name = %s, flush = %s, caused by dependencies",
239
- name,
240
- flush,
241
- {
242
- prevDependencies,
243
- newDependencies: dependencies
244
- }
245
- );
246
- await scheduleEffect(scope, cursor);
247
- }
248
- } else {
249
- scope.log.debug(
250
- "Effect name = %s, flush = %s, caused by missing dependencies",
251
- name,
252
- flush
253
- );
254
- await scheduleEffect(scope, cursor);
255
- }
256
- scope.hooks.effects.cursor++;
167
+ function useTransactionEffect(setup, options = {}) {
168
+ const scope = injectTransactionScope();
169
+ const { cursor, byCursor } = scope.hooks.effects;
170
+ const effectConfig = byCursor[cursor] ?? {};
171
+ const flush = options?.flush || "pre";
172
+ const name = options?.name || `Effect #${cursor + 1}`;
173
+ const dependencies = options.dependencies ?? [];
174
+ const prevDependencies = effectConfig.dependencies;
175
+ byCursor[cursor] = {
176
+ ...effectConfig,
177
+ dependencies,
178
+ flush,
179
+ name,
180
+ setup
181
+ };
182
+ if (dependencies && prevDependencies) {
183
+ if (!isEqual(dependencies, prevDependencies)) {
184
+ scope.log.debug("Effect name = %s, flush = %s, caused by dependencies", name, flush, {
185
+ prevDependencies,
186
+ newDependencies: dependencies
187
+ });
188
+ return scheduleEffect(scope, cursor).then(() => {
189
+ scope.hooks.effects.cursor++;
190
+ });
191
+ }
192
+ } else {
193
+ scope.log.debug("Effect name = %s, flush = %s, caused by missing dependencies", name, flush);
194
+ return scheduleEffect(scope, cursor).then(() => {
195
+ scope.hooks.effects.cursor++;
196
+ });
197
+ }
198
+ return Promise.resolve();
257
199
  }
258
- async function scheduleEffect(scope, cursor) {
259
- const { byCursor } = scope.hooks.effects;
260
- const effect = byCursor[cursor];
261
- const cleanupErr = await cleanupEffect(scope, effect, "schedule pre");
262
- if (cleanupErr) {
263
- return Promise.reject(cleanupErr);
264
- }
265
- if (effect.flush === "pre") {
266
- const err = await applyEffect(scope, effect, "schedule pre");
267
- if (err) {
268
- return Promise.reject(err);
269
- }
270
- }
200
+ function scheduleEffect(scope, cursor) {
201
+ const { byCursor } = scope.hooks.effects;
202
+ const effect = byCursor[cursor];
203
+ return cleanupEffect(scope, effect, "schedule pre").then((cleanupErr) => {
204
+ if (cleanupErr) return Promise.reject(cleanupErr);
205
+ if (effect.flush === "pre") return applyEffect(scope, effect, "schedule pre").then((err) => {
206
+ if (err) return Promise.reject(err);
207
+ });
208
+ });
271
209
  }
272
-
273
210
  function onCommitted(callback, dependencies) {
274
- const scope = injectTransactionScope();
275
- const { cursor, byCursor } = scope.hooks.committed;
276
- const config = byCursor[cursor];
277
- if (dependencies && config?.dependencies) {
278
- if (!isEqual(dependencies, config.dependencies)) {
279
- scope.log.debug("OnCommitted caused by dependencies", {
280
- prevDependencies: config.dependencies,
281
- newDependencies: dependencies,
282
- cursor
283
- });
284
- byCursor[cursor] = { callback, dependencies };
285
- }
286
- } else {
287
- scope.log.debug("OnCommitted caused by missing dependencies", { cursor });
288
- byCursor[cursor] = { callback, dependencies };
289
- }
290
- scope.hooks.committed.cursor++;
291
- return () => {
292
- byCursor[cursor].callback = noop;
293
- };
211
+ const scope = injectTransactionScope();
212
+ const { cursor, byCursor } = scope.hooks.committed;
213
+ const config = byCursor[cursor];
214
+ if (dependencies && config?.dependencies) {
215
+ if (!isEqual(dependencies, config.dependencies)) {
216
+ scope.log.debug("OnCommitted caused by dependencies", {
217
+ prevDependencies: config.dependencies,
218
+ newDependencies: dependencies,
219
+ cursor
220
+ });
221
+ byCursor[cursor] = {
222
+ callback,
223
+ dependencies
224
+ };
225
+ }
226
+ } else {
227
+ scope.log.debug("OnCommitted caused by missing dependencies", { cursor });
228
+ byCursor[cursor] = {
229
+ callback,
230
+ dependencies
231
+ };
232
+ }
233
+ scope.hooks.committed.cursor++;
234
+ return () => {
235
+ byCursor[cursor].callback = noop;
236
+ };
294
237
  }
295
-
296
238
  function onRollback(callback, dependencies) {
297
- const scope = injectTransactionScope();
298
- const { cursor, byCursor } = scope.hooks.rollbacks;
299
- const config = byCursor[cursor];
300
- if (dependencies && config?.dependencies) {
301
- if (!isEqual(dependencies, config.dependencies)) {
302
- scope.log.debug("OnCommitted caused by dependencies", {
303
- prevDependencies: config.dependencies,
304
- newDependencies: dependencies,
305
- cursor
306
- });
307
- byCursor[cursor] = { callback, dependencies };
308
- }
309
- } else {
310
- scope.log.debug("OnCommitted caused by missing dependencies", { cursor });
311
- byCursor[cursor] = { callback, dependencies };
312
- }
313
- scope.hooks.committed.cursor++;
314
- return () => {
315
- byCursor[cursor].callback = noop;
316
- };
239
+ const scope = injectTransactionScope();
240
+ const { cursor, byCursor } = scope.hooks.rollbacks;
241
+ const config = byCursor[cursor];
242
+ if (dependencies && config?.dependencies) {
243
+ if (!isEqual(dependencies, config.dependencies)) {
244
+ scope.log.debug("OnCommitted caused by dependencies", {
245
+ prevDependencies: config.dependencies,
246
+ newDependencies: dependencies,
247
+ cursor
248
+ });
249
+ byCursor[cursor] = {
250
+ callback,
251
+ dependencies
252
+ };
253
+ }
254
+ } else {
255
+ scope.log.debug("OnCommitted caused by missing dependencies", { cursor });
256
+ byCursor[cursor] = {
257
+ callback,
258
+ dependencies
259
+ };
260
+ }
261
+ scope.hooks.committed.cursor++;
262
+ return () => {
263
+ byCursor[cursor].callback = noop;
264
+ };
317
265
  }
318
-
319
266
  function isTransactionAborted(transaction) {
320
- return transaction?.state === "TRANSACTION_ABORTED";
267
+ return transaction?.state === "TRANSACTION_ABORTED";
321
268
  }
322
269
  function isTransactionCommittedEmpty(transaction) {
323
- return transaction?.state === "TRANSACTION_COMMITTED_EMPTY";
270
+ return transaction?.state === "TRANSACTION_COMMITTED_EMPTY";
324
271
  }
325
272
  function isMongoClientLike(value) {
326
- return has(value, ["startSession"]) && isFunction(value.startSession);
273
+ return has(value, ["startSession"]) && isFunction(value.startSession);
327
274
  }
328
-
329
- const DEF_SESSION_OPTIONS = Object.freeze({
330
- defaultTransactionOptions: {
331
- readPreference: "primary",
332
- readConcern: { level: "local" },
333
- writeConcern: { w: "majority" }
334
- }
335
- });
275
+ const DEF_SESSION_OPTIONS = Object.freeze({ defaultTransactionOptions: {
276
+ readPreference: "primary",
277
+ readConcern: { level: "local" },
278
+ writeConcern: { w: "majority" }
279
+ } });
336
280
  function withMongoTransaction(connectionOrOptions, maybeFn, maybeOptions) {
337
- const {
338
- connection: connectionValue,
339
- fn,
340
- sessionOptions = {},
341
- timeoutMS
342
- } = prepareOptions(connectionOrOptions, maybeFn, maybeOptions);
343
- return async function(...args) {
344
- const connection = isFunction(connectionValue) ? await connectionValue() : connectionValue;
345
- const session = await connection.startSession(
346
- sessionOptions
347
- );
348
- const scope = createTransactionScope(function(...args2) {
349
- provideMongoSession(session);
350
- return fn.call(this, session, ...args2);
351
- });
352
- const timeoutAt = timeoutMS ? Date.now() + timeoutMS : 0;
353
- const timeoutError = new MongoTransactionError(
354
- "Transaction client-side timeout"
355
- );
356
- let [transactionError, transactionResult] = await catchError(
357
- () => session.withTransaction(async () => {
358
- if (timeoutAt && timeoutAt < Date.now()) {
359
- return Promise.reject(timeoutError);
360
- }
361
- await scope.run.apply(this, args);
362
- if (scope.error) {
363
- return Promise.reject(scope.error);
364
- }
365
- })
366
- );
367
- const { result } = scope;
368
- await session.endSession().catch(noop);
369
- if (transactionResult === void 0 && isTransactionCommittedEmpty(session.transaction)) ; else if (isTransactionAborted(session.transaction) && transactionResult === void 0 && transactionError === void 0) {
370
- transactionError = new MongoTransactionError(
371
- "Transaction is explicitly aborted"
372
- );
373
- }
374
- if (transactionError) {
375
- await scope.rollback();
376
- return Promise.reject(transactionError);
377
- }
378
- await scope.commit();
379
- return result;
380
- };
281
+ const { connection: connectionValue, fn, sessionOptions = {}, timeoutMS } = prepareOptions(connectionOrOptions, maybeFn, maybeOptions);
282
+ return function(...args) {
283
+ return Promise.resolve().then(() => isFunction(connectionValue) ? connectionValue() : connectionValue).then((connection) => connection.startSession(sessionOptions)).then((session) => {
284
+ const scope = createTransactionScope(function(...args) {
285
+ provideMongoSession(session);
286
+ return fn.call(this, session, ...args);
287
+ });
288
+ const timeoutAt = timeoutMS ? Date.now() + timeoutMS : 0;
289
+ const timeoutError = new MongoTransactionError("Transaction client-side timeout");
290
+ return catchError(() => session.withTransaction(() => {
291
+ if (timeoutAt && timeoutAt < Date.now()) return Promise.reject(timeoutError);
292
+ return Promise.resolve().then(() => scope.run.apply(this, args)).then(() => {
293
+ if (scope.error) return Promise.reject(scope.error);
294
+ });
295
+ })).then(({ 0: transactionError, 1: transactionResult }) => {
296
+ const { result } = scope;
297
+ return session.endSession().catch(noop).then(() => {
298
+ if (transactionResult === void 0 && isTransactionCommittedEmpty(session.transaction)) {} else if (isTransactionAborted(session.transaction) && transactionResult === void 0 && transactionError === void 0) transactionError = new MongoTransactionError("Transaction is explicitly aborted");
299
+ if (transactionError) return scope.rollback().then(() => Promise.reject(transactionError));
300
+ return scope.commit().then(() => result);
301
+ });
302
+ });
303
+ });
304
+ };
381
305
  }
382
306
  function prepareOptions(connectionOrOptions, maybeFn, maybeOptions) {
383
- let options;
384
- if (isFunction(connectionOrOptions) || isMongoClientLike(connectionOrOptions)) {
385
- options = {
386
- ...maybeOptions || {},
387
- connection: connectionOrOptions,
388
- fn: maybeFn
389
- };
390
- } else {
391
- options = connectionOrOptions;
392
- }
393
- return deepDefaults(options, {
394
- sessionOptions: DEF_SESSION_OPTIONS
395
- });
307
+ let options;
308
+ if (isFunction(connectionOrOptions) || isMongoClientLike(connectionOrOptions)) options = {
309
+ ...maybeOptions || {},
310
+ connection: connectionOrOptions,
311
+ fn: maybeFn
312
+ };
313
+ else options = connectionOrOptions;
314
+ return deepDefaults(options, { sessionOptions: DEF_SESSION_OPTIONS });
396
315
  }
397
-
398
- function withTransaction(fn, {
399
- beforeRetryCallback,
400
- shouldRetryBasedOnError = () => true,
401
- maxAttempts,
402
- maxRetriesNumber = 5,
403
- delayFactor = 0,
404
- delayMaxMs = 1e3,
405
- delayMinMs = 100
406
- } = {}) {
407
- return async function(...args) {
408
- const scope = createTransactionScope(fn);
409
- await retryOnError(
410
- {
411
- beforeRetryCallback,
412
- shouldRetryBasedOnError,
413
- maxAttempts,
414
- maxRetriesNumber,
415
- delayFactor,
416
- delayMaxMs,
417
- delayMinMs
418
- },
419
- async () => {
420
- await scope.run.apply(this, args);
421
- if (scope.error) {
422
- return Promise.reject(scope.error);
423
- }
424
- }
425
- )().catch(noop);
426
- const { error, result } = scope;
427
- if (error) {
428
- await scope.rollback();
429
- return Promise.reject(error);
430
- } else {
431
- await scope.commit();
432
- }
433
- return result;
434
- };
316
+ function withTransaction(fn, { beforeRetryCallback, shouldRetryBasedOnError = () => true, maxAttempts, maxRetriesNumber = 5, delayFactor = 0, delayMaxMs = 1e3, delayMinMs = 100 } = {}) {
317
+ return function(...args) {
318
+ const scope = createTransactionScope(fn);
319
+ return retryOnError({
320
+ beforeRetryCallback,
321
+ shouldRetryBasedOnError,
322
+ maxAttempts,
323
+ maxRetriesNumber,
324
+ delayFactor,
325
+ delayMaxMs,
326
+ delayMinMs
327
+ }, () => {
328
+ return Promise.resolve().then(() => scope.run.apply(this, args)).then(() => {
329
+ if (scope.error) return Promise.reject(scope.error);
330
+ });
331
+ })().catch(noop).then(() => {
332
+ const { error, result } = scope;
333
+ if (error) return scope.rollback().then(() => Promise.reject(error));
334
+ return scope.commit().then(() => result);
335
+ });
336
+ };
435
337
  }
436
-
437
338
  function withTransactionControlled(fn) {
438
- const scope = createTransactionScope(fn);
439
- const controlled = {
440
- run(...args) {
441
- const self = this === controlled ? void 0 : this;
442
- return scope.run.apply(self, args);
443
- },
444
- commit() {
445
- return scope.commit();
446
- },
447
- rollback() {
448
- return scope.rollback();
449
- },
450
- get active() {
451
- return scope.active;
452
- },
453
- get result() {
454
- return scope.result;
455
- },
456
- get error() {
457
- return scope.error;
458
- }
459
- };
460
- return controlled;
339
+ const scope = createTransactionScope(fn);
340
+ const controlled = {
341
+ run(...args) {
342
+ const self = this === controlled ? void 0 : this;
343
+ return scope.run.apply(self, args);
344
+ },
345
+ commit() {
346
+ return scope.commit();
347
+ },
348
+ rollback() {
349
+ return scope.rollback();
350
+ },
351
+ get active() {
352
+ return scope.active;
353
+ },
354
+ get result() {
355
+ return scope.result;
356
+ },
357
+ get error() {
358
+ return scope.error;
359
+ }
360
+ };
361
+ return controlled;
461
362
  }
462
-
463
363
  export { onCommitted, onMongoSessionCommitted, onRollback, useMongoSession, useTransactionEffect, withMongoTransaction, withTransaction, withTransactionControlled };
464
- //# sourceMappingURL=index.mjs.map
364
+
365
+ //# sourceMappingURL=index.mjs.map