@andrew_l/mongo-transaction 0.3.22 → 0.4.1
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.d.mts +86 -88
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +332 -431
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -9
- package/dist/index.cjs +0 -473
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -391
- package/dist/index.d.ts +0 -391
package/dist/index.mjs
CHANGED
|
@@ -1,464 +1,365 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { createContext, hasInjectionContext, withContext } from
|
|
3
|
-
import { MongoTransactionError } from
|
|
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
|
-
|
|
6
|
+
return hasInjectionContext() ? injectMongoSession(null) : null;
|
|
8
7
|
}
|
|
9
|
-
|
|
10
8
|
function onMongoSessionCommitted(...args) {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
37
|
+
"withTransaction",
|
|
38
|
+
"withTransactionControlled",
|
|
39
|
+
"withMongoTransaction"
|
|
49
40
|
]);
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
133
|
+
return new TransactionScope(fn);
|
|
153
134
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
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
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
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
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
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
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
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
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
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
|
-
|
|
267
|
+
return transaction?.state === "TRANSACTION_ABORTED";
|
|
321
268
|
}
|
|
322
269
|
function isTransactionCommittedEmpty(transaction) {
|
|
323
|
-
|
|
270
|
+
return transaction?.state === "TRANSACTION_COMMITTED_EMPTY";
|
|
324
271
|
}
|
|
325
272
|
function isMongoClientLike(value) {
|
|
326
|
-
|
|
273
|
+
return has(value, ["startSession"]) && isFunction(value.startSession);
|
|
327
274
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
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
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
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
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
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
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
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
|
-
|
|
364
|
+
|
|
365
|
+
//# sourceMappingURL=index.mjs.map
|