@smartnet360/svelte-components 0.0.123 → 0.0.125

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 (36) hide show
  1. package/dist/apps/antenna-tools/components/AntennaControls.svelte +71 -9
  2. package/dist/apps/antenna-tools/components/AntennaControls.svelte.d.ts +2 -0
  3. package/dist/apps/antenna-tools/components/AntennaSettingsModal.svelte +4 -174
  4. package/dist/apps/antenna-tools/components/AntennaTools.svelte +48 -82
  5. package/dist/apps/antenna-tools/components/DatabaseViewer.svelte +5 -8
  6. package/dist/apps/antenna-tools/components/MSIConverter.svelte +377 -52
  7. package/dist/apps/antenna-tools/db.js +4 -0
  8. package/dist/apps/antenna-tools/utils/db-utils.d.ts +19 -0
  9. package/dist/apps/antenna-tools/utils/db-utils.js +108 -0
  10. package/dist/apps/antenna-tools/utils/msi-parser.d.ts +35 -1
  11. package/dist/apps/antenna-tools/utils/msi-parser.js +105 -35
  12. package/dist/core/Auth/LoginForm.svelte +397 -0
  13. package/dist/core/Auth/LoginForm.svelte.d.ts +16 -0
  14. package/dist/core/Auth/auth.svelte.d.ts +22 -0
  15. package/dist/core/Auth/auth.svelte.js +184 -0
  16. package/dist/core/Auth/config.d.ts +25 -0
  17. package/dist/core/Auth/config.js +256 -0
  18. package/dist/core/Auth/index.d.ts +4 -0
  19. package/dist/core/Auth/index.js +5 -0
  20. package/dist/core/Auth/types.d.ts +140 -0
  21. package/dist/core/Auth/types.js +2 -0
  22. package/dist/core/Benchmark/Benchmark.svelte +662 -0
  23. package/dist/core/Benchmark/Benchmark.svelte.d.ts +3 -0
  24. package/dist/core/Benchmark/benchmark-utils.d.ts +48 -0
  25. package/dist/core/Benchmark/benchmark-utils.js +80 -0
  26. package/dist/core/Benchmark/index.d.ts +2 -0
  27. package/dist/core/Benchmark/index.js +3 -0
  28. package/dist/core/LandingPage/App.svelte +102 -0
  29. package/dist/core/LandingPage/App.svelte.d.ts +20 -0
  30. package/dist/core/LandingPage/LandingPage.svelte +480 -0
  31. package/dist/core/LandingPage/LandingPage.svelte.d.ts +21 -0
  32. package/dist/core/LandingPage/index.d.ts +2 -0
  33. package/dist/core/LandingPage/index.js +3 -0
  34. package/dist/core/index.d.ts +3 -0
  35. package/dist/core/index.js +6 -0
  36. package/package.json +1 -1
@@ -24,12 +24,46 @@ export interface ParseProgress {
24
24
  directoriesScanned: number;
25
25
  /** Number of MSI/PNT files found */
26
26
  antennaFilesFound: number;
27
+ /** Number of files that failed to parse */
28
+ failedFiles: number;
29
+ }
30
+ /**
31
+ * Error information for failed file/folder parsing
32
+ */
33
+ export interface ParseError {
34
+ /** Path to the file or folder that failed */
35
+ path: string;
36
+ /** Error message */
37
+ message: string;
38
+ /** Type of error */
39
+ type: 'file' | 'folder';
40
+ }
41
+ /**
42
+ * Result of folder parsing including errors
43
+ */
44
+ export interface ParseFolderResult {
45
+ /** Successfully parsed antennas */
46
+ antennas: RawAntenna[];
47
+ /** Errors encountered during parsing */
48
+ errors: ParseError[];
49
+ /** Total files attempted */
50
+ totalFilesAttempted: number;
51
+ /** Total directories scanned */
52
+ totalDirectoriesScanned: number;
27
53
  }
28
54
  /**
29
55
  * Parse a folder of MSI files recursively
30
56
  * @param directoryHandle - File system directory handle
31
57
  * @param recursive - Whether to process subdirectories
32
58
  * @param onProgress - Optional callback for progress updates
33
- * @returns Array of parsed antennas
59
+ * @returns Array of parsed antennas (for backward compatibility)
34
60
  */
