UNPKG

qasphere-mcp

Version:
75 lines (74 loc) 2.25 kB
import { QASPHERE_API_KEY, QASPHERE_TENANT_URL } from './config.js'; export class ApiError extends Error { status; body; constructor(message, status, body) { super(message); this.name = 'ApiError'; this.status = status; this.body = body; } } export class ResponseValidationError extends Error { body; constructor(message, body) { super(message); this.name = 'ResponseValidationError'; this.body = body; } } const buildQueryString = (query) => { if (!query) return ''; const params = new URLSearchParams(); for (const [key, value] of Object.entries(query)) { if (value === undefined) continue; if (Array.isArray(value)) { for (const item of value) params.append(key, String(item)); } else { params.append(key, String(value)); } } const str = params.toString(); return str ? `?${str}` : ''; }; export async function apiQuery(path, opts) { const url = `${QASPHERE_TENANT_URL}${path}${buildQueryString(opts.query)}`; const method = opts.method ?? 'GET'; const headers = { Authorization: `ApiKey ${QASPHERE_API_KEY}`, }; if (opts.body !== undefined) { headers['Content-Type'] = 'application/json'; } const res = await fetch(url, { method, headers, body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined, }); if (!res.ok) { let body; let message = res.statusText; try { body = await res.json(); if (body && typeof body === 'object' && 'message' in body) { const m = body.message; if (typeof m === 'string' && m.length > 0) message = m; } } catch { // ignore JSON parse failures; fall back to statusText } throw new ApiError(message, res.status, body); } const raw = await res.json(); const parsed = opts.schema.safeParse(raw); if (!parsed.success) { throw new ResponseValidationError(`Response validation failed: ${parsed.error.message}`, raw); } return parsed.data; }