// A minimal Cloudflare API client. Calls ride through the PDS's /cf-api // passthrough, which forwards the Authorization header and keeps nothing; // api.cloudflare.com itself answers no CORS preflight. /** @param {string} token @param {string} [base] */ export function cfClient(token, base = '/cf-api') { /** * @param {string} path * @param {RequestInit} [init] */ async function call(path, init = {}) { const response = await fetch(`${base}${path}`, { ...init, headers: { Authorization: `Bearer ${token}`, ...(init.body && !(init.body instanceof FormData) ? { 'Content-Type': 'application/json' } : {}), ...init.headers, }, }); const body = await response.json().catch(() => null); if (!body) { throw new Error(`Cloudflare API returned ${response.status}`); } if (!body.success) { const messages = (body.errors ?? []) .map((/** @type {{code?: number, message: string}} */ e) => e.code ? `${e.message} (code ${e.code})` : e.message, ) .join('; '); const error = new Error( messages || `Cloudflare API error ${response.status}`, ); /** @type {any} */ (error).codes = (body.errors ?? []).map( (/** @type {{code?: number}} */ e) => e.code, ); throw error; } return body.result; } return { /** @param {string} path */ get: (path) => call(path), /** @param {string} path @param {unknown} [body] */ post: (path, body) => call(path, { method: 'POST', body: body ? JSON.stringify(body) : undefined, }), /** @param {string} path @param {unknown} body */ put: (path, body) => call(path, { method: 'PUT', body: JSON.stringify(body) }), /** @param {string} path @param {unknown} body */ patch: (path, body) => call(path, { method: 'PATCH', body: JSON.stringify(body) }), /** @param {string} path @param {FormData} form */ putForm: (path, form) => call(path, { method: 'PUT', body: form }), }; }