@juspay/neurolink
Version:
The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002
144 lines (143 loc) • 4.58 kB
JavaScript
/**
* Timeout Utilities
*
* Wrapper functions for adding timeout protection to async operations.
*/
/**
* Error thrown when an operation times out.
*/
export class TimeoutError extends Error {
timeoutMs;
/**
* Creates a new TimeoutError.
*
* @param message - Error message describing the timeout
* @param timeoutMs - The timeout duration that was exceeded
*/
constructor(message, timeoutMs) {
super(message);
this.timeoutMs = timeoutMs;
this.name = "TimeoutError";
// Maintains proper stack trace for where error was thrown (V8 engines)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, TimeoutError);
}
}
}
/**
* Execute a promise with timeout protection.
*
* Wraps a promise and rejects with a TimeoutError if the operation
* takes longer than the specified duration.
*
* @param promise - The promise to wrap with timeout
* @param ms - Maximum time to wait in milliseconds
* @param message - Optional custom error message for timeout
* @returns Promise that resolves with the result or rejects on timeout
* @throws {TimeoutError} If the operation exceeds the timeout duration
*
* @example
* ```typescript
* const result = await withTimeout(
* fetchData(),
* 5000,
* 'Data fetch timed out'
* );
* ```
*
* @example
* ```typescript
* try {
* const data = await withTimeout(slowOperation(), 3000);
* } catch (error) {
* if (error instanceof TimeoutError) {
* console.log(`Timed out after ${error.timeoutMs}ms`);
* }
* }
* ```
*/
export async function withTimeout(promise, ms, message) {
let timeoutId;
const timeoutPromise = new Promise((_, reject) => {
timeoutId = setTimeout(() => {
reject(new TimeoutError(message || `Operation timed out after ${ms}ms`, ms));
}, ms);
});
try {
return await Promise.race([promise, timeoutPromise]);
}
finally {
if (timeoutId !== undefined) {
clearTimeout(timeoutId);
}
}
}
/**
* Race a promise against an AbortSignal so the caller observes an abort
* IMMEDIATELY instead of waiting for the operation to settle (or for a
* separate timeout to expire). Rejects with an abort-shaped error
* (`name === "AbortError"`) so provider loops route it to their existing
* cancellation handling.
*
* The underlying operation is NOT cancelled — it continues as a bounded
* ghost (the same tradeoff as {@link withTimeout}); its eventual settlement
* is swallowed so it can never surface as an unhandled rejection.
*/
export async function raceWithAbort(promise, signal) {
if (!signal || typeof signal.addEventListener !== "function") {
return promise;
}
const makeAbortError = () => {
const e = new Error("Operation aborted");
e.name = "AbortError";
return e;
};
if (signal.aborted) {
promise.catch(() => {
// Swallow the abandoned settlement — it must never surface as an
// unhandled rejection after the race has already been decided.
});
return Promise.reject(makeAbortError());
}
return new Promise((resolve, reject) => {
const onAbort = () => {
promise.catch(() => {
// Swallow the abandoned settlement — it must never surface as an
// unhandled rejection after the race has already been decided.
});
reject(makeAbortError());
};
signal.addEventListener("abort", onAbort, { once: true });
promise.then((value) => {
signal.removeEventListener("abort", onAbort);
resolve(value);
}, (error) => {
signal.removeEventListener("abort", onAbort);
reject(error);
});
});
}
/**
* Execute a function with timeout protection.
*
* Alternative signature that accepts a function instead of a promise,
* useful when you want to delay starting the operation.
*
* @param fn - Async function to execute
* @param ms - Maximum time to wait in milliseconds
* @param message - Optional custom error message for timeout
* @returns Promise that resolves with the function result or rejects on timeout
* @throws {TimeoutError} If the operation exceeds the timeout duration
*
* @example
* ```typescript
* const result = await withTimeoutFn(
* () => fetchData(),
* 5000,
* 'Data fetch timed out'
* );
* ```
*/
export async function withTimeoutFn(fn, ms, message) {
return withTimeout(fn(), ms, message);
}