managarten/packages/shared-auth-ui/src/stores/createAuthStore.svelte.ts
Till JS ab24db36dd fix(packages): cross-package broken imports + missing exports
Five unrelated packages each had a few imports pointing at the wrong
file or missing from their public surface. Grouped because none of
the individual fixes warrants its own commit and they all unblock
the same downstream consumer (apps/mana/apps/web type-check).

packages/help
  - HelpPage.svelte: `'../types.js'` and `'./content'` for
    HelpPageProps/HelpSection/SearchResult — neither path exists.
    Real homes are `../ui-types` (props) and `../search-types`
    (search shapes). Fix the imports.
  - HelpSearch.svelte: same `'../content'` typo for SearchResult →
    `'../search-types'`.
  - translations.ts: `'./types.js'` for HelpPageTranslations →
    `'./ui-types'`.
  - ui-types.ts: was importing SearchResult from `'./content'` but
    that module only exports content shapes. Split into two imports
    so HelpContent stays from content.ts and SearchResult comes from
    search-types.ts.

packages/feedback
  - FeedbackPage.svelte: imported `Feedback` and `CreateFeedbackInput`
    from `'./createFeedbackService'` but the service module only
    exports the service factory. Real homes are `'./feedback'`
    (Feedback) and `'./api'` (CreateFeedbackInput).
  - FeedbackForm.svelte: same `'./feedback'` typo for
    CreateFeedbackInput → `'./api'`.

packages/subscriptions
  - UsageCard / CostCard / pages/SubscriptionPage: all imported
    UsageData / CostItem from `'./plans'` but those types live in
    `'./usage'`. SubscriptionPage additionally had a relative-path
    bug — it's at `src/pages/`, not `src/`, so `./plans` resolved
    to `pages/plans` (nonexistent). Now imports `'../plans'` for
    plan types and `'../usage'` for usage/cost types.

packages/shared-ui
  - index.ts: re-exports the QuickInputItem family from
    `./quick-input` but had forgotten `HighlightPattern`. Added.
    Apps that build their own InputBar pattern config (e.g.
    mana/web/src/lib/quick-input/types.ts) need it as a public type.
  - PillNavigation.svelte: imported `SpotlightAction` and
    `ContentSearcher` from `./GlobalSpotlight.svelte` (a Svelte
    component file), which only re-exports the default. Both types
    live in `./types`. Move them to the existing types-import
    block; the GlobalSpotlight import becomes a plain default.

packages/shared-auth-ui
  - stores/createAuthStore.svelte.ts: imported AuthServiceAdapter /
    AuthResult / BaseUser from `'./types'` (nonexistent — the file
    is `'./store-types'`).

Net: -23 type errors. Zero behavior change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 20:23:34 +02:00

186 lines
4.3 KiB
TypeScript

/**
* 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 '@mana/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 './store-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;
},
};
}