import { normalizeUrl } from "./url.js"; const REMOTE_DEFAULTS_URL ="https://raw.githubusercontent.com/Kuju29/TextPhantomOCR_Overlay/refs/heads/main/defaults_api.json"; const DEFAULT_API_URL_FALLBACK = ""; const RESET_API_URL_FALLBACK = ""; const FETCH_TIMEOUT_MS = 2000; const TTL_MS = 12 * 60 * 60 * 1000; function now() { return Date.now(); } function coerceUrl(v) { return typeof v === "string" ? normalizeUrl(v) : ""; } function parseMaybeJson(raw) { if (typeof raw !== "string") return null; const t = raw.trim(); if (!t) return null; try { return JSON.parse(t); } catch { if (!(t.startsWith("{") && t.endsWith("}"))) return null; const q1 = t.replace(/([{,]\s*)([A-Za-z0-9_]+)\s*:/g, '$1"$2":'); const q2 = q1.includes("'") && !q1.includes('"') ? q1.replace(/'/g, '"') : q1; try { return JSON.parse(q2); } catch { return null; } } } async function fetchRemoteDefaults() { const ctrl = new AbortController(); const t = setTimeout(() => ctrl.abort(), FETCH_TIMEOUT_MS); try { const res = await fetch(REMOTE_DEFAULTS_URL, { method: "GET", headers: { accept: "application/json" }, cache: "no-store", signal: ctrl.signal, }); if (!res.ok) return null; const raw = await res.text(); const data = parseMaybeJson(raw); if (!data || typeof data !== "object") return null; const defaultApiUrl = coerceUrl(data.defaultApiUrl) || coerceUrl(data.DEFAULTS_API) || coerceUrl(data.apiUrlDefault) || coerceUrl(data.default_api_url); const resetApiUrl = coerceUrl(data.resetApiUrl) || coerceUrl(data.apiUrlReset) || coerceUrl(data.reset_api_url); if (!defaultApiUrl && !resetApiUrl) return null; return { defaultApiUrl: defaultApiUrl || "", resetApiUrl: resetApiUrl || "", }; } catch { return null; } finally { clearTimeout(t); } } export async function ensureApiDefaults({ force = false } = {}) { const stored = await chrome.storage.local.get({ apiUrlDefault: DEFAULT_API_URL_FALLBACK, apiUrlReset: RESET_API_URL_FALLBACK, apiDefaultsFetchedAt: 0, }); const current = { defaultApiUrl: coerceUrl(stored.apiUrlDefault) || DEFAULT_API_URL_FALLBACK, resetApiUrl: coerceUrl(stored.apiUrlReset) || RESET_API_URL_FALLBACK, fetchedAt: Number(stored.apiDefaultsFetchedAt) || 0, }; const fresh = current.fetchedAt && now() - current.fetchedAt <= TTL_MS; if (!force && fresh) return current; const remote = await fetchRemoteDefaults(); if (!remote) return current; const next = { defaultApiUrl: remote.defaultApiUrl || current.defaultApiUrl, resetApiUrl: remote.resetApiUrl || current.resetApiUrl, fetchedAt: now(), }; await chrome.storage.local.set({ apiUrlDefault: next.defaultApiUrl, apiUrlReset: next.resetApiUrl, apiDefaultsFetchedAt: next.fetchedAt, }); return next; } export const API_DEFAULTS = { REMOTE_DEFAULTS_URL, DEFAULT_API_URL_FALLBACK, RESET_API_URL_FALLBACK, };