function createDebugBodyPreview(text, limit) { if (text.length <= limit) return { value: text || "(empty response body)", truncated: false }; try { const normalized = text.replace(/^\uFEFF/, "").trimStart() .replace(/^\)\]\}',?\s*/, "").replace(/^while\s*\(1\);\s*/, ""); let value = JSON.parse(normalized); for (let depth = 0; depth < 2 && typeof value === "string" && /^\s*[\[{]/.test(value); depth += 1) { try { value = JSON.parse(value); } catch { break; } } // Bound retained data without cutting JSON syntax or losing sensitive field names. let remaining = Math.max(0, limit - 256); const visit = (node, depth = 0) => { if (remaining <= 64 || depth >= 30) return "[omitted from debug preview]"; if (node === null || typeof node !== "object") { const serialized = JSON.stringify(node); if (serialized.length > remaining) { remaining = 0; return "[omitted from debug preview]"; } remaining -= serialized.length + 1; return node; } const result = Array.isArray(node) ? [] : Object.create(null); remaining -= 2; for (const key of Object.keys(node)) { const cost = Array.isArray(node) ? 1 : JSON.stringify(key).length + 2; if (remaining < cost + 64) break; remaining -= cost; result[key] = visit(node[key], depth + 1); } return result; }; return { value: JSON.stringify({ debugNotice: "Partial response preview: remaining fields/items omitted due to the debug size limit.", preview: visit(value) }), truncated: true }; } catch { return { value: `${text.slice(0, limit)}\n... truncated ${text.length - limit} characters`, truncated: true }; } }