|
- (function (global) {
- const DEFAULT_TIMEOUT_MS = 5000;
-
- function toErrorMessage(err) {
- if (!err) return 'request failed';
- if (typeof err === 'string') return err;
- return err.message || 'request failed';
- }
-
- function createClient(opts = {}) {
- const baseUrl = opts.baseUrl || '';
- const timeoutMs = Number.isFinite(opts.timeoutMs) ? opts.timeoutMs : DEFAULT_TIMEOUT_MS;
-
- async function request(path, options = {}) {
- const controller = new AbortController();
- const start = performance.now();
- const timer = setTimeout(() => controller.abort(), timeoutMs);
- const init = {
- method: options.method || 'GET',
- headers: { ...(options.headers || {}) },
- signal: controller.signal,
- };
- if (options.body !== undefined) {
- init.headers['Content-Type'] = 'application/json';
- init.body = JSON.stringify(options.body);
- }
-
- try {
- const res = await fetch(`${baseUrl}${path}`, init);
- const durationMs = Math.round(performance.now() - start);
- const contentType = res.headers.get('content-type') || '';
- let data = null;
- if (contentType.includes('application/json')) data = await res.json();
- else data = await res.text();
- if (!res.ok) {
- const error = typeof data === 'string' ? data : (data?.error || `http ${res.status}`);
- return { ok: false, status: res.status, error, data, meta: { duration_ms: durationMs } };
- }
- return { ok: true, status: res.status, data, meta: { duration_ms: durationMs } };
- } catch (err) {
- const durationMs = Math.round(performance.now() - start);
- return { ok: false, status: 0, error: toErrorMessage(err), data: null, meta: { duration_ms: durationMs } };
- } finally {
- clearTimeout(timer);
- }
- }
-
- return {
- getConfig: () => request('/api/config'),
- postConfig: (payload) => request('/api/config', { method: 'POST', body: payload }),
- postSettings: (payload) => request('/api/sdr/settings', { method: 'POST', body: payload }),
- getSignals: () => request('/api/signals'),
- getStats: () => request('/api/stats'),
- getGPU: () => request('/api/gpu'),
- getPolicy: () => request('/api/pipeline/policy'),
- getRecommendations: () => request('/api/pipeline/recommendations'),
- getRefinement: () => request('/api/refinement'),
- getEvents: ({ limit, since } = {}) => {
- const params = new URLSearchParams();
- if (Number.isFinite(limit) && limit > 0) params.set('limit', String(limit));
- if (Number.isFinite(since) && since > 0) params.set('since', String(since));
- const suffix = params.toString() ? `?${params.toString()}` : '';
- return request(`/api/events${suffix}`);
- },
- getRecordings: () => request('/api/recordings'),
- getRecording: (id) => request(`/api/recordings/${encodeURIComponent(id)}`),
- decodeRecording: (id, mode) => request(`/api/recordings/${encodeURIComponent(id)}/decode?mode=${encodeURIComponent(mode || '')}`),
- getDecoders: () => request('/api/decoders'),
- getTelemetryLive: () => request('/api/debug/telemetry/live'),
- };
- }
-
- global.SpectreApi = { createClient };
- })(window);
|