35
61
  export declare function parseFolder(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean, onProgress?: (progress: ParseProgress) => void): Promise<RawAntenna[]>;
62
+ /**
63
+ * Parse a folder of MSI files recursively with detailed error reporting
64
+ * @param directoryHandle - File system directory handle
65
+ * @param recursive - Whether to process subdirectories
66
+ * @param onProgress - Optional callback for progress updates
67
+ * @returns Object containing parsed antennas and any errors encountered
68
+ */
69
+ export declare function parseFolderWithErrors(directoryHandle: FileSystemDirectoryHandle, recursive?: boolean, onProgress?: (progress: ParseProgress) => void): Promise<ParseFolderResult>;
@@ -174,55 +174,125 @@ export async function parseMSIFile(file) {
174
174
  * @param directoryHandle - File system directory handle
175
175
  * @param recursive - Whether to process subdirectories
176
176
  * @param onProgress - Optional callback for progress updates
177
- * @returns Array of parsed antennas
177
+ * @returns Array of parsed antennas (for backward compatibility)
178
178
  */
179
179
  export async function parseFolder(directoryHandle, recursive = true, onProgress) {
180
+ const result = await parseFolderWithErrors(directoryHandle, recursive, onProgress);
181
+ return result.antennas;
182
+ }
183
+ /**
184
+ * Parse a folder of MSI files recursively with detailed error reporting
185
+ * @param directoryHandle - File system directory handle
186
+ * @param recursive - Whether to process subdirectories
187
+ * @param onProgress - Optional callback for progress updates
188
+ * @returns Object containing parsed antennas and any errors encountered
189
+ */
190
+ export async function parseFolderWithErrors(directoryHandle, recursive = true, onProgress) {
180
191
  const antennas = [];
192
+ const errors = [];
181
193
  let filesProcessed = 0;
182
194
  let directoriesScanned = 0;
195
+ let failedFiles = 0;
183
196
  // Process directory recursively
184
197
  async function processDirectory(dirHandle, path = '') {
185
198
  directoriesScanned++;
186
- const iterator = dirHandle.values();
187
- for await (const entry of iterator) {
188
- if (entry.kind === 'file') {
189
- try {
190
- // Type assertion to access getFile method
191
- const fileHandle = entry;
192
- const file = await fileHandle.getFile();
193
- const ext = file.name.split('.').pop()?.toLowerCase();
194
- if (ext === 'msi' || ext === 'pnt') {
195
- filesProcessed++;
196
- const currentPath = path ? `${path}/${file.name}` : file.name;
197
- // Report progress
198
- onProgress?.({
199
- currentFile: currentPath,
200
- filesProcessed,
201
- directoriesScanned,
202
- antennaFilesFound: antennas.length
203
- });
204
- try {
205
- const antenna = await parseMSIFile(file);
206
- // Add the path info to help identify where the file was found
207
- antenna.sourcePath = currentPath;
208
- antennas.push(antenna);
209
- }
210
- catch (error) {
211
- console.error(`Error parsing file ${currentPath}:`, error);
199
+ let iterator;
200
+ try {
201
+ iterator = dirHandle.values();
202
+ }
203
+ catch (error) {
204
+ const dirPath = path || dirHandle.name;
205
+ errors.push({
206
+ path: dirPath,
207
+ message: error instanceof Error ? error.message : 'Failed to read directory',
208
+ type: 'folder'
209
+ });
210
+ console.error(`Error reading directory ${dirPath}:`, error);
211
+ return; // Skip this directory but continue with others
212
+ }
213
+ try {
214
+ for await (const entry of iterator) {
215
+ if (entry.kind === 'file') {
216
+ try {
217
+ // Type assertion to access getFile method
218
+ const fileHandle = entry;
219
+ const file = await fileHandle.getFile();
220
+ const ext = file.name.split('.').pop()?.toLowerCase();
221
+ if (ext === 'msi' || ext === 'pnt') {
222
+ filesProcessed++;
223
+ const currentPath = path ? `${path}/${file.name}` : file.name;
224
+ // Report progress
225
+ onProgress?.({
226
+ currentFile: currentPath,
227
+ filesProcessed,
228
+ directoriesScanned,
229
+ antennaFilesFound: antennas.length,
230
+ failedFiles
231
+ });
232
+ try {
233
+ const antenna = await parseMSIFile(file);
234
+ // Add the path info to help identify where the file was found
235
+ antenna.sourcePath = currentPath;
236
+ antennas.push(antenna);
237
+ }
238
+ catch (parseError) {
239
+ failedFiles++;
240
+ errors.push({
241
+ path: currentPath,
242
+ message: parseError instanceof Error ? parseError.message : 'Failed to parse file',
243
+ type: 'file'
244
+ });
245
+ console.error(`Error parsing file ${currentPath}:`, parseError);
246
+ // Continue with next file
247
+ }
212
248
  }
213
249
  }
250
+ catch (fileError) {
251
+ const filePath = path ? `${path}/${entry.name}` : entry.name;
252
+ failedFiles++;
253
+ errors.push({
254
+ path: filePath,
255
+ message: fileError instanceof Error ? fileError.message : 'Failed to access file',
256
+ type: 'file'
257
+ });
258
+ console.error(`Error accessing file ${filePath}:`, fileError);
259
+ // Continue with next file
260
+ }
214
261
  }
215
- catch (error) {
216
- console.error('Error accessing file:', error);
262
+ else if (entry.kind === 'directory' && recursive) {
263
+ // Process subdirectory if recursive flag is true
264
+ const subDirPath = path ? `${path}/${entry.name}` : entry.name;
265
+ try {
266
+ await processDirectory(entry, subDirPath);
267
+ }
268
+ catch (subDirError) {
269
+ errors.push({
270
+ path: subDirPath,
271
+ message: subDirError instanceof Error ? subDirError.message : 'Failed to process subdirectory',
272
+ type: 'folder'
273
+ });
274
+ console.error(`Error processing subdirectory ${subDirPath}:`, subDirError);
275
+ // Continue with next entry
276
+ }
217
277
  }
218
278
  }
219
- else if (entry.kind === 'directory' && recursive) {
220
- // Process subdirectory if recursive flag is true
221
- const subDirPath = path ? `${path}/${entry.name}` : entry.name;
222
- await processDirectory(entry, subDirPath);
223
- }
279
+ }
280
+ catch (iterError) {
281
+ const dirPath = path || dirHandle.name;
282
+ errors.push({
283
+ path: dirPath,
284
+ message: iterError instanceof Error ? iterError.message : 'Error iterating directory contents',
285
+ type: 'folder'
286
+ });
287
+ console.error(`Error iterating directory ${dirPath}:`, iterError);
288
+ // Continue - the directory iteration failed but we can still return what we have
224
289
  }
225
290
  }
226
291
  await processDirectory(directoryHandle);
227
- return antennas;
292
+ return {
293
+ antennas,
294
+ errors,
295
+ totalFilesAttempted: filesProcessed,
296
+ totalDirectoriesScanned: directoriesScanned
297
+ };
228
298
  }
@@ -0,0 +1,397 @@
1
+ <script lang="ts">
2
+ import type { AuthState } from './auth.svelte.js';
3
+
4
+ interface Props {
5
+ /** Auth state instance */
6
+ auth: AuthState;
7
+ /** Title for login form */
8
+ title?: string;
9
+ /** Subtitle/description */
10
+ subtitle?: string;
11
+ /** Logo URL (optional) */
12
+ logo?: string;
13
+ /** Callback on successful login */
14
+ onLogin?: () => void;
15
+ }
16
+
17
+ let {
18
+ auth,
19
+ title = 'Welcome',
20
+ subtitle = 'Sign in to access your tools',
21
+ logo,
22
+ onLogin
23
+ }: Props = $props();
24
+
25
+ // Form state
26
+ let username = $state('');
27
+ let password = $state('');
28
+ let agreedToMonitoring = $state(false);
29
+ let showPassword = $state(false);
30
+
31
+ // Form validation
32
+ const isFormValid = $derived(
33
+ username.trim().length > 0 &&
34
+ password.length > 0 &&
35
+ agreedToMonitoring
36
+ );
37
+
38
+ async function handleSubmit(e: SubmitEvent) {
39
+ e.preventDefault();
40
+
41
+ if (!isFormValid) return;
42
+
43
+ const success = await auth.login(username.trim(), password, agreedToMonitoring);
44
+
45
+ if (success) {
46
+ username = '';
47
+ password = '';
48
+ agreedToMonitoring = false;
49
+ onLogin?.();
50
+ }
51
+ }
52
+
53
+ function togglePassword() {
54
+ showPassword = !showPassword;
55
+ }
56
+ </script>
57
+
58
+ <div class="login-page">
59
+ <div class="login-card">
60
+ <!-- Header -->
61
+ <div class="login-header">
62
+ {#if logo}
63
+ <img src={logo} alt="Logo" class="login-logo" />
64
+ {:else}
65
+ <div class="login-icon">
66
+ <i class="bi bi-broadcast-pin"></i>
67
+ </div>
68
+ {/if}
69
+ <h1>{title}</h1>
70
+ <p>{subtitle}</p>
71
+ </div>
72
+
73
+ <!-- Error -->
74
+ {#if auth.error}
75
+ <div class="login-error">
76
+ <i class="bi bi-exclamation-circle"></i>
77
+ <span>{auth.error}</span>
78
+ <button type="button" onclick={() => auth.clearError()} aria-label="Dismiss error">
79
+ <i class="bi bi-x"></i>
80
+ </button>
81
+ </div>
82
+ {/if}
83
+
84
+ <!-- Form -->
85
+ <form onsubmit={handleSubmit} class="login-form">
86
+ <div class="form-field">
87
+ <label for="username">Username</label>
88
+ <div class="input-wrapper">
89
+ <i class="bi bi-person"></i>
90
+ <input
91
+ type="text"
92
+ id="username"
93
+ bind:value={username}
94
+ placeholder="Enter username"
95
+ autocomplete="username"
96
+ disabled={auth.isLoading}
97
+ />
98
+ </div>
99
+ </div>
100
+
101
+ <div class="form-field">
102
+ <label for="password">Password</label>
103
+ <div class="input-wrapper">
104
+ <i class="bi bi-lock"></i>
105
+ <input
106
+ type={showPassword ? 'text' : 'password'}
107
+ id="password"
108
+ bind:value={password}
109
+ placeholder="Enter password"
110
+ autocomplete="current-password"
111
+ disabled={auth.isLoading}
112
+ />
113
+ <button
114
+ type="button"
115
+ class="toggle-password"
116
+ onclick={togglePassword}
117
+ tabindex={-1}
118
+ aria-label={showPassword ? 'Hide password' : 'Show password'}
119
+ >
120
+ <i class="bi {showPassword ? 'bi-eye-slash' : 'bi-eye'}"></i>
121
+ </button>
122
+ </div>
123
+ </div>
124
+
125
+ <label class="checkbox-field">
126
+ <input
127
+ type="checkbox"
128
+ bind:checked={agreedToMonitoring}
129
+ disabled={auth.isLoading}
130
+ />
131
+ <span class="checkmark"></span>
132
+ <span class="checkbox-text">
133
+ I agree to activity monitoring for security purposes
134
+ </span>
135
+ </label>
136
+
137
+ <button
138
+ type="submit"
139
+ class="login-button"
140
+ disabled={!isFormValid || auth.isLoading}
141
+ >
142
+ {#if auth.isLoading}
143
+ <span class="spinner"></span>
144
+ Signing in...
145
+ {:else}
146
+ Sign In
147
+ <i class="bi bi-arrow-right"></i>
148
+ {/if}
149
+ </button>
150
+ </form>
151
+
152
+ <div class="login-footer">
153
+ <i class="bi bi-shield-check"></i>
154
+ LDAP Authentication
155
+ </div>
156
+ </div>
157
+ </div>
158
+
159
+ <style>
160
+ .login-page {
161
+ min-height: 100vh;
162
+ display: flex;
163
+ align-items: center;
164
+ justify-content: center;
165
+ padding: 1.5rem;
166
+ background: linear-gradient(135deg, #0f172a 0%, #1e293b 100%);
167
+ }
168
+
169
+ .login-card {
170
+ width: 100%;
171
+ max-width: 380px;
172
+ background: #fff;
173
+ border-radius: 16px;
174
+ box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.4);
175
+ overflow: hidden;
176
+ }
177
+
178
+ .login-header {
179
+ text-align: center;
180
+ padding: 2.5rem 2rem 1.5rem;
181
+ background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
182
+ color: #fff;
183
+ }
184
+
185
+ .login-logo {
186
+ height: 48px;
187
+ margin-bottom: 1rem;
188
+ }
189
+
190
+ .login-icon {
191
+ font-size: 2.5rem;
192
+ margin-bottom: 0.75rem;
193
+ }
194
+
195
+ .login-header h1 {
196
+ font-size: 1.5rem;
197
+ font-weight: 600;
198
+ margin: 0 0 0.25rem;
199
+ }
200
+
201
+ .login-header p {
202
+ font-size: 0.875rem;
203
+ opacity: 0.85;
204
+ margin: 0;
205
+ }
206
+
207
+ .login-error {
208
+ display: flex;
209
+ align-items: center;
210
+ gap: 0.5rem;
211
+ margin: 1rem 1.5rem 0;
212
+ padding: 0.75rem 1rem;
213
+ background: #fef2f2;
214
+ border: 1px solid #fecaca;
215
+ border-radius: 8px;
216
+ color: #dc2626;
217
+ font-size: 0.875rem;
218
+ }
219
+
220
+ .login-error button {
221
+ margin-left: auto;
222
+ background: none;
223
+ border: none;
224
+ color: inherit;
225
+ cursor: pointer;
226
+ padding: 0;
227
+ font-size: 1.25rem;
228
+ line-height: 1;
229
+ }
230
+
231
+ .login-form {
232
+ padding: 1.5rem 2rem 2rem;
233
+ }
234
+
235
+ .form-field {
236
+ margin-bottom: 1.25rem;
237
+ }
238
+
239
+ .form-field label {
240
+ display: block;
241
+ font-size: 0.8125rem;
242
+ font-weight: 500;
243
+ color: #374151;
244
+ margin-bottom: 0.5rem;
245
+ }
246
+
247
+ .input-wrapper {
248
+ position: relative;
249
+ display: flex;
250
+ align-items: center;
251
+ }
252
+
253
+ .input-wrapper > i:first-child {
254
+ position: absolute;
255
+ left: 0.875rem;
256
+ color: #9ca3af;
257
+ font-size: 1rem;
258
+ }
259
+
260
+ .input-wrapper input {
261
+ width: 100%;
262
+ padding: 0.75rem 0.875rem 0.75rem 2.5rem;
263
+ border: 1px solid #e5e7eb;
264
+ border-radius: 8px;
265
+ font-size: 0.9375rem;
266
+ transition: border-color 0.15s, box-shadow 0.15s;
267
+ }
268
+
269
+ .input-wrapper input:focus {
270
+ outline: none;
271
+ border-color: #3b82f6;
272
+ box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.15);
273
+ }
274
+
275
+ .input-wrapper input:disabled {
276
+ background: #f9fafb;
277
+ cursor: not-allowed;
278
+ }
279
+
280
+ .toggle-password {
281
+ position: absolute;
282
+ right: 0.5rem;
283
+ background: none;
284
+ border: none;
285
+ color: #9ca3af;
286
+ cursor: pointer;
287
+ padding: 0.5rem;
288
+ font-size: 1rem;
289
+ }
290
+
291
+ .toggle-password:hover {
292
+ color: #6b7280;
293
+ }
294
+
295
+ .checkbox-field {
296
+ display: flex;
297
+ align-items: flex-start;
298
+ gap: 0.625rem;
299
+ cursor: pointer;
300
+ margin-bottom: 1.5rem;
301
+ }
302
+
303
+ .checkbox-field input {
304
+ position: absolute;
305
+ opacity: 0;
306
+ pointer-events: none;
307
+ }
308
+
309
+ .checkmark {
310
+ flex-shrink: 0;
311
+ width: 18px;
312
+ height: 18px;
313
+ border: 2px solid #d1d5db;
314
+ border-radius: 4px;
315
+ transition: all 0.15s;
316
+ display: flex;
317
+ align-items: center;
318
+ justify-content: center;
319
+ }
320
+
321
+ .checkbox-field input:checked + .checkmark {
322
+ background: #3b82f6;
323
+ border-color: #3b82f6;
324
+ }
325
+
326
+ .checkbox-field input:checked + .checkmark::after {
327
+ content: '';
328
+ width: 5px;
329
+ height: 9px;
330
+ border: solid #fff;
331
+ border-width: 0 2px 2px 0;
332
+ transform: rotate(45deg);
333
+ margin-bottom: 2px;
334
+ }
335
+
336
+ .checkbox-text {
337
+ font-size: 0.8125rem;
338
+ color: #4b5563;
339
+ line-height: 1.4;
340
+ }
341
+
342
+ .login-button {
343
+ width: 100%;
344
+ padding: 0.875rem 1.5rem;
345
+ background: linear-gradient(135deg, #3b82f6 0%, #1d4ed8 100%);
346
+ color: #fff;
347
+ border: none;
348
+ border-radius: 8px;
349
+ font-size: 0.9375rem;
350
+ font-weight: 500;
351
+ cursor: pointer;
352
+ display: flex;
353
+ align-items: center;
354
+ justify-content: center;
355
+ gap: 0.5rem;
356
+ transition: opacity 0.15s, transform 0.15s;
357
+ }
358
+
359
+ .login-button:hover:not(:disabled) {
360
+ opacity: 0.9;
361
+ }
362
+
363
+ .login-button:active:not(:disabled) {
364
+ transform: scale(0.98);
365
+ }
366
+
367
+ .login-button:disabled {
368
+ opacity: 0.5;
369
+ cursor: not-allowed;
370
+ }
371
+
372
+ .spinner {
373
+ width: 16px;
374
+ height: 16px;
375
+ border: 2px solid rgba(255, 255, 255, 0.3);
376
+ border-top-color: #fff;
377
+ border-radius: 50%;
378
+ animation: spin 0.8s linear infinite;
379
+ }
380
+
381
+ @keyframes spin {
382
+ to { transform: rotate(360deg); }
383
+ }
384
+
385
+ .login-footer {
386
+ text-align: center;
387
+ padding: 1rem;
388
+ background: #f8fafc;
389
+ border-top: 1px solid #e5e7eb;
390
+ font-size: 0.75rem;
391
+ color: #64748b;
392
+ display: flex;
393
+ align-items: center;
394
+ justify-content: center;
395
+ gap: 0.375rem;
396
+ }
397
+ </style>
@@ -0,0 +1,16 @@
1
+ import type { AuthState } from './auth.svelte.js';
2
+ interface Props {
3
+ /** Auth state instance */
4
+ auth: AuthState;
5
+ /** Title for login form */
6
+ title?: string;
7
+ /** Subtitle/description */
8
+ subtitle?: string;
9
+ /** Logo URL (optional) */
10
+ logo?: string;
11
+ /** Callback on successful login */
12
+ onLogin?: () => void;
13
+ }
14
+ declare const LoginForm: import("svelte").Component<Props, {}, "">;
15
+ type LoginForm = ReturnType<typeof LoginForm>;
16
+ export default LoginForm;
@@ -0,0 +1,22 @@
1
+ import type { UserSession, User, FeatureAccess, AuthConfig } from './types.js';
2
+ /**
3
+ * Create auth state store with Svelte 5 runes
4
+ * This must be called within a component context or .svelte.ts file
5
+ */
6
+ export declare function createAuthState(config?: Partial<AuthConfig>): {
7
+ readonly session: UserSession | null;
8
+ readonly isLoading: boolean;
9
+ readonly error: string | null;
10
+ readonly initialized: boolean;
11
+ readonly isAuthenticated: boolean;
12
+ readonly user: User | null;
13
+ readonly permissions: FeatureAccess[];
14
+ readonly token: string | null;
15
+ login: (username: string, password: string, agreedToMonitoring: boolean) => Promise<boolean>;
16
+ loginDev: (devUser: User, devPermissions: FeatureAccess[]) => void;
17
+ logout: () => Promise<void>;
18
+ hasPermission: (featureId: string, level?: "view" | "edit" | "admin") => boolean;
19
+ canAccessRoute: (route: string, requiredPermission?: string) => boolean;
20
+ clearError: () => void;
21
+ };
22
+ export type AuthState = ReturnType<typeof createAuthState>;