fix(spa): clientseitiger 404 nach hydration behoben (D)

Etappe 5 hatte einen bug: nach dem prerendered HTML hat svelteKit beim
ersten clientseitigen navigations-tick nochmal load() aufgerufen, das
hat im browser-pfad pauschal 404 geworfen. Sichtbarer effekt: seite
zeigt kurz den korrekten inhalt, springt dann auf "Post nicht
gefunden".

Fix-strategie D aus dem chat:
- snapshot/output/posts/*.json wird vor dem build nach
  app/static/snapshot-data/posts/ kopiert (deploy-skript + workflow)
- +page.ts im browser-pfad fetcht dann /snapshot-data/posts/<slug>.json
- 404 nur noch wenn die JSON wirklich nicht existiert (= slug nicht
  im snapshot)

Damit funktionieren sowohl hard-reload (svelteKit hydriert die
prerendered page-data direkt) als auch clientseitige navigation
zwischen detail-seiten.

app/static/snapshot-data/ ist gitignored — wird zur build-zeit aus
snapshot/output/ generiert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jörg Lohrer 2026-04-28 11:36:44 +02:00
parent f39b4ad62f
commit c3be5c167d
4 changed files with 33 additions and 2 deletions

View File

@ -73,6 +73,12 @@ jobs:
run: | run: |
deno run --allow-env --allow-read --allow-write --allow-net src/cli.ts deno run --allow-env --allow-read --allow-write --allow-net src/cli.ts
- name: Snapshot-JSONs in app/static kopieren
run: |
rm -rf app/static/snapshot-data
mkdir -p app/static/snapshot-data
cp -r snapshot/output/posts app/static/snapshot-data/posts
- name: Install + build SPA - name: Install + build SPA
working-directory: ./app working-directory: ./app
run: | run: |

3
app/.gitignore vendored
View File

@ -25,3 +25,6 @@ vite.config.ts.timestamp-*
# npm # npm
*.log *.log
test-results/ test-results/
# Snapshot-data wird vor dem build aus snapshot/output/ reinkopiert
static/snapshot-data/

View File

@ -55,7 +55,7 @@ export const entries: EntryGenerator = async () => {
return idx.posts.map((p) => ({ slug: p.slug })) return idx.posts.map((p) => ({ slug: p.slug }))
} }
export const load: PageLoad = async ({ url }) => { export const load: PageLoad = async ({ url, fetch }) => {
const pathname = url.pathname const pathname = url.pathname
const legacyDtag = parseLegacyUrl(pathname) const legacyDtag = parseLegacyUrl(pathname)
@ -75,5 +75,18 @@ export const load: PageLoad = async ({ url }) => {
return { dtag, snapshot } return { dtag, snapshot }
} }
// Im Browser: snapshot per fetch von /snapshot-data/posts/<slug>.json laden.
// Beim Hard-Reload einer prerenderten URL nutzt SvelteKit das ins HTML
// serialisierte page-data; bei clientseitiger Navigation kommt der
// request hier durch und holt das fehlende JSON. 404 vom server (slug
// nicht im snapshot) → kein post.
try {
const resp = await fetch(`/snapshot-data/posts/${dtag}.json`)
if (!resp.ok) throw error(404, 'Post nicht gefunden')
const snapshot = (await resp.json()) as PostJson
return { dtag, snapshot }
} catch (err) {
if (err && typeof err === 'object' && 'status' in err) throw err
throw error(404, 'Post nicht gefunden') throw error(404, 'Post nicht gefunden')
} }
}

View File

@ -89,6 +89,15 @@ if [ ! -f "$SNAPSHOT_DIR/index.json" ]; then
exit 1 exit 1
fi fi
# Snapshot-JSONs ins SvelteKit-static-verzeichnis kopieren, damit sie
# im build-output unter /snapshot-data/posts/<slug>.json gehostet sind.
# Browser-load() macht dann einen fetch auf diesen pfad bei
# clientseitiger navigation zwischen detail-seiten.
echo "Kopiere snapshot/output/posts/ → app/static/snapshot-data/posts/ …"
rm -rf "$ROOT/app/static/snapshot-data"
mkdir -p "$ROOT/app/static/snapshot-data"
cp -r "$SNAPSHOT_DIR/posts" "$ROOT/app/static/snapshot-data/posts"
echo "Baue SvelteKit …" echo "Baue SvelteKit …"
(cd "$ROOT/app" && npm run build >/dev/null 2>&1) || { (cd "$ROOT/app" && npm run build >/dev/null 2>&1) || {
echo "FEHLER: Build fehlgeschlagen. 'cd app && npm run build' manuell ausführen zum Debuggen." >&2 echo "FEHLER: Build fehlgeschlagen. 'cd app && npm run build' manuell ausführen zum Debuggen." >&2