|
1 | 1 | import type { DataRecord, ConsolidatedRecord, Dictionary, CutoutRecord } from './types'; |
2 | 2 |
|
3 | | -// In absence of a backend we will load static JSON via fetch (can be swapped later) |
| 3 | +// NOTE: When deployed on GitHub Pages the app is usually served from /<repo-name>/. |
| 4 | +// Using absolute paths (starting with "/") causes fetches to point at the domain root |
| 5 | +// (e.g. https://<user>.github.io/data/...) which 404s and returns the HTML index page. |
| 6 | +// That HTML then triggers: "SyntaxError: Unexpected token '<'" when res.json() is called. |
| 7 | +// We instead build URLs relative to Vite's injected BASE_URL (import.meta.env.BASE_URL). |
4 | 8 |
|
5 | | -export const loadDatabase = async (): Promise<DataRecord[]> => { |
6 | | - const res = await fetch('/data/database.json'); |
7 | | - return res.json(); |
8 | | -}; |
| 9 | +const BASE_URL: string = (import.meta as { env?: Record<string, string> }).env?.BASE_URL || '/'; |
9 | 10 |
|
10 | | -export const loadConsolidated = async (): Promise<ConsolidatedRecord[]> => { |
11 | | - const res = await fetch('/data/consolidated_database.json'); |
12 | | - return res.json(); |
| 11 | +const buildDataUrl = (file: string) => { |
| 12 | + // Ensure exactly one trailing slash for BASE_URL then append data/<file> |
| 13 | + const base = BASE_URL.endsWith('/') ? BASE_URL : `${BASE_URL}/`; |
| 14 | + return `${base}data/${file}`; |
13 | 15 | }; |
14 | 16 |
|
15 | | -export const loadDictionary = async (): Promise<Dictionary> => { |
16 | | - // dictionary.npy not easily consumable directly; expect a JSON conversion later. |
17 | | - // Placeholder expects a json representation placed at /data/dictionary.json |
18 | | - const res = await fetch('/data/dictionary.json'); |
19 | | - return res.json(); |
20 | | -}; |
| 17 | +async function fetchJson<T>(file: string): Promise<T> { |
| 18 | + const url = buildDataUrl(file); |
| 19 | + const res = await fetch(url, { cache: 'no-cache' }); |
| 20 | + if (!res.ok) { |
| 21 | + // Surface clearer diagnostics when something goes wrong (like path issues on Pages) |
| 22 | + const text = await res.text(); |
| 23 | + throw new Error(`Failed to fetch ${url} (HTTP ${res.status}) - First 120 chars: ${text.slice(0, 120)}`); |
| 24 | + } |
| 25 | + try { |
| 26 | + return await res.json(); |
| 27 | + } catch (err) { |
| 28 | + // Provide snippet of body to aid debugging of unexpected HTML responses |
| 29 | + const body = await res.clone().text().catch(() => ''); |
| 30 | + throw new Error(`Invalid JSON at ${url}: ${(err as Error).message}. Snippet: ${body.slice(0, 120)}`); |
| 31 | + } |
| 32 | +} |
21 | 33 |
|
22 | | -export const loadCutouts = async (): Promise<CutoutRecord[]> => { |
23 | | - const res = await fetch('/data/cutouts.json'); |
24 | | - return res.json(); |
25 | | -}; |
| 34 | +// Public data loaders (can be swapped for real API later) |
| 35 | +export const loadDatabase = (): Promise<DataRecord[]> => fetchJson<DataRecord[]>('database.json'); |
| 36 | +export const loadConsolidated = (): Promise<ConsolidatedRecord[]> => fetchJson<ConsolidatedRecord[]>('consolidated_database.json'); |
| 37 | +export const loadDictionary = (): Promise<Dictionary> => fetchJson<Dictionary>('dictionary.json'); |
| 38 | +export const loadCutouts = (): Promise<CutoutRecord[]> => fetchJson<CutoutRecord[]>('cutouts.json'); |
26 | 39 |
|
27 | 40 | // MinIO direct image retrieval (signed URL pattern) - placeholder using fetch of gateway |
28 | 41 | // Direct public path construction (no proxy). Expect objectKey like |
|
0 commit comments