fix(auth+pwa): resolve stuck-loading race condition and PWA build error

Auth: `getSession()` in a SvelteKit load function races with Supabase's async
localStorage init and can return null for a valid session, triggering a spurious
login() redirect. Moved auth redirect to (app)/+layout.svelte via $effect, which
correctly waits for onAuthStateChange to fire. Also fixed collective data never
loading on page refresh (INITIAL_SESSION event, not SIGNED_IN).

PWA: SvelteKit blocks Workbox imports in src/service-worker.ts, preventing
@vite-pwa/sveltekit from finding the compiled SW output. Switched to generateSW
strategy (plugin auto-generates the SW). Custom SW deferred to Fase 2b using
src/sw.ts (a filename SvelteKit does not intercept).

Docs: added gotchas 6–8 to CLAUDE.md covering both root causes so they are not
reintroduced. Updated plan/fase-2b with the sw.ts workaround note.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-12 21:44:14 +02:00
parent 5ffd9ca28a
commit b297d253d9
16 changed files with 2125 additions and 279 deletions

View File

@@ -2,6 +2,7 @@
import type { Snippet } from 'svelte';
import { page } from '$app/stores';
import { isAuthenticated, authLoading, displayName } from '$lib/stores/auth';
import { login } from '$lib/auth';
let { children }: { children: Snippet } = $props();
import { currentCollective, userCollectives } from '$lib/stores/collective';
@@ -9,6 +10,15 @@
import Avatar from '$lib/components/Avatar.svelte';
import * as m from '$lib/paraglide/messages';
// When auth resolves as unauthenticated, redirect to Keycloak.
// This runs after onAuthStateChange fires (authLoading = false), so there
// is no race with Supabase's async localStorage initialisation.
$effect(() => {
if (!$authLoading && !$isAuthenticated) {
login();
}
});
const navItems = [
{ href: '/lists', label: () => m.nav_lists(), icon: '🛒' },
{ href: '/tasks', label: () => m.nav_tasks(), icon: '✓' },

View File

@@ -1,21 +1,9 @@
import { browser } from '$app/environment';
import { getSupabase } from '$lib/supabase';
import { login } from '$lib/auth';
// No SSR — this is a client-rendered PWA
// No SSR — this is a client-rendered PWA.
// Auth redirect is handled in +layout.svelte via the onAuthStateChange store,
// NOT here. Using getSession() in a load function races with Supabase's async
// localStorage initialisation and can return null for a valid session.
export const ssr = false;
export async function load() {
if (!browser) return {};
const {
data: { session }
} = await getSupabase().auth.getSession();
if (!session) {
// Not authenticated — redirect to Keycloak
await login();
}
export function load() {
return {};
}