import { loadConfig } from "./configLoader"; import type { ConfigResult } from "./types"; /** * Fetch and validate config.json at runtime. The URL is resolved against the * app's configured base (import.meta.env.BASE_URL), so it works whether the app * is served from a domain root or a subpath. */ export async function fetchConfig(): Promise { const url = configUrl(); let response: Response; try { response = await fetch(url.toString(), { cache: "no-cache" }); } catch (err) { return { ok: false, errors: [`Could not fetch config.json (${describe(err)}).`], }; } if (!response.ok) { return { ok: false, errors: [ `Could not load config.json (HTTP ${response.status} ${response.statusText}).`, ], }; } let raw: unknown; try { raw = await response.json(); } catch { return { ok: false, errors: ["config.json is not valid JSON."], }; } return loadConfig(raw); } /** * Resolve the config.json URL for the deployed location. Uses Vite's configured * base when it is an absolute path (e.g. "/meal-estimator/"); otherwise falls * back to the directory of the current document so a relative "./" base works * whether or not the URL has a trailing slash. */ function configUrl(): string { const base = import.meta.env.BASE_URL; if (base.startsWith("/")) { // Absolute base path: config sits directly under it. return new URL(`${base}config.json`, window.location.origin).toString(); } // Relative base: resolve against the current document's directory. const path = window.location.pathname; const dir = path.endsWith("/") ? path : path.slice(0, path.lastIndexOf("/") + 1); return new URL(`${dir}config.json`, window.location.origin).toString(); } function describe(err: unknown): string { return err instanceof Error ? err.message : "network error"; }