Merge main and keep the read timeout armed without AbortSignal.any
When the caller passes its own AbortSignal and AbortSignal.any is unavailable, the timeout was silently dropped — disabling the fix on exactly the bootstrap reads it targets, since those carry a cancellation signal. Compose the two signals manually through an AbortController in that case; listeners detach when the request settles.
This commit is contained in:
@@ -240,10 +240,34 @@ export const createRuntimeOpencodeClient = (config: RuntimeOpencodeClientConfig)
|
|||||||
const callerSignal = init?.signal;
|
const callerSignal = init?.signal;
|
||||||
const supportsAny = typeof AbortSignal !== 'undefined'
|
const supportsAny = typeof AbortSignal !== 'undefined'
|
||||||
&& typeof (AbortSignal as { any?: unknown }).any === 'function';
|
&& typeof (AbortSignal as { any?: unknown }).any === 'function';
|
||||||
const signal: AbortSignal = callerSignal && supportsAny
|
let signal: AbortSignal;
|
||||||
? (AbortSignal as typeof AbortSignal & { any: (signals: AbortSignal[]) => AbortSignal })
|
let detachFallback: (() => void) | null = null;
|
||||||
.any([callerSignal, timeout.signal])
|
if (callerSignal && supportsAny) {
|
||||||
: (callerSignal ?? timeout.signal);
|
signal = (AbortSignal as typeof AbortSignal & { any: (signals: AbortSignal[]) => AbortSignal })
|
||||||
|
.any([callerSignal, timeout.signal]);
|
||||||
|
} else if (callerSignal) {
|
||||||
|
// No AbortSignal.any: compose manually. Silently dropping the timeout
|
||||||
|
// here would disable the fix on exactly the bootstrap reads it
|
||||||
|
// targets, since those carry a cancellation signal.
|
||||||
|
const controller = new AbortController();
|
||||||
|
const abortFromCaller = () => controller.abort(callerSignal.reason);
|
||||||
|
const abortFromTimeout = () => controller.abort(timeout.signal.reason);
|
||||||
|
if (callerSignal.aborted) {
|
||||||
|
abortFromCaller();
|
||||||
|
} else if (timeout.signal.aborted) {
|
||||||
|
abortFromTimeout();
|
||||||
|
} else {
|
||||||
|
callerSignal.addEventListener('abort', abortFromCaller, { once: true });
|
||||||
|
timeout.signal.addEventListener('abort', abortFromTimeout, { once: true });
|
||||||
|
detachFallback = () => {
|
||||||
|
callerSignal.removeEventListener('abort', abortFromCaller);
|
||||||
|
timeout.signal.removeEventListener('abort', abortFromTimeout);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
signal = controller.signal;
|
||||||
|
} else {
|
||||||
|
signal = timeout.signal;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
return await runtimeFetch(input, { ...init, signal });
|
return await runtimeFetch(input, { ...init, signal });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
@@ -252,6 +276,7 @@ export const createRuntimeOpencodeClient = (config: RuntimeOpencodeClientConfig)
|
|||||||
}
|
}
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
|
detachFallback?.();
|
||||||
timeout.cleanup();
|
timeout.cleanup();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user