svelte-firekit 0.0.24 → 0.1.0

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 (95) hide show
  1. package/README.md +445 -213
  2. package/dist/components/Collection.svelte +150 -0
  3. package/dist/components/Collection.svelte.d.ts +27 -0
  4. package/dist/components/Ddoc.svelte +131 -0
  5. package/dist/components/Ddoc.svelte.d.ts +28 -0
  6. package/dist/components/Node.svelte +97 -0
  7. package/dist/components/Node.svelte.d.ts +23 -0
  8. package/dist/components/auth-guard.svelte +89 -0
  9. package/dist/components/auth-guard.svelte.d.ts +26 -0
  10. package/dist/components/custom-guard.svelte +122 -0
  11. package/dist/components/custom-guard.svelte.d.ts +31 -0
  12. package/dist/components/download-url.svelte +92 -0
  13. package/dist/components/download-url.svelte.d.ts +19 -0
  14. package/dist/components/firebase-app.svelte +30 -0
  15. package/dist/components/firebase-app.svelte.d.ts +7 -0
  16. package/dist/components/node-list.svelte +102 -0
  17. package/dist/components/node-list.svelte.d.ts +27 -0
  18. package/dist/components/signed-in.svelte +42 -0
  19. package/dist/components/signed-in.svelte.d.ts +11 -0
  20. package/dist/components/signed-out.svelte +42 -0
  21. package/dist/components/signed-out.svelte.d.ts +11 -0
  22. package/dist/components/storage-list.svelte +97 -0
  23. package/dist/components/storage-list.svelte.d.ts +26 -0
  24. package/dist/components/upload-task.svelte +108 -0
  25. package/dist/components/upload-task.svelte.d.ts +24 -0
  26. package/dist/config.js +17 -39
  27. package/dist/firebase.d.ts +43 -21
  28. package/dist/firebase.js +121 -35
  29. package/dist/index.d.ts +21 -12
  30. package/dist/index.js +27 -14
  31. package/dist/services/auth.d.ts +389 -0
  32. package/dist/services/auth.js +824 -0
  33. package/dist/services/collection.svelte.d.ts +286 -0
  34. package/dist/services/collection.svelte.js +871 -0
  35. package/dist/services/document.svelte.d.ts +288 -0
  36. package/dist/services/document.svelte.js +555 -0
  37. package/dist/services/mutations.d.ts +336 -0
  38. package/dist/services/mutations.js +1079 -0
  39. package/dist/services/presence.svelte.d.ts +141 -0
  40. package/dist/services/presence.svelte.js +727 -0
  41. package/dist/{realtime → services}/realtime.svelte.d.ts +3 -1
  42. package/dist/{realtime → services}/realtime.svelte.js +13 -7
  43. package/dist/services/storage.svelte.d.ts +257 -0
  44. package/dist/services/storage.svelte.js +374 -0
  45. package/dist/services/user.svelte.d.ts +290 -0
  46. package/dist/services/user.svelte.js +533 -0
  47. package/dist/types/auth.d.ts +158 -0
  48. package/dist/types/auth.js +106 -0
  49. package/dist/types/collection.d.ts +360 -0
  50. package/dist/types/collection.js +167 -0
  51. package/dist/types/document.d.ts +342 -0
  52. package/dist/types/document.js +148 -0
  53. package/dist/types/firebase.d.ts +44 -0
  54. package/dist/types/firebase.js +33 -0
  55. package/dist/types/index.d.ts +6 -0
  56. package/dist/types/index.js +4 -0
  57. package/dist/types/mutations.d.ts +387 -0
  58. package/dist/types/mutations.js +205 -0
  59. package/dist/types/presence.d.ts +282 -0
  60. package/dist/types/presence.js +80 -0
  61. package/dist/utils/errors.d.ts +21 -0
  62. package/dist/utils/errors.js +35 -0
  63. package/dist/utils/firestore.d.ts +9 -0
  64. package/dist/utils/firestore.js +33 -0
  65. package/dist/utils/index.d.ts +4 -0
  66. package/dist/utils/index.js +8 -0
  67. package/dist/utils/providers.d.ts +16 -0
  68. package/dist/utils/providers.js +30 -0
  69. package/dist/utils/user.d.ts +8 -0
  70. package/dist/utils/user.js +29 -0
  71. package/package.json +65 -65
  72. package/dist/auth/auth.d.ts +0 -117
  73. package/dist/auth/auth.js +0 -194
  74. package/dist/auth/presence.svelte.d.ts +0 -139
  75. package/dist/auth/presence.svelte.js +0 -373
  76. package/dist/auth/user.svelte.d.ts +0 -112
  77. package/dist/auth/user.svelte.js +0 -155
  78. package/dist/firestore/awaitable-doc.svelte.d.ts +0 -141
  79. package/dist/firestore/awaitable-doc.svelte.js +0 -183
  80. package/dist/firestore/batch.svelte.d.ts +0 -140
  81. package/dist/firestore/batch.svelte.js +0 -218
  82. package/dist/firestore/collection-group.svelte.d.ts +0 -78
  83. package/dist/firestore/collection-group.svelte.js +0 -120
  84. package/dist/firestore/collection.svelte.d.ts +0 -96
  85. package/dist/firestore/collection.svelte.js +0 -137
  86. package/dist/firestore/doc.svelte.d.ts +0 -90
  87. package/dist/firestore/doc.svelte.js +0 -131
  88. package/dist/firestore/document-mutations.svelte.d.ts +0 -164
  89. package/dist/firestore/document-mutations.svelte.js +0 -273
  90. package/dist/storage/download-url.svelte.d.ts +0 -83
  91. package/dist/storage/download-url.svelte.js +0 -114
  92. package/dist/storage/storage-list.svelte.d.ts +0 -89
  93. package/dist/storage/storage-list.svelte.js +0 -123
  94. package/dist/storage/upload-task.svelte.d.ts +0 -94
  95. package/dist/storage/upload-task.svelte.js +0 -138
