-
Notifications
You must be signed in to change notification settings - Fork 706
Sample Strategy #1313
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Sample Strategy #1313
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -287,7 +287,8 @@ export async function tryPost( | |||||||||||||||||||||||||||||||||||||||||||||||||
requestHeaders: Record<string, string>, | ||||||||||||||||||||||||||||||||||||||||||||||||||
fn: endpointStrings, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentIndex: number | string, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method: string = 'POST' | ||||||||||||||||||||||||||||||||||||||||||||||||||
method: string = 'POST', | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal?: AbortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
): Promise<Response> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const requestContext = new RequestContext( | ||||||||||||||||||||||||||||||||||||||||||||||||||
c, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -298,6 +299,9 @@ export async function tryPost( | |||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentIndex as number | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (abortSignal) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
requestContext.setAbortSignal(abortSignal); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
const hooksService = new HooksService(requestContext); | ||||||||||||||||||||||||||||||||||||||||||||||||||
const providerContext = new ProviderContext(requestContext.provider); | ||||||||||||||||||||||||||||||||||||||||||||||||||
const logsService = new LogsService(c); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -472,7 +476,8 @@ export async function tryTargetsRecursively( | |||||||||||||||||||||||||||||||||||||||||||||||||
fn: endpointStrings, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||
jsonPath: string, | ||||||||||||||||||||||||||||||||||||||||||||||||||
inheritedConfig: Record<string, any> = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||
inheritedConfig: Record<string, any> = {}, | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal?: AbortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
): Promise<Response> { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const currentTarget: any = { ...targetGroup }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let currentJsonPath = jsonPath; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -662,7 +667,8 @@ export async function tryTargetsRecursively( | |||||||||||||||||||||||||||||||||||||||||||||||||
fn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
`${currentJsonPath}.targets[${originalIndex}]`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
const codes = currentTarget.strategy?.onStatusCodes; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const gatewayException = | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -705,14 +711,131 @@ export async function tryTargetsRecursively( | |||||||||||||||||||||||||||||||||||||||||||||||||
fn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentJsonPath, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
randomWeight -= provider.weight; | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
case StrategyModes.SAMPLE: { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const targets = currentTarget.targets || []; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const onStatusCodes = currentTarget.strategy?.onStatusCodes; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const cancelOthers = currentTarget.strategy?.cancelOthers; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// v1 limitation: do not support sampling when request body is a ReadableStream | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (request instanceof ReadableStream) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
response = new Response( | ||||||||||||||||||||||||||||||||||||||||||||||||||
JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||
status: 'failure', | ||||||||||||||||||||||||||||||||||||||||||||||||||
message: | ||||||||||||||||||||||||||||||||||||||||||||||||||
'Strategy "sample" does not support streaming request bodies in v1', | ||||||||||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||||||||||
{ status: 400, headers: { 'content-type': 'application/json' } } | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Fire all requests in parallel; pick first-success | ||||||||||||||||||||||||||||||||||||||||||||||||||
let winnerResolved = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||
let resolveWinner: (value: Response) => void = () => {}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const winnerPromise = new Promise<Response>((resolve) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
resolveWinner = resolve; | ||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
const controllers: AbortController[] = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const pendingPromises: Array< | ||||||||||||||||||||||||||||||||||||||||||||||||||
Promise<{ resp: Response; idx: number; abort: AbortController }> | ||||||||||||||||||||||||||||||||||||||||||||||||||
> = targets.map((t: Targets, index: number) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const originalIndex = (t.originalIndex as number | undefined) ?? index; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const controller = new AbortController(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
controllers.push(controller); | ||||||||||||||||||||||||||||||||||||||||||||||||||
return tryTargetsRecursively( | ||||||||||||||||||||||||||||||||||||||||||||||||||
c, | ||||||||||||||||||||||||||||||||||||||||||||||||||
t, | ||||||||||||||||||||||||||||||||||||||||||||||||||
request, | ||||||||||||||||||||||||||||||||||||||||||||||||||
requestHeaders, | ||||||||||||||||||||||||||||||||||||||||||||||||||
fn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
`${currentJsonPath}.targets[${originalIndex}]`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||
controller.signal | ||||||||||||||||||||||||||||||||||||||||||||||||||
).then((resp) => ({ resp, idx: originalIndex, abort: controller })); | ||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Resolve on first-success | ||||||||||||||||||||||||||||||||||||||||||||||||||
for (const p of pendingPromises) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
p.then(({ resp, abort }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (winnerResolved) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const gatewayException = | ||||||||||||||||||||||||||||||||||||||||||||||||||
resp?.headers.get('x-portkey-gateway-exception') === 'true'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const isSuccess = | ||||||||||||||||||||||||||||||||||||||||||||||||||
(Array.isArray(onStatusCodes) && | ||||||||||||||||||||||||||||||||||||||||||||||||||
!onStatusCodes.includes(resp?.status)) || | ||||||||||||||||||||||||||||||||||||||||||||||||||
(!onStatusCodes && resp?.ok) || | ||||||||||||||||||||||||||||||||||||||||||||||||||
gatewayException; | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+774
to
+778
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🐛 Bug Fix Issue: Logic error in success condition - the boolean OR operator
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
if (isSuccess && !winnerResolved) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
winnerResolved = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||
resolveWinner(resp); | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (cancelOthers) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
for (const ctl of controllers) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||
ctl.abort(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} catch {} | ||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Code Refactor Issue: Empty catch block silently ignores abort errors, making debugging difficult
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+780
to
+788
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Code Refactor Issue: Duplicate abort controller cleanup logic - same code block appears twice (lines 782-788 and 812-818)
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
}).catch(() => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Ignore individual errors; overall fallback handled below | ||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// If none succeed, return the last completed response | ||||||||||||||||||||||||||||||||||||||||||||||||||
(async () => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const results = await Promise.allSettled(pendingPromises); | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (winnerResolved) return; | ||||||||||||||||||||||||||||||||||||||||||||||||||
const fulfilled = results.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||
( | ||||||||||||||||||||||||||||||||||||||||||||||||||
r | ||||||||||||||||||||||||||||||||||||||||||||||||||
): r is PromiseFulfilledResult<{ | ||||||||||||||||||||||||||||||||||||||||||||||||||
resp: Response; | ||||||||||||||||||||||||||||||||||||||||||||||||||
idx: number; | ||||||||||||||||||||||||||||||||||||||||||||||||||
abort: AbortController; | ||||||||||||||||||||||||||||||||||||||||||||||||||
}> => r.status === 'fulfilled' | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (fulfilled.length) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const { resp } = fulfilled[fulfilled.length - 1].value; | ||||||||||||||||||||||||||||||||||||||||||||||||||
winnerResolved = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||
resolveWinner(resp); | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (cancelOthers) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
for (const ctl of controllers) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||
ctl.abort(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} catch {} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||||||||||||||
// If all rejected (shouldn't generally happen because tryTargetsRecursively guards), pick a generic 500 | ||||||||||||||||||||||||||||||||||||||||||||||||||
winnerResolved = true; | ||||||||||||||||||||||||||||||||||||||||||||||||||
resolveWinner( | ||||||||||||||||||||||||||||||||||||||||||||||||||
new Response( | ||||||||||||||||||||||||||||||||||||||||||||||||||
JSON.stringify({ | ||||||||||||||||||||||||||||||||||||||||||||||||||
status: 'failure', | ||||||||||||||||||||||||||||||||||||||||||||||||||
message: 'All sample targets failed', | ||||||||||||||||||||||||||||||||||||||||||||||||||
}), | ||||||||||||||||||||||||||||||||||||||||||||||||||
{ status: 500, headers: { 'content-type': 'application/json' } } | ||||||||||||||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
})(); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
response = await winnerPromise; | ||||||||||||||||||||||||||||||||||||||||||||||||||
// Note: cancelOthers is a no-op for now; underlying fetch cancellation will be wired in a later update | ||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
case StrategyModes.CONDITIONAL: { | ||||||||||||||||||||||||||||||||||||||||||||||||||
let metadata: Record<string, string>; | ||||||||||||||||||||||||||||||||||||||||||||||||||
try { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -749,7 +872,8 @@ export async function tryTargetsRecursively( | |||||||||||||||||||||||||||||||||||||||||||||||||
fn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
`${currentJsonPath}.targets[${originalIndex}]`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -764,7 +888,8 @@ export async function tryTargetsRecursively( | |||||||||||||||||||||||||||||||||||||||||||||||||
fn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
`${currentJsonPath}.targets[${originalIndex}]`, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentInheritedConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
break; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -777,7 +902,8 @@ export async function tryTargetsRecursively( | |||||||||||||||||||||||||||||||||||||||||||||||||
requestHeaders, | ||||||||||||||||||||||||||||||||||||||||||||||||||
fn, | ||||||||||||||||||||||||||||||||||||||||||||||||||
currentJsonPath, | ||||||||||||||||||||||||||||||||||||||||||||||||||
method | ||||||||||||||||||||||||||||||||||||||||||||||||||
method, | ||||||||||||||||||||||||||||||||||||||||||||||||||
abortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
if (isHandlingCircuitBreaker) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
await c.get('handleCircuitBreakerResponse')?.( | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -1165,7 +1291,8 @@ export async function recursiveAfterRequestHookHandler( | |||||||||||||||||||||||||||||||||||||||||||||||||
retry.onStatusCodes, | ||||||||||||||||||||||||||||||||||||||||||||||||||
requestTimeout, | ||||||||||||||||||||||||||||||||||||||||||||||||||
requestHandler, | ||||||||||||||||||||||||||||||||||||||||||||||||||
retry.useRetryAfterHeader | ||||||||||||||||||||||||||||||||||||||||||||||||||
retry.useRetryAfterHeader, | ||||||||||||||||||||||||||||||||||||||||||||||||||
requestContext.abortSignal | ||||||||||||||||||||||||||||||||||||||||||||||||||
)); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
// Check if sync hooks are available | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -5,10 +5,18 @@ async function fetchWithTimeout( | |||||||||||||||||||||||||||
url: string, | ||||||||||||||||||||||||||||
options: RequestInit, | ||||||||||||||||||||||||||||
timeout: number, | ||||||||||||||||||||||||||||
requestHandler?: () => Promise<Response> | ||||||||||||||||||||||||||||
requestHandler?: () => Promise<Response>, | ||||||||||||||||||||||||||||
externalAbortSignal?: AbortSignal | ||||||||||||||||||||||||||||
) { | ||||||||||||||||||||||||||||
const controller = new AbortController(); | ||||||||||||||||||||||||||||
const timeoutId = setTimeout(() => controller.abort(), timeout); | ||||||||||||||||||||||||||||
if (externalAbortSignal) { | ||||||||||||||||||||||||||||
if (externalAbortSignal.aborted) { | ||||||||||||||||||||||||||||
controller.abort(); | ||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||
externalAbortSignal.addEventListener('abort', () => controller.abort()); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
Comment on lines
+13
to
+19
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🐛 Bug Fix Issue: Race condition - external abort signal could be triggered between the
Suggested change
|
||||||||||||||||||||||||||||
const timeoutRequestOptions = { | ||||||||||||||||||||||||||||
...options, | ||||||||||||||||||||||||||||
signal: controller.signal, | ||||||||||||||||||||||||||||
|
@@ -69,7 +77,8 @@ export const retryRequest = async ( | |||||||||||||||||||||||||||
statusCodesToRetry: number[], | ||||||||||||||||||||||||||||
timeout: number | null, | ||||||||||||||||||||||||||||
requestHandler?: () => Promise<Response>, | ||||||||||||||||||||||||||||
followProviderRetry?: boolean | ||||||||||||||||||||||||||||
followProviderRetry?: boolean, | ||||||||||||||||||||||||||||
externalAbortSignal?: AbortSignal | ||||||||||||||||||||||||||||
): Promise<{ | ||||||||||||||||||||||||||||
response: Response; | ||||||||||||||||||||||||||||
attempt: number | undefined; | ||||||||||||||||||||||||||||
|
@@ -93,12 +102,17 @@ export const retryRequest = async ( | |||||||||||||||||||||||||||
url, | ||||||||||||||||||||||||||||
options, | ||||||||||||||||||||||||||||
timeout, | ||||||||||||||||||||||||||||
requestHandler | ||||||||||||||||||||||||||||
requestHandler, | ||||||||||||||||||||||||||||
externalAbortSignal | ||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||
} else if (requestHandler) { | ||||||||||||||||||||||||||||
response = await requestHandler(); | ||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||
response = await fetch(url, options); | ||||||||||||||||||||||||||||
const noTimeoutOptions = { ...options } as RequestInit; | ||||||||||||||||||||||||||||
if (externalAbortSignal) { | ||||||||||||||||||||||||||||
noTimeoutOptions.signal = externalAbortSignal; | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
response = await fetch(url, noTimeoutOptions); | ||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||
if (statusCodesToRetry.includes(response.status)) { | ||||||||||||||||||||||||||||
const errorObj: any = new Error(await response.text()); | ||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🐛 Bug Fix
Issue: Type confusion bug -
request instanceof ReadableStream
will always be false. Therequest
parameter is aRequest
object, not aReadableStream
. This check should verify if the request body is a stream.Fix: Check the request body type instead of the request object type
Impact: Prevents incorrect rejection of streaming requests, ensuring proper validation