interface ReadableResponse { headers: { get: (name: string) => string | null }; body: { getReader: () => { read: () => Promise<{ done: boolean; value?: Uint8Array }>; cancel: () => Promise; }; } | null; } export async function readBoundedJsonResponseAsync( response: ReadableResponse, maxBytes: number, label = "Response", ): Promise { const declaredLength = Number(response.headers.get("content-length")); if (Number.isFinite(declaredLength) && declaredLength > maxBytes) { throw new Error(`${label} exceeds ${maxBytes} bytes`); } if (!response.body) return null; const reader = response.body.getReader(); const chunks: Uint8Array[] = []; let byteLength = 0; for (;;) { const { done, value } = await reader.read(); if (done) break; if (!value) continue; byteLength += value.byteLength; if (byteLength > maxBytes) { await reader.cancel(); throw new Error(`${label} exceeds ${maxBytes} bytes`); } chunks.push(value); } const body = new Uint8Array(byteLength); let offset = 0; for (const chunk of chunks) { body.set(chunk, offset); offset += chunk.byteLength; } return JSON.parse(new TextDecoder().decode(body)) as unknown; }