UNPKG

nestjs-aborter

Version:

Automatic request cancellation and timeout handling for NestJS applications

76 lines (75 loc) 2.55 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.AbortError = void 0; exports.withAbort = withAbort; /** * Custom error thrown when an operation is aborted. */ class AbortError extends Error { constructor(reason) { super(reason || 'Operation aborted'); this.name = 'AbortError'; Error.captureStackTrace?.(this, AbortError); } } exports.AbortError = AbortError; /** * Wraps a promise with abort signal and optional timeout support. * * @param promise - The promise to wrap * @param signal - Optional AbortSignal to cancel the operation * @param options - Optional timeout configuration * @returns Promise that rejects with AbortError if aborted or timed out * @throws Error if operation timeout exceeds request timeout */ function withAbort(promise, signal, options) { // Early returns for simple cases if (!signal && !options?.timeout) return promise; if (signal?.aborted) { return Promise.reject(new AbortError(signal.reason ? String(signal.reason) : undefined)); } validateTimeouts(signal, options); const promises = [promise]; if (signal) { promises.push(createAbortPromise(signal)); } if (options?.timeout) { promises.push(createTimeoutPromise(options)); } return Promise.race(promises); } /** * Validates that operation timeout doesn't exceed request timeout */ function validateTimeouts(signal, options) { if (!options?.timeout || !signal?._requestTimeout) { return; } const { timeout } = options; const requestTimeout = signal._requestTimeout; if (timeout > requestTimeout) { throw new Error(`Operation timeout (${timeout}ms) cannot exceed request timeout (${requestTimeout}ms). ` + `Use @RequestTimeout(${timeout}) decorator to increase the request timeout.`); } } /** * Creates a promise that rejects when the signal is aborted */ function createAbortPromise(signal) { return new Promise((_, reject) => { signal.addEventListener('abort', () => reject(new AbortError(signal.reason ? String(signal.reason) : undefined)), { once: true }); }); } /** * Creates a promise that rejects after a timeout */ function createTimeoutPromise(options) { return new Promise((_, reject) => { setTimeout(() => { const message = options.timeoutMessage || `Operation timed out after ${options.timeout}ms`; reject(new AbortError(message)); }, options.timeout); }); }