import axios from "axios"; import * as fs from "fs"; import * as path from "path"; function getApiBaseUrl() { return ( process.env.DEBUGBEAR_API_BASE_URL || "https://www.debugbear.com/api/v1" ); } // Set WRITE_API_LOG=1 (or any truthy value) to append a record of every // request the SDK makes — method, URL, body, status, duration, response // — to ./debugbear-npm-api-log.txt in the caller's cwd. Useful when // debugging SDK calls from sandbox scripts where you can't easily attach // an interceptor (the SDK's axios is a separate module instance from // the caller's). function formatBodyForLog(body: unknown): string { if (body === undefined || body === null) { return "(no body)"; } if (typeof body === "string") { return body; } try { return JSON.stringify(body, null, 2); } catch { return String(body); } } function writeApiLogEntry(entry: { timestamp: string; durationMs: number; method: string; url: string; status: number | null; requestBody?: unknown; responseBody?: unknown; error?: string; }) { if (!process.env.WRITE_API_LOG) { return; } try { const statusPart = entry.status !== null ? ` ${entry.status}` : entry.error ? " ERROR" : ""; const lines: string[] = []; lines.push(`${entry.timestamp} (Duration: ${entry.durationMs}ms)`); lines.push(""); lines.push(`${entry.method} ${entry.url}${statusPart}`); if (entry.requestBody !== undefined) { lines.push(formatBodyForLog(entry.requestBody)); } lines.push(""); if (entry.error) { lines.push(`Error: ${entry.error}`); } lines.push(formatBodyForLog(entry.responseBody)); lines.push(""); lines.push(""); fs.appendFileSync( path.join(process.cwd(), "debugbear-npm-api-log.txt"), lines.join("\n"), ); } catch { // Don't let logging failures break the SDK. } } export async function callApi({ method, path: reqPath, body, explicitApiKeyParam, }: { method: "get" | "post" | "delete" | "patch"; path: string; body?: any; explicitApiKeyParam?: string; }): Promise { const reqDetail = { method, json: true, data: body, url: getApiBaseUrl() + reqPath, headers: { "X-Api-Key": explicitApiKeyParam, "Cli-Version": require("../package.json").version, }, // Ensure that Axios doesn't make preflight requests // (I think this happens because Jest makes the env look // more like a browser than Node) adapter: "http", }; if (process.env.DEBUG) { console.log("Making API request", reqDetail); } const start = Date.now(); const logBase = { timestamp: new Date().toISOString(), method: method.toUpperCase(), url: reqDetail.url, requestBody: body, }; try { const resp = await axios(reqDetail); writeApiLogEntry({ ...logBase, status: resp.status, durationMs: Date.now() - start, responseBody: resp.data, }); return resp.data; } catch (err: any) { writeApiLogEntry({ ...logBase, status: err.response?.status ?? null, durationMs: Date.now() - start, responseBody: err.response?.data ?? null, error: err.message, }); let msg = err.response?.data?.error || err.message; if (err.response && err.response.body && err.response.body.error) { msg = err.response.body.error; } const error = Error( `Error making request ${method.toUpperCase()} ${reqPath}: ${msg}`, ); error["status"] = err.response?.status; throw error; } }