better-auth-ui-svelte 0.1.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +47 -7
  2. package/dist/components/account/account-view.svelte +2 -7
  3. package/dist/components/auth/auth-view.svelte +2 -2
  4. package/dist/components/form-error.svelte +55 -0
  5. package/dist/components/form-error.svelte.d.ts +29 -0
  6. package/dist/components/redirect-to-sign-in.svelte +12 -0
  7. package/dist/components/redirect-to-sign-in.svelte.d.ts +18 -0
  8. package/dist/components/redirect-to-sign-up.svelte +12 -0
  9. package/dist/components/redirect-to-sign-up.svelte.d.ts +18 -0
  10. package/dist/components/settings/api-key/api-key-cell.svelte +90 -0
  11. package/dist/components/settings/api-key/api-key-cell.svelte.d.ts +14 -0
  12. package/dist/components/settings/api-key/api-key-delete-dialog.svelte +133 -0
  13. package/dist/components/settings/api-key/api-key-delete-dialog.svelte.d.ts +15 -0
  14. package/dist/components/settings/api-key/api-key-display-dialog.svelte +89 -0
  15. package/dist/components/settings/api-key/api-key-display-dialog.svelte.d.ts +12 -0
  16. package/dist/components/settings/api-key/api-keys-card.svelte +98 -0
  17. package/dist/components/settings/api-key/api-keys-card.svelte.d.ts +13 -0
  18. package/dist/components/settings/api-key/create-api-key-dialog.svelte +337 -0
  19. package/dist/components/settings/api-key/create-api-key-dialog.svelte.d.ts +15 -0
  20. package/dist/components/settings/api-key/index.d.ts +7 -0
  21. package/dist/components/settings/api-key/index.js +5 -0
  22. package/dist/components/settings/index.d.ts +1 -0
  23. package/dist/components/settings/index.js +1 -0
  24. package/dist/index.d.ts +14 -0
  25. package/dist/index.js +13 -11
  26. package/package.json +2 -1
package/README.md CHANGED
@@ -8,11 +8,11 @@
8
8
 
