@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.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.d.ts
DELETED
|
@@ -1,391 +0,0 @@
|
|
|
1
|
-
import { Awaitable, Fn, AnyFunction, RetryOnErrorConfig } from '@andrew_l/toolkit';
|
|
2
|
-
import { ClientSession, ClientSessionOptions } from 'mongodb';
|
|
3
|
-
|
|
4
|
-
type OnMongoSessionCommittedResult<T> = {
|
|
5
|
-
/**
|
|
6
|
-
* Executes the provided function upon transaction commit.
|
|
7
|
-
*
|
|
8
|
-
* Returns `T` if the transaction is committed and the function completes successfully.
|
|
9
|
-
*
|
|
10
|
-
* Returns `undefined` if the transaction is explicitly aborted or ends without committing.
|
|
11
|
-
*
|
|
12
|
-
* Rejects if the function throws an error.
|
|
13
|
-
*/
|
|
14
|
-
promise: Promise<T | undefined>;
|
|
15
|
-
cancel: () => void;
|
|
16
|
-
};
|
|
17
|
-
/**
|
|
18
|
-
* Executes the provided function upon transaction commit.
|
|
19
|
-
*
|
|
20
|
-
* Returns `T` if the transaction is committed and the function completes successfully.
|
|
21
|
-
*
|
|
22
|
-
* Returns `false` if the transaction ends without committing.
|
|
23
|
-
*
|
|
24
|
-
* Rejects if the function throws an error.
|
|
25
|
-
*
|
|
26
|
-
* @example
|
|
27
|
-
* const { promise } = onTransactionCommitted(async () => {
|
|
28
|
-
* console.info('Transaction committed successfully!');
|
|
29
|
-
* return Math.random(); // Random value generated after commit
|
|
30
|
-
* });
|
|
31
|
-
*
|
|
32
|
-
* promise.then(result => {
|
|
33
|
-
* if (result !== false) {
|
|
34
|
-
* console.info('Handler result:', result); // e.g., Handler result: 0.07576196837476501
|
|
35
|
-
* }
|
|
36
|
-
* });
|
|
37
|
-
*
|
|
38
|
-
* @group Hooks
|
|
39
|
-
*/
|
|
40
|
-
declare function onMongoSessionCommitted<T>(fn: () => Awaitable<T>): OnMongoSessionCommittedResult<T>;
|
|
41
|
-
declare function onMongoSessionCommitted<T>(session: ClientSession, fn: () => Awaitable<T>): OnMongoSessionCommittedResult<T>;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Returns the current transaction session if executed within `withMongoTransaction()` otherwise returns `null`
|
|
45
|
-
*
|
|
46
|
-
* @example
|
|
47
|
-
* async function createAlert() {
|
|
48
|
-
* const session = useMongoSession();
|
|
49
|
-
*
|
|
50
|
-
* await db.alerts.insertOne(
|
|
51
|
-
* { title: 'Order Created' },
|
|
52
|
-
* { session: session ?? undefined }
|
|
53
|
-
* );
|
|
54
|
-
* }
|
|
55
|
-
*
|
|
56
|
-
* @group Hooks
|
|
57
|
-
*/
|
|
58
|
-
declare function useMongoSession(): ClientSession | null;
|
|
59
|
-
|
|
60
|
-
interface TransactionEffect {
|
|
61
|
-
/**
|
|
62
|
-
* Specifies when the transaction effect should run:
|
|
63
|
-
*
|
|
64
|
-
* `pre` - execute immediately
|
|
65
|
-
*
|
|
66
|
-
* `post` - execute before transaction commit
|
|
67
|
-
*
|
|
68
|
-
* @default: "pre"
|
|
69
|
-
*/
|
|
70
|
-
flush: 'pre' | 'post';
|
|
71
|
-
/**
|
|
72
|
-
* Setup effect function. You can return a cleanup callback to be used as a rollback.
|
|
73
|
-
*/
|
|
74
|
-
setup: EffectCallback;
|
|
75
|
-
/**
|
|
76
|
-
* Cleanup function.
|
|
77
|
-
*/
|
|
78
|
-
cleanup?: EffectCleanup;
|
|
79
|
-
/**
|
|
80
|
-
* Useful for debugging execution logs.
|
|
81
|
-
*/
|
|
82
|
-
name?: string;
|
|
83
|
-
dependencies?: readonly any[];
|
|
84
|
-
}
|
|
85
|
-
type EffectCallback = () => Awaitable<EffectCleanup | void>;
|
|
86
|
-
type EffectCleanup = () => Awaitable<void>;
|
|
87
|
-
type OnCommittedCallback = () => Awaitable<void>;
|
|
88
|
-
type OnRollbackCallback = () => Awaitable<void>;
|
|
89
|
-
|
|
90
|
-
type UseTransactionEffectOptions = Partial<Pick<TransactionEffect, 'name' | 'flush' | 'dependencies'>>;
|
|
91
|
-
/**
|
|
92
|
-
* Executes a transactional effect with cleanup on error or rollback.
|
|
93
|
-
*
|
|
94
|
-
* Ensures the `callback` function is executed only once per transaction, even during retries.
|
|
95
|
-
* On errors or dependency changes, the cleanup logic is invoked before re-execution to maintain consistency.
|
|
96
|
-
*
|
|
97
|
-
* @param setup A function defining the transactional effect. It is guaranteed to run once per transaction
|
|
98
|
-
* and may be re-executed after cleanup if dependencies change.
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* const confirmOrder = withMongoTransaction({
|
|
102
|
-
* connection: () => mongoose.connection.getClient(),
|
|
103
|
-
* async fn(session) {
|
|
104
|
-
* // Register an alert as a transactional effect
|
|
105
|
-
* await useTransactionEffect(async () => {
|
|
106
|
-
* const alertId = await alertService.create({
|
|
107
|
-
* title: `Order Confirmed: ${orderId}`,
|
|
108
|
-
* });
|
|
109
|
-
*
|
|
110
|
-
* // Define cleanup logic to remove the alert on rollback
|
|
111
|
-
* return () => alertService.removeById(alertId);
|
|
112
|
-
* });
|
|
113
|
-
*
|
|
114
|
-
* // Simulate order processing (e.g., database updates)
|
|
115
|
-
* await db
|
|
116
|
-
* .collection('orders')
|
|
117
|
-
* .updateOne({ orderId }, { $set: { status: 'confirmed' } }, { session });
|
|
118
|
-
*
|
|
119
|
-
* // Simulate an error to test rollback
|
|
120
|
-
* throw new Error('Simulated transaction failure');
|
|
121
|
-
* },
|
|
122
|
-
* });
|
|
123
|
-
*
|
|
124
|
-
* @group Hooks
|
|
125
|
-
*/
|
|
126
|
-
declare function useTransactionEffect(setup: TransactionEffect['setup'], options?: UseTransactionEffectOptions): Promise<void>;
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* Registers a callback to be executed upon transaction commitment, with support
|
|
130
|
-
* for dependency-based updates.
|
|
131
|
-
*
|
|
132
|
-
* This function is used within a transaction scope to perform specific actions
|
|
133
|
-
* when a transaction is committed. If dependencies are provided, the callback
|
|
134
|
-
* is re-registered only if the dependencies have changed. Otherwise, the
|
|
135
|
-
* callback is registered unconditionally.
|
|
136
|
-
*
|
|
137
|
-
* @param {OnCommittedCallback} callback - The function to be executed upon
|
|
138
|
-
* transaction commitment.
|
|
139
|
-
* @param {readonly any[]} [dependencies=[]] - An optional array of dependencies
|
|
140
|
-
* to determine if the callback should be re-registered. If the dependencies
|
|
141
|
-
* differ from the previously registered ones, the callback is updated.
|
|
142
|
-
* @returns {Fn} A cleanup function to cancel event listener.
|
|
143
|
-
*
|
|
144
|
-
* @example
|
|
145
|
-
* // Basic usage without dependencies
|
|
146
|
-
* onCommitted(() => {
|
|
147
|
-
* console.log('Transaction committed!');
|
|
148
|
-
* });
|
|
149
|
-
*
|
|
150
|
-
* @example
|
|
151
|
-
* // Using dependencies
|
|
152
|
-
* count++;
|
|
153
|
-
* onCommitted(() => {
|
|
154
|
-
* console.log(`Commit #${count}`);
|
|
155
|
-
* }, [count]);
|
|
156
|
-
*
|
|
157
|
-
* @example
|
|
158
|
-
* // Cancel by request
|
|
159
|
-
* const cancel = onCommitted(() => {
|
|
160
|
-
* console.log('This will run only once!');
|
|
161
|
-
* });
|
|
162
|
-
*
|
|
163
|
-
* if (orderReceived) {
|
|
164
|
-
* cancel(); // Prevents onCommitted from running
|
|
165
|
-
* }
|
|
166
|
-
*
|
|
167
|
-
* @group Hooks
|
|
168
|
-
*/
|
|
169
|
-
declare function onCommitted(callback: OnCommittedCallback, dependencies?: readonly any[]): Fn;
|
|
170
|
-
|
|
171
|
-
/**
|
|
172
|
-
* Registers a callback to be executed upon transaction rollback, with support
|
|
173
|
-
* for dependency-based updates.
|
|
174
|
-
*
|
|
175
|
-
* This function is used within a transaction scope to perform specific actions
|
|
176
|
-
* when a transaction is rolled back. If dependencies are provided, the callback
|
|
177
|
-
* is re-registered only if the dependencies have changed. Otherwise, the
|
|
178
|
-
* callback is registered unconditionally.
|
|
179
|
-
*
|
|
180
|
-
* @param {OnRollbackCallback} callback - The function to be executed upon
|
|
181
|
-
* transaction rollback.
|
|
182
|
-
* @param {readonly any[]} [dependencies=[]] - An optional array of dependencies
|
|
183
|
-
* to determine if the callback should be re-registered. If the dependencies
|
|
184
|
-
* differ from the previously registered ones, the callback is updated.
|
|
185
|
-
* @returns {Fn} A cleanup function to cancel event listener.
|
|
186
|
-
*
|
|
187
|
-
* @example
|
|
188
|
-
* // Basic usage without dependencies
|
|
189
|
-
* onRollback(() => {
|
|
190
|
-
* console.log('Transaction rolled back!');
|
|
191
|
-
* });
|
|
192
|
-
*
|
|
193
|
-
* @example
|
|
194
|
-
* // Using dependencies
|
|
195
|
-
* count++;
|
|
196
|
-
* onRollback(() => {
|
|
197
|
-
* console.log(`Rollback detected, flag is ${flag}`);
|
|
198
|
-
* }, [count]);
|
|
199
|
-
*
|
|
200
|
-
* @example
|
|
201
|
-
* // Cancel by request
|
|
202
|
-
* const cancel = onRollback(() => {
|
|
203
|
-
* console.log('This will run only once on rollback!');
|
|
204
|
-
* });
|
|
205
|
-
*
|
|
206
|
-
* if (orderReceived) {
|
|
207
|
-
* cancel(); // Prevents onRollback from running
|
|
208
|
-
* }
|
|
209
|
-
*
|
|
210
|
-
* @group Hooks
|
|
211
|
-
*/
|
|
212
|
-
declare function onRollback(callback: OnRollbackCallback, dependencies?: readonly any[]): Fn;
|
|
213
|
-
|
|
214
|
-
interface MongoClientLike {
|
|
215
|
-
startSession(options: Record<string, any>): ClientSessionLike;
|
|
216
|
-
}
|
|
217
|
-
interface ClientSessionLike {
|
|
218
|
-
withTransaction(fn: AnyFunction): Promise<any>;
|
|
219
|
-
endSession(): Promise<void>;
|
|
220
|
-
}
|
|
221
|
-
type ConnectionValue = MongoClientLike | (() => Awaitable<MongoClientLike>);
|
|
222
|
-
type Callback<T, K = any, Args extends Array<any> = any[]> = (this: K, session: ClientSession, ...args: Args) => Awaitable<T>;
|
|
223
|
-
interface WithMongoTransactionOptions<T, K = any, Args extends Array<any> = any[]> {
|
|
224
|
-
/**
|
|
225
|
-
* Mongodb connection getter
|
|
226
|
-
*/
|
|
227
|
-
connection: ConnectionValue;
|
|
228
|
-
/**
|
|
229
|
-
* Transaction session options
|
|
230
|
-
*
|
|
231
|
-
* @default: {
|
|
232
|
-
* defaultTransactionOptions: {
|
|
233
|
-
* readPreference: 'primary',
|
|
234
|
-
* readConcern: { level: 'local' },
|
|
235
|
-
* writeConcern: { w: 'majority' },
|
|
236
|
-
* }
|
|
237
|
-
* }
|
|
238
|
-
*/
|
|
239
|
-
sessionOptions?: ClientSessionOptions;
|
|
240
|
-
/**
|
|
241
|
-
* Configures a timeoutMS expiry for the entire withTransactionCallback.
|
|
242
|
-
*
|
|
243
|
-
* @remarks
|
|
244
|
-
* - The remaining timeout will not be applied to callback operations that do not use the ClientSession.
|
|
245
|
-
* - Overriding timeoutMS for operations executed using the explicit session inside the provided callback will result in a client-side error.
|
|
246
|
-
*/
|
|
247
|
-
timeoutMS?: number;
|
|
248
|
-
/**
|
|
249
|
-
* Transaction function that will be executed
|
|
250
|
-
*
|
|
251
|
-
* ⚠️ Possible several times!
|
|
252
|
-
*/
|
|
253
|
-
fn: Callback<T, K, Args>;
|
|
254
|
-
}
|
|
255
|
-
type WithMongoTransactionWrapped<T, K = any, Args extends Array<any> = any[]> = (this: K, ...args: Args) => Promise<T>;
|
|
256
|
-
/**
|
|
257
|
-
* Runs a provided callback within a transaction, retrying either the commitTransaction operation or entire transaction as needed (and when the error permits) to better ensure that the transaction can complete successfully.
|
|
258
|
-
*
|
|
259
|
-
* Passes the session as the function's first argument or via `useMongoSession()` hook
|
|
260
|
-
*
|
|
261
|
-
* @example
|
|
262
|
-
* const executeTransaction = withMongoTransaction({
|
|
263
|
-
* connection: () => mongoose.connection.getClient(),
|
|
264
|
-
* async fn() {
|
|
265
|
-
* const session = useMongoSession();
|
|
266
|
-
* const orders = mongoose.connection.collection('orders');
|
|
267
|
-
*
|
|
268
|
-
* const { modifiedCount } = await orders.updateMany(
|
|
269
|
-
* { status: 'pending' },
|
|
270
|
-
* { $set: { status: 'confirmed' } },
|
|
271
|
-
* { session },
|
|
272
|
-
* );
|
|
273
|
-
* },
|
|
274
|
-
* });
|
|
275
|
-
*
|
|
276
|
-
* @group Main
|
|
277
|
-
*/
|
|
278
|
-
declare function withMongoTransaction<T, K = any, Args extends Array<any> = any[]>(options: WithMongoTransactionOptions<T, K, Args>): WithMongoTransactionWrapped<T, K, Args>;
|
|
279
|
-
/**
|
|
280
|
-
* Runs a provided callback within a transaction, retrying either the commitTransaction operation or entire transaction as needed (and when the error permits) to better ensure that the transaction can complete successfully.
|
|
281
|
-
*
|
|
282
|
-
* Passes the session as the function's first argument or via `useMongoSession()` hook
|
|
283
|
-
*
|
|
284
|
-
* @example
|
|
285
|
-
* const executeTransaction = withMongoTransaction(mongoose.connection.getClient(), async () => {
|
|
286
|
-
* const session = useMongoSession();
|
|
287
|
-
* const orders = mongoose.connection.collection('orders');
|
|
288
|
-
*
|
|
289
|
-
* const { modifiedCount } = await orders.updateMany(
|
|
290
|
-
* { status: 'pending' },
|
|
291
|
-
* { $set: { status: 'confirmed' } },
|
|
292
|
-
* { session },
|
|
293
|
-
* );
|
|
294
|
-
* });
|
|
295
|
-
*/
|
|
296
|
-
declare function withMongoTransaction<T, K = any, Args extends Array<any> = any[]>(connection: ConnectionValue, fn: Callback<T, K, Args>, options?: Omit<WithMongoTransactionOptions<any>, 'fn' | 'connection'>): WithMongoTransactionWrapped<T, K, Args>;
|
|
297
|
-
|
|
298
|
-
interface WithTransactionOptions extends Partial<RetryOnErrorConfig> {
|
|
299
|
-
}
|
|
300
|
-
/**
|
|
301
|
-
* Wraps a function with transaction context, enabling retry logic and transactional effects.
|
|
302
|
-
*
|
|
303
|
-
* The wrapped function may be executed multiple times (up to `maxRetriesNumber`) to ensure
|
|
304
|
-
* all side effects complete successfully. If the retries are exhausted without success,
|
|
305
|
-
* registered cleanup functions will be executed to undo any applied effects.
|
|
306
|
-
*
|
|
307
|
-
* This utility is useful for managing transactional side effects, such as
|
|
308
|
-
* updates to external systems, and ensures proper cleanup in case of failure.
|
|
309
|
-
*
|
|
310
|
-
* Additionally, this enables hooks like `useTransactionEffect()`, which allows
|
|
311
|
-
* defining effects with automatic rollback mechanisms.
|
|
312
|
-
*
|
|
313
|
-
* @param fn - The target function to wrap with transaction handling.
|
|
314
|
-
* @param [options] - Configuration options for the transaction handling.
|
|
315
|
-
* @param [options.beforeRetryCallback] - An optional callback to execute before each retry attempt.
|
|
316
|
-
* @param [options.shouldRetryBasedOnError] - A predicate to determine if a retry should occur based on the thrown error. Defaults to always retry.
|
|
317
|
-
* @param [options.maxRetriesNumber=5] - The maximum number of retries before failing the transaction. Defaults to 5.
|
|
318
|
-
* @param [options.delayFactor=0] - A multiplier for the delay between retries. Default is 0 (no exponential backoff).
|
|
319
|
-
* @param [options.delayMaxMs=1000] - The maximum delay between retries, in milliseconds. Defaults to 1000 ms.
|
|
320
|
-
* @param [options.delayMinMs=100] - The minimum delay between retries, in milliseconds. Defaults to 100 ms.
|
|
321
|
-
*
|
|
322
|
-
* @example
|
|
323
|
-
* const confirmOrder = withTransaction(async (orderId) => {
|
|
324
|
-
* // Register Alert
|
|
325
|
-
* await useTransactionEffect(async () => {
|
|
326
|
-
* const alertId = await alertService.create({
|
|
327
|
-
* title: 'New Order: ' + orderId,
|
|
328
|
-
* });
|
|
329
|
-
*
|
|
330
|
-
* return () => alertService.removeById(alertId); // Cleanup in case of failure
|
|
331
|
-
* });
|
|
332
|
-
*
|
|
333
|
-
* // Update Statistics
|
|
334
|
-
* await useTransactionEffect(async () => {
|
|
335
|
-
* await statService.increment('orders_amount', 1);
|
|
336
|
-
*
|
|
337
|
-
* return () => statService.decrement('orders_amount', 1); // Cleanup in case of failure
|
|
338
|
-
* });
|
|
339
|
-
*
|
|
340
|
-
* // Simulate failure to trigger rollback
|
|
341
|
-
* throw new Error('Cancel transaction.');
|
|
342
|
-
* });
|
|
343
|
-
*
|
|
344
|
-
* @group Main
|
|
345
|
-
*/
|
|
346
|
-
declare function withTransaction<T, K = any, Args extends Array<any> = any[]>(fn: (this: K, ...args: Args) => T, { beforeRetryCallback, shouldRetryBasedOnError, maxAttempts, maxRetriesNumber, delayFactor, delayMaxMs, delayMinMs, }?: WithTransactionOptions): (this: K, ...args: Args) => Promise<Awaited<T>>;
|
|
347
|
-
|
|
348
|
-
interface TransactionControlled<T, K = any, Args extends Array<any> = any[]> {
|
|
349
|
-
run: (this: K, ...args: Args) => Promise<void>;
|
|
350
|
-
commit: () => Promise<void>;
|
|
351
|
-
rollback: () => Promise<void>;
|
|
352
|
-
result: Readonly<T | undefined>;
|
|
353
|
-
error: Readonly<Error | undefined>;
|
|
354
|
-
active: boolean;
|
|
355
|
-
}
|
|
356
|
-
/**
|
|
357
|
-
* Wraps a function and returns a `TransactionControlled` interface, allowing manual control
|
|
358
|
-
* over transaction commit and rollback operations.
|
|
359
|
-
*
|
|
360
|
-
* This provides finer-grained control over the transaction lifecycle, enabling users to
|
|
361
|
-
* explicitly commit or rollback a transaction based on custom logic. It's especially useful
|
|
362
|
-
* in scenarios where transactional state or conditions need to be externally determined.
|
|
363
|
-
*
|
|
364
|
-
* @example
|
|
365
|
-
* const t = withTransactionControlled(async (userId) => {
|
|
366
|
-
* await useTransactionEffect(async () => {
|
|
367
|
-
* await db.users.updateById(userId, { premium: true });
|
|
368
|
-
*
|
|
369
|
-
* return () => db.users.updateById(userId, { premium: false })
|
|
370
|
-
* });
|
|
371
|
-
*
|
|
372
|
-
* const user = await db.users.findById(userId);
|
|
373
|
-
*
|
|
374
|
-
* return user;
|
|
375
|
-
* });
|
|
376
|
-
*
|
|
377
|
-
*
|
|
378
|
-
* await t.run();
|
|
379
|
-
*
|
|
380
|
-
* // Remove premium when no subscriptions
|
|
381
|
-
* if (t.result.activeSubscriptions > 0) {
|
|
382
|
-
* await t.commit();
|
|
383
|
-
* } else {
|
|
384
|
-
* await t.rollback();
|
|
385
|
-
* }
|
|
386
|
-
*
|
|
387
|
-
* @group Main
|
|
388
|
-
*/
|
|
389
|
-
declare function withTransactionControlled<T, K = any, Args extends Array<any> = any[]>(fn: (this: K, ...args: Args) => T): TransactionControlled<Awaited<T>, K, Args>;
|
|
390
|
-
|
|
391
|
-
export { type OnMongoSessionCommittedResult, type TransactionControlled, type UseTransactionEffectOptions, type WithMongoTransactionOptions, type WithTransactionOptions, onCommitted, onMongoSessionCommitted, onRollback, useMongoSession, useTransactionEffect, withMongoTransaction, withTransaction, withTransactionControlled };
|