feat: upgrade to SvelteKit 3 and adapter-bun
Some checks failed
Test and deploy staging / staging (push) Failing after 43s

Run the sveltekit-3 migration and finish its manual tasks:

- Move kit config into the sveltekit() Vite plugin; drop svelte.config.js
- Replace $lib with #lib subpath imports
- Declare env vars in src/env.ts and import them from $app/env/{private,public}
- Consolidate param matchers into src/params.ts
- Use error(status, message), Response.json, refreshAll and goto reset/shallow
- Fall back to a full page load when goto rejects a login return path

Switch from adapter-node to adapter-bun: the build script runs Vite under
Bun (the adapter requires it) and the image starts with `bun ./build`.
Runtime env names (PORT, BODY_SIZE_LIMIT, ADDRESS_HEADER, ...) are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-18 22:17:30 +00:00
parent b52e9188f4
commit 54924b39f0
240 changed files with 894 additions and 944 deletions

View File

@@ -1,14 +1,14 @@
<script lang="ts">
import { pokemonImage } from '$lib/game-tracker/game-art';
import { pokemonImage } from '#lib/game-tracker/game-art.js';
import { enhance } from '$app/forms';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { Textarea } from '$lib/components/ui/textarea/index.js';
import { NativeSelect } from '$lib/components/ui/native-select/index.js';
import { Checkbox } from '$lib/components/ui/checkbox/index.js';
import * as Dialog from '#lib/components/ui/dialog/index.js';
import { Button } from '#lib/components/ui/button/index.js';
import { Input } from '#lib/components/ui/input/index.js';
import { Textarea } from '#lib/components/ui/textarea/index.js';
import { NativeSelect } from '#lib/components/ui/native-select/index.js';
import { Checkbox } from '#lib/components/ui/checkbox/index.js';
import { stages, obtainedMethods, type Stage } from './journal';
import type { PokemonCatch } from '$lib/server/db/schema/tracker-journal';
import type { PokemonCatch } from '#lib/server/db/schema/tracker-journal.js';
let {
open = $bindable(false),
species,

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { cn } from '$lib/utils.js';
import { base } from '$app/paths';
import { cn } from '#lib/utils.js';
import { resolve } from '$app/paths';
let {
slug,
name,
@@ -16,7 +16,7 @@
)}
>
<img
src={`${base}/game-logos/${slug}.webp`}
src={resolve(`game-logos/${slug}.webp`)}
alt={`${name} artwork`}
class={`max-h-full max-w-full object-contain ${slug === 'osu' ? 'rounded-full bg-[#ff66aa]' : ''}`}
loading="lazy"

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { base } from '$app/paths';
import { resolve } from '$app/paths';
import ChevronsUp from 'lucide-svelte/icons/chevrons-up';
import Award from 'lucide-svelte/icons/award';
let {
@@ -34,7 +34,9 @@
<span class="stat">
{#if game === 'black-ops-2' && prestige > 0 && prestige <= 10}
<img
src={`${base}/game-art/call-of-duty/ranks/black-ops-2-prestige-${master ? 11 : prestige}.webp`}
src={resolve(
`game-art/call-of-duty/ranks/black-ops-2-prestige-${master ? 11 : prestige}.webp`
)}
alt={master ? 'Prestige Master emblem' : `Prestige ${prestige} emblem`}
width="48"
height="48"

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { pokemonImage } from '$lib/game-tracker/game-art';
import { pokemonImage } from '#lib/game-tracker/game-art.js';
import { onMount } from 'svelte';
let hydrated = $state(false);
onMount(() => {
@@ -7,22 +7,22 @@
});
import { untrack } from 'svelte';
import { deserialize, enhance } from '$app/forms';
import { invalidateAll } from '$app/navigation';
import { refreshAll } from '$app/navigation';
import { SvelteSet } from 'svelte/reactivity';
import * as Tabs from '$lib/components/ui/tabs/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import * as AlertDialog from '$lib/components/ui/alert-dialog/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { NativeSelect } from '$lib/components/ui/native-select/index.js';
import { Checkbox } from '$lib/components/ui/checkbox/index.js';
import * as Tabs from '#lib/components/ui/tabs/index.js';
import * as Dialog from '#lib/components/ui/dialog/index.js';
import * as AlertDialog from '#lib/components/ui/alert-dialog/index.js';
import { Button } from '#lib/components/ui/button/index.js';
import { Input } from '#lib/components/ui/input/index.js';
import { NativeSelect } from '#lib/components/ui/native-select/index.js';
import { Checkbox } from '#lib/components/ui/checkbox/index.js';
import MapIcon from 'lucide-svelte/icons/map';
import BookOpen from 'lucide-svelte/icons/book-open';
import Archive from 'lucide-svelte/icons/archive';
import Trophy from 'lucide-svelte/icons/trophy';
import ProgressMeter from './ProgressMeter.svelte';
import PokemonProgress from './PokemonProgress.svelte';
import PokemonDetails from '$lib/catch-em-all/PokemonDetails.svelte';
import PokemonDetails from '#lib/catch-em-all/PokemonDetails.svelte';
import PokemonMap from './PokemonMap.svelte';
import CatchEditor from './CatchEditor.svelte';
import { stages, firstCatches, type Stage } from './journal';
@@ -31,9 +31,9 @@
availabilityLabels,
availabilityValues,
obtainableAvailability
} from '$lib/availability';
} from '#lib/availability.js';
import type { PageData } from '../../routes/projects/game-tracker/pokemon/$types';
import type { PokemonCatch } from '$lib/server/db/schema/tracker-journal';
import type { PokemonCatch } from '#lib/server/db/schema/tracker-journal.js';
let { data }: { data: PageData } = $props();
const game = $derived(data.activeGame!);
const gameProgress = $derived(data.progress.games.find((g) => g.id === game.id));
@@ -65,7 +65,7 @@
if (seen) seenIds.add(species.id);
else seenIds.delete(species.id);
pendingSeen.delete(number);
void invalidateAll().catch(() => {
void refreshAll().catch(() => {
message = 'Sighting saved. Reload to refresh the progress totals.';
});
} catch (error) {
@@ -200,7 +200,7 @@
throw new Error(
r.type === 'failure' ? String(r.data?.message ?? 'Could not save.') : 'Could not save.'
);
await invalidateAll();
await refreshAll();
} catch (e) {
message = e instanceof Error ? e.message : 'Could not save.';
} finally {
@@ -217,7 +217,7 @@
await (await fetch('?/deleteCatch', { method: 'POST', body })).text()
);
if (result.type !== 'success') throw new Error('Could not remove catch.');
await invalidateAll();
await refreshAll();
catchOpen = false;
deleteOpen = false;
} catch (e) {
@@ -660,7 +660,7 @@
number={detailsNumber}
gameId={game.id}
isAdmin={data.isAdmin}
onsaved={() => invalidateAll()}
onsaved={() => refreshAll()}
/>
{/if}
</Dialog.Content></Dialog.Root

View File

@@ -1,14 +1,14 @@
<script lang="ts">
import { pokemonImage } from '$lib/game-tracker/game-art';
import { pokemonImage } from '#lib/game-tracker/game-art.js';
import { untrack } from 'svelte';
import { base } from '$app/paths';
import { Slider } from '$lib/components/ui/slider/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import { Input } from '$lib/components/ui/input/index.js';
import { NativeSelect } from '$lib/components/ui/native-select/index.js';
import { Checkbox } from '$lib/components/ui/checkbox/index.js';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import * as Tabs from '$lib/components/ui/tabs/index.js';
import { resolve } from '$app/paths';
import { Slider } from '#lib/components/ui/slider/index.js';
import { Button } from '#lib/components/ui/button/index.js';
import { Input } from '#lib/components/ui/input/index.js';
import { NativeSelect } from '#lib/components/ui/native-select/index.js';
import { Checkbox } from '#lib/components/ui/checkbox/index.js';
import * as Dialog from '#lib/components/ui/dialog/index.js';
import * as Tabs from '#lib/components/ui/tabs/index.js';
import EncounterGrid from './EncounterGrid.svelte';
import ProgressMeter from './ProgressMeter.svelte';
import { fitViewport } from './fit-viewport';
@@ -146,7 +146,7 @@
loading = !!id;
if (!id) return;
const controller = new AbortController();
fetch(`${base}/projects/game-tracker/pokemon/api/encounters?game=${game}&location=${id}`, {
fetch(resolve(`projects/game-tracker/pokemon/api/encounters?game=${game}&location=${id}`), {
signal: controller.signal
})
.then((r) => {
@@ -266,7 +266,7 @@
style:height={`${(mapWidth * map.height) / map.width}px`}
>
<img
src={`${base}${map.image}`}
src={resolve(`${map.image}`.slice(1))}
alt={`${map.name} region map from the Pokémon games`}
width={map.width}
height={map.height}

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { Button } from '$lib/components/ui/button/index.js';
import * as Dialog from '#lib/components/ui/dialog/index.js';
import { Button } from '#lib/components/ui/button/index.js';
import ChartNoAxesCombined from 'lucide-svelte/icons/chart-no-axes-combined';
import ProgressMeter from './ProgressMeter.svelte';
import type { pokemonProgress } from './progress';

View File

@@ -1,5 +1,5 @@
<script lang="ts">
import { Progress } from '$lib/components/ui/progress/index.js';
import { Progress } from '#lib/components/ui/progress/index.js';
import { fraction, type ProgressCount } from './progress';
let {
label,

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { base } from '$app/paths';
import * as Dialog from '$lib/components/ui/dialog/index.js';
import { resolve } from '$app/paths';
import * as Dialog from '#lib/components/ui/dialog/index.js';
import artwork from '../../../data/cod-art.json';
let {
game,
@@ -17,7 +17,7 @@
const source = $derived(
artwork.find((a) => a.game === game && a.name.toLowerCase() === name.toLowerCase())
);
const src = $derived(source ? `${base}${source.path}` : icon);
const src = $derived(source ? resolve(`${source.path}`.slice(1)) : icon);
</script>
{#if src}

View File

@@ -1,5 +1,6 @@
import releases from '../../../data/pokemon-releases.json';
import { base } from '$app/paths';
import { asset } from '$app/paths';
import type { AssetPath } from '$app/types';
const dates: Record<string, string> = releases.dates;
export function pokemonRelease(slug: string | null) {
@@ -11,5 +12,5 @@ export function chronologicalPokemon<T extends { slug: string | null; id: number
);
}
export function pokemonImage(number: number, kind: 'sprite' | 'shiny' | 'artwork' = 'sprite') {
return `${base}/game-art/pokemon/${kind}/${number}.webp`;
return asset(`game-art/pokemon/${kind}/${number}.webp` as AssetPath);
}

View File

@@ -1,4 +1,4 @@
import { obtainableAvailability, type Availability } from '$lib/availability';
import { obtainableAvailability, type Availability } from '#lib/availability.js';
import { gameRegions, milestonesFor } from './pokemon-world';
export type ProgressCount = { value: number; total: number };