Spaces:
Paused
Paused
| const DEFAULT_CAPTURED_OUTPUT_BYTES = 256 * 1024; | |
| const DEFAULT_JSON_RESPONSE_BYTES = 64 * 1024; | |
| function normalizeByteLimit(maxBytes) { | |
| return Math.max(1, Math.trunc(maxBytes)); | |
| } | |
| export function createCapturedOutputBuffer(maxBytes = DEFAULT_CAPTURED_OUTPUT_BYTES) { | |
| const limit = normalizeByteLimit(maxBytes); | |
| const chunks = []; | |
| let bufferedBytes = 0; | |
| let totalBytes = 0; | |
| let truncated = false; | |
| return { | |
| append(chunk) { | |
| if (chunk === null || chunk === undefined) return; | |
| const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); | |
| if (buffer.length === 0) return; | |
| chunks.push(buffer); | |
| bufferedBytes += buffer.length; | |
| totalBytes += buffer.length; | |
| while (bufferedBytes > limit && chunks.length > 0) { | |
| const overflow = bufferedBytes - limit; | |
| const head = chunks[0]; | |
| if (head.length <= overflow) { | |
| chunks.shift(); | |
| bufferedBytes -= head.length; | |
| truncated = true; | |
| continue; | |
| } | |
| chunks[0] = head.subarray(overflow); | |
| bufferedBytes -= overflow; | |
| truncated = true; | |
| } | |
| }, | |
| finish() { | |
| const body = Buffer.concat(chunks).toString("utf8"); | |
| if (!truncated) { | |
| return { | |
| text: body, | |
| truncated, | |
| totalBytes, | |
| }; | |
| } | |
| return { | |
| text: `[output truncated to last ${limit} bytes; total ${totalBytes} bytes]\n${body}`, | |
| truncated, | |
| totalBytes, | |
| }; | |
| }, | |
| }; | |
| } | |
| export async function parseJsonResponseWithLimit(response, maxBytes = DEFAULT_JSON_RESPONSE_BYTES) { | |
| const limit = normalizeByteLimit(maxBytes); | |
| const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10); | |
| if (Number.isFinite(contentLength) && contentLength > limit) { | |
| throw new Error(`Response exceeds ${limit} bytes`); | |
| } | |
| if (!response.body) { | |
| return JSON.parse(""); | |
| } | |
| const reader = response.body.getReader(); | |
| const decoder = new TextDecoder(); | |
| let text = ""; | |
| let totalBytes = 0; | |
| try { | |
| while (true) { | |
| const { done, value } = await reader.read(); | |
| if (done) break; | |
| totalBytes += value.byteLength; | |
| if (totalBytes > limit) { | |
| await reader.cancel("response too large"); | |
| throw new Error(`Response exceeds ${limit} bytes`); | |
| } | |
| text += decoder.decode(value, { stream: true }); | |
| } | |
| text += decoder.decode(); | |
| } finally { | |
| reader.releaseLock(); | |
| } | |
| return JSON.parse(text); | |
| } | |