feat: add Tier 3 shared auth store patterns

- Create @manacore/shared-auth-stores package with Svelte 5 factories:
  - createAuthStore<T>(): Generic factory with custom auth service adapter
  - createSupabaseAuthStore(): Direct Supabase integration for simpler setups
- Add standardized auth error types to @manacore/shared-types:
  - AuthErrorCode enum for consistent error handling
  - mapSupabaseErrorToCode() helper function
  - createAuthError() factory function

This enables apps to share auth state management while supporting
both middleware-based auth (ManaCore/Manadeck) and direct Supabase
auth (ManaCore-web simple mode).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Till-JS 2025-11-24 23:54:27 +01:00
parent 10325026f9
commit 9449fff6f7
8 changed files with 717 additions and 0 deletions

View file

@ -0,0 +1,186 @@
/**
* Svelte 5 Auth Store Factory
*
* Creates a reactive auth store using Svelte 5 runes.
* Generic over user type to support app-specific user models.
*
* @example
* ```ts
* // In your app's auth store file
* import { createAuthStore } from '@manacore/shared-auth-stores';
* import { authService } from '$lib/auth';
* import type { AppUser } from '$lib/types';
*
* export const authStore = createAuthStore<AppUser>(authService);
* ```
*/
import type { AuthServiceAdapter, AuthResult, BaseUser } from './types';
/**
* Create a Svelte 5 runes-based auth store
*
* @param authService - Auth service adapter implementing the AuthServiceAdapter interface
* @returns Reactive auth store with state and actions
*/
export function createAuthStore<TUser extends BaseUser>(authService: AuthServiceAdapter<TUser>) {
// Reactive state using Svelte 5 runes
let user = $state<TUser | null>(null);
let loading = $state(true);
let error = $state<string | null>(null);
return {
// Reactive getters
get user() {
return user;
},
get loading() {
return loading;
},
get error() {
return error;
},
get isAuthenticated() {
return !!user;
},
/**
* Initialize auth state from stored tokens/session
*/
async initialize() {
loading = true;
error = null;
try {
const isAuth = await authService.isAuthenticated();
if (isAuth) {
user = await authService.getUserFromToken();
} else {
user = null;
}
} catch (e) {
console.error('Failed to initialize auth:', e);
error = e instanceof Error ? e.message : 'Failed to initialize authentication';
user = null;
} finally {
loading = false;
}
},
/**
* Set user manually (useful for SSR hydration)
*/
setUser(newUser: TUser | null) {
user = newUser;
error = null;
},
/**
* Sign in with email and password
*/
async signIn(email: string, password: string): Promise<AuthResult<TUser>> {
loading = true;
error = null;
try {
const result = await authService.signIn(email, password);
if (result.success) {
user = await authService.getUserFromToken();
} else {
error = result.error || 'Sign in failed';
}
return result;
} catch (e) {
const errorMessage = e instanceof Error ? e.message : 'Sign in failed';
error = errorMessage;
return { success: false, error: errorMessage };
} finally {
loading = false;
}
},
/**
* Sign up with email and password
*/
async signUp(email: string, password: string): Promise<AuthResult<TUser>> {
loading = true;
error = null;
try {
const result = await authService.signUp(email, password);
if (result.success && !result.needsVerification) {
user = await authService.getUserFromToken();
} else if (!result.success) {
error = result.error || 'Sign up failed';
}
return result;
} catch (e) {
const errorMessage = e instanceof Error ? e.message : 'Sign up failed';
error = errorMessage;
return { success: false, error: errorMessage };
} finally {
loading = false;
}
},
/**
* Send password reset email
*/
async forgotPassword(email: string): Promise<{ success: boolean; error?: string }> {
loading = true;
error = null;
try {
const result = await authService.forgotPassword(email);
if (!result.success) {
error = result.error || 'Password reset failed';
}
return result;
} catch (e) {
const errorMessage = e instanceof Error ? e.message : 'Password reset failed';
error = errorMessage;
return { success: false, error: errorMessage };
} finally {
loading = false;
}
},
/**
* Sign out user
*/
async signOut() {
loading = true;
error = null;
try {
await authService.signOut();
user = null;
} catch (e) {
console.error('Sign out failed:', e);
error = e instanceof Error ? e.message : 'Sign out failed';
} finally {
loading = false;
}
},
/**
* Check authentication status
*/
async checkAuth(): Promise<boolean> {
try {
const isAuth = await authService.isAuthenticated();
if (!isAuth) {
user = null;
return false;
}
return true;
} catch (e) {
console.error('Auth check failed:', e);
user = null;
return false;
}
},
/**
* Clear error state
*/
clearError() {
error = null;
}
};
}