import assert from "node:assert/strict"; export class AbortError extends Error { constructor(signal: AbortSignal) { assert(signal.aborted, "Abort signal must be aborted"); const message = signal.reason instanceof Error ? signal.reason.message : "Cancelled"; super(`Aborted: ${message}`, { cause: signal.reason }); this.name = "AbortError"; } } /** * Creates an abortable stream from a given stream and signal. * * @param stream - The stream to make abortable * @param signal - The signal to abort the stream * @returns The abortable stream */ export function createAbortableStream(stream: ReadableStream, signal?: AbortSignal): ReadableStream { if (!signal) return stream; return stream.pipeThrough(new TransformStream(), { signal }); } /** * Runs a promise-returning function (`pr`). If the given AbortSignal is aborted before or during * execution, the promise is rejected with a standard error. * * @param signal - Optional AbortSignal to cancel the operation * @param pr - Function returning a promise to run * @returns Promise resolving as `pr` would, or rejecting on abort */ export function untilAborted( signal: AbortSignal | undefined | null, pr: Promise | (() => Promise), ): Promise { if (!signal) return typeof pr === "function" ? pr() : pr; if (signal.aborted) return Promise.reject(new AbortError(signal)); const { promise, resolve, reject } = Promise.withResolvers(); const onAbort = () => reject(new AbortError(signal)); signal.addEventListener("abort", onAbort, { once: true }); void (async () => { try { resolve(await (typeof pr === "function" ? pr() : pr)); } catch (err) { reject(err); } finally { signal.removeEventListener("abort", onAbort); } })(); return promise; } /** * Memoizes a function with no arguments, calling it once and caching the result. * * @param fn - Function to be called once * @returns A function that returns the cached result of `fn` */ export function once(fn: () => T): () => T { let store = undefined as { value: T } | undefined; return () => { if (store) { return store.value; } const value = fn(); store = { value }; return value; }; }