'use strict'; const PagerAPI = (() => { let apiBase = ''; let on401 = null; // Reads are polled and must never hang a page's refresh loop; writes have // server-side timeouts up to 45s (radio deploys) so they get no client // abort. const GET_TIMEOUT_MS = 20000; async function request(method, path, body) { const opts = { method, headers: {}, credentials: 'include' }; if (body !== undefined) { opts.headers['Content-Type'] = 'application/json'; opts.body = JSON.stringify(body); } const ctl = new AbortController(); const timer = method === 'GET' ? setTimeout(() => ctl.abort(), GET_TIMEOUT_MS) : null; if (timer) opts.signal = ctl.signal; let res; try { res = await fetch(apiBase + path, opts); } catch (e) { if (timer && e && e.name === 'AbortError') { const error = new Error('Request timed out'); error.status = 0; throw error; } throw e; } finally { if (timer) clearTimeout(timer); } if (res.status === 401) { if (on401) on401(); throw new Error('unauthorized'); } const ct = res.headers.get('Content-Type') || ''; let data; if (ct.indexOf('json') !== -1) { try { data = await res.json(); } catch (e) { data = null; } } else { data = await res.text(); } if (!res.ok) { const detail = data && typeof data.detail === 'string' ? data.detail : ''; const message = (data && data.error ? data.error : ('HTTP ' + res.status)) + (detail ? ': ' + detail : ''); const error = new Error(message); error.status = res.status; error.data = data; throw error; } return { status: res.status, data }; } return { setBase: (b) => { apiBase = b; }, on401: (fn) => { on401 = fn; }, get: (p) => request('GET', p), post: (p, b) => request('POST', p, b === undefined ? {} : b), del: (p, b) => request('DELETE', p, b === undefined ? {} : b), login: async (username, password) => request('POST', '/api/login', { username, password }) }; })();