@@ -0,0 +1,150 @@
1
+ <script lang="ts">
2
+ import { firekitCollection } from '../services/collection.svelte.js';
3
+ import { firebaseService } from '../firebase.js';
4
+ import { collection } from 'firebase/firestore';
5
+ import { browser } from '$app/environment';
6
+ import type {
7
+ CollectionReference,
8
+ Query,
9
+ DocumentData,
10
+ Firestore,
11
+ QueryConstraint
12
+ } from 'firebase/firestore';
13
+ import type { Snippet } from 'svelte';
14
+
15
+ /**
16
+ * Props for Collection component
17
+ */
18
+ let {
19
+ ref,
20
+ startWith,
21
+ children,
22
+ loading,
23
+ queryConstraints = []
24
+ }: {
25
+ /**
26
+ * Firestore collection reference, query reference, or path string
27
+ */
28
+ ref: CollectionReference | Query | string;
29
+ /**
30
+ * Initial value to use before collection is fetched
31
+ */
32
+ startWith?: DocumentData[];
33
+ /**
34
+ * Content to render when collection is loaded
35
+ */
36
+ children: Snippet<[DocumentData[], CollectionReference | Query, Firestore, number]>;
37
+ /**
38
+ * Content to render while loading
39
+ */
40
+ loading?: Snippet<[]>;
41
+ /**
42
+ * Optional query constraints to apply to the collection
43
+ */
44
+ queryConstraints?: QueryConstraint[];
45
+ } = $props();
46
+
47
+ // Get Firestore instance only in browser environment
48
+ let firestore: Firestore | null = $state(null);
49
+ let collectionRef: CollectionReference | Query | null = $state(null);
50
+ let collectionService: ReturnType<typeof firekitCollection> | null = $state(null);
51
+
52
+ // Track collection state
53
+ let collectionState = $state({
54
+ loading: true,
55
+ data: startWith ?? ([] as DocumentData[]),
56
+ error: null as Error | null,
57
+ count: startWith?.length ?? 0
58
+ });
59
+
60
+ // Subscribe to collection changes only if in browser and collection service exists
61
+ $effect(() => {
62
+ if (!browser) {
63
+ collectionState = {
64
+ loading: false,
65
+ data: startWith ?? [],
66
+ error: null,
67
+ count: startWith?.length ?? 0
68
+ };
69
+ return;
70
+ }
71
+
72
+ // Initialize Firestore and collection service
73
+ firestore = firebaseService.getDbInstance();
74
+ if (!firestore) {
75
+ collectionState = {
76
+ loading: false,
77
+ data: [],
78
+ error: new Error('Firestore instance not available'),
79
+ count: 0
80
+ };
81
+ return;
82
+ }
83
+
84
+ // Create collection reference if path string is provided
85
+ collectionRef =
86
+ typeof ref === 'string' ? collection(firestore, ref) : (ref as CollectionReference | Query);
87
+
88
+ // Create collection service
89
+ collectionService = firekitCollection(
90
+ typeof ref === 'string' ? ref : (ref as CollectionReference).path,
91
+ queryConstraints
92
+ );
93
+
94
+ // Update state based on collection service state
95
+ collectionState = {
96
+ loading: collectionService.loading,
97
+ data: collectionService.data,
98
+ error: collectionService.error,
99
+ count: collectionService.size
100
+ };
101
+
102
+ // Set up event listener for real-time updates
103
+ const unsubscribe = collectionService.addEventListener((event) => {
104
+ if (event.type === 'data_changed') {
105
+ collectionState = {
106
+ loading: false,
107
+ data: event.data || [],
108
+ error: null,
109
+ count: event.data?.length || 0
110
+ };
111
+ } else if (event.type === 'error') {
112
+ collectionState = {
113
+ loading: false,
114
+ data: [],
115
+ error: event.error || null,
116
+ count: 0
117
+ };
118
+ } else if (event.type === 'loading_started') {
119
+ collectionState = {
120
+ ...collectionState,
121
+ loading: true
122
+ };
123
+ } else if (event.type === 'loading_finished') {
124
+ collectionState = {
125
+ ...collectionState,
126
+ loading: false
127
+ };
128
+ }
129
+ });
130
+
131
+ return () => {
132
+ unsubscribe();
133
+ collectionService?.dispose();
134
+ };
135
+ });
136
+ </script>
137
+
138
+ {#if !browser}
139
+ {@render children(startWith ?? [], null as any, null as any, startWith?.length ?? 0)}
140
+ {:else if collectionState.loading}
141
+ {#if loading}
142
+ {@render loading()}
143
+ {:else}
144
+ <div>Loading...</div>
145
+ {/if}
146
+ {:else if collectionState.error}
147
+ <div class="error">Error: {collectionState.error.message}</div>
148
+ {:else}
149
+ {@render children(collectionState.data, collectionRef!, firestore!, collectionState.count)}
150
+ {/if}
@@ -0,0 +1,27 @@
1
+ import type { CollectionReference, Query, DocumentData, Firestore, QueryConstraint } from 'firebase/firestore';
2
+ import type { Snippet } from 'svelte';
3
+ type $$ComponentProps = {
4
+ /**
5
+ * Firestore collection reference, query reference, or path string
6
+ */
7
+ ref: CollectionReference | Query | string;
8
+ /**
9
+ * Initial value to use before collection is fetched
10
+ */
11
+ startWith?: DocumentData[];
12
+ /**
13
+ * Content to render when collection is loaded
14
+ */
15
+ children: Snippet<[DocumentData[], CollectionReference | Query, Firestore, number]>;
16
+ /**
17
+ * Content to render while loading
18
+ */
19
+ loading?: Snippet<[]>;
20
+ /**
21
+ * Optional query constraints to apply to the collection
22
+ */
23
+ queryConstraints?: QueryConstraint[];
24
+ };
25
+ declare const Collection: import("svelte").Component<$$ComponentProps, {}, "">;
26
+ type Collection = ReturnType<typeof Collection>;
27
+ export default Collection;
@@ -0,0 +1,131 @@
1
+ <script lang="ts">
2
+ import { firekitDoc } from '../services/document.svelte.js';
3
+ import { firebaseService } from '../firebase.js';
4
+ import { doc } from 'firebase/firestore';
5
+ import { browser } from '$app/environment';
6
+ import { onDestroy } from 'svelte';
7
+ import type { DocumentReference, DocumentData, Firestore } from 'firebase/firestore';
8
+ import type { Snippet } from 'svelte';
9
+ import type { DocumentOptions } from '../types/document.js';
10
+
11
+ /**
12
+ * Props for Doc component
13
+ */
14
+ let {
15
+ ref,
16
+ startWith,
17
+ children,
18
+ loading,
19
+ options = {}
20
+ }: {
21
+ /**
22
+ * Firestore document reference or path string
23
+ */
24
+ ref: DocumentReference | string;
25
+ /**
26
+ * Initial value to use before document is fetched
27
+ */
28
+ startWith?: DocumentData | null;
29
+ /**
30
+ * Content to render when document is loaded
31
+ */
32
+ children: Snippet<[DocumentData | null, DocumentReference, Firestore]>;
33
+ /**
34
+ * Content to render while loading
35
+ */
36
+ loading?: Snippet<[]>;
37
+ /**
38
+ * Document options for configuration
39
+ */
40
+ options?: DocumentOptions;
41
+ } = $props();
42
+
43
+ // Get Firestore instance only in browser environment
44
+ let firestore: Firestore | null = $state(null);
45
+ let docRef: DocumentReference | null = $state(null);
46
+ let documentService: any = $state(null);
47
+
48
+ // Reactive document state
49
+ let componentState = $state({
50
+ loading: true,
51
+ data: null as DocumentData | null,
52
+ error: null as Error | null,
53
+ exists: false
54
+ });
55
+
56
+ // Initialize in browser environment
57
+ $effect(() => {
58
+ if (!browser) return;
59
+
60
+ firestore = firebaseService.getDbInstance();
61
+ if (!firestore) {
62
+ throw new Error('Firestore instance not available');
63
+ }
64
+
65
+ // Create document reference if path string is provided
66
+ docRef = typeof ref === 'string' ? doc(firestore, ref) : ref;
67
+
68
+ // Create document service
69
+ documentService = firekitDoc(docRef.path, startWith ?? undefined, options);
70
+ });
71
+
72
+ // Subscribe to document changes only if in browser and document service exists
73
+ $effect(() => {
74
+ if (!browser || !documentService) {
75
+ componentState = {
76
+ loading: false,
77
+ data: startWith ?? null,
78
+ error: null,
79
+ exists: !!startWith
80
+ };
81
+ return;
82
+ }
83
+
84
+ // Update component state based on document service state
85
+ componentState = {
86
+ loading: documentService.loading,
87
+ data: documentService.data,
88
+ error: documentService.error,
89
+ exists: documentService.exists
90
+ };
91
+ });
92
+
93
+ // Cleanup on component destruction
94
+ onDestroy(() => {
95
+ if (documentService) {
96
+ documentService.dispose();
97
+ }
98
+ });
99
+ </script>
100
+
101
+ {#if !browser}
102
+ {@render children(startWith ?? null, null as any, null as any)}
103
+ {:else if componentState.loading}
104
+ {#if loading}
105
+ {@render loading()}
106
+ {:else}
107
+ <div class="flex items-center justify-center min-h-screen">
108
+ <div class="text-center">
109
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto"></div>
110
+ <p class="mt-2 text-gray-600">Loading document...</p>
111
+ </div>
112
+ </div>
113
+ {/if}
114
+ {:else if componentState.error}
115
+ <div class="flex items-center justify-center min-h-screen">
116
+ <div class="text-center">
117
+ <div class="text-red-500 text-lg font-semibold mb-2">Error Loading Document</div>
118
+ <p class="text-gray-600 mb-4">{componentState.error.message}</p>
119
+ {#if documentService?.canRefresh}
120
+ <button
121
+ class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600"
122
+ onclick={() => documentService?.refresh()}
123
+ >
124
+ Retry
125
+ </button>
126
+ {/if}
127
+ </div>
128
+ </div>
129
+ {:else}
130
+ {@render children(componentState.data ?? null, docRef!, firestore!)}
131
+ {/if}
@@ -0,0 +1,28 @@
1
+ import type { DocumentReference, DocumentData, Firestore } from 'firebase/firestore';
2
+ import type { Snippet } from 'svelte';
3
+ import type { DocumentOptions } from '../types/document.js';
4
+ type $$ComponentProps = {
5
+ /**
6
+ * Firestore document reference or path string
7
+ */
8
+ ref: DocumentReference | string;
9
+ /**
10
+ * Initial value to use before document is fetched
11
+ */
12
+ startWith?: DocumentData | null;
13
+ /**
14
+ * Content to render when document is loaded
15
+ */
16
+ children: Snippet<[DocumentData | null, DocumentReference, Firestore]>;
17
+ /**
18
+ * Content to render while loading
19
+ */
20
+ loading?: Snippet<[]>;
21
+ /**
22
+ * Document options for configuration
23
+ */
24
+ options?: DocumentOptions;
25
+ };
26
+ declare const Ddoc: import("svelte").Component<$$ComponentProps, {}, "">;
27
+ type Ddoc = ReturnType<typeof Ddoc>;
28
+ export default Ddoc;
@@ -0,0 +1,97 @@
1
+ <script lang="ts">
2
+ import { ref, type DatabaseReference, type Database } from 'firebase/database';
3
+ import { firebaseService } from '../firebase.js';
4
+ import { firekitRealtimeDB } from '../services/realtime.svelte.js';
5
+ import { browser } from '$app/environment';
6
+ import type { Snippet } from 'svelte';
7
+
8
+ /**
9
+ * Props for Node component
10
+ */
11
+ let {
12
+ path,
13
+ startWith,
14
+ children,
15
+ loading
16
+ }: {
17
+ /**
18
+ * Database reference path string (e.g. 'users/123')
19
+ */
20
+ path: string;
21
+ /**
22
+ * Initial value to use before node is fetched
23
+ */
24
+ startWith?: any;
25
+ /**
26
+ * Content to render when node is loaded
27
+ */
28
+ children: Snippet<[any, DatabaseReference, Database]>;
29
+ /**
30
+ * Content to render while loading
31
+ */
32
+ loading?: Snippet<[]>;
33
+ } = $props();
34
+
35
+ // Get Database instance and create references
36
+ let database: Database | null = $state(null);
37
+ let nodeRef: DatabaseReference | null = $state(null);
38
+ let nodeService: ReturnType<typeof firekitRealtimeDB> | null = $state(null);
39
+
40
+ // Track node state
41
+ let nodeState = $state({
42
+ loading: true,
43
+ data: startWith ?? null,
44
+ error: null as Error | null
45
+ });
46
+
47
+ // Subscribe to node changes
48
+ $effect(() => {
49
+ if (!browser) {
50
+ nodeState = {
51
+ loading: false,
52
+ data: startWith ?? null,
53
+ error: new Error('Realtime Database not available in SSR')
54
+ };
55
+ return;
56
+ }
57
+
58
+ // Initialize database and service
59
+ database = firebaseService.getDatabaseInstance();
60
+ if (!database) {
61
+ nodeState = {
62
+ loading: false,
63
+ data: null,
64
+ error: new Error('Realtime Database instance not available')
65
+ };
66
+ return;
67
+ }
68
+
69
+ // Create database reference
70
+ nodeRef = ref(database, path);
71
+
72
+ // Create node service
73
+ nodeService = firekitRealtimeDB(path, startWith);
74
+
75
+ // The service state is reactive, so we can directly access it
76
+ // The effect will re-run when the service state changes
77
+ nodeState = {
78
+ loading: nodeService.loading,
79
+ data: nodeService.data,
80
+ error: nodeService.error
81
+ };
82
+ });
83
+ </script>
84
+
85
+ {#if !browser}
86
+ {@render children(startWith ?? null, null as any, null as any)}
87
+ {:else if nodeState.loading}
88
+ {#if loading}
89
+ {@render loading()}
90
+ {:else}
91
+ <div>Loading...</div>
92
+ {/if}
93
+ {:else if nodeState.error}
94
+ <div class="error">Error: {nodeState.error.message}</div>
95
+ {:else}
96
+ {@render children(nodeState.data, nodeRef!, database!)}
97
+ {/if}
@@ -0,0 +1,23 @@
1
+ import { type DatabaseReference, type Database } from 'firebase/database';
2
+ import type { Snippet } from 'svelte';
3
+ type $$ComponentProps = {
4
+ /**
5
+ * Database reference path string (e.g. 'users/123')
6
+ */
7
+ path: string;
8
+ /**
9
+ * Initial value to use before node is fetched
10
+ */
11
+ startWith?: any;
12
+ /**
13
+ * Content to render when node is loaded
14
+ */
15
+ children: Snippet<[any, DatabaseReference, Database]>;
16
+ /**
17
+ * Content to render while loading
18
+ */
19
+ loading?: Snippet<[]>;
20
+ };
21
+ declare const Node: import("svelte").Component<$$ComponentProps, {}, "">;
22
+ type Node = ReturnType<typeof Node>;
23
+ export default Node;
@@ -0,0 +1,89 @@
1
+ <script lang="ts">
2
+ import { firekitAuth } from '../services/auth.js';
3
+ import { firebaseService } from '../firebase.js';
4
+ import { goto } from '$app/navigation';
5
+ import { onDestroy } from 'svelte';
6
+ import type { UserProfile } from '../types/auth.js';
7
+ import type { Auth } from 'firebase/auth';
8
+ import type { Snippet } from 'svelte';
9
+
10
+ /**
11
+ * Props for AuthGuard component
12
+ */
13
+ let {
14
+ children,
15
+ requireAuth = true,
16
+ redirectTo = '/',
17
+ fallback
18
+ }: {
19
+ /**
20
+ * Children content to render when auth state matches requirements
21
+ */
22
+ children: Snippet<[UserProfile, Auth, () => Promise<void>]>;
23
+ /**
24
+ * Whether authentication is required to view the content
25
+ * @default true
26
+ */
27
+ requireAuth?: boolean;
28
+ /**
29
+ * Path to redirect to if auth state doesn't match requirements
30
+ * @default '/'
31
+ */
32
+ redirectTo?: string;
33
+ /**
34
+ * Fallback content to show while checking auth state
35
+ */
36
+ fallback?: Snippet<[]>;
37
+ } = $props();
38
+
39
+ // Get Firebase Auth instance
40
+ const auth = firebaseService.getAuthInstance();
41
+ if (!auth) {
42
+ throw new Error('Firebase Auth instance not available');
43
+ }
44
+
45
+ // Reactive auth state
46
+ let authState = $state(firekitAuth.getState());
47
+
48
+ // Subscribe to auth state changes
49
+ const unsubscribe = firekitAuth.onAuthStateChanged((state) => {
50
+ authState = state;
51
+ });
52
+
53
+ // Sign out function
54
+ async function signOut() {
55
+ await firekitAuth.signOut();
56
+ }
57
+
58
+ // Check if current auth state matches requirements
59
+ $effect(() => {
60
+ if (authState.loading) return;
61
+
62
+ const isAuthenticated = firekitAuth.isAuthenticated();
63
+ const shouldRedirect = requireAuth ? !isAuthenticated : isAuthenticated;
64
+
65
+ if (shouldRedirect) {
66
+ goto(redirectTo);
67
+ }
68
+ });
69
+
70
+ // Cleanup subscription on component destruction
71
+ onDestroy(() => {
72
+ unsubscribe();
73
+ });
74
+ </script>
75
+
76
+ {#if authState.loading}
77
+ {#if fallback}
78
+ {@render fallback()}
79
+ {:else}
80
+ <div class="flex items-center justify-center min-h-screen">
81
+ <div class="text-center">
82
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto"></div>
83
+ <p class="mt-2 text-gray-600">Loading...</p>
84
+ </div>
85
+ </div>
86
+ {/if}
87
+ {:else if firekitAuth.isAuthenticated() === requireAuth}
88
+ {@render children(authState.user!, auth, signOut)}
89
+ {/if}
@@ -0,0 +1,26 @@
1
+ import type { UserProfile } from '../types/auth.js';
2
+ import type { Auth } from 'firebase/auth';
3
+ import type { Snippet } from 'svelte';
4
+ type $$ComponentProps = {
5
+ /**
6
+ * Children content to render when auth state matches requirements
7
+ */
8
+ children: Snippet<[UserProfile, Auth, () => Promise<void>]>;
9
+ /**
10
+ * Whether authentication is required to view the content
11
+ * @default true
12
+ */
13
+ requireAuth?: boolean;
14
+ /**
15
+ * Path to redirect to if auth state doesn't match requirements
16
+ * @default '/'
17
+ */
18
+ redirectTo?: string;
19
+ /**
20
+ * Fallback content to show while checking auth state
21
+ */
22
+ fallback?: Snippet<[]>;
23
+ };
24
+ declare const AuthGuard: import("svelte").Component<$$ComponentProps, {}, "">;
25
+ type AuthGuard = ReturnType<typeof AuthGuard>;
26
+ export default AuthGuard;
@@ -0,0 +1,122 @@
1
+ <script lang="ts">
2
+ import { firekitAuth } from '../services/auth.js';
3
+ import { firebaseService } from '../firebase.js';
4
+ import { goto } from '$app/navigation';
5
+ import { onDestroy } from 'svelte';
6
+ import type { UserProfile } from '../types/auth.js';
7
+ import type { Auth } from 'firebase/auth';
8
+ import type { Snippet } from 'svelte';
9
+
10
+ /**
11
+ * Props for CustomGuard component
12
+ */
13
+ let {
14
+ children,
15
+ requireAuth = true,
16
+ redirectTo = '/',
17
+ fallback,
18
+ verificationChecks = []
19
+ }: {
20
+ /**
21
+ * Children content to render when all checks pass
22
+ */
23
+ children: Snippet<[UserProfile, Auth, () => Promise<void>]>;
24
+ /**
25
+ * Whether authentication is required to view the content
26
+ * @default true
27
+ */
28
+ requireAuth?: boolean;
29
+ /**
30
+ * Path to redirect to if checks fail
31
+ * @default '/'
32
+ */
33
+ redirectTo?: string;
34
+ /**
35
+ * Fallback content to show while checking auth state
36
+ */
37
+ fallback?: Snippet<[]>;
38
+ /**
39
+ * Array of verification functions that must return true to allow access
40
+ * Each function receives the user profile and auth instance
41
+ */
42
+ verificationChecks?: ((user: UserProfile, auth: Auth) => boolean | Promise<boolean>)[];
43
+ } = $props();
44
+
45
+ // Get Firebase Auth instance
46
+ const auth = firebaseService.getAuthInstance();
47
+ if (!auth) {
48
+ throw new Error('Firebase Auth instance not available');
49
+ }
50
+
51
+ // Reactive auth state
52
+ let authState = $state(firekitAuth.getState());
53
+ let verificationPassed = $state(true);
54
+ let isVerifying = $state(false);
55
+
56
+ // Subscribe to auth state changes
57
+ const unsubscribe = firekitAuth.onAuthStateChanged((state) => {
58
+ authState = state;
59
+ });
60
+
61
+ // Sign out function
62
+ async function signOut() {
63
+ await firekitAuth.signOut();
64
+ }
65
+
66
+ // Check if current auth state and verification checks pass
67
+ $effect(() => {
68
+ if (authState.loading) return;
69
+
70
+ const isAuthenticated = firekitAuth.isAuthenticated();
71
+ const shouldRedirect = requireAuth ? !isAuthenticated : isAuthenticated;
72
+
73
+ if (shouldRedirect) {
74
+ goto(redirectTo);
75
+ return;
76
+ }
77
+
78
+ // If authenticated and verification checks exist, run them
79
+ if (isAuthenticated && verificationChecks.length > 0) {
80
+ isVerifying = true;
81
+ Promise.all(verificationChecks.map((check) => check(authState.user!, auth)))
82
+ .then((results) => {
83
+ verificationPassed = results.every((result) => result === true);
84
+ if (!verificationPassed) {
85
+ goto(redirectTo);
86
+ }
87
+ })
88
+ .catch((error) => {
89
+ console.error('Verification check failed:', error);
90
+ verificationPassed = false;
91
+ goto(redirectTo);
92
+ })
93
+ .finally(() => {
94
+ isVerifying = false;
95
+ });
96
+ } else {
97
+ verificationPassed = true;
98
+ }
99
+ });
100
+
101
+ // Cleanup subscription on component destruction
102
+ onDestroy(() => {
103
+ unsubscribe();
104
+ });
105
+ </script>
106
+
107
+ {#if authState.loading || isVerifying}
108
+ {#if fallback}
109
+ {@render fallback()}
110
+ {:else}
111
+ <div class="flex items-center justify-center min-h-screen">
112
+ <div class="text-center">
113
+ <div class="animate-spin rounded-full h-8 w-8 border-b-2 border-gray-900 mx-auto"></div>
114
+ <p class="mt-2 text-gray-600">
115
+ {isVerifying ? 'Verifying access...' : 'Loading...'}
116
+ </p>
117
+ </div>
118
+ </div>
119
+ {/if}
120
+ {:else if firekitAuth.isAuthenticated() === requireAuth && verificationPassed}
121
+ {@render children(authState.user!, auth, signOut)}
122
+ {/if}