From fb6ad3eb0c7725864ab584d2e036f466e64c3ee9 Mon Sep 17 00:00:00 2001 From: hermes-explorigin Date: Sun, 16 Aug 2026 14:39:58 +0000 Subject: [PATCH] Fix: resolve data paths against app base for subdirectory deploys The static app is served at /maps/ but the data manifest and graph/poi/layer fetches used absolute /data/... paths, which resolved to the server root and failed. Add src/lib/base.ts exporting upath() that prefixes a path with the runtime-detected base (mirroring SvelteKit's 'base: new URL(".", location)'), and apply it to the manifest, layer, graph and POI fetches. Works at both root and any sub-path. --- src/lib/base.ts | 27 +++++++++++++++++++++++++++ src/lib/components/MapView.svelte | 5 +++-- src/routes/+page.svelte | 3 ++- 3 files changed, 32 insertions(+), 3 deletions(-) create mode 100644 src/lib/base.ts diff --git a/src/lib/base.ts b/src/lib/base.ts new file mode 100644 index 0000000..821f4a4 --- /dev/null +++ b/src/lib/base.ts @@ -0,0 +1,27 @@ +// Base-path-aware data URL resolution for the Navigator static app. +// +// The app is built with the static adapter and deployed to a subdirectory +// (e.g. /maps/). Its built index.html auto-detects the base from location +// (base: new URL(".", location)), so all internal asset/data fetches must be +// prefixed with that same base. Data manifest paths are stored as absolute +// (/data/...) by the build scripts, so we resolve them against the running +// location rather than the server root. +// +// E.g. served at https://site/maps/ -> base '/maps'; the manifest path +// '/data/_index.json' becomes '/maps/data/_index.json'. + +export function appBase(): string { + if (typeof window === 'undefined') return ''; + // Mirror the SvelteKit runtime detection: base = directory of the current + // page, with the trailing slash removed (so '/' -> '', '/maps/' -> '/maps'). + const dir = new URL('.', window.location.href).pathname; + return dir.replace(/\/+$/, ''); +} + +// Resolve a path that may be absolute ('/data/x.json') relative to the app's +// base. Leaves already-relative and non-leading-slash/dotted paths untouched. +export function upath(p: string): string { + if (!p) return p; + if (p.startsWith('/')) return appBase() + p; + return p; +} \ No newline at end of file diff --git a/src/lib/components/MapView.svelte b/src/lib/components/MapView.svelte index cbba7e4..a982456 100644 --- a/src/lib/components/MapView.svelte +++ b/src/lib/components/MapView.svelte @@ -9,6 +9,7 @@ import { prefs } from '$lib/prefs'; import type { RouteGraph, AvoidRule, RouteResult } from '$lib/routing'; import { savedPoints, addSavedPoint, removeSavedPoint, exportSavedPoints, importSavedPoints, type SavedPoint } from '$lib/savedPoints'; + import { upath } from '$lib/base'; type FeatureCollection = { type: string; @@ -184,7 +185,7 @@ if (!layer || !layer.graphPath || !layer.poiPath) { routeGraph = null; hasRouteGraph = false; return; } routeLoading = true; try { - const [g, p] = await Promise.all([fetch(layer.graphPath), fetch(layer.poiPath)]); + const [g, p] = await Promise.all([fetch(upath(layer.graphPath)), fetch(upath(layer.poiPath))]); if (!g.ok) throw new Error(`graph HTTP ${g.status}`); if (!p.ok) throw new Error(`poi HTTP ${p.status}`); routeGraph = await g.json(); @@ -420,7 +421,7 @@ try { for (const layer of layers) { try { - const res = await fetch(layer.path); + const res = await fetch(upath(layer.path)); if (!res.ok) throw new Error(`fetch ${layer.path}: HTTP ${res.status}`); const fc: FeatureCollection = await res.json(); diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index 1dfb5cb..e2cb523 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,6 +1,7 @@