9
9
  Pre-built, customizable authentication UI components for [Better Auth](https://www.better-auth.com) in Svelte 5.
10
10
 
11
- This is a Svelte 5 port of the [Better Auth UI React library](https://github.com/daveyplate/better-auth-ui). The API is nearly identical to the original React library, making it easy to follow the [official documentation](https://better-auth-ui.com). All credits for the original design and architecture go to [daveycodez](https://github.com/daveycodez).
11
+ This is a **complete Svelte 5 port** of the [Better Auth UI React library](https://github.com/daveyplate/better-auth-ui) with **full feature parity**. The API is nearly identical to the original React library, making it easy to follow the [official documentation](https://better-auth-ui.com). All credits for the original design and architecture go to [daveycodez](https://github.com/daveycodez).
12
12
 
13
- ## Warning
13
+ ## ⚠️ Early Stage Warning
14
14
 
15
- This library is currently in **active development** and not yet on NPM. Breaking changes may occur.
15
+ > **Important:** This library is in early stage development. While we have achieved full feature parity with the React version and all components have been ported, the library has not been battle-tested in production environments yet. Issues may arise. **Use at your own risk until we reach v1.0 stable.**
16
16
 
17
17
  ## Why Choose Better Auth UI?
18
18
 
@@ -22,6 +22,7 @@ This library is currently in **active development** and not yet on NPM. Breaking
22
22
 
23
23
  ## Key Features
24
24
 
25
+ - ✨ **Full Feature Parity** - Complete port of all React components and functionality
25
26
  - 🚀 **Svelte 5 Runes** - Built with the latest Svelte 5 reactive primitives
26
27
  - 🔐 **Better Auth Integration** - Native integration with Better Auth's Svelte client
27
28
  - 🛣️ **Path Helpers** - Server & client-side path utilities (unique to Svelte port)
@@ -33,7 +34,9 @@ This library is currently in **active development** and not yet on NPM. Breaking
33
34
 
34
35
  ## Installation
35
36
 
36
- Install the package using your preferred package manager:
37
+ > **Note:** The package is not yet published to NPM. For now, you can clone the repository and link it locally.
38
+
39
+ Once published, install the package using your preferred package manager:
37
40
 
38
41
  ```bash
39
42
  pnpm install better-auth-ui-svelte better-auth zod svelte-sonner
@@ -233,10 +236,46 @@ Display a user button with avatar and dropdown menu:
233
236
  - **`<SignedIn />`** - Renders children only when authenticated
234
237
  - **`<SignedOut />`** - Renders children only when not authenticated
235
238
 
236
- ### Other Components
239
+ ### Settings Components
240
+
241
+ #### Account Settings
242
+
243
+ - **`<AccountSettingsCards />`** - Complete account settings UI
244
+ - **`<AccountsCard />`** - Manage connected accounts
245
+ - **`<UpdateAvatarCard />`** - Avatar upload and management
246
+ - **`<UpdateNameCard />`** - Update user name
247
+ - **`<UpdateUsernameCard />`** - Update username
248
+ - **`<UpdateFieldCard />`** - Generic field update card
249
+ - **`<DeleteAccountCard />`** - Account deletion with confirmation
250
+
251
+ #### Security Settings
252
+
253
+ - **`<SecuritySettingsCards />`** - Complete security settings UI
254
+ - **`<ChangeEmailCard />`** - Email change with verification
255
+ - **`<ChangePasswordCard />`** - Password change with validation
256
+ - **`<SessionsCard />`** - Active sessions management
257
+ - **`<PasskeysCard />`** - Passkey authentication management
258
+ - **`<TwoFactorCard />`** - Two-factor authentication setup
259
+ - **`<ApiKeysCard />`** - API key management
260
+
261
+ ### Organization Components
262
+
263
+ - **`<OrganizationSwitcher />`** - Switch between organizations
264
+ - **`<CreateOrganizationDialog />`** - Create new organization
265
+ - **`<OrganizationView />`** - Organization details view
266
+ - **`<OrganizationSettingsCards />`** - Organization settings
267
+ - **`<OrganizationMembersCard />`** - Member management
268
+ - **`<OrganizationInvitationsCard />`** - Invitation management
269
+ - **`<UserInvitationsCard />`** - User's received invitations
270
+
271
+ ### Utility Components
237
272
 
238
273
  - **`<AuthCallback />`** - OAuth callback handler
239
274
  - **`<SignOut />`** - Sign out component
275
+ - **`<RedirectToSignIn />`** - Redirect unauthenticated users to sign in
276
+ - **`<RedirectToSignUp />`** - Redirect users to sign up
277
+ - **`<PasswordInput />`** - Password input with visibility toggle
278
+ - **`<FormError />`** - Form error display component
240
279
 
241
280
  ## Customization
242
281
 
@@ -579,7 +618,7 @@ Better Auth UI for Svelte is built with:
579
618
 
580
619
  ## Differences from React Version
581
620
 
582
- While the API is nearly identical, there are some Svelte-specific differences:
621
+ This Svelte port maintains **full feature parity** with the React version. The API is nearly identical, with these framework-specific differences:
583
622
 
584
623
  1. **Path Helpers**: Unique to this Svelte port - utility functions for generating auth paths that work client and server-side (see [Path Helpers](#path-helpers-svelte-specific-feature))
585
624
  2. **Navigation**: Instead of Next.js router, use SvelteKit's `goto` function
@@ -587,6 +626,7 @@ While the API is nearly identical, there are some Svelte-specific differences:
587
626
  4. **Dynamic Routes**: Use SvelteKit's `[path]` syntax instead of Next.js `[path]`
588
627
  5. **Reactivity**: Built with Svelte 5 runes instead of React hooks
589
628
  6. **Link Component**: SvelteKit doesn't need a custom Link component
629
+ 7. **Form Handling**: Uses TanStack Svelte Form instead of React Hook Form (same API)
590
630
 
591
631
  ## Contributing
592
632
 
@@ -599,7 +639,7 @@ MIT
599
639
  ## Credits
600
640
 
601
641
  - Original React version by [daveycodez](https://github.com/daveyplate/better-auth-ui)
602
- - Svelte 5 port by Chris Jayden [multiplehats](https://github.com/multiplehats)
642
+ - Complete Svelte 5 port with full feature parity by Chris Jayden [multiplehats](https://github.com/multiplehats)
603
643
  - Built with [Better Auth](https://www.better-auth.com)
604
644
  - UI components from [shadcn-svelte](https://www.shadcn-svelte.com)
605
645
 
@@ -1,5 +1,4 @@
1
1
  <script lang="ts" module>
2
- import type { Snippet } from 'svelte';
3
2
  import type { AuthLocalization } from '../../types/index.js';
4
3
  import type { AccountViewPath } from '../../utils/view-paths.js';
5
4
  import type { SettingsCardClassNames } from '../settings/shared/settings-card.svelte';
@@ -26,10 +25,8 @@
26
25
  import { cn, getViewByPath } from '../../utils/utils.js';
27
26
  import { useAuthenticate } from '../../hooks/use-authenticate.svelte.js';
28
27
  import { AccountSettingsCards } from '../settings/account/index.js';
29
- // TODO: Import SecuritySettingsCards when security directory is ported
30
28
  import SecuritySettingsCards from '../settings/security-settings-cards.svelte';
31
- // TODO: Import ApiKeysCard when api-key directory is ported
32
- // import ApiKeysCard from '../settings/api-key/api-keys-card.svelte';
29
+ import ApiKeysCard from '../settings/api-key/api-keys-card.svelte';
33
30
  import OrganizationsCard from '../organization/organizations-card.svelte';
34
31
  import UserInvitationsCard from '../organization/user-invitations-card.svelte';
35
32
  import { Button } from '../ui/button/index.js';
@@ -183,9 +180,7 @@
183
180
  {:else if view === 'SECURITY'}
184
181
  <SecuritySettingsCards {classNames} {localization} />
185
182
  {:else if view === 'API_KEYS'}
186
- <!-- TODO: Uncomment when ApiKeysCard is ported -->
187
- <div class="text-muted-foreground">API Keys coming soon (ApiKeysCard not yet ported)</div>
188
- <!-- <ApiKeysCard classNames={classNames?.card} localization={localization} /> -->
183
+ <ApiKeysCard classNames={classNames?.card} {localization} /> -->
189
184
  {:else if view === 'ORGANIZATIONS' && organization}
190
185
  <div class="grid w-full gap-4 md:gap-6">
191
186
  <OrganizationsCard classNames={classNames?.card} {localization} />
@@ -19,6 +19,7 @@
19
19
  import SignOut from './sign-out.svelte';
20
20
  import type { AuthViewPath } from '../../utils/view-paths.js';
21
21
  import type { AuthLocalization } from '../../localization/auth-localization.js';
22
+ import AcceptInvitationCard from '../organization/accept-invitation-card.svelte';
22
23
 
23
24
  export interface AuthViewClassNames {
24
25
  base?: string;
@@ -128,8 +129,7 @@
128
129
  {:else if view === 'SIGN_OUT'}
129
130
  <SignOut />
130
131
  {:else if view === 'ACCEPT_INVITATION'}
131
- <!-- TODO: Import and render AcceptInvitationCard when it's created -->
132
- <div>Accept Invitation (not yet implemented)</div>
132
+ <AcceptInvitationCard {localization} />
133
133
  {:else}
134
134
  <!-- Main card layout for all other views -->
135
135
  <Card.Root class={cn('w-full max-w-sm', className, classNames?.base)}>
@@ -0,0 +1,55 @@
1
+ <script lang="ts">
2
+ import AlertCircle from '@lucide/svelte/icons/alert-circle';
3
+ import { cn } from '../utils/utils.js';
4
+ import type { AuthFormClassNames } from './auth/auth-form.svelte';
5
+ import Alert from './ui/alert/alert.svelte';
6
+ import AlertTitle from './ui/alert/alert-title.svelte';
7
+ import AlertDescription from './ui/alert/alert-description.svelte';
8
+
9
+ export interface FormErrorProps {
10
+ /**
11
+ * Optional form instance from @tanstack/svelte-form
12
+ * Used to access form-level errors from form state
13
+ */
14
+ form?: {
15
+ state: {
16
+ errorMap?: Record<string, unknown>;
17
+ };
18
+ };
19
+ /**
20
+ * Optional error message to display directly
21
+ * If provided, this takes precedence over form errors
22
+ */
23
+ message?: string | null;
24
+ /**
25
+ * Optional title for the error alert
26
+ * @default "Error"
27
+ */
28
+ title?: string;
29
+ /**
30
+ * Optional class names for styling
31
+ */
32
+ classNames?: AuthFormClassNames;
33
+ }
34
+
35
+ let { form, message, title, classNames }: FormErrorProps = $props();
36
+
37
+ // Determine error message from props or form state
38
+ // Priority: message prop > form.state.errorMap
39
+ const errorMessage = $derived(
40
+ message ||
41
+ (form?.state.errorMap?.onSubmit
42
+ ? String(form.state.errorMap.onSubmit)
43
+ : form?.state.errorMap?.root
44
+ ? String(form.state.errorMap.root)
45
+ : null)
46
+ );
47
+ </script>
48
+
49
+ {#if errorMessage}
50
+ <Alert variant="destructive" class={cn(classNames?.error)}>
51
+ <AlertCircle class="self-center" />
52
+ <AlertTitle>{title || 'Error'}</AlertTitle>
53
+ <AlertDescription>{errorMessage}</AlertDescription>
54
+ </Alert>
55
+ {/if}
@@ -0,0 +1,29 @@
1
+ import type { AuthFormClassNames } from './auth/auth-form.svelte';
2
+ export interface FormErrorProps {
3
+ /**
4
+ * Optional form instance from @tanstack/svelte-form
5
+ * Used to access form-level errors from form state
6
+ */
7
+ form?: {
8
+ state: {
9
+ errorMap?: Record<string, unknown>;
10
+ };
11
+ };
12
+ /**
13
+ * Optional error message to display directly
14
+ * If provided, this takes precedence over form errors
15
+ */
16
+ message?: string | null;
17
+ /**
18
+ * Optional title for the error alert
19
+ * @default "Error"
20
+ */
21
+ title?: string;
22
+ /**
23
+ * Optional class names for styling
24
+ */
25
+ classNames?: AuthFormClassNames;
26
+ }
27
+ declare const FormError: import("svelte").Component<FormErrorProps, {}, "">;
28
+ type FormError = ReturnType<typeof FormError>;
29
+ export default FormError;
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import { useAuthenticate } from '../hooks/use-authenticate.svelte.js';
3
+
4
+ /**
5
+ * Redirects the user to the sign-in page
6
+ *
7
+ * Renders nothing and automatically redirects the current user to the authentication
8
+ * sign-in page. Useful for protecting routes that require authentication or
9
+ * redirecting users to sign in from various parts of the application.
10
+ */
11
+ useAuthenticate({ authView: 'SIGN_IN' });
12
+ </script>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const RedirectToSignIn: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type RedirectToSignIn = InstanceType<typeof RedirectToSignIn>;
18
+ export default RedirectToSignIn;
@@ -0,0 +1,12 @@
1
+ <script lang="ts">
2
+ import { useAuthenticate } from '../hooks/use-authenticate.svelte.js';
3
+
4
+ /**
5
+ * Redirects the user to the sign-up page
6
+ *
7
+ * Renders nothing and automatically redirects the current user to the authentication
8
+ * sign-up page. Useful for directing new users to create an account or
9
+ * for redirecting from marketing pages to the registration flow.
10
+ */
11
+ useAuthenticate({ authView: 'SIGN_UP' });
12
+ </script>
@@ -0,0 +1,18 @@
1
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
2
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
3
+ $$bindings?: Bindings;
4
+ } & Exports;
5
+ (internal: unknown, props: {
6
+ $$events?: Events;
7
+ $$slots?: Slots;
8
+ }): Exports & {
9
+ $set?: any;
10
+ $on?: any;
11
+ };
12
+ z_$$bindings?: Bindings;
13
+ }
14
+ declare const RedirectToSignUp: $$__sveltets_2_IsomorphicComponent<Record<string, never>, {
15
+ [evt: string]: CustomEvent<any>;
16
+ }, {}, {}, string>;
17
+ type RedirectToSignUp = InstanceType<typeof RedirectToSignUp>;
18
+ export default RedirectToSignUp;
@@ -0,0 +1,90 @@
1
+ <script lang="ts">
2
+ import KeyRound from '@lucide/svelte/icons/key-round';
3
+ import { getAuthUIConfig } from '../../../context/auth-ui-config.svelte.js';
4
+ import { cn } from '../../../utils/utils.js';
5
+ import type { AuthLocalization, ApiKey, Refetch } from '../../../types/index.js';
6
+ import { Button } from '../../ui/button/index.js';
7
+ import { Card } from '../../ui/card/index.js';
8
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
9
+ import ApiKeyDeleteDialog from './api-key-delete-dialog.svelte';
10
+ import { useLang } from '../../../hooks/use-lang.svelte.js';
11
+
12
+ export interface ApiKeyCellProps {
13
+ className?: string;
14
+ classNames?: SettingsCardClassNames;
15
+ apiKey: ApiKey;
16
+ localization?: Partial<AuthLocalization>;
17
+ refetch?: Refetch;
18
+ }
19
+
20
+ interface Props extends ApiKeyCellProps {}
21
+
22
+ let {
23
+ className,
24
+ classNames,
25
+ apiKey,
26
+ localization: propLocalization,
27
+ refetch
28
+ }: Props = $props();
29
+
30
+ const { localization: contextLocalization } = getAuthUIConfig();
31
+
32
+ const mergedLocalization = $derived({ ...contextLocalization, ...propLocalization });
33
+
34
+ const { lang } = useLang();
35
+
36
+ let showDeleteDialog = $state(false);
37
+
38
+ // Format expiration date or show "Never expires"
39
+ const formatExpiration = () => {
40
+ if (!apiKey.expiresAt) return mergedLocalization.NEVER_EXPIRES;
41
+
42
+ const expiresDate = new Date(apiKey.expiresAt);
43
+ return `${mergedLocalization.EXPIRES} ${expiresDate.toLocaleDateString(
44
+ lang ?? 'en',
45
+ {
46
+ month: 'short',
47
+ day: 'numeric',
48
+ year: 'numeric'
49
+ }
50
+ )}`;
51
+ };
52
+ </script>
53
+
54
+ <Card class={cn('flex-row items-center gap-3 truncate px-4 py-3', className, classNames?.cell)}>
55
+ <KeyRound class={cn('size-4 flex-shrink-0', classNames?.icon)} />
56
+
57
+ <div class="flex flex-col truncate">
58
+ <div class="flex items-center gap-2">
59
+ <span class="truncate font-semibold text-sm">
60
+ {apiKey.name}
61
+ </span>
62
+
63
+ <span class="flex-1 truncate text-muted-foreground text-sm">
64
+ {apiKey.start}{'******'}
65
+ </span>
66
+ </div>
67
+
68
+ <div class="truncate text-muted-foreground text-xs">
69
+ {formatExpiration()}
70
+ </div>
71
+ </div>
72
+
73
+ <Button
74
+ class={cn('relative ms-auto', classNames?.button, classNames?.outlineButton)}
75
+ size="sm"
76
+ variant="outline"
77
+ onclick={() => (showDeleteDialog = true)}
78
+ >
79
+ {mergedLocalization.DELETE}
80
+ </Button>
81
+ </Card>
82
+
83
+ <ApiKeyDeleteDialog
84
+ {classNames}
85
+ {apiKey}
86
+ localization={mergedLocalization}
87
+ open={showDeleteDialog}
88
+ onOpenChange={(open) => (showDeleteDialog = open)}
89
+ {refetch}
90
+ />
@@ -0,0 +1,14 @@
1
+ import type { AuthLocalization, ApiKey, Refetch } from '../../../types/index.js';
2
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
3
+ export interface ApiKeyCellProps {
4
+ className?: string;
5
+ classNames?: SettingsCardClassNames;
6
+ apiKey: ApiKey;
7
+ localization?: Partial<AuthLocalization>;
8
+ refetch?: Refetch;
9
+ }
10
+ interface Props extends ApiKeyCellProps {
11
+ }
12
+ declare const ApiKeyCell: import("svelte").Component<Props, {}, "">;
13
+ type ApiKeyCell = ReturnType<typeof ApiKeyCell>;
14
+ export default ApiKeyCell;
@@ -0,0 +1,133 @@
1
+ <script lang="ts">
2
+ import KeyRound from '@lucide/svelte/icons/key-round';
3
+ import Loader2 from '@lucide/svelte/icons/loader-2';
4
+ import { getAuthUIConfig, getLocalization } from '../../../context/auth-ui-config.svelte.js';
5
+ import { useLang } from '../../../hooks/use-lang.svelte.js';
6
+ import { cn, getLocalizedError } from '../../../utils/utils.js';
7
+ import type { AuthLocalization } from '../../../types/index.js';
8
+ import type { ApiKey } from '../../../types/api-key.js';
9
+ import type { Refetch } from '../../../types/refetch.js';
10
+ import { Button } from '../../ui/button/index.js';
11
+ import { Card } from '../../ui/card/index.js';
12
+ import * as Dialog from '../../ui/dialog/index.js';
13
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
14
+
15
+ interface Props {
16
+ classNames?: SettingsCardClassNames;
17
+ apiKey: ApiKey;
18
+ localization?: Partial<AuthLocalization>;
19
+ refetch?: Refetch;
20
+ open?: boolean;
21
+ onOpenChange?: (open: boolean) => void;
22
+ }
23
+
24
+ let {
25
+ classNames,
26
+ apiKey,
27
+ localization: propLocalization,
28
+ refetch,
29
+ open = $bindable(false),
30
+ onOpenChange
31
+ }: Props = $props();
32
+
33
+ const config = getAuthUIConfig();
34
+ const contextLocalization = getLocalization();
35
+
36
+ const toast = config.toast;
37
+ const mutators = config.mutators;
38
+
39
+ const localization = $derived({ ...contextLocalization, ...propLocalization });
40
+
41
+ const { lang } = useLang();
42
+ let isLoading = $state(false);
43
+
44
+ async function handleDelete() {
45
+ isLoading = true;
46
+
47
+ try {
48
+ await mutators.deleteApiKey({ keyId: apiKey.id });
49
+ await refetch?.();
50
+ handleOpenChange(false);
51
+ } catch (error) {
52
+ toast.error(getLocalizedError({ error, localization }));
53
+ }
54
+
55
+ isLoading = false;
56
+ }
57
+
58
+ // Format expiration date or show "Never expires"
59
+ function formatExpiration() {
60
+ if (!apiKey.expiresAt) return localization.NEVER_EXPIRES;
61
+
62
+ const expiresDate = new Date(apiKey.expiresAt);
63
+ return `${localization.EXPIRES} ${expiresDate.toLocaleDateString(lang ?? 'en', {
64
+ month: 'short',
65
+ day: 'numeric',
66
+ year: 'numeric'
67
+ })}`;
68
+ }
69
+
70
+ function handleOpenChange(newOpen: boolean) {
71
+ open = newOpen;
72
+ onOpenChange?.(newOpen);
73
+ }
74
+ </script>
75
+
76
+ <Dialog.Root {open} onOpenChange={handleOpenChange}>
77
+ <Dialog.Content class={classNames?.dialog?.content} onOpenAutoFocus={(e) => e.preventDefault()}>
78
+ <Dialog.Header class={classNames?.dialog?.header}>
79
+ <Dialog.Title class={cn('text-lg md:text-xl', classNames?.title)}>
80
+ {localization.DELETE} {localization.API_KEY}
81
+ </Dialog.Title>
82
+
83
+ <Dialog.Description class={cn('text-xs md:text-sm', classNames?.description)}>
84
+ {localization.DELETE_API_KEY_CONFIRM}
85
+ </Dialog.Description>
86
+ </Dialog.Header>
87
+
88
+ <Card class={cn('my-2 flex-row items-center gap-3 px-4 py-3', classNames?.cell)}>
89
+ <KeyRound class={cn('size-4', classNames?.icon)} />
90
+
91
+ <div class="flex flex-col">
92
+ <div class="flex items-center gap-2">
93
+ <span class="text-sm font-semibold">
94
+ {apiKey.name}
95
+ </span>
96
+
97
+ <span class="text-sm text-muted-foreground">
98
+ {apiKey.start}{'******'}
99
+ </span>
100
+ </div>
101
+
102
+ <div class="text-xs text-muted-foreground">
103
+ {formatExpiration()}
104
+ </div>
105
+ </div>
106
+ </Card>
107
+
108
+ <Dialog.Footer class={classNames?.dialog?.footer}>
109
+ <Button
110
+ type="button"
111
+ variant="secondary"
112
+ onclick={() => handleOpenChange(false)}
113
+ disabled={isLoading}
114
+ class={cn(classNames?.button, classNames?.secondaryButton)}
115
+ >
116
+ {localization.CANCEL}
117
+ </Button>
118
+
119
+ <Button
120
+ type="button"
121
+ variant="destructive"
122
+ onclick={handleDelete}
123
+ disabled={isLoading}
124
+ class={cn(classNames?.button, classNames?.destructiveButton)}
125
+ >
126
+ {#if isLoading}
127
+ <Loader2 class="animate-spin" />
128
+ {/if}
129
+ {localization.DELETE}
130
+ </Button>
131
+ </Dialog.Footer>
132
+ </Dialog.Content>
133
+ </Dialog.Root>
@@ -0,0 +1,15 @@
1
+ import type { AuthLocalization } from '../../../types/index.js';
2
+ import type { ApiKey } from '../../../types/api-key.js';
3
+ import type { Refetch } from '../../../types/refetch.js';
4
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
5
+ interface Props {
6
+ classNames?: SettingsCardClassNames;
7
+ apiKey: ApiKey;
8
+ localization?: Partial<AuthLocalization>;
9
+ refetch?: Refetch;
10
+ open?: boolean;
11
+ onOpenChange?: (open: boolean) => void;
12
+ }
13
+ declare const ApiKeyDeleteDialog: import("svelte").Component<Props, {}, "open">;
14
+ type ApiKeyDeleteDialog = ReturnType<typeof ApiKeyDeleteDialog>;
15
+ export default ApiKeyDeleteDialog;
@@ -0,0 +1,89 @@
1
+ <script lang="ts">
2
+ import Check from '@lucide/svelte/icons/check';
3
+ import Copy from '@lucide/svelte/icons/copy';
4
+ import { getLocalization } from '../../../context/auth-ui-config.svelte';
5
+ import { cn } from '../../../utils/utils.js';
6
+ import type { AuthLocalization } from '../../../types/index.js';
7
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
8
+ import { Button } from '../../ui/button/index.js';
9
+ import * as Dialog from '../../ui/dialog/index.js';
10
+
11
+ interface Props {
12
+ classNames?: SettingsCardClassNames;
13
+ localization?: Partial<AuthLocalization>;
14
+ apiKey: string;
15
+ open?: boolean;
16
+ onOpenChange?: (open: boolean) => void;
17
+ }
18
+
19
+ let {
20
+ classNames,
21
+ apiKey,
22
+ localization: propLocalization,
23
+ open = $bindable(false),
24
+ onOpenChange
25
+ }: Props = $props();
26
+
27
+ const contextLocalization = getLocalization();
28
+ const localization = $derived({ ...contextLocalization, ...propLocalization });
29
+
30
+ let copied = $state(false);
31
+
32
+ function handleCopy() {
33
+ navigator.clipboard.writeText(apiKey);
34
+ copied = true;
35
+ setTimeout(() => {
36
+ copied = false;
37
+ }, 2000);
38
+ }
39
+
40
+ function handleOpenChange(newOpen: boolean) {
41
+ open = newOpen;
42
+ onOpenChange?.(newOpen);
43
+ }
44
+ </script>
45
+
46
+ <Dialog.Root {open} onOpenChange={handleOpenChange}>
47
+ <Dialog.Content onOpenAutoFocus={(e) => e.preventDefault()} class={classNames?.dialog?.content}>
48
+ <Dialog.Header class={classNames?.dialog?.header}>
49
+ <Dialog.Title class={cn('text-lg md:text-xl', classNames?.title)}>
50
+ {localization.API_KEY_CREATED}
51
+ </Dialog.Title>
52
+
53
+ <Dialog.Description class={cn('text-xs md:text-sm', classNames?.description)}>
54
+ {localization.CREATE_API_KEY_SUCCESS}
55
+ </Dialog.Description>
56
+ </Dialog.Header>
57
+
58
+ <div class="break-all rounded-md bg-muted p-4 font-mono text-sm">
59
+ {apiKey}
60
+ </div>
61
+
62
+ <Dialog.Footer class={classNames?.dialog?.footer}>
63
+ <Button
64
+ type="button"
65
+ variant="outline"
66
+ onclick={handleCopy}
67
+ disabled={copied}
68
+ class={cn(classNames?.button, classNames?.outlineButton)}
69
+ >
70
+ {#if copied}
71
+ <Check class={classNames?.icon} />
72
+ {localization.COPIED_TO_CLIPBOARD}
73
+ {:else}
74
+ <Copy class={classNames?.icon} />
75
+ {localization.COPY_TO_CLIPBOARD}
76
+ {/if}
77
+ </Button>
78
+
79
+ <Button
80
+ type="button"
81
+ variant="default"
82
+ onclick={() => handleOpenChange(false)}
83
+ class={cn(classNames?.button, classNames?.primaryButton)}
84
+ >
85
+ {localization.DONE}
86
+ </Button>
87
+ </Dialog.Footer>
88
+ </Dialog.Content>
89
+ </Dialog.Root>
@@ -0,0 +1,12 @@
1
+ import type { AuthLocalization } from '../../../types/index.js';
2
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
3
+ interface Props {
4
+ classNames?: SettingsCardClassNames;
5
+ localization?: Partial<AuthLocalization>;
6
+ apiKey: string;
7
+ open?: boolean;
8
+ onOpenChange?: (open: boolean) => void;
9
+ }
10
+ declare const ApiKeyDisplayDialog: import("svelte").Component<Props, {}, "open">;
11
+ type ApiKeyDisplayDialog = ReturnType<typeof ApiKeyDisplayDialog>;
12
+ export default ApiKeyDisplayDialog;
@@ -0,0 +1,98 @@
1
+ <script lang="ts">
2
+ import { getAuthUIConfig } from '../../../context/auth-ui-config.svelte.js';
3
+ import { cn } from '../../../utils/utils.js';
4
+ import type { AuthLocalization } from '../../../types/index.js';
5
+ import { CardContent } from '../../ui/card/index.js';
6
+ import SettingsCard, { type SettingsCardClassNames } from '../shared/settings-card.svelte';
7
+ import ApiKeyCell from './api-key-cell.svelte';
8
+ import ApiKeyDisplayDialog from './api-key-display-dialog.svelte';
9
+ import CreateApiKeyDialog from './create-api-key-dialog.svelte';
10
+
11
+ export interface ApiKeysCardProps {
12
+ className?: string;
13
+ classNames?: SettingsCardClassNames;
14
+ localization?: Partial<AuthLocalization>;
15
+ organizationId?: string;
16
+ }
17
+
18
+ interface Props extends ApiKeysCardProps {}
19
+
20
+ let {
21
+ className,
22
+ classNames,
23
+ localization: propLocalization,
24
+ organizationId,
25
+ ...restProps
26
+ }: Props = $props();
27
+
28
+ const {
29
+ hooks: { useListApiKeys },
30
+ localization: contextLocalization
31
+ } = getAuthUIConfig();
32
+
33
+ const mergedLocalization = $derived({ ...contextLocalization, ...propLocalization });
34
+
35
+ const listApiKeys = useListApiKeys();
36
+ const apiKeys = $derived(listApiKeys.data);
37
+ const isPending = $derived(listApiKeys.isPending);
38
+ const refetch = $derived(listApiKeys.refetch);
39
+
40
+ // Filter API keys by organizationId
41
+ const filteredApiKeys = $derived(
42
+ !apiKeys ? null : !organizationId ? apiKeys : apiKeys.filter(
43
+ (apiKey) => organizationId === apiKey.metadata?.organizationId
44
+ )
45
+ );
46
+
47
+ let createDialogOpen = $state(false);
48
+ let displayDialogOpen = $state(false);
49
+ let createdApiKey = $state('');
50
+
51
+ const handleCreateApiKey = (apiKey: string) => {
52
+ createdApiKey = apiKey;
53
+ displayDialogOpen = true;
54
+ };
55
+ </script>
56
+
57
+ <SettingsCard
58
+ {className}
59
+ {classNames}
60
+ actionLabel={mergedLocalization.CREATE_API_KEY}
61
+ description={mergedLocalization.API_KEYS_DESCRIPTION}
62
+ instructions={mergedLocalization.API_KEYS_INSTRUCTIONS}
63
+ {isPending}
64
+ title={mergedLocalization.API_KEYS}
65
+ action={() => (createDialogOpen = true)}
66
+ {...restProps}
67
+ >
68
+ {#if filteredApiKeys && filteredApiKeys.length > 0}
69
+ <CardContent class={cn('grid gap-4', classNames?.content)}>
70
+ {#each filteredApiKeys as apiKey (apiKey.id)}
71
+ <ApiKeyCell
72
+ {classNames}
73
+ {apiKey}
74
+ localization={mergedLocalization}
75
+ {refetch}
76
+ />
77
+ {/each}
78
+ </CardContent>
79
+ {/if}
80
+ </SettingsCard>
81
+
82
+ <CreateApiKeyDialog
83
+ {classNames}
84
+ localization={mergedLocalization}
85
+ open={createDialogOpen}
86
+ onOpenChange={(open) => (createDialogOpen = open)}
87
+ onSuccess={handleCreateApiKey}
88
+ {refetch}
89
+ {organizationId}
90
+ />
91
+
92
+ <ApiKeyDisplayDialog
93
+ {classNames}
94
+ apiKey={createdApiKey}
95
+ localization={mergedLocalization}
96
+ open={displayDialogOpen}
97
+ onOpenChange={(open) => (displayDialogOpen = open)}
98
+ />
@@ -0,0 +1,13 @@
1
+ import type { AuthLocalization } from '../../../types/index.js';
2
+ import { type SettingsCardClassNames } from '../shared/settings-card.svelte';
3
+ export interface ApiKeysCardProps {
4
+ className?: string;
5
+ classNames?: SettingsCardClassNames;
6
+ localization?: Partial<AuthLocalization>;
7
+ organizationId?: string;
8
+ }
9
+ interface Props extends ApiKeysCardProps {
10
+ }
11
+ declare const ApiKeysCard: import("svelte").Component<Props, {}, "">;
12
+ type ApiKeysCard = ReturnType<typeof ApiKeysCard>;
13
+ export default ApiKeysCard;
@@ -0,0 +1,337 @@
1
+ <script lang="ts">
2
+ import { z } from 'zod';
3
+ import { createForm } from '@tanstack/svelte-form';
4
+ import Loader2 from '@lucide/svelte/icons/loader-2';
5
+ import type { Organization } from 'better-auth/plugins/organization';
6
+ import {
7
+ getAuthClient,
8
+ getAuthUIConfig,
9
+ getLocalization
10
+ } from '../../../context/auth-ui-config.svelte';
11
+ import { useLang } from '../../../hooks/use-lang.svelte.js';
12
+ import { cn, getLocalizedError, getFieldError } from '../../../utils/utils.js';
13
+ import type { AuthLocalization } from '../../../types/index.js';
14
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
15
+ import { Button } from '../../ui/button/index.js';
16
+ import * as Dialog from '../../ui/dialog/index.js';
17
+ import { Input } from '../../ui/input/index.js';
18
+ import { Label } from '../../ui/label/index.js';
19
+ import * as Select from '../../ui/select/index.js';
20
+ import OrganizationCellView from '../../organization/organization-cell-view.svelte';
21
+ import PersonalAccountView from '../../organization/personal-account-view.svelte';
22
+
23
+ interface Props {
24
+ class?: string;
25
+ classNames?: SettingsCardClassNames;
26
+ open?: boolean;
27
+ onOpenChange?: (open: boolean) => void;
28
+ localization?: Partial<AuthLocalization>;
29
+ onSuccess: (key: string) => void;
30
+ refetch?: import('../../../types/refetch.js').Refetch;
31
+ organizationId?: string;
32
+ }
33
+
34
+ let {
35
+ class: className,
36
+ classNames,
37
+ open = $bindable(false),
38
+ onOpenChange,
39
+ localization: propLocalization,
40
+ onSuccess,
41
+ refetch,
42
+ organizationId
43
+ }: Props = $props();
44
+
45
+ const authClient = getAuthClient();
46
+ const config = getAuthUIConfig();
47
+ const contextLocalization = getLocalization();
48
+
49
+ const { hooks, toast, apiKey, organization: contextOrganization } = config;
50
+
51
+ const localization = $derived({ ...contextLocalization, ...propLocalization });
52
+
53
+ // Use the lang hook
54
+ const { lang } = useLang();
55
+
56
+ // Get current user from session - must be at top level
57
+ const sessionStore = hooks.useSession();
58
+ const sessionData = $derived('data' in $sessionStore ? $sessionStore.data : undefined);
59
+ const user = $derived(sessionData?.user);
60
+
61
+ // Fetch organizations if organization plugin is available - must be at top level
62
+ const organizationsStore = contextOrganization ? hooks.useListOrganizations() : null;
63
+ const organizations = $derived(
64
+ organizationsStore && 'data' in organizationsStore
65
+ ? (organizationsStore.data as Organization[] | null | undefined)
66
+ : undefined
67
+ );
68
+
69
+ const showOrganizationSelect = $derived(contextOrganization?.apiKey);
70
+
71
+ // Form validation schema
72
+ const formSchema = $derived(
73
+ z.object({
74
+ name: z.string().min(1, `${localization.NAME} ${localization.IS_REQUIRED}`),
75
+ expiresInDays: z.string().optional(),
76
+ organizationId: showOrganizationSelect
77
+ ? z.string().min(1, `${localization.ORGANIZATION} ${localization.IS_REQUIRED}`)
78
+ : z.string().optional()
79
+ })
80
+ );
81
+
82
+ // Track selected values for Select components
83
+ let selectedExpiresInDays = $state('none');
84
+ let selectedOrganizationId = $state(organizationId ?? 'personal');
85
+
86
+ // Create form
87
+ const form = createForm(() => ({
88
+ defaultValues: {
89
+ name: '',
90
+ expiresInDays: 'none',
91
+ organizationId: organizationId ?? 'personal'
92
+ },
93
+ onSubmit: async ({ value }) => {
94
+ try {
95
+ const expiresIn =
96
+ value.expiresInDays && value.expiresInDays !== 'none'
97
+ ? Number.parseInt(value.expiresInDays) * 60 * 60 * 24
98
+ : undefined;
99
+
100
+ const selectedOrgId =
101
+ value.organizationId === 'personal' ? undefined : value.organizationId;
102
+
103
+ const metadata = {
104
+ ...(typeof apiKey === 'object' ? apiKey.metadata : {}),
105
+ ...(contextOrganization && selectedOrgId ? { organizationId: selectedOrgId } : {})
106
+ };
107
+
108
+ const result = await (authClient as any).apiKey.create({
109
+ name: value.name,
110
+ expiresIn,
111
+ prefix: typeof apiKey === 'object' ? apiKey.prefix : undefined,
112
+ metadata: Object.keys(metadata).length > 0 ? metadata : undefined,
113
+ fetchOptions: { throw: true }
114
+ });
115
+
116
+ await refetch?.();
117
+ onSuccess(result.key);
118
+ handleOpenChange(false);
119
+ form.reset();
120
+ selectedExpiresInDays = 'none';
121
+ selectedOrganizationId = organizationId ?? 'personal';
122
+ } catch (error) {
123
+ toast.error(getLocalizedError({ error, localization }));
124
+ }
125
+ }
126
+ }));
127
+
128
+ const isSubmitting = $derived(form.state.isSubmitting);
129
+
130
+ function handleOpenChange(newOpen: boolean) {
131
+ open = newOpen;
132
+ onOpenChange?.(newOpen);
133
+
134
+ // Reset form when dialog closes
135
+ if (!newOpen) {
136
+ form.reset();
137
+ selectedExpiresInDays = 'none';
138
+ selectedOrganizationId = organizationId ?? 'personal';
139
+ }
140
+ }
141
+
142
+ // Create relative time formatter
143
+ const rtf = $derived(new Intl.RelativeTimeFormat(lang ?? 'en'));
144
+
145
+ // Expiration options
146
+ const expirationDays = [1, 7, 30, 60, 90, 180, 365];
147
+ </script>
148
+
149
+ <Dialog.Root {open} onOpenChange={handleOpenChange}>
150
+ <Dialog.Content
151
+ class={classNames?.dialog?.content}
152
+ onOpenAutoFocus={(e) => e.preventDefault()}
153
+ >
154
+ <Dialog.Header class={classNames?.dialog?.header}>
155
+ <Dialog.Title class={cn('text-lg md:text-xl', classNames?.title)}>
156
+ {localization.CREATE_API_KEY}
157
+ </Dialog.Title>
158
+
159
+ <Dialog.Description class={cn('text-xs md:text-sm', classNames?.description)}>
160
+ {localization.CREATE_API_KEY_DESCRIPTION}
161
+ </Dialog.Description>
162
+ </Dialog.Header>
163
+
164
+ <form
165
+ onsubmit={(e) => {
166
+ e.preventDefault();
167
+ form.handleSubmit();
168
+ }}
169
+ class="space-y-6"
170
+ >
171
+ {#if showOrganizationSelect}
172
+ <form.Field name="organizationId">
173
+ {#snippet children(field)}
174
+ <div class="space-y-2">
175
+ <Label for="organizationId" class={classNames?.label}>
176
+ {localization.ORGANIZATION}
177
+ </Label>
178
+
179
+ <Select.Root
180
+ type="single"
181
+ value={selectedOrganizationId}
182
+ onValueChange={(value?: string) => {
183
+ if (value) {
184
+ selectedOrganizationId = value;
185
+ field.handleChange(value);
186
+ }
187
+ }}
188
+ disabled={isSubmitting}
189
+ >
190
+ <Select.Trigger id="organizationId" class={cn('w-full p-2', classNames?.input)}>
191
+ {#snippet children()}
192
+ {#if selectedOrganizationId === 'personal'}
193
+ <PersonalAccountView {user} {localization} size="sm" />
194
+ {:else}
195
+ {@const org = organizations?.find((o) => o.id === selectedOrganizationId)}
196
+ {#if org}
197
+ <OrganizationCellView organization={org} {localization} size="sm" />
198
+ {/if}
199
+ {/if}
200
+ {/snippet}
201
+ </Select.Trigger>
202
+
203
+ <Select.Content class="w-[--radix-select-trigger-width]">
204
+ <Select.Item value="personal" label="">
205
+ {#snippet children()}
206
+ <PersonalAccountView {user} {localization} size="sm" />
207
+ {/snippet}
208
+ </Select.Item>
209
+
210
+ {#if organizations}
211
+ {#each organizations as org (org.id)}
212
+ <Select.Item value={org.id} label="">
213
+ {#snippet children()}
214
+ <OrganizationCellView organization={org} {localization} size="sm" />
215
+ {/snippet}
216
+ </Select.Item>
217
+ {/each}
218
+ {/if}
219
+ </Select.Content>
220
+ </Select.Root>
221
+
222
+ {#if field.state.meta.errors.length > 0}
223
+ <p class="text-sm font-medium text-destructive">
224
+ {getFieldError(field.state.meta.errors[0])}
225
+ </p>
226
+ {/if}
227
+ </div>
228
+ {/snippet}
229
+ </form.Field>
230
+ {/if}
231
+
232
+ <div class="flex gap-4">
233
+ <form.Field name="name" validators={{ onChange: formSchema.shape.name }}>
234
+ {#snippet children(field)}
235
+ <div class="flex-1 space-y-2">
236
+ <Label for="name" class={classNames?.label}>
237
+ {localization.NAME}
238
+ </Label>
239
+
240
+ <Input
241
+ id="name"
242
+ placeholder={localization.API_KEY_NAME_PLACEHOLDER}
243
+ autofocus
244
+ value={field.state.value}
245
+ oninput={(e) => field.handleChange(e.currentTarget.value)}
246
+ onblur={field.handleBlur}
247
+ disabled={isSubmitting}
248
+ class={classNames?.input}
249
+ />
250
+
251
+ {#if field.state.meta.errors.length > 0}
252
+ <p class="text-sm font-medium text-destructive">
253
+ {getFieldError(field.state.meta.errors[0])}
254
+ </p>
255
+ {/if}
256
+ </div>
257
+ {/snippet}
258
+ </form.Field>
259
+
260
+ <form.Field name="expiresInDays">
261
+ {#snippet children(field)}
262
+ <div class="space-y-2">
263
+ <Label for="expiresInDays" class={classNames?.label}>
264
+ {localization.EXPIRES}
265
+ </Label>
266
+
267
+ <Select.Root
268
+ type="single"
269
+ value={selectedExpiresInDays}
270
+ onValueChange={(value?: string) => {
271
+ if (value !== undefined) {
272
+ selectedExpiresInDays = value;
273
+ field.handleChange(value);
274
+ }
275
+ }}
276
+ disabled={isSubmitting}
277
+ >
278
+ <Select.Trigger id="expiresInDays" class={classNames?.input}>
279
+ {#snippet children()}
280
+ {#if selectedExpiresInDays === 'none'}
281
+ {localization.NO_EXPIRATION}
282
+ {:else if selectedExpiresInDays === '365'}
283
+ {rtf.format(1, 'year')}
284
+ {:else}
285
+ {rtf.format(Number.parseInt(selectedExpiresInDays), 'day')}
286
+ {/if}
287
+ {/snippet}
288
+ </Select.Trigger>
289
+
290
+ <Select.Content>
291
+ <Select.Item value="none" label={localization.NO_EXPIRATION} />
292
+
293
+ {#each expirationDays as days (days)}
294
+ <Select.Item
295
+ value={days.toString()}
296
+ label={days === 365 ? rtf.format(1, 'year') : rtf.format(days, 'day')}
297
+ />
298
+ {/each}
299
+ </Select.Content>
300
+ </Select.Root>
301
+
302
+ {#if field.state.meta.errors.length > 0}
303
+ <p class="text-sm font-medium text-destructive">
304
+ {getFieldError(field.state.meta.errors[0])}
305
+ </p>
306
+ {/if}
307
+ </div>
308
+ {/snippet}
309
+ </form.Field>
310
+ </div>
311
+
312
+ <Dialog.Footer class={classNames?.dialog?.footer}>
313
+ <Button
314
+ type="button"
315
+ variant="outline"
316
+ onclick={() => handleOpenChange(false)}
317
+ class={cn(classNames?.button, classNames?.outlineButton)}
318
+ disabled={isSubmitting}
319
+ >
320
+ {localization.CANCEL}
321
+ </Button>
322
+
323
+ <Button
324
+ type="submit"
325
+ variant="default"
326
+ class={cn(classNames?.button, classNames?.primaryButton)}
327
+ disabled={isSubmitting}
328
+ >
329
+ {#if isSubmitting}
330
+ <Loader2 class="animate-spin" />
331
+ {/if}
332
+ {localization.CREATE_API_KEY}
333
+ </Button>
334
+ </Dialog.Footer>
335
+ </form>
336
+ </Dialog.Content>
337
+ </Dialog.Root>
@@ -0,0 +1,15 @@
1
+ import type { AuthLocalization } from '../../../types/index.js';
2
+ import type { SettingsCardClassNames } from '../shared/settings-card.svelte';
3
+ interface Props {
4
+ class?: string;
5
+ classNames?: SettingsCardClassNames;
6
+ open?: boolean;
7
+ onOpenChange?: (open: boolean) => void;
8
+ localization?: Partial<AuthLocalization>;
9
+ onSuccess: (key: string) => void;
10
+ refetch?: import('../../../types/refetch.js').Refetch;
11
+ organizationId?: string;
12
+ }
13
+ declare const CreateApiKeyDialog: import("svelte").Component<Props, {}, "open">;
14
+ type CreateApiKeyDialog = ReturnType<typeof CreateApiKeyDialog>;
15
+ export default CreateApiKeyDialog;
@@ -0,0 +1,7 @@
1
+ export { default as ApiKeysCard } from './api-keys-card.svelte';
2
+ export { default as ApiKeyCell } from './api-key-cell.svelte';
3
+ export { default as ApiKeyDisplayDialog } from './api-key-display-dialog.svelte';
4
+ export { default as ApiKeyDeleteDialog } from './api-key-delete-dialog.svelte';
5
+ export { default as CreateApiKeyDialog } from './create-api-key-dialog.svelte';
6
+ export type { ApiKeysCardProps } from './api-keys-card.svelte';
7
+ export type { ApiKeyCellProps } from './api-key-cell.svelte';
@@ -0,0 +1,5 @@
1
+ export { default as ApiKeysCard } from './api-keys-card.svelte';
2
+ export { default as ApiKeyCell } from './api-key-cell.svelte';
3
+ export { default as ApiKeyDisplayDialog } from './api-key-display-dialog.svelte';
4
+ export { default as ApiKeyDeleteDialog } from './api-key-delete-dialog.svelte';
5
+ export { default as CreateApiKeyDialog } from './create-api-key-dialog.svelte';
@@ -4,5 +4,6 @@ export * from './security/index.js';
4
4
  export * from './providers/index.js';
5
5
  export * from './two-factor/index.js';
6
6
  export * from './passkey/index.js';
7
+ export * from './api-key/index.js';
7
8
  export { default as SecuritySettingsCards } from './security-settings-cards.svelte';
8
9
  export type { SecuritySettingsCardsProps } from './security-settings-cards.svelte';
@@ -4,4 +4,5 @@ export * from './security/index.js';
4
4
  export * from './providers/index.js';
5
5
  export * from './two-factor/index.js';
6
6
  export * from './passkey/index.js';
7
+ export * from './api-key/index.js';
7
8
  export { default as SecuritySettingsCards } from './security-settings-cards.svelte';
package/dist/index.d.ts CHANGED
@@ -6,6 +6,9 @@ export { default as UserButton } from './components/user-button.svelte';
6
6
  export { default as UserView } from './components/user-view.svelte';
7
7
  export { default as AuthLoading } from './components/auth-loading.svelte';
8
8
  export * as ProviderIcons from './components/provider-icons/index.js';
9
+ export { default as PasswordInput } from './components/password-input.svelte';
10
+ export { default as RedirectToSignIn } from './components/redirect-to-sign-in.svelte';
11
+ export { default as RedirectToSignUp } from './components/redirect-to-sign-up.svelte';
9
12
  export * from './components/account/index.js';
10
13
  export { default as AuthCallback } from './components/auth/auth-callback.svelte';
11
14
  export { default as AuthForm } from './components/auth/auth-form.svelte';
@@ -18,6 +21,8 @@ export { default as ResetPasswordForm } from './components/auth/forms/reset-pass
18
21
  export { default as SignInForm } from './components/auth/forms/sign-in-form.svelte';
19
22
  export { default as SignUpForm } from './components/auth/forms/sign-up-form.svelte';
20
23
  export { default as TwoFactorForm } from './components/auth/forms/two-factor-form.svelte';
24
+ export { default as FormError } from './components/form-error.svelte';
25
+ export type { FormErrorProps } from './components/form-error.svelte';
21
26
  export { default as CreateOrganizationDialog } from './components/organization/create-organization-dialog.svelte';
22
27
  export { default as DeleteOrganizationCard } from './components/organization/delete-organization-card.svelte';
23
28
  export { default as DeleteOrganizationDialog } from './components/organization/delete-organization-dialog.svelte';
@@ -49,9 +54,18 @@ export { default as UpdateFieldCard } from './components/settings/account/update
49
54
  export { default as UpdateNameCard } from './components/settings/account/update-name-card.svelte';
50
55
  export { default as UpdateUsernameCard } from './components/settings/account/update-username-card.svelte';
51
56
  export { default as AccountSettingsCards } from './components/settings/account/account-settings-cards.svelte';
57
+ export { default as ApiKeysCard } from './components/settings/api-key/api-keys-card.svelte';
58
+ export type { ApiKeysCardProps } from './components/settings/api-key/api-keys-card.svelte';
59
+ export { default as PasskeysCard } from './components/settings/passkey/passkeys-card.svelte';
52
60
  export { default as ProvidersCard } from './components/settings/providers/providers-card.svelte';
53
61
  export type { ProvidersCardProps } from './components/settings/providers/providers-card.svelte';
62
+ export { default as ChangeEmailCard } from './components/settings/security/change-email-card.svelte';
63
+ export { default as ChangePasswordCard } from './components/settings/security/change-password-card.svelte';
64
+ export { default as SessionsCard } from './components/settings/security/sessions-card.svelte';
65
+ export { default as SecuritySettingsCards } from './components/settings/security-settings-cards.svelte';
66
+ export { default as SettingsCard } from './components/settings/shared/settings-card.svelte';
54
67
  export { default as SettingsCellSkeleton } from './components/settings/skeletons/settings-cell-skeleton.svelte';
68
+ export { default as TwoFactorCard } from './components/settings/two-factor/two-factor-card.svelte';
55
69
  export * from './stores/use-auth-data.svelte.js';
56
70
  export * from './hooks/use-authenticate.svelte.js';
57
71
  export * from './hooks/use-current-organization.svelte.js';
package/dist/index.js CHANGED
@@ -7,9 +7,9 @@ export { default as UserButton } from './components/user-button.svelte';
7
7
  export { default as UserView } from './components/user-view.svelte';
8
8
  export { default as AuthLoading } from './components/auth-loading.svelte';
9
9
  export * as ProviderIcons from './components/provider-icons/index.js';
10
- // export { default as PasswordInput } from './components/password-input.svelte';
11
- // export { default as RedirectToSignIn } from './components/redirect-to-sign-in.svelte';
12
- // export { default as RedirectToSignUp } from './components/redirect-to-sign-up.svelte';
10
+ export { default as PasswordInput } from './components/password-input.svelte';
11
+ export { default as RedirectToSignIn } from './components/redirect-to-sign-in.svelte';
12
+ export { default as RedirectToSignUp } from './components/redirect-to-sign-up.svelte';
13
13
  // Account Components
14
14
  export * from './components/account/index.js';
15
15
  // Auth Components
@@ -25,6 +25,8 @@ export { default as ResetPasswordForm } from './components/auth/forms/reset-pass
25
25
  export { default as SignInForm } from './components/auth/forms/sign-in-form.svelte';
26
26
  export { default as SignUpForm } from './components/auth/forms/sign-up-form.svelte';
27
27
  export { default as TwoFactorForm } from './components/auth/forms/two-factor-form.svelte';
28
+ // Form Utilities
29
+ export { default as FormError } from './components/form-error.svelte';
28
30
  // Organization Components
29
31
  // export { default as AcceptInvitationCard } from './components/organization/accept-invitation-card.svelte';
30
32
  export { default as CreateOrganizationDialog } from './components/organization/create-organization-dialog.svelte';
@@ -60,23 +62,23 @@ export { default as UpdateNameCard } from './components/settings/account/update-
60
62
  export { default as UpdateUsernameCard } from './components/settings/account/update-username-card.svelte';
61
63
  export { default as AccountSettingsCards } from './components/settings/account/account-settings-cards.svelte';
62
64
  // Settings Components - API Key
63
- // export { default as ApiKeysCard } from './components/settings/api-key/api-keys-card.svelte';
65
+ export { default as ApiKeysCard } from './components/settings/api-key/api-keys-card.svelte';
64
66
  // Settings Components - Passkey
65
- // export { default as PasskeysCard } from './components/settings/passkey/passkeys-card.svelte';
67
+ export { default as PasskeysCard } from './components/settings/passkey/passkeys-card.svelte';
66
68
  // Settings Components - Providers
67
69
  export { default as ProvidersCard } from './components/settings/providers/providers-card.svelte';
68
70
  // Settings Components - Security
69
- // export { default as ChangeEmailCard } from './components/settings/security/change-email-card.svelte';
70
- // export { default as ChangePasswordCard } from './components/settings/security/change-password-card.svelte';
71
- // export { default as SessionsCard } from './components/settings/security/sessions-card.svelte';
72
- // export { default as SecuritySettingsCards } from './components/settings/security-settings-cards.svelte';
71
+ export { default as ChangeEmailCard } from './components/settings/security/change-email-card.svelte';
72
+ export { default as ChangePasswordCard } from './components/settings/security/change-password-card.svelte';
73
+ export { default as SessionsCard } from './components/settings/security/sessions-card.svelte';
74
+ export { default as SecuritySettingsCards } from './components/settings/security-settings-cards.svelte';
73
75
  // Settings Components - Shared
74
- // export { default as SettingsCard } from './components/settings/shared/settings-card.svelte';
76
+ export { default as SettingsCard } from './components/settings/shared/settings-card.svelte';
75
77
  // Settings Components - Skeletons
76
78
  // export { default as InputFieldSkeleton } from './components/settings/skeletons/input-field-skeleton.svelte';
77
79
  export { default as SettingsCellSkeleton } from './components/settings/skeletons/settings-cell-skeleton.svelte';
78
80
  // Settings Components - Two Factor
79
- // export { default as TwoFactorCard } from './components/settings/two-factor/two-factor-card.svelte';
81
+ export { default as TwoFactorCard } from './components/settings/two-factor/two-factor-card.svelte';
80
82
  // Hooks
81
83
  export * from './stores/use-auth-data.svelte.js';
82
84
  export * from './hooks/use-authenticate.svelte.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "better-auth-ui-svelte",
3
- "version": "0.1.2",
3
+ "version": "0.2.3",
4
4
  "description": "Pre-built authentication UI components for Better Auth in Svelte 5",
5
5
  "files": [
6
6
  "dist",
@@ -14,6 +14,7 @@
14
14
  "types": "./dist/index.d.ts",
15
15
  "type": "module",
16
16
  "exports": {
17
+ "./css": "./dist/style.css",
17
18
  ".": {
18
19
  "types": "./dist/index.d.ts",
19
20
  "svelte": "./dist/index.js"