function createHttpBackgroundJson({ previewDebugBody, observeResponseHeaders = () => {}, environment }) { const { fetch } = environment; async function fetchJson(url, options, requestName, debugRequests = null, { allowEmpty = false } = {}) { const startedAt = new Date(); const debugEntry = createBackgroundDebugEntry(requestName, url, options, startedAt); let response; try { response = await fetch(url, options); } catch (error) { completeBackgroundDebugEntry(debugEntry, null, "", error.message || "Network request failed."); appendBackgroundDebugEntry(debugRequests, debugEntry); throw error; } observeResponseHeaders(response.url || url, response.headers); const responsePayload = await readResponsePayload(response); const responseBody = createDebugBody(responsePayload.text); completeBackgroundDebugEntry(debugEntry, response, responseBody, ""); debugEntry.metadata.responseContentType = response.headers.get("content-type") || "(not provided)"; debugEntry.metadata.responseLength = String(responsePayload.text.length); appendBackgroundDebugEntry(debugRequests, debugEntry); if (!response.ok) { const error = new Error(`${requestName} failed with status ${response.status}.`); error.httpStatus = response.status; debugEntry.error = error.message; throw error; } if (responsePayload.error) { throw new Error(`${requestName} response body could not be read.`); } try { if (allowEmpty && !responsePayload.text.trim()) return null; return parseJsonResponse(responsePayload.text, requestName); } catch (error) { error.httpStatus = response.status; debugEntry.status = "failed"; debugEntry.error = error.message; throw error; } } function createBackgroundDebugEntry(requestName, url, options, startedAt) { return { name: requestName, method: options.method || "GET", url, status: "pending", startedAt: startedAt.toISOString(), completedAt: null, durationMs: null, httpStatus: null, statusText: "", requestHeaders: normalizeDebugHeaders(options.headers), requestBody: formatDebugRequestBody(options.body), metadata: { executionContext: "background" }, responseHeaders: {}, responseBody: "", responseBodyTruncated: false, error: "" }; } function completeBackgroundDebugEntry(entry, response, responseBody, errorMessage) { const completedAt = new Date(); entry.completedAt = completedAt.toISOString(); entry.durationMs = completedAt.getTime() - new Date(entry.startedAt).getTime(); entry.error = errorMessage; if (!response) { entry.status = "failed"; return; } entry.status = response.ok ? "success" : "failed"; entry.httpStatus = response.status; entry.statusText = response.statusText || ""; entry.responseHeaders = headersToObject(response.headers); entry.responseBody = responseBody.value; entry.responseBodyTruncated = responseBody.truncated; } function appendBackgroundDebugEntry(debugRequests, entry) { if (Array.isArray(debugRequests)) { debugRequests.push(entry); } } async function readResponsePayload(response) { try { return { text: await response.text(), error: "" }; } catch (error) { return { text: `Unable to read response body: ${error.message || "unknown error"}`, error: error.message || "unknown error" }; } } function parseJsonResponse(text, requestName) { const normalizedText = String(text) .replace(/^\uFEFF/, "") .trimStart() .replace(/^\)\]\}',?\s*/, "") .replace(/^while\s*\(1\);\s*/, ""); try { return JSON.parse(normalizedText); } catch (error) { const invalidJsonError = new Error(`${requestName} returned an invalid JSON response.`); invalidJsonError.code = "INVALID_JSON_RESPONSE"; throw invalidJsonError; } } function createDebugBody(text) { return previewDebugBody(text); } function normalizeDebugHeaders(headers) { const normalized = {}; Object.entries(headers || {}).forEach(([key, value]) => { normalized[key] = String(value); }); return normalized; } function headersToObject(headers) { const values = {}; if (headers && typeof headers.forEach === "function") { headers.forEach((value, key) => { values[key] = value; }); } return values; } function formatDebugRequestBody(body) { if (!body) { return ""; } try { return JSON.stringify(JSON.parse(body), null, 2); } catch (error) { return String(body); } } return { fetchJson, createBackgroundDebugEntry, completeBackgroundDebugEntry, appendBackgroundDebugEntry, readResponsePayload, parseJsonResponse, createDebugBody, normalizeDebugHeaders, headersToObject, formatDebugRequestBody }; }