### Install rollbackit with bun Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Install the rollbackit package using bun. ```bash bun add rollbackit ``` -------------------------------- ### Install rollbackit with yarn Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Install the rollbackit package using yarn. ```bash yarn add rollbackit ``` -------------------------------- ### Install rollbackit with pnpm Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Install the rollbackit package using pnpm. ```bash pnpm add rollbackit ``` -------------------------------- ### Install rollbackit with npm Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Install the rollbackit package using npm. ```bash npm install rollbackit ``` -------------------------------- ### Entry Points Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/modules.md These are the main functions to start using RollbackIt. `withRollback` is recommended for automatic lifecycle management. ```APIDOC ## `createRollback()` ### Description Provides manual control over rollback operations, suitable for multi-batch scenarios. ### Returns - `Rollback` - An instance of the Rollback manager. ## `withRollback()` ### Description Manages rollback operations within a scoped context with automatic lifecycle management. This is the recommended approach for most use cases. ### Returns - `Promise` - A promise that resolves with the result of the operations within the scope. ``` -------------------------------- ### Rollback Options Usage Example Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Illustrates how to use RollbackOptions to control the rollback process. The first example shows collecting all failures, while the second demonstrates halting at the first failure. ```typescript // Collect all failures and continue const result1 = await rb.rollback(); // Halt at the first failure const { failures, pending } = await rb.rollback({ stopOnFailure: true }); ``` -------------------------------- ### Example: Whole-Operation Timeout Handling Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/errors.md Demonstrates catching a TimeoutError for the entire operation managed by withRollback. This example shows how to set a deadline for a sequence of operations, including user creation and email sending. ```typescript try { await withRollback( async (rb, signal) => { const user = await rb.step( 'create user', (sig) => api.createUser(data, { signal: sig }), (user) => api.deleteUser(user.id) ); await api.sendEmail(user, { signal }); // thread the signal return user; }, { timeout: 30000 } // 30 second deadline for the whole operation ); } catch (error) { if (error instanceof TimeoutError) { console.error('Operation timed out; rollback has executed'); } } ``` -------------------------------- ### Basic Rollback Usage Example Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Demonstrates how to create a rollback instance, register a cleanup operation, execute a step, commit changes, and perform a rollback. ```typescript const rb = createRollback(); b.add('cleanup', () => cleanup()); const result = await rb.step(...); b.commit(); const { failures } = await rb.rollback(); ``` -------------------------------- ### withRollback Configuration Example Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Shows how to use `withRollback()` with custom options, including a timeout, disabling stop on failure, and providing an `onFailures` callback for logging. ```typescript await withRollback( async (rb, signal) => { // ... }, { timeout: 25000, stopOnFailure: false, onFailures: ({ failures, pending }) => { logger.warn('Rollback incomplete', { failures, pending }); }, } ); ``` -------------------------------- ### Basic Usage Example Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/withRollback.md Demonstrates the fundamental use of `withRollback` to perform an operation (creating a user) and register a corresponding rollback action (deleting the user). ```APIDOC ## Example: Basic Usage ```typescript import { withRollback } from 'rollbackit'; const user = await withRollback(async (rb) => { const user = await db.createUser({ name: 'Alice' }); // Register a rollback operation to delete the user if subsequent steps fail rb.add('delete user', () => db.deleteUser(user.id)); await sendWelcomeEmail(user); return user; }); ``` ``` -------------------------------- ### RollbackIt Learning Path Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/00-START-HERE.md This diagram illustrates the recommended sequence for learning and using the RollbackIt library, starting from this introductory file and progressing through the README, core functions, patterns, and API references. ```markdown START: This file (00-START-HERE.md) ↓ READ: README.md (overview & quick start) ↓ CHOOSE: withRollback (scoped) or createRollback (manual) ↓ FIND: Your pattern in patterns.md ↓ REFERENCE: api-reference/ or type/error/config files ↓ LOOKUP: INDEX.md for anything else ``` -------------------------------- ### Example Usage of withRollback with Options Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/configuration.md Demonstrates how to use `withRollback` with specific options for timeout, stopping on failure, and a custom failure handler. ```typescript await withRollback( async (rb, signal) => { // ... }, { timeout: 30000, stopOnFailure: false, onFailures: ({ failures, pending }) => { logger.warn('Rollback incomplete', { failures, pending }); }, } ); ``` -------------------------------- ### Execute a Rollback Step with timeout and stopOnFailure Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Example demonstrating the usage of Rollback.step() with specific timeout and stopOnFailure configurations. ```typescript const user = await rb.step( 'create user', (signal) => api.createUser(data, { signal }), (user) => api.deleteUser(user.id), { timeout: 5000, stopOnFailure: false } ); ``` -------------------------------- ### Rollback Result Example Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Demonstrates how to process the result of a rollback operation, handling both failures and pending operations that were not executed. ```typescript const { failures, pending } = await rb.rollback({ stopOnFailure: true }); // failures: operations that threw // pending: older operations that never ran because stopOnFailure halted if (pending.length > 0) { for (const op of pending) { try { await op.rollback(); } catch (err) { console.error(`Could not retry ${op.description}:`, err); } } } ``` -------------------------------- ### Fetch Data with Timeout Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/runWithTimeout.md Demonstrates how to use runWithTimeout to fetch data from an API with a 5-second timeout. Ensure 'rollbackit' is installed and imported. ```typescript import { runWithTimeout } from 'rollbackit'; const data = await runWithTimeout( (signal) => fetch('https://api.example.com/data', { signal }), 5000 // 5 second timeout ); ``` -------------------------------- ### RollbackIt Error Messages Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/errors.md These are example error messages that might be logged by the RollbackIt library. ```text [rollbackit] cannot register rollback operations after rollback ``` ```text [rollbackit] operation timed out after 5000ms ``` -------------------------------- ### Example: Per-Step Timeout Handling Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/errors.md Illustrates catching a TimeoutError for an individual 'create user' step. This is useful when you need to monitor and react to specific long-running steps within a larger process. ```typescript const rb = createRollback(); try { await rb.step( 'create user', (signal) => api.createUser(data, { signal }), (user) => api.deleteUser(user.id), { timeout: 5000 } ); } catch (error) { if (error instanceof TimeoutError) { console.error('User creation took too long'); // Rollback is handled by outer withRollback, if present } } ``` -------------------------------- ### Example with Timeout and Step-based Rollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/withRollback.md Illustrates using `withRollback` with a specific timeout for a step within the operation and a broader timeout for the entire operation. It also shows how to thread the `AbortSignal` into nested asynchronous calls. ```APIDOC ## Example: With Timeout ```typescript const result = await withRollback( async (rb, signal) => { // Use rb.step for a specific operation with its own timeout and rollback const user = await rb.step( 'create user', (sig) => api.createUser(payload, { signal: sig }), // Operation (user) => api.deleteUser(user.id), // Rollback operation { timeout: 5000 } // Timeout for this specific step ); // Thread the main operation's signal into other async calls await api.activate(user, { signal }); return user; }, { timeout: 25000 } // Overall operation timeout ); ``` ``` -------------------------------- ### Executing rollback and handling results Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Example of calling the rollback method and processing the results, including checking for failures and handling pending operations. ```typescript const { failures, pending } = await rb.rollback(); if (failures.length > 0) { console.warn('Some rollbacks failed:', failures); } if (pending.length > 0) { console.warn('Incomplete rollback, pending operations:', pending); // Caller can retry the pending operations for (const op of pending) { try { await op.rollback(); } catch (err) { console.error(`Failed to retry ${op.description}:`, err); } } } ``` -------------------------------- ### Quick start with withRollback Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Use the `withRollback` function to execute an async operation and register its rollback. Rollbacks are automatically discarded on success or executed on failure. ```typescript import { withRollback } from "rollbackit"; const result = await withRollback(async (rb) => { const user = await db.createUser(data); rb.add("delete user", () => db.deleteUser(user.id)); // rollback for the step above await sendWelcomeEmail(user); // if this throws, "delete user" runs, then the error re-throws return user; // success → nothing is rolled back }); ``` -------------------------------- ### Database Query with Timeout Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/runWithTimeout.md Shows an example of executing a database query with a 3-second timeout using runWithTimeout. Assumes a 'db' object with a 'query' method is available. ```typescript const result = await runWithTimeout( (signal) => db.query('SELECT * FROM users', { signal }), 3000 ); ``` -------------------------------- ### Add Rollback Operation with stopOnFailure Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Example demonstrating how to add a rollback operation with the 'stopOnFailure' option set to true. ```typescript rb.add('critical cleanup', () => cleanup(), { stopOnFailure: true, }); ``` -------------------------------- ### Handle Rollback Failures Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Example of iterating through rollback failures and logging the description and error for each. This is useful for diagnosing issues when a rollback sequence encounters errors. ```typescript const { failures } = await rb.rollback(); for (const failure of failures) { console.error(`Rollback failed: ${failure.description}`, failure.error); } ``` -------------------------------- ### Example: Attempting to Register After Rollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/errors.md Demonstrates catching a RolledBackError when trying to add a new operation after the rollback instance has already been finalized by calling `rollback()`. ```typescript try { const rb = createRollback(); rb.add('step 1', () => Promise.resolve()); await rb.rollback(); // instance is now finalized rb.add('step 2', () => Promise.resolve()); // throws } catch (error) { if (error instanceof RolledBackError) { console.error('Attempted to register after rollback'); } } ``` -------------------------------- ### Thread AbortSignal into Fetch Request Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/runWithTimeout.md This example demonstrates the correct way to thread an AbortSignal into a fetch request within runWithTimeout. This allows the fetch operation to be cancelled if the timeout is reached, preventing background resource leaks. ```typescript const result = await runWithTimeout( async (signal) => { // good — signal is threaded in const response = await fetch(url, { signal }); return response.json(); }, 5000 ); ``` -------------------------------- ### Handling Pending Rollback Operations Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/RollbackResult.md Shows how to check for and handle operations that were not executed due to an early halt in the rollback sequence. It includes an example of retrying these pending operations. ```typescript const { failures, pending } = await rb.rollback({ stopOnFailure: true }); if (pending.length > 0) { console.warn('Rollback halted early; these operations were not run:', pending.map(op => op.description) ); // Retry the pending operations for (const op of pending) { try { await op.rollback(); console.log(`Retried: ${op.description}`); } catch (err) { console.error(`Failed to retry ${op.description}:`, err); } } } ``` -------------------------------- ### Matching Rollbackit Errors by Message Prefix Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/errors.md Check if an error message starts with the '[rollbackit]' prefix to identify errors originating from the rollbackit library. This is a fallback for cases where instanceof checks might not be sufficient. ```typescript try { // ... } catch (error) { const msg = error instanceof Error ? error.message : String(error); if (msg.startsWith('[rollbackit]')) { console.error('Rollbackit error:', msg); } } ``` -------------------------------- ### step(description, run, rollback, options?) Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Runs a forward action and registers its compensation in one call. The rollback is only registered if the `run` function resolves successfully. ```APIDOC ## step(description, run, rollback, options?) ### Description Runs a forward action and registers its compensation in one call. The rollback is only registered if `run` resolves. ### Method Signature ```typescript step: (description: string, run: (signal: AbortSignal) => Promise, rollback: (result: T) => Promise, options?: StepOptions) => Promise ``` ### Parameters #### Path Parameters * **description** (string) - Required - Names the step by its forward intent (e.g., `"create user"`). * **run** (function) - Required - Forward action; receives an `AbortSignal` that fires on timeout (if `options.timeout` is set). Must return a promise. If it throws or times out, no rollback is registered. * **rollback** (function) - Required - Compensation function that receives `run`'s result. Called only if `run` resolves. * **options** (StepOptions) - Optional - Optional `{ timeout?: number, stopOnFailure?: boolean }`. ### Returns The resolved value of `run`. ### Throws * `RolledBackError` if the instance is already rolled back. * `TimeoutError` if `options.timeout` elapses before `run` settles. * Any error thrown by `run` (rollback is not registered if this happens). ### Example ```typescript const user = await rb.step( 'create user', (signal) => api.createUser(payload, { signal }), (user) => api.deleteUser(user.id), { timeout: 5000 } ); ``` ``` -------------------------------- ### Catching RollbackError Instances Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/errors.md Example of how to catch and identify RollbackError instances in a try-catch block. This pattern helps in handling library-specific errors gracefully. ```typescript try { // ... } catch (error) { if (error instanceof RollbackError) { console.error(`[rollbackit] operation failed: ${error.message}`); } } ``` -------------------------------- ### Minimal RollbackIt Usage Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/configuration.md Demonstrates the bare minimum to create a rollback instance and add a cleanup step. Shows equivalent behavior to default options. ```typescript // Bare minimal usage const rb = createRollback(); rb.add('cleanup', () => cleanup()); await rb.rollback(); // Equivalent to: await rb.rollback({ stopOnFailure: false }) // withRollback defaults await withRollback( async (rb) => { // no timeout, continue on all failures rb.add('cleanup', () => cleanup()); } // no options provided ); // Equivalent to: { timeout: undefined, stopOnFailure: false, onFailures: undefined } ``` -------------------------------- ### Rollback Instance Methods Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/MANIFEST.txt Documentation for the methods available on a Rollback instance, including adding steps, committing, and initiating rollbacks. ```APIDOC ## Rollback Instance Methods ### add(description, rollback, options?) Adds a new operation step to the rollback manager. - **description** (string) - A description of the operation step. - **rollback** (function) - The function to execute for rolling back this step. - **options** (RollbackOperationOptions, optional) - Options for this specific operation step. ### step(description, run, rollback, options?) Adds a new operation step that includes both a run function and a rollback function. - **description** (string) - A description of the operation step. - **run** (function) - The function to execute for the operation step. - **rollback** (function) - The function to execute for rolling back this step. - **options** (StepOptions, optional) - Options for this specific step, such as timeout or stopOnFailure. ### commit() Marks the current operation as successfully completed, preventing future rollbacks. ### rollback(options?) Executes the rollback logic for all completed steps in reverse order. - **options** (RollbackOptions, optional) - Options for the rollback process, such as stopOnFailure. ### Examples ```typescript const rb = createRollback(); // Add a step with only rollback logic rb.add('Cleanup temporary files', async () => { console.log('Rolling back temporary files...'); // ... cleanup logic ... }); // Add a step with both run and rollback logic await rb.step('Process data', async () => { console.log('Processing data...'); // ... processing logic ... return 'processed data'; }, async () => { console.log('Rolling back data processing...'); // ... rollback processing logic ... }); try { await rb.commit(); // Finalize the operation console.log('Operation committed successfully.'); } catch (error) { console.error('Operation failed, initiating rollback:', error); await rb.rollback(); // Explicitly trigger rollback if commit fails or is not called } ``` ``` -------------------------------- ### Create and Use Rollback Instance Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Demonstrates how to create a Rollback instance and use its add, commit, and rollback methods for managing compensation operations in a try-catch block. This is useful for multi-batch workflows or when direct inspection of RollbackResult is needed. ```typescript import { createRollback } from 'rollbackit'; const rb = createRollback(); try { const user = await db.createUser({ name: 'Alice' }); rb.add('delete user', () => db.deleteUser(user.id)); const bucket = await storage.createBucket(user.id); rb.add('delete bucket', () => storage.deleteBucket(bucket.id)); rb.commit(); // seal the batch — these rollbacks are now permanent } catch (error) { const { failures, pending } = await rb.rollback(); // unwind in reverse order if (failures.length > 0) { console.error('Rollback incomplete:', failures); } throw error; } ``` -------------------------------- ### Multi-Batch Rollback with Failure Handling Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/configuration.md Demonstrates managing complex rollback scenarios with multiple independent batches. It shows how to commit batches and handle failures across different stages of the operation, including logging pending rollbacks. ```typescript const rb = createRollback(); try { // Batch 1 const user = await db.createUser(data); rb.add('delete user', () => db.deleteUser(user.id), { stopOnFailure: true }); // Batch 2 const bucket = await storage.createBucket(user.id); rb.add('delete bucket', () => storage.deleteBucket(bucket.id)); rb.commit(); // seal batch 1 // Batch 3: independent, more lenient await email.send(user); rb.add('log email', () => emailLog.record(user.id)); } catch (error) { const { failures, pending } = await rb.rollback(); if (pending.length > 0) { logger.warn('Incomplete rollback', { pending: pending.map(p => p.description) }); } throw error; } ``` -------------------------------- ### Module Map Structure Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/README.md Illustrates the directory structure and main files of the rollbackit library. Public API is exposed from index.ts, with types in types.ts and core logic in the lib directory. ```tree src/ ├── index.ts ← Public API (re-exports) ├── types.ts ← Type definitions └── lib/ ├── operations.ts ← createRollback, runWithTimeout ├── helpers.ts ← withRollback └── errors.ts ← RolledBackError, TimeoutError ``` -------------------------------- ### Handle Pending Rollback Operations Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Example of iterating through pending rollback operations and retrying their compensation functions. This is useful when a rollback sequence is halted early due to failures. ```typescript // Example operation inside pending array const { pending } = await rb.rollback({ stopOnFailure: true }); for (const op of pending) { console.log(op.description); // "delete bucket" await op.rollback(); // retry the compensation } ``` -------------------------------- ### Public API Entry Point Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/MANIFEST.txt The primary entry point for the rollbackit library is located at src/index.ts. This exports the core functions and types that users can directly interact with. ```APIDOC ## Public API ### Description This section details the functions and types exported from the main entry point of the rollbackit library, allowing direct integration into user applications. ### Entry Point `src/index.ts` ### Exported Functions - `createRollback` - `withRollback` ### Exported Types - `RollbackError` - `RolledBackError` - `TimeoutError` - [Other 5 type definitions] ``` -------------------------------- ### Standard TypeScript Imports Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/modules.md Recommended way to import rollbackit functionalities in TypeScript projects. Includes core functions and error types. ```typescript import { createRollback, withRollback } from 'rollbackit'; import { RolledBackError, TimeoutError } from 'rollbackit'; import type { Rollback, RollbackResult } from 'rollbackit'; ``` -------------------------------- ### Basic Usage with RollbackIt Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/README.md Use `withRollback` for automatic lifecycle management. It commits on success and rolls back on failure, returning the result of the operation. ```typescript import { withRollback } from 'rollbackit'; const user = await withRollback(async (rb) => { const user = await db.createUser(data); rb.add('delete user', () => db.deleteUser(user.id)); await sendWelcomeEmail(user.email); return user; }); // On success: user returned, nothing rolled back // On failure: user deleted, error re-thrown ``` -------------------------------- ### Ignoring AbortSignal in Fetch Request (Bad Practice) Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/runWithTimeout.md This example shows an incorrect usage where the AbortSignal is ignored within the async function. If the timeout elapses, a TimeoutError is thrown, but the fetch operation continues in the background, potentially causing resource leaks. ```typescript const result = await runWithTimeout( async (signal) => { // bad — signal is ignored const response = await fetch(url); return response.json(); }, 5000 ); // After 5s, TimeoutError is thrown, but the fetch keeps running in the background ``` -------------------------------- ### Basic Usage of withRollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/withRollback.md Demonstrates how to use withRollback to perform an operation and register a rollback action. The rollback action is executed if the main operation throws an error. ```typescript import { withRollback } from 'rollbackit'; const user = await withRollback(async (rb) => { const user = await db.createUser({ name: 'Alice' }); rb.add('delete user', () => db.deleteUser(user.id)); await sendWelcomeEmail(user); return user; }); ``` -------------------------------- ### Commit Early with createRollback Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Call `rb.commit()` mid-flow at the pivot point after which rolling back earlier work would be incorrect. Everything registered before `commit()` is sealed and will not be rolled back. Work registered after `commit()` starts a new, reversible batch. ```typescript const rb = createRollback(); try { const order = await db.createOrder(data); rb.add("delete order", () => db.deleteOrder(order.id)); await inventory.reserve(order); rb.add("release stock", () => inventory.release(order)); // Pivot: once the card is charged, we're committed to fulfilling — // rolling back the order now would be worse than the inconsistency. await payment.charge(order); rb.commit(); // seal everything; do not roll back from here // Post-pivot work. If this throws, rollback() is a no-op — handle it forward. await email.sendReceipt(order); return order; } catch (error) { await rb.rollback(); // only rolls back if we threw *before* commit (before charging) throw error; } ``` -------------------------------- ### Committing Operations and Starting New Batches Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Use `commit` to seal the current batch of operations, discarding their rollbacks. The instance remains open for registering a new batch. Subsequent `rollback()` calls only affect operations registered after the last `commit`. ```typescript commit: () => void ``` ```typescript try { // Stage 1: user and bucket const user = await db.createUser(data); rb.add('delete user', () => db.deleteUser(user.id)); const bucket = await storage.createBucket(user.id); rb.add('delete bucket', () => storage.deleteBucket(bucket.id)); rb.commit(); // seal stage 1 — these are now permanent // Stage 2: subscription (independent batch) const sub = await billing.subscribe('premium'); rb.add('cancel subscription', () => billing.cancel(sub.id)); // If this throws, only stage 2 rolls back; stage 1 stays committed await sendWelcomeEmail(user); rb.commit(); } catch (error) { await rb.rollback(); throw error; } ``` -------------------------------- ### step Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/Rollback.md Runs a forward action and registers its compensation in one call. This method is used to execute a primary operation and define its corresponding rollback procedure, handling potential timeouts and failures. ```APIDOC ## step ### Description Runs a forward action and registers its compensation in one call. ### Method ```typescript step(description: string, run: (signal: AbortSignal) => Promise, rollback: (result: T) => Promise, options?: StepOptions): Promise ``` ### Parameters #### Parameters - **description** (string) - Required - Names the step (e.g., "create user"). - **run** ((signal: AbortSignal) => Promise) - Required - Forward action that receives an abort signal. Must resolve before the rollback is registered. - **rollback** (result: T) => Promise) - Required - Compensation that receives `run`'s result. - **options.timeout** (number) - Optional - Abort `run` after this many milliseconds. - **options.stopOnFailure** (boolean) - Optional - If `true`, halt the unwind if this step's rollback throws. ### Returns - The resolved value of `run`. ### Throws - `RolledBackError`, `TimeoutError`, or any error from `run`. ### Usage ```typescript const user = await rb.step( 'create user', (signal) => api.createUser(payload, { signal }), (user) => api.deleteUser(user.id), { timeout: 5000 } ); ``` ``` -------------------------------- ### withRollback with Timeout and Step-level Rollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/withRollback.md Illustrates using withRollback with a timeout for the entire operation and also for individual steps within the operation. It shows how to thread AbortSignals for cancellation. ```typescript const result = await withRollback( async (rb, signal) => { const user = await rb.step( 'create user', (sig) => api.createUser(payload, { signal: sig }), (user) => api.deleteUser(user.id), { timeout: 5000 } ); await api.activate(user, { signal }); // thread the scope signal return user; }, { timeout: 25000 } // whole-operation budget ); ``` -------------------------------- ### Basic Rollback Scope with `withRollback` Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/patterns.md Use `withRollback` for the simplest and most common pattern where a rollback is registered right after each step. This pattern provides automatic lifecycle management and keeps cleanup logic next to the step it reverses. ```typescript import { withRollback } from 'rollbackit'; const user = await withRollback(async (rb) => { const user = await db.createUser(data); rb.add('delete user', () => db.deleteUser(user.id)); await sendWelcomeEmail(user.email); return user; }); // On success: user is returned, nothing rolled back // On failure (after user created): user is deleted, then error re-throws ``` -------------------------------- ### Execute Step with Rollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/Rollback.md Runs a forward action and automatically registers its compensation. This is useful for encapsulating a task and its cleanup. The forward action receives an abort signal, and the compensation receives the result of the forward action. Supports timeouts and failure handling. ```typescript step( description: string, run: (signal: AbortSignal) => Promise, rollback: (result: T) => Promise, options?: StepOptions ): Promise ``` ```typescript const user = await rb.step( 'create user', (signal) => api.createUser(payload, { signal }), (user) => api.deleteUser(user.id), { timeout: 5000 } ); ``` -------------------------------- ### rollback(options?) Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Executes all registered operations in reverse order (LIFO) and finalizes the instance. It can be configured to stop on failure or collect all failures. ```APIDOC ## rollback(options?) ### Description Executes all registered operations in reverse order (LIFO) and finalizes the instance. It can be configured to stop on failure or collect all failures. ### Method `rollback` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **options** (`RollbackOptions`) - Optional - Default: `{ stopOnFailure: false }` - Run-level behavior: if `stopOnFailure: true`, halt at the first throwing operation; if `false`, collect failures and continue. ### Returns `RollbackResult` with `failures` (array of operations that threw) and `pending` (operations never run due to early stop). ### Example ```typescript const { failures, pending } = await rb.rollback(); if (failures.length > 0) { console.warn('Some rollbacks failed:', failures); } if (pending.length > 0) { console.warn('Incomplete rollback, pending operations:', pending); // Caller can retry the pending operations for (const op of pending) { try { await op.rollback(); } catch (err) { console.error(`Failed to retry ${op.description}:`, err); } } } ``` ``` -------------------------------- ### WithRollbackOptions Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/types.md Configuration options for the `withRollback` function, which allows executing a function within a rollback context and provides options for timeout and failure handling. ```APIDOC ## WithRollbackOptions ### Description Configuration options for the `withRollback` function, which allows executing a function within a rollback context and provides options for timeout and failure handling. ### Fields #### `timeout` - **Type**: `number` - **Required**: — - **Default**: — - **Description**: Whole-operation budget in milliseconds. If `fn` does not settle in time, `withRollback` rolls back and throws `TimeoutError`. `fn` receives an `AbortSignal` that fires on timeout. #### `stopOnFailure` - **Type**: `boolean` - **Required**: — - **Default**: `false` - **Description**: (Inherited) If `true`, halt the rollback sequence at the first failure. #### `onFailures` - **Type**: `(result: RollbackResult) => void` - **Required**: — - **Default**: — - **Description**: Observation hook called when `fn` throws and one or more rollbacks also throw. Used to log or alert. Must not throw (exceptions are silently ignored so they cannot mask the original error). ``` -------------------------------- ### Configure Per-Step and Scope Timeouts Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/configuration.md Illustrates setting independent timeouts for individual steps and the entire rollback operation scope. Use this to enforce deadlines on specific actions or the overall process. ```typescript await withRollback( async (rb, signal) => { // This step has a 5-second deadline const user = await rb.step( 'create user', (sig) => api.createUser(data, { signal: sig }), () => Promise.resolve(), { timeout: 5000 } ); // This one has no per-step timeout, but is bounded by the scope await api.activate(user, { signal }); }, { timeout: 30000 } // whole operation bounded to 30 seconds ); ``` -------------------------------- ### Progressive Commit with Manual Rollback Source: https://github.com/alexmarqs/rollbackit/blob/main/README.md Demonstrates staging operations into batches, committing them progressively, and handling errors by rolling back only the uncommitted batch. Use this for sequential or data-driven stages. ```typescript const rb = createRollback(); // stage one — two side effects, rolled back together if this batch fails async function stageOne() { const user = await db.createUser(data); rb.add("delete user", () => db.deleteUser(user.id)); const bucket = await storage.createBucket(user.id); rb.add("delete bucket", () => storage.deleteBucket(bucket.id)); } // stage two — an independent batch async function stageTwo() { const sub = await billing.subscribe(plan); rb.add("cancel subscription", () => billing.cancel(sub.id)); } try { await stageOne(); rb.commit(); // stage one succeeded — seal it; its rollbacks are dropped await stageTwo(); // throws here? only stage two rolls back — stage one stays rb.commit(); } catch (error) { await rb.rollback(); // unwinds only the batch in progress throw error; } ``` -------------------------------- ### Iterating Through Rollback Failures Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/RollbackResult.md Demonstrates how to access and log details of operations that failed during the rollback process. This is useful for debugging and understanding why a rollback did not complete successfully. ```typescript const { failures } = await rb.rollback(); for (const failure of failures) { console.error(`Failed to ${failure.description}:`, failure.error); ``` -------------------------------- ### withRollback Function Signature and Options Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/withRollback.md Defines the `withRollback` function and its configurable options. The function takes an async callback that receives a rollback manager and an optional AbortSignal. Options allow customization of timeout, failure handling, and error callbacks. ```APIDOC ## Function: withRollback ### Description Manages a series of operations with automatic rollback on failure. It accepts an asynchronous function that can register rollback operations and an optional configuration object. ### Parameters #### Callback Function - `callback` (function) - Required - An async function that receives a rollback manager (`rb`) and an `AbortSignal`. This function should perform the main operations and register rollback actions using `rb.add()` or `rb.step()`. - `rb` (RollbackManager) - An object to manage rollback operations. - `signal` (AbortSignal) - An `AbortSignal` that fires when the operation times out or is cancelled. #### Options - `timeout` (number) - Optional - The total budget in milliseconds for the entire operation. If the `callback` does not settle within this time, the scope rolls back and a `TimeoutError` is thrown. The `AbortSignal` passed to the callback will fire on timeout. - `stopOnFailure` (boolean) - Optional - Defaults to `false`. If `true`, and a rollback operation throws an error, the rollback process halts at that operation. If `false`, all registered rollback operations will attempt to complete even if one fails. - `onFailures` ((result: RollbackResult) => void) - Optional - A callback hook invoked when the main `callback` throws an error AND one or more rollback operations also throw errors during unwinding. This hook is intended for logging or metrics and must not throw any exceptions itself. ``` -------------------------------- ### Basic Usage with Failure Observation Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/withRollback.md Demonstrates how to use withRollback to execute an operation and register cleanup actions. The `onFailures` callback is invoked if any registered cleanup fails, logging the failures and pending operations. ```typescript await withRollback( async (rb) => { const user = await db.createUser(data); rb.add('delete user', () => db.deleteUser(user.id)); await thirdPartyAPI.process(user); // might throw }, { onFailures: ({ failures, pending }) => { if (failures.length > 0) { logger.warn('Rollback incomplete', { failures: failures.map((f) => f.description), pending: pending.map((op) => op.description), }); metrics.increment('rollback.failed'); } }, } ); ``` -------------------------------- ### Inspect Failures and Pending Operations After Catching Error Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/RollbackResult.md This pattern demonstrates how to inspect both failures and pending operations after a rollback has been caught by a `try...catch` block. It's useful for detailed error reporting and handling situations where manual intervention might be required for pending operations. ```typescript try { await withRollback( async (rb) => { // ... steps ... }, { stopOnFailure: true } ); } catch (error) { const { failures, pending } = await rb.rollback(); if (failures.length > 0) { console.error(`Rollback failed on: ${failures[0].description}`); } if (pending.length > 0) { console.warn(`${pending.length} operations left un-run`); // Hand off for manual intervention or logging } throw error; } ``` -------------------------------- ### add(description, rollback, options?) Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Registers a rollback operation for work that has already been performed. This method is used to add a compensation action that can be executed if subsequent operations fail. ```APIDOC ## add(description, rollback, options?) ### Description Registers a rollback operation for work that has already been performed. ### Method Signature ```typescript add: (description: string, rollback: () => Promise, options?: RollbackOperationOptions) => void ``` ### Parameters #### Path Parameters * **description** (string) - Required - Human-readable name for the operation (e.g., `"delete user"`); appears in `RollbackFailure` if this rollback throws. * **rollback** (function) - Required - An async function that performs the compensation (e.g., `() => db.deleteUser(id)`). Must return a promise; exceptions are caught and collected into `failures`. * **options** (RollbackOperationOptions) - Optional - Optional object with `stopOnFailure: boolean`. If `true` and this operation's rollback throws, the unwind halts here and all older operations are returned as `pending`. ### Throws * `RolledBackError` if called after `rollback()` (but allowed after `commit()`) ### Example ```typescript const userId = '123'; rb.add('delete user', () => db.deleteUser(userId)); rb.add('delete profile', () => db.deleteProfile(userId), { stopOnFailure: true, }); ``` ``` -------------------------------- ### Package.json Exports Configuration Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/modules.md This JSON configuration defines the export paths for different module formats (ESM and CommonJS) and their corresponding type definitions, enabling compatibility with various environments and build tools. ```json { "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.cts", "exports": { ".": { "import": { "types": "./dist/index.d.mts", "default": "./dist/index.mjs" }, "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } } } ``` -------------------------------- ### Executing a Step with Rollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/api-reference/createRollback.md Use `step` to run a forward action and register its compensation. The rollback is only registered if the `run` function resolves. The `run` function receives an `AbortSignal` for timeouts, and the `rollback` function receives the result of `run`. Optional `timeout` and `stopOnFailure` can be provided. ```typescript step: ( description: string, run: (signal: AbortSignal) => Promise, rollback: (result: T) => Promise, options?: StepOptions ) => Promise ``` ```typescript const user = await rb.step( 'create user', (signal) => api.createUser(payload, { signal }), (user) => api.deleteUser(user.id), { timeout: 5000 } ); ``` -------------------------------- ### createRollback Source: https://github.com/alexmarqs/rollbackit/blob/main/_autodocs/modules.md Creates and returns a Rollback instance. This instance provides methods for manual lifecycle control, managing multi-batch workflows, and explicit result inspection. The instance includes methods such as `add`, `step`, `commit`, and `rollback`. ```APIDOC ## createRollback ### Description Creates and returns a `Rollback` instance. This instance provides methods for manual lifecycle control, managing multi-batch workflows, and explicit result inspection. The instance includes methods such as `add`, `step`, `commit`, and `rollback`. ### Signature ```typescript function createRollback(): Rollback ``` ### Usage Import from: `import { createRollback } from 'rollbackit'` ```