UNPKG

@andrew_l/mongo-transaction

Version:

Manages side effects in MongoDB transactions, rollback on failure and preventing duplicates on retries.

1 lines 45.6 kB
{"version":3,"file":"index.mjs","sources":["../src/hooks/useMongoSession.ts","../src/hooks/onMongoSessionCommitted.ts","../src/hooks/scope.ts","../src/scope.ts","../src/hooks/useTransactionEffect.ts","../src/hooks/onCommitted.ts","../src/hooks/onRollback.ts","../src/utils.ts","../src/withMongoTransaction.ts","../src/withTransaction.ts","../src/withTransactionControlled.ts"],"sourcesContent":["import { createContext, hasInjectionContext } from '@andrew_l/context';\nimport type { ClientSession } from 'mongodb';\n\nexport const [injectMongoSession, provideMongoSession] =\n createContext<ClientSession>('withMongoTransaction');\n\n/**\n * Returns the current transaction session if executed within `withMongoTransaction()` otherwise returns `null`\n *\n * @example\n * async function createAlert() {\n * const session = useMongoSession();\n *\n * await db.alerts.insertOne(\n * { title: 'Order Created' },\n * { session: session ?? undefined }\n * );\n * }\n *\n * @group Hooks\n */\nexport function useMongoSession(): ClientSession | null {\n return hasInjectionContext() ? injectMongoSession(null) : null;\n}\n","import {\n type AnyFunction,\n type Awaitable,\n defer,\n isPromise,\n} from '@andrew_l/toolkit';\nimport type { ClientSession } from 'mongodb';\nimport { injectMongoSession } from './useMongoSession';\n\nexport type OnMongoSessionCommittedResult<T> = {\n /**\n * Executes the provided function upon transaction commit.\n *\n * Returns `T` if the transaction is committed and the function completes successfully.\n *\n * Returns `undefined` if the transaction is explicitly aborted or ends without committing.\n *\n * Rejects if the function throws an error.\n */\n promise: Promise<T | undefined>;\n\n cancel: () => void;\n};\n\n/**\n * Executes the provided function upon transaction commit.\n *\n * Returns `T` if the transaction is committed and the function completes successfully.\n *\n * Returns `false` if the transaction ends without committing.\n *\n * Rejects if the function throws an error.\n *\n * @example\n * const { promise } = onTransactionCommitted(async () => {\n * console.info('Transaction committed successfully!');\n * return Math.random(); // Random value generated after commit\n * });\n *\n * promise.then(result => {\n * if (result !== false) {\n * console.info('Handler result:', result); // e.g., Handler result: 0.07576196837476501\n * }\n * });\n *\n * @group Hooks\n */\nexport function onMongoSessionCommitted<T>(\n fn: () => Awaitable<T>,\n): OnMongoSessionCommittedResult<T>;\n\nexport function onMongoSessionCommitted<T>(\n session: ClientSession,\n fn: () => Awaitable<T>,\n): OnMongoSessionCommittedResult<T>;\n\nexport function onMongoSessionCommitted(\n ...args: any[]\n): OnMongoSessionCommittedResult<unknown> {\n let session: ClientSession;\n let fn: AnyFunction;\n\n if (args.length === 2) {\n [session, fn] = args;\n } else {\n session = injectMongoSession();\n fn = args[0];\n }\n\n const q = defer<undefined | unknown>();\n\n const onEnded = () => {\n if (!session.transaction.isCommitted) {\n return q.resolve(undefined);\n }\n\n try {\n const result = fn();\n\n if (isPromise(result)) {\n result.then(r => q.resolve(r)).catch(q.reject);\n } else {\n q.resolve(result);\n }\n } catch (err) {\n q.reject(err);\n }\n };\n\n session.once('ended', onEnded);\n\n const cancel = () => {\n session.off('ended', onEnded);\n };\n\n return {\n promise: q.promise,\n cancel,\n };\n}\n","import { createContext } from '@andrew_l/context';\nimport type { TransactionScope } from '../scope';\n\nexport const [injectTransactionScope, provideTransactionScope] =\n createContext<TransactionScope>([\n 'withTransaction',\n 'withTransactionControlled',\n 'withMongoTransaction',\n ]);\n","import { withContext } from '@andrew_l/context';\nimport {\n type Awaitable,\n assert,\n asyncForEach,\n catchError,\n env,\n isFunction,\n logger,\n noop,\n} from '@andrew_l/toolkit';\nimport { provideTransactionScope } from './hooks/scope';\n\nexport interface TransactionEffect {\n /**\n * Specifies when the transaction effect should run:\n *\n * `pre` - execute immediately\n *\n * `post` - execute before transaction commit\n *\n * @default: \"pre\"\n */\n flush: 'pre' | 'post';\n\n /**\n * Setup effect function. You can return a cleanup callback to be used as a rollback.\n */\n setup: EffectCallback;\n\n /**\n * Cleanup function.\n */\n cleanup?: EffectCleanup;\n\n /**\n * Useful for debugging execution logs.\n */\n name?: string;\n\n dependencies?: readonly any[];\n}\n\nexport type EffectCallback = () => Awaitable<EffectCleanup | void>;\n\nexport type EffectCleanup = () => Awaitable<void>;\n\nexport interface TransactionOnCommitted {\n callback: OnCommittedCallback;\n dependencies?: readonly any[];\n}\n\nexport type OnCommittedCallback = () => Awaitable<void>;\n\nexport interface TransactionOnRollback {\n callback: OnRollbackCallback;\n dependencies?: readonly any[];\n}\n\nexport type OnRollbackCallback = () => Awaitable<void>;\n\ninterface TransactionScopeHooks {\n effects: {\n cursor: number;\n byCursor: TransactionEffect[];\n };\n committed: {\n cursor: number;\n byCursor: TransactionOnCommitted[];\n };\n rollbacks: {\n cursor: number;\n byCursor: TransactionOnRollback[];\n };\n}\n\nexport class TransactionScope<T = any, Args extends any[] = any[]> {\n /**\n * @internal\n */\n log = logger('TransactionScope');\n\n /**\n * @internal\n * Indicates currently ran scope\n */\n _active: boolean = false;\n\n /**\n * Last run error\n */\n error: Error | undefined;\n\n /**\n * Last run result\n */\n result: T | undefined;\n\n run: (this: any, ...args: Args) => Promise<void>;\n\n /**\n * @internal\n */\n hooks: TransactionScopeHooks = {\n committed: { byCursor: [], cursor: 0 },\n effects: { byCursor: [], cursor: 0 },\n rollbacks: { byCursor: [], cursor: 0 },\n };\n\n constructor(private fn: (...args: Args) => T) {\n const scope = this;\n\n this.run = function (...args) {\n const self = this === scope ? undefined : this;\n return scope._run(self, ...args);\n };\n }\n\n get active(): boolean {\n return this._active;\n }\n\n /**\n * @internal\n */\n async _run(self?: any, ...args: Args): Promise<void> {\n assert.ok(!this._active, 'Cannot run while transaction active.');\n\n this.reset();\n this._active = true;\n\n const [cbError, cbResult] = await withContext(() => {\n provideTransactionScope(this);\n return catchError(this.fn.bind(self, ...(args as any[])));\n })();\n\n if (cbError) {\n this.error = cbError;\n } else {\n const applyError = await effectsApply(this, 'post');\n\n if (applyError) {\n this.error = applyError;\n }\n\n this.result = cbResult;\n }\n\n this._active = false;\n }\n\n async commit(): Promise<void> {\n assert.ok(!this._active, 'Cannot commit while transaction active.');\n assert.ok(!this.error, this.error);\n\n await asyncForEach(\n this.hooks.committed.byCursor,\n h => catchError(h.callback) as any,\n { concurrency: 4 },\n );\n\n this.reset();\n this.clean();\n }\n\n async rollback(): Promise<void> {\n assert.ok(!this._active, 'Cannot rollback while transaction active.');\n\n const error = await effectsCleanup(this);\n\n if (error) {\n return Promise.reject(error);\n }\n\n await asyncForEach(\n this.hooks.rollbacks.byCursor,\n h => catchError(h.callback) as any,\n { concurrency: 4 },\n );\n\n this.reset();\n this.clean();\n }\n\n reset() {\n assert.ok(!this._active, 'Cannot reset while transaction active.');\n\n this.hooks.effects.cursor = 0;\n this.hooks.committed.cursor = 0;\n this.hooks.rollbacks.cursor = 0;\n this.result = undefined;\n this.error = undefined;\n }\n\n clean() {\n assert.ok(!this._active, 'Cannot clean while transaction active.');\n this.hooks.effects = { byCursor: [], cursor: 0 };\n this.hooks.committed = { byCursor: [], cursor: 0 };\n this.hooks.rollbacks = { byCursor: [], cursor: 0 };\n }\n}\n\nexport function createTransactionScope<T = any, Args extends any[] = any[]>(\n fn: (...args: Args) => T,\n): TransactionScope<Awaited<T>, Args> {\n return new TransactionScope<any>(fn);\n}\n\nexport async function effectsApply(\n scope: TransactionScope,\n reason: string = 'no reason',\n): Promise<Error | undefined> {\n let error: Error | undefined;\n\n const onComplete = (err: Error | undefined) => void (error = err || error);\n\n await asyncForEach(\n scope.hooks.effects.byCursor,\n effect => applyEffect(scope, effect, reason).then(onComplete),\n {\n concurrency: 4,\n },\n );\n\n return error;\n}\n\nexport async function effectsCleanup(\n scope: TransactionScope,\n reason: string = 'no reason',\n): Promise<Error | undefined> {\n let error: Error | undefined;\n\n const onComplete = (err: Error | undefined) => void (error = err || error);\n\n await asyncForEach(\n scope.hooks.effects.byCursor,\n effect => cleanupEffect(scope, effect, reason).then(onComplete),\n {\n concurrency: 4,\n },\n );\n\n return error;\n}\n\nexport async function applyEffect(\n scope: TransactionScope,\n effect: TransactionEffect,\n reason: string = 'no reason',\n): Promise<Error | undefined> {\n if (effect.cleanup) return;\n\n scope.log.debug(\n 'Effect name = %s, flush = %s, apply by %s',\n effect.name,\n effect.flush,\n reason,\n );\n\n const [err, effectResult] = await catchError(effect.setup);\n\n if (err) {\n !env.isTest &&\n scope.log.error(\n 'Effect name = %s, flush = %s apply error',\n effect.name,\n effect.flush,\n err,\n );\n return err;\n }\n\n effect.cleanup = isFunction(effectResult) ? effectResult : noop;\n\n return;\n}\n\nexport async function cleanupEffect(\n scope: TransactionScope,\n effect: TransactionEffect,\n reason: string = 'no reason',\n): Promise<Error | undefined> {\n if (!effect.cleanup) return;\n\n scope.log.debug(\n 'Effect name = %s, flush = %s, cleanup by %s',\n effect.name,\n effect.flush,\n reason,\n );\n\n const [err] = await catchError(effect.cleanup);\n\n if (err) {\n !env.isTest &&\n scope.log.error(\n 'Effect name = %s, flush = %s, cleanup error',\n effect.name,\n effect.flush,\n err,\n );\n return err;\n }\n\n effect.cleanup = undefined;\n}\n","import { isEqual } from '@andrew_l/toolkit';\nimport {\n type TransactionEffect,\n type TransactionScope,\n applyEffect,\n cleanupEffect,\n} from '../scope';\nimport { injectTransactionScope } from './scope';\n\nexport type UseTransactionEffectOptions = Partial<\n Pick<TransactionEffect, 'name' | 'flush' | 'dependencies'>\n>;\n\n/**\n * Executes a transactional effect with cleanup on error or rollback.\n *\n * Ensures the `callback` function is executed only once per transaction, even during retries.\n * On errors or dependency changes, the cleanup logic is invoked before re-execution to maintain consistency.\n *\n * @param setup A function defining the transactional effect. It is guaranteed to run once per transaction\n * and may be re-executed after cleanup if dependencies change.\n *\n * @example\n * const confirmOrder = withMongoTransaction({\n * connection: () => mongoose.connection.getClient(),\n * async fn(session) {\n * // Register an alert as a transactional effect\n * await useTransactionEffect(async () => {\n * const alertId = await alertService.create({\n * title: `Order Confirmed: ${orderId}`,\n * });\n *\n * // Define cleanup logic to remove the alert on rollback\n * return () => alertService.removeById(alertId);\n * });\n *\n * // Simulate order processing (e.g., database updates)\n * await db\n * .collection('orders')\n * .updateOne({ orderId }, { $set: { status: 'confirmed' } }, { session });\n *\n * // Simulate an error to test rollback\n * throw new Error('Simulated transaction failure');\n * },\n * });\n *\n * @group Hooks\n */\nexport async function useTransactionEffect(\n setup: TransactionEffect['setup'],\n options: UseTransactionEffectOptions = {},\n): Promise<void> {\n const scope = injectTransactionScope();\n\n const { cursor, byCursor } = scope.hooks.effects;\n\n const effectConfig: Partial<TransactionEffect> = byCursor[cursor] ?? {};\n\n const flush = options?.flush || 'pre';\n const name = options?.name || `Effect #${cursor + 1}`;\n const dependencies = options.dependencies ?? [];\n const prevDependencies = effectConfig.dependencies;\n\n byCursor[cursor] = {\n ...effectConfig,\n dependencies,\n flush,\n name,\n setup,\n };\n\n if (dependencies && prevDependencies) {\n if (!isEqual(dependencies, prevDependencies)) {\n scope.log.debug(\n 'Effect name = %s, flush = %s, caused by dependencies',\n name,\n flush,\n {\n prevDependencies,\n newDependencies: dependencies,\n },\n );\n\n await scheduleEffect(scope, cursor);\n }\n } else {\n scope.log.debug(\n 'Effect name = %s, flush = %s, caused by missing dependencies',\n name,\n flush,\n );\n\n await scheduleEffect(scope, cursor);\n }\n\n scope.hooks.effects.cursor++;\n}\n\nasync function scheduleEffect(scope: TransactionScope, cursor: number) {\n const { byCursor } = scope.hooks.effects;\n const effect = byCursor[cursor]!;\n\n const cleanupErr = await cleanupEffect(scope, effect, 'schedule pre');\n\n if (cleanupErr) {\n return Promise.reject(cleanupErr);\n }\n\n if (effect.flush === 'pre') {\n const err = await applyEffect(scope, effect, 'schedule pre');\n\n if (err) {\n return Promise.reject(err);\n }\n }\n}\n","import { type Fn, isEqual, noop } from '@andrew_l/toolkit';\nimport type { OnCommittedCallback } from '../scope';\nimport { injectTransactionScope } from './scope';\n\n/**\n * Registers a callback to be executed upon transaction commitment, with support\n * for dependency-based updates.\n *\n * This function is used within a transaction scope to perform specific actions\n * when a transaction is committed. If dependencies are provided, the callback\n * is re-registered only if the dependencies have changed. Otherwise, the\n * callback is registered unconditionally.\n *\n * @param {OnCommittedCallback} callback - The function to be executed upon\n * transaction commitment.\n * @param {readonly any[]} [dependencies=[]] - An optional array of dependencies\n * to determine if the callback should be re-registered. If the dependencies\n * differ from the previously registered ones, the callback is updated.\n * @returns {Fn} A cleanup function to cancel event listener.\n *\n * @example\n * // Basic usage without dependencies\n * onCommitted(() => {\n * console.log('Transaction committed!');\n * });\n *\n * @example\n * // Using dependencies\n * count++;\n * onCommitted(() => {\n * console.log(`Commit #${count}`);\n * }, [count]);\n *\n * @example\n * // Cancel by request\n * const cancel = onCommitted(() => {\n * console.log('This will run only once!');\n * });\n *\n * if (orderReceived) {\n * cancel(); // Prevents onCommitted from running\n * }\n *\n * @group Hooks\n */\nexport function onCommitted(\n callback: OnCommittedCallback,\n dependencies?: readonly any[],\n): Fn {\n const scope = injectTransactionScope();\n const { cursor, byCursor } = scope.hooks.committed;\n\n const config = byCursor[cursor];\n\n if (dependencies && config?.dependencies) {\n if (!isEqual(dependencies, config.dependencies)) {\n scope.log.debug('OnCommitted caused by dependencies', {\n prevDependencies: config.dependencies,\n newDependencies: dependencies,\n cursor,\n });\n\n byCursor[cursor] = { callback, dependencies };\n }\n } else {\n scope.log.debug('OnCommitted caused by missing dependencies', { cursor });\n byCursor[cursor] = { callback, dependencies };\n }\n\n scope.hooks.committed.cursor++;\n\n return () => {\n byCursor[cursor].callback = noop;\n };\n}\n","import { type Fn, isEqual, noop } from '@andrew_l/toolkit';\nimport type { OnRollbackCallback } from '../scope';\nimport { injectTransactionScope } from './scope';\n\n/**\n * Registers a callback to be executed upon transaction rollback, with support\n * for dependency-based updates.\n *\n * This function is used within a transaction scope to perform specific actions\n * when a transaction is rolled back. If dependencies are provided, the callback\n * is re-registered only if the dependencies have changed. Otherwise, the\n * callback is registered unconditionally.\n *\n * @param {OnRollbackCallback} callback - The function to be executed upon\n * transaction rollback.\n * @param {readonly any[]} [dependencies=[]] - An optional array of dependencies\n * to determine if the callback should be re-registered. If the dependencies\n * differ from the previously registered ones, the callback is updated.\n * @returns {Fn} A cleanup function to cancel event listener.\n *\n * @example\n * // Basic usage without dependencies\n * onRollback(() => {\n * console.log('Transaction rolled back!');\n * });\n *\n * @example\n * // Using dependencies\n * count++;\n * onRollback(() => {\n * console.log(`Rollback detected, flag is ${flag}`);\n * }, [count]);\n *\n * @example\n * // Cancel by request\n * const cancel = onRollback(() => {\n * console.log('This will run only once on rollback!');\n * });\n *\n * if (orderReceived) {\n * cancel(); // Prevents onRollback from running\n * }\n *\n * @group Hooks\n */\nexport function onRollback(\n callback: OnRollbackCallback,\n dependencies?: readonly any[],\n): Fn {\n const scope = injectTransactionScope();\n const { cursor, byCursor } = scope.hooks.rollbacks;\n\n const config = byCursor[cursor];\n\n if (dependencies && config?.dependencies) {\n if (!isEqual(dependencies, config.dependencies)) {\n scope.log.debug('OnCommitted caused by dependencies', {\n prevDependencies: config.dependencies,\n newDependencies: dependencies,\n cursor,\n });\n\n byCursor[cursor] = { callback, dependencies };\n }\n } else {\n scope.log.debug('OnCommitted caused by missing dependencies', { cursor });\n byCursor[cursor] = { callback, dependencies };\n }\n\n scope.hooks.committed.cursor++;\n\n return () => {\n byCursor[cursor].callback = noop;\n };\n}\n","import { has, isFunction } from '@andrew_l/toolkit';\nimport type { Transaction } from 'mongodb';\nimport type {\n ClientSessionLike,\n MongoClientLike,\n} from './withMongoTransaction';\n\nexport function isTransactionAborted(transaction: Transaction): boolean {\n return (transaction as any)?.state === 'TRANSACTION_ABORTED';\n}\n\nexport function isTransactionCommittedEmpty(transaction: Transaction): boolean {\n return (transaction as any)?.state === 'TRANSACTION_COMMITTED_EMPTY';\n}\n\nexport function isMongoClientLike(value: unknown): value is MongoClientLike {\n return has(value, ['startSession']) && isFunction(value.startSession);\n}\n\nexport function isClientSessionLike(\n value: unknown,\n): value is ClientSessionLike {\n return (\n has(value, ['withTransaction', 'endSession']) &&\n isFunction(value.withTransaction) &&\n isFunction(value.endSession)\n );\n}\n","import {\n type ClientSession,\n type ClientSessionOptions,\n MongoTransactionError,\n} from 'mongodb';\n\nimport {\n type AnyFunction,\n type Awaitable,\n catchError,\n deepDefaults,\n isFunction,\n noop,\n} from '@andrew_l/toolkit';\nimport { provideMongoSession } from './hooks/useMongoSession';\nimport { createTransactionScope } from './scope';\nimport {\n isMongoClientLike,\n isTransactionAborted,\n isTransactionCommittedEmpty,\n} from './utils';\n\nconst DEF_SESSION_OPTIONS = Object.freeze({\n defaultTransactionOptions: {\n readPreference: 'primary',\n readConcern: { level: 'local' },\n writeConcern: { w: 'majority' },\n },\n} as ClientSessionOptions);\n\nexport interface MongoClientLike {\n startSession(options: Record<string, any>): ClientSessionLike;\n}\n\nexport interface ClientSessionLike {\n withTransaction(fn: AnyFunction): Promise<any>;\n endSession(): Promise<void>;\n}\n\ntype ConnectionValue = MongoClientLike | (() => Awaitable<MongoClientLike>);\n\ntype Callback<T, K = any, Args extends Array<any> = any[]> = (\n this: K,\n session: ClientSession,\n ...args: Args\n) => Awaitable<T>;\n\nexport interface WithMongoTransactionOptions<\n T,\n K = any,\n Args extends Array<any> = any[],\n> {\n /**\n * Mongodb connection getter\n */\n connection: ConnectionValue;\n\n /**\n * Transaction session options\n *\n * @default: {\n * defaultTransactionOptions: {\n * readPreference: 'primary',\n * readConcern: { level: 'local' },\n * writeConcern: { w: 'majority' },\n * }\n * }\n */\n sessionOptions?: ClientSessionOptions;\n\n /**\n * Configures a timeoutMS expiry for the entire withTransactionCallback.\n *\n * @remarks\n * - The remaining timeout will not be applied to callback operations that do not use the ClientSession.\n * - Overriding timeoutMS for operations executed using the explicit session inside the provided callback will result in a client-side error.\n */\n timeoutMS?: number;\n\n /**\n * Transaction function that will be executed\n *\n * ⚠️ Possible several times!\n */\n fn: Callback<T, K, Args>;\n}\n\ntype WithMongoTransactionWrapped<\n T,\n K = any,\n Args extends Array<any> = any[],\n> = (this: K, ...args: Args) => Promise<T>;\n\n/**\n * 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.\n *\n * Passes the session as the function's first argument or via `useMongoSession()` hook\n *\n * @example\n * const executeTransaction = withMongoTransaction({\n * connection: () => mongoose.connection.getClient(),\n * async fn() {\n * const session = useMongoSession();\n * const orders = mongoose.connection.collection('orders');\n *\n * const { modifiedCount } = await orders.updateMany(\n * { status: 'pending' },\n * { $set: { status: 'confirmed' } },\n * { session },\n * );\n * },\n * });\n *\n * @group Main\n */\nexport function withMongoTransaction<\n T,\n K = any,\n Args extends Array<any> = any[],\n>(\n options: WithMongoTransactionOptions<T, K, Args>,\n): WithMongoTransactionWrapped<T, K, Args>;\n\n/**\n * 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.\n *\n * Passes the session as the function's first argument or via `useMongoSession()` hook\n *\n * @example\n * const executeTransaction = withMongoTransaction(mongoose.connection.getClient(), async () => {\n * const session = useMongoSession();\n * const orders = mongoose.connection.collection('orders');\n *\n * const { modifiedCount } = await orders.updateMany(\n * { status: 'pending' },\n * { $set: { status: 'confirmed' } },\n * { session },\n * );\n * });\n */\nexport function withMongoTransaction<\n T,\n K = any,\n Args extends Array<any> = any[],\n>(\n connection: ConnectionValue,\n fn: Callback<T, K, Args>,\n options?: Omit<WithMongoTransactionOptions<any>, 'fn' | 'connection'>,\n): WithMongoTransactionWrapped<T, K, Args>;\n\nexport function withMongoTransaction(\n connectionOrOptions: ConnectionValue | WithMongoTransactionOptions<any>,\n maybeFn?: Callback<any>,\n maybeOptions?: Partial<WithMongoTransactionOptions<any>>,\n): WithMongoTransactionWrapped<any> {\n const {\n connection: connectionValue,\n fn,\n sessionOptions = {},\n timeoutMS,\n } = prepareOptions(connectionOrOptions, maybeFn, maybeOptions);\n\n return async function (this: any, ...args: any[]) {\n const connection = isFunction(connectionValue)\n ? await connectionValue()\n : connectionValue;\n\n const session = (await connection.startSession(\n sessionOptions,\n )) as ClientSession;\n\n const scope = createTransactionScope(function (this: any, ...args: any[]) {\n provideMongoSession(session);\n return fn.call(this, session, ...args);\n });\n\n const timeoutAt = timeoutMS ? Date.now() + timeoutMS : 0;\n const timeoutError = new MongoTransactionError(\n 'Transaction client-side timeout',\n );\n\n let [transactionError, transactionResult] = await catchError(() =>\n session.withTransaction(async () => {\n if (timeoutAt && timeoutAt < Date.now()) {\n return Promise.reject(timeoutError);\n }\n\n await scope.run.apply(this, args);\n\n if (scope.error) {\n return Promise.reject(scope.error);\n }\n }),\n );\n\n const { result } = scope;\n\n await session.endSession().catch(noop);\n\n if (\n transactionResult === undefined &&\n isTransactionCommittedEmpty(session.transaction)\n ) {\n // do nothing here\n } else if (\n isTransactionAborted(session.transaction) &&\n transactionResult === undefined &&\n transactionError === undefined\n ) {\n transactionError = new MongoTransactionError(\n 'Transaction is explicitly aborted',\n );\n }\n\n if (transactionError) {\n await scope.rollback();\n return Promise.reject(transactionError);\n }\n\n await scope.commit();\n\n return result!;\n };\n}\n\nfunction prepareOptions(\n connectionOrOptions: ConnectionValue | WithMongoTransactionOptions<any>,\n maybeFn?: Callback<any>,\n maybeOptions?: Partial<WithMongoTransactionOptions<any>>,\n): WithMongoTransactionOptions<any> {\n let options: WithMongoTransactionOptions<any>;\n\n if (\n isFunction(connectionOrOptions) ||\n isMongoClientLike(connectionOrOptions)\n ) {\n options = {\n ...(maybeOptions || {}),\n connection: connectionOrOptions,\n fn: maybeFn!,\n };\n } else {\n options = connectionOrOptions;\n }\n\n return deepDefaults(options, {\n sessionOptions: DEF_SESSION_OPTIONS,\n });\n}\n","import { type RetryOnErrorConfig, noop, retryOnError } from '@andrew_l/toolkit';\nimport { createTransactionScope } from './scope';\n\nexport interface WithTransactionOptions extends Partial<RetryOnErrorConfig> {}\n\n/**\n * Wraps a function with transaction context, enabling retry logic and transactional effects.\n *\n * The wrapped function may be executed multiple times (up to `maxRetriesNumber`) to ensure\n * all side effects complete successfully. If the retries are exhausted without success,\n * registered cleanup functions will be executed to undo any applied effects.\n *\n * This utility is useful for managing transactional side effects, such as\n * updates to external systems, and ensures proper cleanup in case of failure.\n *\n * Additionally, this enables hooks like `useTransactionEffect()`, which allows\n * defining effects with automatic rollback mechanisms.\n *\n * @param fn - The target function to wrap with transaction handling.\n * @param [options] - Configuration options for the transaction handling.\n * @param [options.beforeRetryCallback] - An optional callback to execute before each retry attempt.\n * @param [options.shouldRetryBasedOnError] - A predicate to determine if a retry should occur based on the thrown error. Defaults to always retry.\n * @param [options.maxRetriesNumber=5] - The maximum number of retries before failing the transaction. Defaults to 5.\n * @param [options.delayFactor=0] - A multiplier for the delay between retries. Default is 0 (no exponential backoff).\n * @param [options.delayMaxMs=1000] - The maximum delay between retries, in milliseconds. Defaults to 1000 ms.\n * @param [options.delayMinMs=100] - The minimum delay between retries, in milliseconds. Defaults to 100 ms.\n *\n * @example\n * const confirmOrder = withTransaction(async (orderId) => {\n * // Register Alert\n * await useTransactionEffect(async () => {\n * const alertId = await alertService.create({\n * title: 'New Order: ' + orderId,\n * });\n *\n * return () => alertService.removeById(alertId); // Cleanup in case of failure\n * });\n *\n * // Update Statistics\n * await useTransactionEffect(async () => {\n * await statService.increment('orders_amount', 1);\n *\n * return () => statService.decrement('orders_amount', 1); // Cleanup in case of failure\n * });\n *\n * // Simulate failure to trigger rollback\n * throw new Error('Cancel transaction.');\n * });\n *\n * @group Main\n */\nexport function withTransaction<T, K = any, Args extends Array<any> = any[]>(\n fn: (this: K, ...args: Args) => T,\n {\n beforeRetryCallback,\n shouldRetryBasedOnError = () => true,\n maxAttempts,\n maxRetriesNumber = 5,\n delayFactor = 0,\n delayMaxMs = 1000,\n delayMinMs = 100,\n }: WithTransactionOptions = {},\n): (this: K, ...args: Args) => Promise<Awaited<T>> {\n return async function (this: K, ...args: Args): Promise<Awaited<T>> {\n const scope = createTransactionScope(fn);\n\n await retryOnError(\n {\n beforeRetryCallback,\n shouldRetryBasedOnError,\n maxAttempts,\n maxRetriesNumber,\n delayFactor,\n delayMaxMs,\n delayMinMs,\n },\n async () => {\n await scope.run.apply(this, args);\n\n // explicitly reject to trigger retry\n if (scope.error) {\n return Promise.reject(scope.error);\n }\n },\n )().catch(noop);\n\n const { error, result } = scope;\n\n if (error) {\n await scope.rollback();\n return Promise.reject(error) as any;\n } else {\n await scope.commit();\n }\n\n return result as Awaited<T>;\n };\n}\n","import { createTransactionScope } from './scope';\n\nexport interface TransactionControlled<\n T,\n K = any,\n Args extends Array<any> = any[],\n> {\n run: (this: K, ...args: Args) => Promise<void>;\n\n commit: () => Promise<void>;\n\n rollback: () => Promise<void>;\n\n result: Readonly<T | undefined>;\n\n error: Readonly<Error | undefined>;\n\n active: boolean;\n}\n\n/**\n * Wraps a function and returns a `TransactionControlled` interface, allowing manual control\n * over transaction commit and rollback operations.\n *\n * This provides finer-grained control over the transaction lifecycle, enabling users to\n * explicitly commit or rollback a transaction based on custom logic. It's especially useful\n * in scenarios where transactional state or conditions need to be externally determined.\n *\n * @example\n * const t = withTransactionControlled(async (userId) => {\n * await useTransactionEffect(async () => {\n * await db.users.updateById(userId, { premium: true });\n *\n * return () => db.users.updateById(userId, { premium: false })\n * });\n *\n * const user = await db.users.findById(userId);\n *\n * return user;\n * });\n *\n *\n * await t.run();\n *\n * // Remove premium when no subscriptions\n * if (t.result.activeSubscriptions > 0) {\n * await t.commit();\n * } else {\n * await t.rollback();\n * }\n *\n * @group Main\n */\nexport function withTransactionControlled<\n T,\n K = any,\n Args extends Array<any> = any[],\n>(\n fn: (this: K, ...args: Args) => T,\n): TransactionControlled<Awaited<T>, K, Args> {\n const scope = createTransactionScope(fn);\n\n const controlled = {\n run(...args: Args) {\n const self = this === controlled ? undefined : this;\n return scope.run.apply(self, args);\n },\n commit() {\n return scope.commit();\n },\n rollback() {\n return scope.rollback();\n },\n get active() {\n return scope.active;\n },\n get result() {\n return scope.result;\n },\n get error() {\n return scope.error;\n },\n };\n\n return controlled;\n}\n"],"names":["args"],"mappings":"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"}