UNPKG

result-guard

Version:

Type-safe error handling with discriminated unions and type guards for TypeScript

176 lines (174 loc) 5.77 kB
import { Result, TimeoutOptions, CleanupFunction, EventOptions, IteratorOptions, ConcurrentOptions, CallbackHandlers } from './types'; import { EventEmitter } from 'events'; /** * Wraps an event emitter or stream operation, handling both promise resolution * and error events. Automatically cleans up error listeners. * * @template T The type of the successful result * @template E The type of the error (defaults to Error) * @param emitter The event emitter or stream to handle * @param operation The operation to perform with the emitter * @param options Configuration options for event handling * @returns A Result containing the operation result * * @example * ```typescript * import { createReadStream } from 'fs'; * * const readFile = async (path: string) => { * const stream = createReadStream(path); * const result = await withEvents( * stream, * async () => { * const chunks = []; * for await (const chunk of stream) { * chunks.push(chunk); * } * return Buffer.concat(chunks); * }, * { * timeout: 5000, * cleanup: () => stream.destroy() * } * ); * * if (isSuccess(result)) { * return result.data.toString('utf8'); * } * throw result.error; * }; * ``` */ export declare function withEvents<T, E = Error>(emitter: EventEmitter, operation: () => Promise<T>, options?: EventOptions): Promise<Result<T, E>>; /** * Safely handles an async iterator, ensuring proper cleanup even if the iteration * is interrupted. Also handles timeouts and early termination. * * @template T The type of items yielded by the iterator * @template E The type of the error (defaults to Error) * @param iterator The async iterator to consume * @param options Configuration options for iteration * @returns A Result containing the collected values * * @example * ```typescript * async function* numberGenerator() { * for (let i = 0; i < 100; i++) { * yield i; * await new Promise(resolve => setTimeout(resolve, 100)); * } * } * * const result = await withIterator(numberGenerator(), { * timeout: 5000, * maxItems: 10, * onItem: value => value < 50 // Stop if value >= 50 * }); * * if (isSuccess(result)) { * console.log('Collected values:', result.data); * } * ``` */ export declare function withIterator<T, E = Error>(iterator: AsyncIterator<T>, options?: IteratorOptions<T>): Promise<Result<T[], E>>; /** * Wraps a callback-style operation in a Promise with proper cleanup and timeout handling. * Useful for converting callback-based APIs to promise-based ones. * * @template T The type of the successful result * @template E The type of the error (defaults to Error) * @param setup Function that sets up callbacks and returns cleanup function * @param options Configuration options * @returns A Result containing the operation result * * @example * ```typescript * const readFileWithCallback = (path: string) => { * return withCallbacks<Buffer>(({ resolve, reject }) => { * const stream = createReadStream(path); * const chunks: Buffer[] = []; * * stream.on('data', chunk => chunks.push(chunk)); * stream.on('end', () => resolve(Buffer.concat(chunks))); * stream.on('error', reject); * * return () => stream.destroy(); * }, { timeout: 5000 }); * }; * ``` */ export declare function withCallbacks<T, E = Error>(setup: (handlers: CallbackHandlers<T>) => CleanupFunction | void, options?: TimeoutOptions): Promise<Result<T, E>>; /** * Safely executes multiple concurrent operations, ensuring all operations are * properly wrapped in tryCatch and handling partial failures. Each operation's * exact return type is preserved in the result tuple. * * @template Ops The tuple type of operations to execute * @param operations Array of operations to execute concurrently * @param options Configuration options for concurrency * @returns A tuple of Results, with each element preserving the exact type of its corresponding operation * * @example * ```typescript * // Example with typed functions * interface User { name: string; id: number } * interface Post { title: string; content: string } * * const getUser = async (): Promise<User> => ({ name: 'bob', id: 1 }); * const getPost = async (): Promise<Post> => ({ * title: 'Hello', * content: 'World' * }); * * const results = await concurrent([ * getUser, * getPost * ] as const); * * const [userResult, postResult] = results; * * if (!userResult.isError) { * const user = userResult.data; // TypeScript knows this is User * console.log(user.name, user.id); * } * * if (!postResult.isError) { * const post = postResult.data; // TypeScript knows this is Post * console.log(post.title, post.content); * } * * // Example with literal types * const literalResults = await concurrent([ * async () => 42 as const, * async () => 'hello' as const, * async () => ({ status: 'ok' as const }) * ] as const); * * const [numResult, strResult, objResult] = literalResults; * * if (!numResult.isError) { * const num = numResult.data; // Type is exactly 42 * } * * if (!strResult.isError) { * const str = strResult.data; // Type is exactly 'hello' * } * * if (!objResult.isError) { * const obj = objResult.data; // Type is { status: 'ok' } * } * * // With concurrency options * const results = await concurrent( * [getUser, getPost], * { * timeout: 5000, * maxConcurrent: 2, * stopOnError: false * } * ); * ``` */ export declare function concurrent<Ops extends readonly (() => Promise<any>)[]>(operations: Ops, options?: ConcurrentOptions): Promise<{ [P in keyof Ops]: Result<Awaited<ReturnType<Ops[P]>>, Error>; }>;