@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.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 };