function abortError(signal: AbortSignal): Error {
  if (signal.reason instanceof Error) return signal.reason;
  return new DOMException('Operation aborted', 'AbortError');
}

/**
 * Settles immediately when an AbortSignal fires while still attaching a
 * rejection handler to the underlying engine promise. Browser WASM workers do
 * not all reject their pending RPC promise when terminated, so cancellation
 * must not depend on engine-specific behaviour.
 */
export function abortable<T>(
  operation: Promise<T>,
  signal: AbortSignal | undefined,
  onAbort?: () => void,
): Promise<T> {
  if (!signal) return operation;
  if (signal.aborted) {
    onAbort?.();
    return Promise.reject(abortError(signal));
  }

  return new Promise<T>((resolve, reject) => {
    const handleAbort = () => {
      onAbort?.();
      reject(abortError(signal));
    };
    signal.addEventListener('abort', handleAbort, { once: true });
    operation.then(
      (value) => {
        signal.removeEventListener('abort', handleAbort);
        resolve(value);
      },
      (error: unknown) => {
        signal.removeEventListener('abort', handleAbort);
        reject(error);
      },
    );
  });
}
