@vaiftech/react 1.0.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.
@@ -0,0 +1,1609 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+ import { VaifClientConfig, User, VaifClient, AuthResponse, OAuthProviderType, MFAMethod, MFASetupResponse, QueryOptions, PaginatedResult, WhereFilter, DbOperation, DbChangeEvent, PresenceState, PresenceEntry, ConnectionState, UploadOptions, UploadResult, FileMetadata, InvokeResult, VaifFunction } from '@vaif/client';
4
+ export { AuthResponse, ConnectionState, DbChangeEvent, DbOperation, FileMetadata, InvokeResult, OrderByClause, PresenceState, QueryOptions, UploadResult, User, VaifClient, VaifClientConfig, VaifFunction, WhereFilter } from '@vaif/client';
5
+
6
+ interface VaifContextValue {
7
+ /** The VAIF client instance */
8
+ client: VaifClient;
9
+ /** Current authenticated user (null if not authenticated) */
10
+ user: User | null;
11
+ /** Current access token */
12
+ token: string | null;
13
+ /** Whether authentication is being checked */
14
+ isLoading: boolean;
15
+ /** Whether user is authenticated */
16
+ isAuthenticated: boolean;
17
+ /** Sign in with email/password */
18
+ signIn: (email: string, password: string) => Promise<AuthResponse>;
19
+ /** Sign up with email/password */
20
+ signUp: (email: string, password: string, metadata?: Record<string, unknown>) => Promise<AuthResponse>;
21
+ /** Sign out the current user */
22
+ signOut: () => Promise<void>;
23
+ /** Refresh the session */
24
+ refreshSession: () => Promise<void>;
25
+ /** Update the current user in state */
26
+ updateUser: (updates: Partial<User>) => void;
27
+ }
28
+ interface VaifProviderProps {
29
+ /** VAIF client configuration */
30
+ config: VaifClientConfig;
31
+ /** Child components */
32
+ children: ReactNode;
33
+ /** Callback when auth state changes */
34
+ onAuthStateChange?: (user: User | null) => void;
35
+ /** Auto refresh session before expiry (default: true) */
36
+ autoRefreshSession?: boolean;
37
+ /** Storage key for session persistence (default: 'vaif-auth') */
38
+ storageKey?: string;
39
+ }
40
+ declare function VaifProvider({ config, children, onAuthStateChange, autoRefreshSession, storageKey, }: VaifProviderProps): react_jsx_runtime.JSX.Element;
41
+ /**
42
+ * Access the VAIF context
43
+ *
44
+ * @throws Error if used outside VaifProvider
45
+ */
46
+ declare function useVaif(): VaifContextValue;
47
+ /**
48
+ * Access only the VAIF client (lightweight alternative to useVaif)
49
+ */
50
+ declare function useVaifClient(): VaifClient;
51
+
52
+ interface UseAuthReturn {
53
+ /** Current user */
54
+ user: User | null;
55
+ /** Current access token */
56
+ token: string | null;
57
+ /** Whether auth is loading */
58
+ isLoading: boolean;
59
+ /** Whether user is authenticated */
60
+ isAuthenticated: boolean;
61
+ /** Sign in with email/password */
62
+ signIn: (email: string, password: string) => Promise<AuthResponse>;
63
+ /** Sign up with email/password */
64
+ signUp: (email: string, password: string, metadata?: Record<string, unknown>) => Promise<AuthResponse>;
65
+ /** Sign out */
66
+ signOut: () => Promise<void>;
67
+ /** Refresh session */
68
+ refreshSession: () => Promise<void>;
69
+ }
70
+ interface UsePasswordResetReturn {
71
+ /** Request password reset email */
72
+ requestReset: (email: string) => Promise<void>;
73
+ /** Confirm password reset with token */
74
+ confirmReset: (token: string, newPassword: string) => Promise<void>;
75
+ /** Loading state */
76
+ isLoading: boolean;
77
+ /** Error state */
78
+ error: Error | null;
79
+ /** Whether reset was requested */
80
+ isRequested: boolean;
81
+ /** Whether reset was confirmed */
82
+ isConfirmed: boolean;
83
+ }
84
+ interface UseEmailVerificationReturn {
85
+ /** Request verification email */
86
+ requestVerification: (email: string) => Promise<void>;
87
+ /** Confirm email with token */
88
+ confirmVerification: (token: string) => Promise<void>;
89
+ /** Loading state */
90
+ isLoading: boolean;
91
+ /** Error state */
92
+ error: Error | null;
93
+ /** Whether verification was sent */
94
+ isSent: boolean;
95
+ /** Whether email was verified */
96
+ isVerified: boolean;
97
+ }
98
+ interface UseMagicLinkReturn {
99
+ /** Send magic link */
100
+ sendMagicLink: (email: string) => Promise<void>;
101
+ /** Verify magic link token */
102
+ verifyMagicLink: (token: string) => Promise<AuthResponse>;
103
+ /** Loading state */
104
+ isLoading: boolean;
105
+ /** Error state */
106
+ error: Error | null;
107
+ /** Whether link was sent */
108
+ isSent: boolean;
109
+ }
110
+ interface UseOAuthReturn {
111
+ /** Start OAuth flow */
112
+ signInWithOAuth: (provider: OAuthProviderType, redirectTo?: string) => Promise<void>;
113
+ /** Loading state */
114
+ isLoading: boolean;
115
+ /** Error state */
116
+ error: Error | null;
117
+ }
118
+ interface UseMFAReturn {
119
+ /** Set up MFA */
120
+ setup: (method: MFAMethod) => Promise<MFASetupResponse>;
121
+ /** Verify MFA with token and code */
122
+ verify: (mfaToken: string, code: string) => Promise<AuthResponse>;
123
+ /** Disable MFA */
124
+ disable: (code: string) => Promise<void>;
125
+ /** Loading state */
126
+ isLoading: boolean;
127
+ /** Error state */
128
+ error: Error | null;
129
+ }
130
+ /**
131
+ * Main authentication hook
132
+ *
133
+ * @example
134
+ * ```tsx
135
+ * function LoginForm() {
136
+ * const { signIn, isLoading, user } = useAuth();
137
+ *
138
+ * const handleSubmit = async (e: FormEvent) => {
139
+ * e.preventDefault();
140
+ * await signIn(email, password);
141
+ * };
142
+ *
143
+ * if (user) {
144
+ * return <div>Welcome, {user.email}!</div>;
145
+ * }
146
+ *
147
+ * return <form onSubmit={handleSubmit}>...</form>;
148
+ * }
149
+ * ```
150
+ */
151
+ declare function useAuth(): UseAuthReturn;
152
+ /**
153
+ * Hook for getting the current user only
154
+ *
155
+ * @example
156
+ * ```tsx
157
+ * function UserAvatar() {
158
+ * const user = useUser();
159
+ * if (!user) return null;
160
+ * return <img src={user.avatarUrl} alt={user.name} />;
161
+ * }
162
+ * ```
163
+ */
164
+ declare function useUser(): User | null;
165
+ /**
166
+ * Hook for getting the current access token
167
+ */
168
+ declare function useToken(): string | null;
169
+ /**
170
+ * Password reset hook
171
+ *
172
+ * @example
173
+ * ```tsx
174
+ * function ForgotPassword() {
175
+ * const { requestReset, confirmReset, isLoading, isRequested } = usePasswordReset();
176
+ *
177
+ * if (!isRequested) {
178
+ * return <button onClick={() => requestReset(email)}>Send Reset Link</button>;
179
+ * }
180
+ *
181
+ * return (
182
+ * <form onSubmit={() => confirmReset(email, code, newPassword)}>
183
+ * <input placeholder="Enter code" />
184
+ * <input type="password" placeholder="New password" />
185
+ * <button type="submit">Reset Password</button>
186
+ * </form>
187
+ * );
188
+ * }
189
+ * ```
190
+ */
191
+ declare function usePasswordReset(): UsePasswordResetReturn;
192
+ /**
193
+ * Email verification hook
194
+ *
195
+ * @example
196
+ * ```tsx
197
+ * function VerifyEmail() {
198
+ * const { requestVerification, confirmVerification, isSent, isVerified } = useEmailVerification();
199
+ *
200
+ * if (isVerified) {
201
+ * return <div>Email verified successfully!</div>;
202
+ * }
203
+ *
204
+ * if (isSent) {
205
+ * return (
206
+ * <form onSubmit={() => confirmVerification(email, code)}>
207
+ * <input placeholder="Enter verification code" />
208
+ * <button type="submit">Verify</button>
209
+ * </form>
210
+ * );
211
+ * }
212
+ *
213
+ * return <button onClick={() => requestVerification(email)}>Send Verification</button>;
214
+ * }
215
+ * ```
216
+ */
217
+ declare function useEmailVerification(): UseEmailVerificationReturn;
218
+ /**
219
+ * Magic link authentication hook
220
+ *
221
+ * @example
222
+ * ```tsx
223
+ * function MagicLinkLogin() {
224
+ * const { sendMagicLink, isSent, isLoading } = useMagicLink();
225
+ *
226
+ * if (isSent) {
227
+ * return <div>Check your email for the login link!</div>;
228
+ * }
229
+ *
230
+ * return (
231
+ * <button onClick={() => sendMagicLink(email)} disabled={isLoading}>
232
+ * {isLoading ? 'Sending...' : 'Send Magic Link'}
233
+ * </button>
234
+ * );
235
+ * }
236
+ * ```
237
+ */
238
+ declare function useMagicLink(): UseMagicLinkReturn;
239
+ /**
240
+ * OAuth authentication hook
241
+ *
242
+ * @example
243
+ * ```tsx
244
+ * function OAuthButtons() {
245
+ * const { signInWithOAuth, isLoading } = useOAuth();
246
+ *
247
+ * return (
248
+ * <div>
249
+ * <button onClick={() => signInWithOAuth('google')} disabled={isLoading}>
250
+ * Sign in with Google
251
+ * </button>
252
+ * <button onClick={() => signInWithOAuth('github')} disabled={isLoading}>
253
+ * Sign in with GitHub
254
+ * </button>
255
+ * </div>
256
+ * );
257
+ * }
258
+ * ```
259
+ */
260
+ declare function useOAuth(): UseOAuthReturn;
261
+ /**
262
+ * Multi-factor authentication hook
263
+ *
264
+ * @example
265
+ * ```tsx
266
+ * function MFASetup() {
267
+ * const { setup, verify, isLoading } = useMFA();
268
+ * const [qrCode, setQrCode] = useState<string | null>(null);
269
+ * const [mfaToken, setMfaToken] = useState<string | null>(null);
270
+ *
271
+ * const handleSetup = async () => {
272
+ * const result = await setup('totp');
273
+ * setQrCode(result.qrCode);
274
+ * };
275
+ *
276
+ * const handleVerify = async (code: string) => {
277
+ * if (mfaToken) {
278
+ * await verify(mfaToken, code);
279
+ * alert('MFA verified!');
280
+ * }
281
+ * };
282
+ *
283
+ * return (
284
+ * <div>
285
+ * {qrCode ? (
286
+ * <>
287
+ * <img src={qrCode} alt="Scan with authenticator app" />
288
+ * <input placeholder="Enter code" />
289
+ * <button onClick={() => handleVerify(code)}>Verify</button>
290
+ * </>
291
+ * ) : (
292
+ * <button onClick={handleSetup}>Enable 2FA</button>
293
+ * )}
294
+ * </div>
295
+ * );
296
+ * }
297
+ * ```
298
+ */
299
+ declare function useMFA(): UseMFAReturn;
300
+
301
+ type QueryStatus = "idle" | "loading" | "success" | "error";
302
+ interface UseQueryOptions<T extends Record<string, unknown>> extends Partial<QueryOptions<T>> {
303
+ /** Enable/disable the query */
304
+ enabled?: boolean;
305
+ /** Refetch on window focus */
306
+ refetchOnWindowFocus?: boolean;
307
+ /** Refetch interval in ms (0 = disabled) */
308
+ refetchInterval?: number;
309
+ /** Keep previous data while fetching new */
310
+ keepPreviousData?: boolean;
311
+ /** Callback on success */
312
+ onSuccess?: (data: T[]) => void;
313
+ /** Callback on error */
314
+ onError?: (error: Error) => void;
315
+ /** Initial data */
316
+ initialData?: T[];
317
+ /** Stale time in ms (default: 0) */
318
+ staleTime?: number;
319
+ }
320
+ interface UseQueryReturn<T> {
321
+ /** Query result data */
322
+ data: T[];
323
+ /** Loading state */
324
+ isLoading: boolean;
325
+ /** Fetching state (loading or refetching) */
326
+ isFetching: boolean;
327
+ /** Error state */
328
+ error: Error | null;
329
+ /** Query status */
330
+ status: QueryStatus;
331
+ /** Refetch function */
332
+ refetch: () => Promise<void>;
333
+ /** Whether data is stale */
334
+ isStale: boolean;
335
+ /** Whether query is enabled */
336
+ isEnabled: boolean;
337
+ }
338
+ interface UseQueryFirstReturn<T> {
339
+ /** Query result (single item) */
340
+ data: T | null;
341
+ /** Loading state */
342
+ isLoading: boolean;
343
+ /** Error state */
344
+ error: Error | null;
345
+ /** Not found state */
346
+ isNotFound: boolean;
347
+ /** Refetch function */
348
+ refetch: () => Promise<void>;
349
+ }
350
+ interface UsePaginatedQueryOptions<T extends Record<string, unknown>> extends Partial<QueryOptions<T>> {
351
+ /** Page size */
352
+ pageSize?: number;
353
+ /** Initial page */
354
+ initialPage?: number;
355
+ /** Callback on success */
356
+ onSuccess?: (result: PaginatedResult<T>) => void;
357
+ /** Callback on error */
358
+ onError?: (error: Error) => void;
359
+ }
360
+ interface UsePaginatedQueryReturn<T> {
361
+ /** Current page data */
362
+ data: T[];
363
+ /** Current page number (1-indexed) */
364
+ page: number;
365
+ /** Page size */
366
+ pageSize: number;
367
+ /** Total items count */
368
+ totalCount: number;
369
+ /** Total pages count */
370
+ totalPages: number;
371
+ /** Whether there's a next page */
372
+ hasNextPage: boolean;
373
+ /** Whether there's a previous page */
374
+ hasPrevPage: boolean;
375
+ /** Loading state */
376
+ isLoading: boolean;
377
+ /** Error state */
378
+ error: Error | null;
379
+ /** Go to next page */
380
+ nextPage: () => void;
381
+ /** Go to previous page */
382
+ prevPage: () => void;
383
+ /** Go to specific page */
384
+ goToPage: (page: number) => void;
385
+ /** Refetch current page */
386
+ refetch: () => Promise<void>;
387
+ }
388
+ interface UseInfiniteQueryOptions<T extends Record<string, unknown>> extends Partial<QueryOptions<T>> {
389
+ /** Page size */
390
+ pageSize?: number;
391
+ /** Callback on success */
392
+ onSuccess?: (data: T[], hasMore: boolean) => void;
393
+ /** Callback on error */
394
+ onError?: (error: Error) => void;
395
+ }
396
+ interface UseInfiniteQueryReturn<T> {
397
+ /** All loaded data (flattened) */
398
+ data: T[];
399
+ /** Pages loaded */
400
+ pages: T[][];
401
+ /** Whether there's a next page */
402
+ hasNextPage: boolean;
403
+ /** Loading state */
404
+ isLoading: boolean;
405
+ /** Fetching next page state */
406
+ isFetchingNextPage: boolean;
407
+ /** Error state */
408
+ error: Error | null;
409
+ /** Fetch next page */
410
+ fetchNextPage: () => Promise<void>;
411
+ /** Refetch all pages */
412
+ refetch: () => Promise<void>;
413
+ /** Reset to first page */
414
+ reset: () => void;
415
+ }
416
+ /**
417
+ * Query hook for fetching multiple records
418
+ *
419
+ * @example
420
+ * ```tsx
421
+ * function UserList() {
422
+ * const { data, isLoading, error, refetch } = useQuery<User>('users', {
423
+ * where: { status: 'active' },
424
+ * orderBy: { field: 'createdAt', direction: 'desc' },
425
+ * limit: 20,
426
+ * onSuccess: (users) => console.log('Loaded', users.length, 'users'),
427
+ * });
428
+ *
429
+ * if (isLoading) return <Spinner />;
430
+ * if (error) return <Error message={error.message} />;
431
+ *
432
+ * return (
433
+ * <ul>
434
+ * {data.map(user => (
435
+ * <li key={user.id}>{user.name}</li>
436
+ * ))}
437
+ * </ul>
438
+ * );
439
+ * }
440
+ * ```
441
+ */
442
+ declare function useQuery<T extends Record<string, unknown>>(table: string, options?: UseQueryOptions<T>): UseQueryReturn<T>;
443
+ /**
444
+ * Query hook for fetching a single record by ID
445
+ *
446
+ * @example
447
+ * ```tsx
448
+ * function UserProfile({ userId }: { userId: string }) {
449
+ * const { data: user, isLoading, error } = useQueryById<User>('users', userId);
450
+ *
451
+ * if (isLoading) return <Spinner />;
452
+ * if (error) return <Error message={error.message} />;
453
+ * if (!user) return <NotFound />;
454
+ *
455
+ * return <div>{user.name}</div>;
456
+ * }
457
+ * ```
458
+ */
459
+ declare function useQueryById<T extends Record<string, unknown>>(table: string, id: string | null, options?: {
460
+ enabled?: boolean;
461
+ }): UseQueryFirstReturn<T>;
462
+ /**
463
+ * Query hook for fetching the first matching record
464
+ *
465
+ * @example
466
+ * ```tsx
467
+ * function LatestPost() {
468
+ * const { data: post, isLoading } = useQueryFirst<Post>('posts', {
469
+ * where: { published: true },
470
+ * orderBy: { field: 'createdAt', direction: 'desc' },
471
+ * });
472
+ *
473
+ * if (isLoading) return <Spinner />;
474
+ * if (!post) return <Empty message="No posts yet" />;
475
+ *
476
+ * return <PostCard post={post} />;
477
+ * }
478
+ * ```
479
+ */
480
+ declare function useQueryFirst<T extends Record<string, unknown>>(table: string, options?: UseQueryOptions<T>): UseQueryFirstReturn<T>;
481
+ /**
482
+ * Paginated query hook with page navigation
483
+ *
484
+ * @example
485
+ * ```tsx
486
+ * function UserTable() {
487
+ * const {
488
+ * data,
489
+ * page,
490
+ * totalPages,
491
+ * hasNextPage,
492
+ * hasPrevPage,
493
+ * nextPage,
494
+ * prevPage,
495
+ * isLoading,
496
+ * } = usePaginatedQuery<User>('users', {
497
+ * pageSize: 10,
498
+ * orderBy: { field: 'createdAt', direction: 'desc' },
499
+ * });
500
+ *
501
+ * return (
502
+ * <>
503
+ * <table>
504
+ * {data.map(user => <UserRow key={user.id} user={user} />)}
505
+ * </table>
506
+ * <div>
507
+ * <button onClick={prevPage} disabled={!hasPrevPage}>Previous</button>
508
+ * <span>Page {page} of {totalPages}</span>
509
+ * <button onClick={nextPage} disabled={!hasNextPage}>Next</button>
510
+ * </div>
511
+ * </>
512
+ * );
513
+ * }
514
+ * ```
515
+ */
516
+ declare function usePaginatedQuery<T extends Record<string, unknown>>(table: string, options?: UsePaginatedQueryOptions<T>): UsePaginatedQueryReturn<T>;
517
+ /**
518
+ * Infinite scroll query hook
519
+ *
520
+ * @example
521
+ * ```tsx
522
+ * function InfiniteUserList() {
523
+ * const {
524
+ * data,
525
+ * hasNextPage,
526
+ * fetchNextPage,
527
+ * isFetchingNextPage,
528
+ * isLoading,
529
+ * } = useInfiniteQuery<User>('users', {
530
+ * pageSize: 20,
531
+ * orderBy: { field: 'createdAt', direction: 'desc' },
532
+ * });
533
+ *
534
+ * const loadMoreRef = useRef<HTMLDivElement>(null);
535
+ *
536
+ * // Intersection observer for infinite scroll
537
+ * useEffect(() => {
538
+ * const observer = new IntersectionObserver(([entry]) => {
539
+ * if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) {
540
+ * fetchNextPage();
541
+ * }
542
+ * });
543
+ *
544
+ * if (loadMoreRef.current) {
545
+ * observer.observe(loadMoreRef.current);
546
+ * }
547
+ *
548
+ * return () => observer.disconnect();
549
+ * }, [hasNextPage, isFetchingNextPage, fetchNextPage]);
550
+ *
551
+ * if (isLoading) return <Spinner />;
552
+ *
553
+ * return (
554
+ * <>
555
+ * {data.map(user => <UserCard key={user.id} user={user} />)}
556
+ * <div ref={loadMoreRef}>
557
+ * {isFetchingNextPage && <Spinner />}
558
+ * </div>
559
+ * </>
560
+ * );
561
+ * }
562
+ * ```
563
+ */
564
+ declare function useInfiniteQuery<T extends Record<string, unknown>>(table: string, options?: UseInfiniteQueryOptions<T>): UseInfiniteQueryReturn<T>;
565
+ /**
566
+ * Count query hook
567
+ *
568
+ * @example
569
+ * ```tsx
570
+ * function UserStats() {
571
+ * const { count, isLoading } = useCount('users', {
572
+ * where: { status: 'active' }
573
+ * });
574
+ *
575
+ * return <div>Active users: {isLoading ? '...' : count}</div>;
576
+ * }
577
+ * ```
578
+ */
579
+ declare function useCount(table: string, options?: {
580
+ where?: WhereFilter;
581
+ enabled?: boolean;
582
+ }): {
583
+ count: number;
584
+ isLoading: boolean;
585
+ error: Error | null;
586
+ refetch: () => Promise<void>;
587
+ };
588
+
589
+ type MutationStatus = "idle" | "loading" | "success" | "error";
590
+ interface UseMutationOptions<TData, TVariables> {
591
+ /** Callback on success */
592
+ onSuccess?: (data: TData, variables: TVariables) => void | Promise<void>;
593
+ /** Callback on error */
594
+ onError?: (error: Error, variables: TVariables) => void | Promise<void>;
595
+ /** Callback on mutation settled (success or error) */
596
+ onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void | Promise<void>;
597
+ /** Callback before mutation starts */
598
+ onMutate?: (variables: TVariables) => void | Promise<void>;
599
+ /** Retry count on failure (default: 0) */
600
+ retry?: number;
601
+ /** Retry delay in ms (default: 1000) */
602
+ retryDelay?: number;
603
+ }
604
+ interface UseMutationReturn<TData, TVariables> {
605
+ /** Execute the mutation */
606
+ mutate: (variables: TVariables) => void;
607
+ /** Execute the mutation (async) */
608
+ mutateAsync: (variables: TVariables) => Promise<TData>;
609
+ /** Mutation result data */
610
+ data: TData | undefined;
611
+ /** Error state */
612
+ error: Error | null;
613
+ /** Loading state */
614
+ isLoading: boolean;
615
+ /** Success state */
616
+ isSuccess: boolean;
617
+ /** Error state (boolean) */
618
+ isError: boolean;
619
+ /** Idle state */
620
+ isIdle: boolean;
621
+ /** Mutation status */
622
+ status: MutationStatus;
623
+ /** Reset mutation state */
624
+ reset: () => void;
625
+ }
626
+ interface UseCreateOptions<T> extends UseMutationOptions<T, Partial<T>> {
627
+ /** Invalidate query cache for this table after mutation */
628
+ invalidateQueries?: boolean;
629
+ }
630
+ interface UseUpdateOptions<T> extends UseMutationOptions<T, {
631
+ id: string;
632
+ data: Partial<T>;
633
+ }> {
634
+ invalidateQueries?: boolean;
635
+ }
636
+ interface UseDeleteOptions extends UseMutationOptions<void, string> {
637
+ invalidateQueries?: boolean;
638
+ }
639
+ interface UseUpsertOptions<T extends Record<string, unknown>> extends UseMutationOptions<T, {
640
+ data: Partial<T>;
641
+ conflictFields: (keyof T)[];
642
+ updateFields?: (keyof T)[];
643
+ }> {
644
+ invalidateQueries?: boolean;
645
+ }
646
+ /**
647
+ * Generic mutation hook
648
+ *
649
+ * @example
650
+ * ```tsx
651
+ * function CreateUserForm() {
652
+ * const { mutate, isLoading, error, isSuccess } = useMutation(
653
+ * async (data: CreateUserInput) => {
654
+ * const response = await fetch('/api/users', {
655
+ * method: 'POST',
656
+ * body: JSON.stringify(data),
657
+ * });
658
+ * return response.json();
659
+ * },
660
+ * {
661
+ * onSuccess: (user) => {
662
+ * console.log('User created:', user);
663
+ * },
664
+ * }
665
+ * );
666
+ *
667
+ * const handleSubmit = (data: CreateUserInput) => {
668
+ * mutate(data);
669
+ * };
670
+ *
671
+ * return <form onSubmit={handleSubmit}>...</form>;
672
+ * }
673
+ * ```
674
+ */
675
+ declare function useMutation<TData, TVariables = void>(mutationFn: (variables: TVariables) => Promise<TData>, options?: UseMutationOptions<TData, TVariables>): UseMutationReturn<TData, TVariables>;
676
+ /**
677
+ * Create mutation hook for a table
678
+ *
679
+ * @example
680
+ * ```tsx
681
+ * function CreatePost() {
682
+ * const { mutate, isLoading, isSuccess } = useCreate<Post>('posts', {
683
+ * onSuccess: (post) => {
684
+ * console.log('Post created:', post.id);
685
+ * },
686
+ * });
687
+ *
688
+ * return (
689
+ * <button onClick={() => mutate({ title: 'New Post', content: '...' })}>
690
+ * {isLoading ? 'Creating...' : 'Create Post'}
691
+ * </button>
692
+ * );
693
+ * }
694
+ * ```
695
+ */
696
+ declare function useCreate<T extends Record<string, unknown>>(table: string, options?: UseCreateOptions<T>): UseMutationReturn<T, Partial<T>>;
697
+ /**
698
+ * Update mutation hook for a table
699
+ *
700
+ * @example
701
+ * ```tsx
702
+ * function EditPost({ postId }: { postId: string }) {
703
+ * const { mutate, isLoading } = useUpdate<Post>('posts', {
704
+ * onSuccess: (post) => {
705
+ * toast.success('Post updated!');
706
+ * },
707
+ * });
708
+ *
709
+ * return (
710
+ * <button onClick={() => mutate({ id: postId, data: { title: 'Updated Title' } })}>
711
+ * {isLoading ? 'Saving...' : 'Save'}
712
+ * </button>
713
+ * );
714
+ * }
715
+ * ```
716
+ */
717
+ declare function useUpdate<T extends Record<string, unknown>>(table: string, options?: UseUpdateOptions<T>): UseMutationReturn<T, {
718
+ id: string;
719
+ data: Partial<T>;
720
+ }>;
721
+ /**
722
+ * Delete mutation hook for a table
723
+ *
724
+ * @example
725
+ * ```tsx
726
+ * function DeletePost({ postId }: { postId: string }) {
727
+ * const { mutate, isLoading } = useDelete('posts', {
728
+ * onSuccess: () => {
729
+ * toast.success('Post deleted');
730
+ * navigate('/posts');
731
+ * },
732
+ * });
733
+ *
734
+ * return (
735
+ * <button onClick={() => mutate(postId)} disabled={isLoading}>
736
+ * {isLoading ? 'Deleting...' : 'Delete'}
737
+ * </button>
738
+ * );
739
+ * }
740
+ * ```
741
+ */
742
+ declare function useDelete(table: string, options?: UseDeleteOptions): UseMutationReturn<void, string>;
743
+ /**
744
+ * Upsert mutation hook for a table
745
+ *
746
+ * @example
747
+ * ```tsx
748
+ * function SaveProfile({ userId }: { userId: string }) {
749
+ * const { mutate, isLoading } = useUpsert<Profile>('profiles', {
750
+ * onSuccess: (profile) => {
751
+ * toast.success('Profile saved');
752
+ * },
753
+ * });
754
+ *
755
+ * return (
756
+ * <button onClick={() => mutate({
757
+ * data: { userId, bio: 'Hello world' },
758
+ * conflictFields: ['userId']
759
+ * })}>
760
+ * {isLoading ? 'Saving...' : 'Save Profile'}
761
+ * </button>
762
+ * );
763
+ * }
764
+ * ```
765
+ */
766
+ declare function useUpsert<T extends Record<string, unknown>>(table: string, options?: UseUpsertOptions<T>): UseMutationReturn<T, {
767
+ data: Partial<T>;
768
+ conflictFields: (keyof T)[];
769
+ updateFields?: (keyof T)[];
770
+ }>;
771
+ /**
772
+ * Batch create mutation hook
773
+ *
774
+ * @example
775
+ * ```tsx
776
+ * function ImportUsers() {
777
+ * const { mutate, isLoading, data } = useBatchCreate<User>('users');
778
+ *
779
+ * const handleImport = (users: Partial<User>[]) => {
780
+ * mutate(users);
781
+ * };
782
+ *
783
+ * return (
784
+ * <>
785
+ * <input type="file" onChange={handleFileSelect} />
786
+ * <button onClick={handleImport} disabled={isLoading}>
787
+ * {isLoading ? `Importing... ${data?.count || 0}` : 'Import'}
788
+ * </button>
789
+ * </>
790
+ * );
791
+ * }
792
+ * ```
793
+ */
794
+ declare function useBatchCreate<T extends Record<string, unknown>>(table: string, options?: UseMutationOptions<{
795
+ count: number;
796
+ records?: T[];
797
+ }, Partial<T>[]>): UseMutationReturn<{
798
+ count: number;
799
+ records?: T[];
800
+ }, Partial<T>[]>;
801
+ /**
802
+ * Batch update mutation hook - updates all records matching the where clause
803
+ *
804
+ * @example
805
+ * ```tsx
806
+ * function BulkUpdateStatus() {
807
+ * const { mutate, isLoading, data } = useBatchUpdate<Task>('tasks');
808
+ *
809
+ * const markAllComplete = () => {
810
+ * mutate({
811
+ * data: { status: 'completed' },
812
+ * where: { assigneeId: currentUserId }
813
+ * });
814
+ * };
815
+ *
816
+ * return (
817
+ * <button onClick={markAllComplete}>
818
+ * {isLoading ? `Updating... ${data?.count || 0}` : 'Mark All Complete'}
819
+ * </button>
820
+ * );
821
+ * }
822
+ * ```
823
+ */
824
+ declare function useBatchUpdate<T extends Record<string, unknown>>(table: string, options?: UseMutationOptions<{
825
+ count: number;
826
+ records?: T[];
827
+ }, {
828
+ data: Partial<T>;
829
+ where: WhereFilter;
830
+ }>): UseMutationReturn<{
831
+ count: number;
832
+ records?: T[];
833
+ }, {
834
+ data: Partial<T>;
835
+ where: WhereFilter;
836
+ }>;
837
+ /**
838
+ * Batch delete mutation hook - deletes all records matching the where clause
839
+ *
840
+ * @example
841
+ * ```tsx
842
+ * function BulkDelete() {
843
+ * const { mutate, isLoading, data } = useBatchDelete('items');
844
+ *
845
+ * const deleteOldItems = () => {
846
+ * mutate({ olderThan: { lt: thirtyDaysAgo } });
847
+ * };
848
+ *
849
+ * return (
850
+ * <button onClick={deleteOldItems}>
851
+ * {isLoading ? 'Deleting...' : `Delete old items (${data?.count || 0} deleted)`}
852
+ * </button>
853
+ * );
854
+ * }
855
+ * ```
856
+ */
857
+ declare function useBatchDelete(table: string, options?: UseMutationOptions<{
858
+ count: number;
859
+ }, WhereFilter>): UseMutationReturn<{
860
+ count: number;
861
+ }, WhereFilter>;
862
+ /**
863
+ * Optimistic update helper hook
864
+ *
865
+ * @example
866
+ * ```tsx
867
+ * function LikeButton({ postId, likes }: { postId: string; likes: number }) {
868
+ * const { mutate, optimisticData, rollback } = useOptimisticMutation(
869
+ * likes,
870
+ * async () => {
871
+ * await vaif.from('posts').update(postId, { likes: likes + 1 });
872
+ * },
873
+ * {
874
+ * optimisticUpdate: (current) => current + 1,
875
+ * onError: () => {
876
+ * toast.error('Failed to like post');
877
+ * },
878
+ * }
879
+ * );
880
+ *
881
+ * return (
882
+ * <button onClick={() => mutate()}>
883
+ * {optimisticData} likes
884
+ * </button>
885
+ * );
886
+ * }
887
+ * ```
888
+ */
889
+ declare function useOptimisticMutation<TData, TVariables = void>(currentData: TData, mutationFn: (variables: TVariables) => Promise<TData>, options: {
890
+ optimisticUpdate: (current: TData, variables: TVariables) => TData;
891
+ onSuccess?: (data: TData, variables: TVariables) => void;
892
+ onError?: (error: Error, variables: TVariables) => void;
893
+ onSettled?: (data: TData | undefined, error: Error | null, variables: TVariables) => void;
894
+ }): {
895
+ mutate: (variables: TVariables) => void;
896
+ mutateAsync: (variables: TVariables) => Promise<TData>;
897
+ optimisticData: TData;
898
+ rollback: () => void;
899
+ isLoading: boolean;
900
+ error: Error | null;
901
+ };
902
+
903
+ interface UseRealtimeOptions {
904
+ /** Enable/disable the subscription */
905
+ enabled?: boolean;
906
+ }
907
+ interface UseSubscriptionOptions<T = Record<string, unknown>> extends UseRealtimeOptions {
908
+ /** Filter by operations */
909
+ operations?: DbOperation[];
910
+ /** Filter expression */
911
+ filter?: string;
912
+ /** Callback on insert */
913
+ onInsert?: (record: T) => void;
914
+ /** Callback on update */
915
+ onUpdate?: (record: T, old: T | null) => void;
916
+ /** Callback on delete */
917
+ onDelete?: (record: T) => void;
918
+ /** Callback on any change */
919
+ onChange?: (event: DbChangeEvent<T>) => void;
920
+ }
921
+ interface UseSubscriptionReturn<T> {
922
+ /** Latest received record */
923
+ data: T | null;
924
+ /** All changes received */
925
+ changes: DbChangeEvent<T>[];
926
+ /** Whether subscribed */
927
+ isSubscribed: boolean;
928
+ /** Clear changes history */
929
+ clearChanges: () => void;
930
+ }
931
+ interface UseChannelOptions extends UseRealtimeOptions {
932
+ /** Channel type */
933
+ type?: "public" | "private" | "presence";
934
+ }
935
+ interface BroadcastMessage<T = unknown> {
936
+ event: string;
937
+ payload: T;
938
+ senderId?: string;
939
+ }
940
+ interface UseChannelReturn<TMessage = unknown> {
941
+ /** Send a broadcast message */
942
+ broadcast: (event: string, payload: TMessage) => void;
943
+ /** Last received broadcast */
944
+ lastMessage: BroadcastMessage<TMessage> | null;
945
+ /** All broadcast messages received */
946
+ messages: BroadcastMessage<TMessage>[];
947
+ /** Whether channel is joined */
948
+ isJoined: boolean;
949
+ /** Leave channel */
950
+ leave: () => void;
951
+ }
952
+ interface UsePresenceOptions {
953
+ /** Enable/disable */
954
+ enabled?: boolean;
955
+ }
956
+ interface UsePresenceReturn<T extends PresenceState> {
957
+ /** Current presence state (all users) */
958
+ presence: Record<string, PresenceEntry<T>[]>;
959
+ /** Number of online users */
960
+ count: number;
961
+ /** List of online user keys */
962
+ onlineUsers: string[];
963
+ /** Update own presence state */
964
+ update: (state: Partial<T>) => void;
965
+ /** Leave presence (go offline) */
966
+ leave: () => void;
967
+ /** Whether currently tracked */
968
+ isTracking: boolean;
969
+ }
970
+ /**
971
+ * Subscribe to database table changes in real-time
972
+ *
973
+ * @example
974
+ * ```tsx
975
+ * function MessageList() {
976
+ * const { data, changes } = useSubscription<Message>('messages', {
977
+ * operations: ['INSERT'],
978
+ * onInsert: (message) => {
979
+ * playNotificationSound();
980
+ * },
981
+ * });
982
+ *
983
+ * return (
984
+ * <ul>
985
+ * {changes.map((change, i) => (
986
+ * <li key={i}>{change.new?.text}</li>
987
+ * ))}
988
+ * </ul>
989
+ * );
990
+ * }
991
+ * ```
992
+ */
993
+ declare function useSubscription<T = Record<string, unknown>>(table: string, options?: UseSubscriptionOptions<T>): UseSubscriptionReturn<T>;
994
+ /**
995
+ * Join a realtime channel for broadcast
996
+ *
997
+ * @example
998
+ * ```tsx
999
+ * function ChatRoom({ roomId }: { roomId: string }) {
1000
+ * const { broadcast, messages } = useChannel<ChatMessage>(`room:${roomId}`);
1001
+ *
1002
+ * const sendMessage = (text: string) => {
1003
+ * broadcast('message', { text, userId: user.id });
1004
+ * };
1005
+ *
1006
+ * return (
1007
+ * <div>
1008
+ * <MessageList messages={messages} />
1009
+ * <MessageInput onSend={sendMessage} />
1010
+ * </div>
1011
+ * );
1012
+ * }
1013
+ * ```
1014
+ */
1015
+ declare function useChannel<TMessage = unknown>(channelName: string, options?: UseChannelOptions): UseChannelReturn<TMessage>;
1016
+ /**
1017
+ * Track and observe presence in a channel
1018
+ *
1019
+ * @example
1020
+ * ```tsx
1021
+ * function OnlineIndicator({ documentId }: { documentId: string }) {
1022
+ * const { count, presence, update } = usePresence<CursorState>(
1023
+ * `doc:${documentId}`,
1024
+ * { x: 0, y: 0, color: randomColor() }
1025
+ * );
1026
+ *
1027
+ * const handleMouseMove = (e: MouseEvent) => {
1028
+ * update({ x: e.clientX, y: e.clientY });
1029
+ * };
1030
+ *
1031
+ * return (
1032
+ * <div onMouseMove={handleMouseMove}>
1033
+ * <span>{count} online</span>
1034
+ * {Object.entries(presence).map(([key, entries]) => (
1035
+ * entries.map((entry, i) => (
1036
+ * <Cursor key={`${key}-${i}`} {...entry.state} />
1037
+ * ))
1038
+ * ))}
1039
+ * </div>
1040
+ * );
1041
+ * }
1042
+ * ```
1043
+ */
1044
+ declare function usePresence<T extends PresenceState = PresenceState>(channelName: string, initialState?: T, options?: UsePresenceOptions): UsePresenceReturn<T>;
1045
+ /**
1046
+ * Hook for realtime connection state
1047
+ *
1048
+ * @example
1049
+ * ```tsx
1050
+ * function ConnectionStatus() {
1051
+ * const { state, isConnected, reconnect } = useRealtimeConnection();
1052
+ *
1053
+ * if (!isConnected) {
1054
+ * return (
1055
+ * <div className="offline-banner">
1056
+ * Disconnected. <button onClick={reconnect}>Reconnect</button>
1057
+ * </div>
1058
+ * );
1059
+ * }
1060
+ *
1061
+ * return null;
1062
+ * }
1063
+ * ```
1064
+ */
1065
+ declare function useRealtimeConnection(): {
1066
+ state: ConnectionState;
1067
+ isConnected: boolean;
1068
+ connectionId: string | null;
1069
+ reconnect: () => void;
1070
+ disconnect: () => void;
1071
+ };
1072
+ /**
1073
+ * Broadcast-only channel hook (simpler than useChannel)
1074
+ *
1075
+ * @example
1076
+ * ```tsx
1077
+ * function Notifications() {
1078
+ * const { send, messages } = useBroadcast<NotificationPayload>('notifications');
1079
+ *
1080
+ * return (
1081
+ * <>
1082
+ * {messages.map((msg, i) => (
1083
+ * <Toast key={i} {...msg.payload} />
1084
+ * ))}
1085
+ * </>
1086
+ * );
1087
+ * }
1088
+ * ```
1089
+ */
1090
+ declare function useBroadcast<T = unknown>(channelName: string, options?: {
1091
+ enabled?: boolean;
1092
+ }): {
1093
+ send: (event: string, payload: T) => void;
1094
+ messages: BroadcastMessage<T>[];
1095
+ lastMessage: BroadcastMessage<T> | null;
1096
+ isConnected: boolean;
1097
+ };
1098
+
1099
+ interface UseUploadOptions extends Partial<UploadOptions> {
1100
+ /** Max file size in bytes */
1101
+ maxSize?: number;
1102
+ /** Allowed mime types */
1103
+ allowedTypes?: string[];
1104
+ /** Generate unique key */
1105
+ uniqueKey?: boolean;
1106
+ /** Callback on upload start */
1107
+ onUploadStart?: (file: File) => void;
1108
+ /** Callback on upload progress */
1109
+ onProgress?: (progress: number, file: File) => void;
1110
+ /** Callback on upload success */
1111
+ onSuccess?: (result: UploadResult, file: File) => void;
1112
+ /** Callback on upload error */
1113
+ onError?: (error: Error, file: File) => void;
1114
+ }
1115
+ interface UseUploadReturn {
1116
+ /** Upload a file */
1117
+ upload: (key: string, file: File) => Promise<UploadResult>;
1118
+ /** Upload multiple files */
1119
+ uploadMultiple: (files: Array<{
1120
+ key: string;
1121
+ file: File;
1122
+ }>) => Promise<UploadResult[]>;
1123
+ /** Current upload progress (0-100) */
1124
+ progress: number;
1125
+ /** Whether upload is in progress */
1126
+ isUploading: boolean;
1127
+ /** Error state */
1128
+ error: Error | null;
1129
+ /** Last upload result */
1130
+ result: UploadResult | null;
1131
+ /** All upload results */
1132
+ results: UploadResult[];
1133
+ /** Reset state */
1134
+ reset: () => void;
1135
+ }
1136
+ interface UseDownloadOptions {
1137
+ /** Callback on download success */
1138
+ onSuccess?: (blob: Blob, filename: string) => void;
1139
+ /** Callback on download error */
1140
+ onError?: (error: Error) => void;
1141
+ }
1142
+ interface UseDownloadReturn {
1143
+ /** Download a file */
1144
+ download: (key: string, filename?: string) => Promise<Blob>;
1145
+ /** Download and trigger browser download */
1146
+ downloadAndSave: (key: string, filename?: string) => Promise<void>;
1147
+ /** Whether download is in progress */
1148
+ isDownloading: boolean;
1149
+ /** Error state */
1150
+ error: Error | null;
1151
+ }
1152
+ interface UseFileOptions {
1153
+ /** Expiry time in seconds for signed URL */
1154
+ expiresIn?: number;
1155
+ /** Enable/disable */
1156
+ enabled?: boolean;
1157
+ }
1158
+ interface UseFileReturn {
1159
+ /** File URL (signed) */
1160
+ url: string | null;
1161
+ /** File metadata */
1162
+ metadata: FileMetadata | null;
1163
+ /** Loading state */
1164
+ isLoading: boolean;
1165
+ /** Error state */
1166
+ error: Error | null;
1167
+ /** Refresh URL and metadata */
1168
+ refresh: () => Promise<void>;
1169
+ /** Delete the file */
1170
+ deleteFile: () => Promise<void>;
1171
+ }
1172
+ interface UseFilesOptions {
1173
+ /** Path prefix filter */
1174
+ prefix?: string;
1175
+ /** Max files to return */
1176
+ limit?: number;
1177
+ /** Enable/disable */
1178
+ enabled?: boolean;
1179
+ }
1180
+ interface UseFilesReturn {
1181
+ /** List of files */
1182
+ files: FileMetadata[];
1183
+ /** Loading state */
1184
+ isLoading: boolean;
1185
+ /** Error state */
1186
+ error: Error | null;
1187
+ /** Refresh file list */
1188
+ refresh: () => Promise<void>;
1189
+ }
1190
+ interface DropzoneOptions extends UseUploadOptions {
1191
+ /** Base path for uploads */
1192
+ basePath?: string;
1193
+ /** Multiple files allowed */
1194
+ multiple?: boolean;
1195
+ }
1196
+ interface DropzoneState {
1197
+ /** Whether file is being dragged over */
1198
+ isDragOver: boolean;
1199
+ /** Files selected (before upload) */
1200
+ selectedFiles: File[];
1201
+ }
1202
+ interface UseDropzoneReturn extends UseUploadReturn {
1203
+ /** Dropzone state */
1204
+ dropzone: DropzoneState;
1205
+ /** Get props for the drop zone element */
1206
+ getDropzoneProps: () => {
1207
+ onDragOver: (e: React.DragEvent) => void;
1208
+ onDragLeave: (e: React.DragEvent) => void;
1209
+ onDrop: (e: React.DragEvent) => void;
1210
+ onClick: () => void;
1211
+ };
1212
+ /** Get props for hidden file input */
1213
+ getInputProps: () => {
1214
+ type: "file";
1215
+ onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
1216
+ multiple: boolean;
1217
+ accept: string;
1218
+ style: {
1219
+ display: "none";
1220
+ };
1221
+ };
1222
+ /** Open file picker */
1223
+ openFilePicker: () => void;
1224
+ /** Clear selected files */
1225
+ clearFiles: () => void;
1226
+ /** Remove a specific file */
1227
+ removeFile: (index: number) => void;
1228
+ }
1229
+ /**
1230
+ * File upload hook
1231
+ *
1232
+ * @example
1233
+ * ```tsx
1234
+ * function FileUploader() {
1235
+ * const { upload, progress, isUploading, error } = useUpload({
1236
+ * maxSize: 5 * 1024 * 1024, // 5MB
1237
+ * allowedTypes: ['image/jpeg', 'image/png'],
1238
+ * onSuccess: (result) => {
1239
+ * toast.success(`Uploaded: ${result.key}`);
1240
+ * },
1241
+ * });
1242
+ *
1243
+ * const handleChange = async (e: ChangeEvent<HTMLInputElement>) => {
1244
+ * const file = e.target.files?.[0];
1245
+ * if (file) {
1246
+ * await upload(`uploads/${file.name}`, file);
1247
+ * }
1248
+ * };
1249
+ *
1250
+ * return (
1251
+ * <div>
1252
+ * <input type="file" onChange={handleChange} disabled={isUploading} />
1253
+ * {isUploading && <progress value={progress} max={100} />}
1254
+ * {error && <p className="error">{error.message}</p>}
1255
+ * </div>
1256
+ * );
1257
+ * }
1258
+ * ```
1259
+ */
1260
+ declare function useUpload(options?: UseUploadOptions): UseUploadReturn;
1261
+ /**
1262
+ * File download hook
1263
+ *
1264
+ * @example
1265
+ * ```tsx
1266
+ * function DownloadButton({ fileKey }: { fileKey: string }) {
1267
+ * const { downloadAndSave, isDownloading } = useDownload();
1268
+ *
1269
+ * return (
1270
+ * <button
1271
+ * onClick={() => downloadAndSave(fileKey)}
1272
+ * disabled={isDownloading}
1273
+ * >
1274
+ * {isDownloading ? 'Downloading...' : 'Download'}
1275
+ * </button>
1276
+ * );
1277
+ * }
1278
+ * ```
1279
+ */
1280
+ declare function useDownload(options?: UseDownloadOptions): UseDownloadReturn;
1281
+ /**
1282
+ * Single file hook (URL, metadata, actions)
1283
+ *
1284
+ * @example
1285
+ * ```tsx
1286
+ * function FilePreview({ fileKey }: { fileKey: string }) {
1287
+ * const { url, metadata, isLoading, deleteFile } = useFile(fileKey, {
1288
+ * expiresIn: 3600
1289
+ * });
1290
+ *
1291
+ * if (isLoading) return <Skeleton />;
1292
+ * if (!url) return <Empty />;
1293
+ *
1294
+ * return (
1295
+ * <div>
1296
+ * <img src={url} alt={metadata?.name} />
1297
+ * <button onClick={deleteFile}>Delete</button>
1298
+ * </div>
1299
+ * );
1300
+ * }
1301
+ * ```
1302
+ */
1303
+ declare function useFile(key: string | null, options?: UseFileOptions): UseFileReturn;
1304
+ /**
1305
+ * List files
1306
+ *
1307
+ * @example
1308
+ * ```tsx
1309
+ * function FileList({ folder }: { folder: string }) {
1310
+ * const { files, isLoading, refresh } = useFiles({
1311
+ * prefix: folder,
1312
+ * limit: 20,
1313
+ * });
1314
+ *
1315
+ * return (
1316
+ * <div>
1317
+ * {files.map(file => (
1318
+ * <FileItem key={file.key} file={file} />
1319
+ * ))}
1320
+ * </div>
1321
+ * );
1322
+ * }
1323
+ * ```
1324
+ */
1325
+ declare function useFiles(options?: UseFilesOptions): UseFilesReturn;
1326
+ /**
1327
+ * Drag and drop file upload hook
1328
+ *
1329
+ * @example
1330
+ * ```tsx
1331
+ * function DropZone() {
1332
+ * const {
1333
+ * dropzone,
1334
+ * getDropzoneProps,
1335
+ * getInputProps,
1336
+ * isUploading,
1337
+ * progress,
1338
+ * results,
1339
+ * } = useDropzone({
1340
+ * basePath: 'uploads',
1341
+ * multiple: true,
1342
+ * maxSize: 10 * 1024 * 1024,
1343
+ * allowedTypes: ['image/*'],
1344
+ * });
1345
+ *
1346
+ * return (
1347
+ * <div
1348
+ * {...getDropzoneProps()}
1349
+ * className={`dropzone ${dropzone.isDragOver ? 'drag-over' : ''}`}
1350
+ * >
1351
+ * <input {...getInputProps()} />
1352
+ * {isUploading ? (
1353
+ * <progress value={progress} max={100} />
1354
+ * ) : (
1355
+ * <p>Drag files here or click to select</p>
1356
+ * )}
1357
+ * {results.length > 0 && (
1358
+ * <ul>
1359
+ * {results.map((r, i) => <li key={i}>{r.key}</li>)}
1360
+ * </ul>
1361
+ * )}
1362
+ * </div>
1363
+ * );
1364
+ * }
1365
+ * ```
1366
+ */
1367
+ declare function useDropzone(options?: DropzoneOptions): UseDropzoneReturn;
1368
+ /**
1369
+ * Get a public URL for a file
1370
+ *
1371
+ * @example
1372
+ * ```tsx
1373
+ * function Avatar({ fileKey }: { fileKey: string }) {
1374
+ * const url = usePublicUrl(fileKey);
1375
+ * return <img src={url} alt="Avatar" />;
1376
+ * }
1377
+ * ```
1378
+ */
1379
+ declare function usePublicUrl(key: string): string;
1380
+
1381
+ interface UseFunctionOptions<TInput, TOutput> {
1382
+ /** Callback on success */
1383
+ onSuccess?: (data: TOutput, input: TInput) => void;
1384
+ /** Callback on error */
1385
+ onError?: (error: Error, input: TInput) => void;
1386
+ /** Timeout in ms */
1387
+ timeout?: number;
1388
+ /** Include execution logs in response */
1389
+ includeLogs?: boolean;
1390
+ /** Enable retry on failure */
1391
+ retry?: boolean;
1392
+ /** Max retry attempts (default: 3) */
1393
+ maxRetries?: number;
1394
+ /** Specific version to invoke */
1395
+ version?: number;
1396
+ }
1397
+ interface UseFunctionReturn<TInput, TOutput> {
1398
+ /** Invoke the function */
1399
+ invoke: (input: TInput) => Promise<TOutput>;
1400
+ /** Result data */
1401
+ data: TOutput | null;
1402
+ /** Loading state */
1403
+ isLoading: boolean;
1404
+ /** Error state */
1405
+ error: Error | null;
1406
+ /** Whether invocation succeeded */
1407
+ isSuccess: boolean;
1408
+ /** Invocation metadata */
1409
+ invocation: InvokeResult<TOutput> | null;
1410
+ /** Reset state */
1411
+ reset: () => void;
1412
+ }
1413
+ interface UseRpcOptions<TInput, TOutput> extends UseFunctionOptions<TInput, TOutput> {
1414
+ /** Cache results */
1415
+ cache?: boolean;
1416
+ /** Cache TTL in ms */
1417
+ cacheTtl?: number;
1418
+ }
1419
+ interface UseRpcReturn<TInput, TOutput> extends UseFunctionReturn<TInput, TOutput> {
1420
+ /** Clear cache */
1421
+ clearCache: () => void;
1422
+ }
1423
+ interface UseFunctionListOptions {
1424
+ /** Project ID (required) */
1425
+ projectId: string;
1426
+ /** Environment ID */
1427
+ envId?: string;
1428
+ /** Filter by enabled status */
1429
+ enabled?: boolean;
1430
+ /** Limit results */
1431
+ limit?: number;
1432
+ /** Offset for pagination */
1433
+ offset?: number;
1434
+ }
1435
+ interface UseFunctionListReturn {
1436
+ /** List of functions */
1437
+ functions: VaifFunction[];
1438
+ /** Loading state */
1439
+ isLoading: boolean;
1440
+ /** Error state */
1441
+ error: Error | null;
1442
+ /** Refresh list */
1443
+ refresh: () => Promise<void>;
1444
+ }
1445
+ /**
1446
+ * Invoke a serverless function
1447
+ *
1448
+ * @example
1449
+ * ```tsx
1450
+ * function SendEmailButton({ to, subject, body }: EmailProps) {
1451
+ * const { invoke, isLoading, error } = useFunction<EmailInput, EmailResult>(
1452
+ * 'send-email',
1453
+ * {
1454
+ * onSuccess: (result) => {
1455
+ * toast.success(`Email sent! ID: ${result.messageId}`);
1456
+ * },
1457
+ * onError: (error) => {
1458
+ * toast.error(`Failed: ${error.message}`);
1459
+ * },
1460
+ * }
1461
+ * );
1462
+ *
1463
+ * return (
1464
+ * <button
1465
+ * onClick={() => invoke({ to, subject, body })}
1466
+ * disabled={isLoading}
1467
+ * >
1468
+ * {isLoading ? 'Sending...' : 'Send Email'}
1469
+ * </button>
1470
+ * );
1471
+ * }
1472
+ * ```
1473
+ */
1474
+ declare function useFunction<TInput = unknown, TOutput = unknown>(functionIdOrName: string, options?: UseFunctionOptions<TInput, TOutput>): UseFunctionReturn<TInput, TOutput>;
1475
+ /**
1476
+ * RPC-style function hook with caching
1477
+ *
1478
+ * @example
1479
+ * ```tsx
1480
+ * // Define typed RPC
1481
+ * type GetUserInput = { userId: string };
1482
+ * type GetUserOutput = { name: string; email: string };
1483
+ *
1484
+ * function UserProfile({ userId }: { userId: string }) {
1485
+ * const { invoke, data, isLoading, error } = useRpc<GetUserInput, GetUserOutput>(
1486
+ * 'get-user',
1487
+ * {
1488
+ * cache: true,
1489
+ * cacheTtl: 60000, // 1 minute
1490
+ * }
1491
+ * );
1492
+ *
1493
+ * useEffect(() => {
1494
+ * invoke({ userId });
1495
+ * }, [userId, invoke]);
1496
+ *
1497
+ * if (isLoading) return <Spinner />;
1498
+ * if (error) return <Error message={error.message} />;
1499
+ * if (!data) return null;
1500
+ *
1501
+ * return <div>{data.name} ({data.email})</div>;
1502
+ * }
1503
+ * ```
1504
+ */
1505
+ declare function useRpc<TInput = unknown, TOutput = unknown>(functionIdOrName: string, options?: UseRpcOptions<TInput, TOutput>): UseRpcReturn<TInput, TOutput>;
1506
+ /**
1507
+ * List available functions
1508
+ *
1509
+ * @example
1510
+ * ```tsx
1511
+ * function FunctionList({ projectId }: { projectId: string }) {
1512
+ * const { functions, isLoading, refresh } = useFunctionList({
1513
+ * projectId,
1514
+ * enabled: true,
1515
+ * });
1516
+ *
1517
+ * return (
1518
+ * <ul>
1519
+ * {functions.map(fn => (
1520
+ * <li key={fn.id}>
1521
+ * {fn.name} - {fn.runtime}
1522
+ * </li>
1523
+ * ))}
1524
+ * </ul>
1525
+ * );
1526
+ * }
1527
+ * ```
1528
+ */
1529
+ declare function useFunctionList(options: UseFunctionListOptions): UseFunctionListReturn;
1530
+ /**
1531
+ * Batch invoke multiple functions
1532
+ *
1533
+ * @example
1534
+ * ```tsx
1535
+ * function ProcessItems({ items }: { items: Item[] }) {
1536
+ * const { invoke, results, isLoading, progress } = useBatchInvoke<ProcessInput, ProcessOutput>(
1537
+ * 'process-item',
1538
+ * {
1539
+ * concurrency: 5,
1540
+ * onProgress: (completed, total) => {
1541
+ * console.log(`${completed}/${total} processed`);
1542
+ * },
1543
+ * }
1544
+ * );
1545
+ *
1546
+ * return (
1547
+ * <button onClick={() => invoke(items.map(i => ({ itemId: i.id })))}>
1548
+ * {isLoading ? `Processing... ${progress}%` : 'Process All'}
1549
+ * </button>
1550
+ * );
1551
+ * }
1552
+ * ```
1553
+ */
1554
+ declare function useBatchInvoke<TInput = unknown, TOutput = unknown>(functionIdOrName: string, options?: {
1555
+ concurrency?: number;
1556
+ onProgress?: (completed: number, total: number) => void;
1557
+ onSuccess?: (results: TOutput[]) => void;
1558
+ onError?: (error: Error) => void;
1559
+ }): {
1560
+ invoke: (inputs: TInput[]) => Promise<TOutput[]>;
1561
+ results: TOutput[];
1562
+ isLoading: boolean;
1563
+ progress: number;
1564
+ error: Error | null;
1565
+ };
1566
+ /**
1567
+ * Scheduled/deferred function invocation
1568
+ *
1569
+ * @example
1570
+ * ```tsx
1571
+ * function ScheduleReminder() {
1572
+ * const { schedule, cancel, isScheduled, scheduledAt } = useScheduledFunction<ReminderInput>(
1573
+ * 'send-reminder'
1574
+ * );
1575
+ *
1576
+ * const handleSchedule = () => {
1577
+ * schedule(
1578
+ * { userId: user.id, message: 'Follow up' },
1579
+ * { delayMs: 24 * 60 * 60 * 1000 } // 24 hours
1580
+ * );
1581
+ * };
1582
+ *
1583
+ * return (
1584
+ * <div>
1585
+ * {isScheduled ? (
1586
+ * <>
1587
+ * <span>Scheduled for {scheduledAt?.toLocaleString()}</span>
1588
+ * <button onClick={cancel}>Cancel</button>
1589
+ * </>
1590
+ * ) : (
1591
+ * <button onClick={handleSchedule}>Schedule Reminder</button>
1592
+ * )}
1593
+ * </div>
1594
+ * );
1595
+ * }
1596
+ * ```
1597
+ */
1598
+ declare function useScheduledFunction<TInput = unknown>(functionIdOrName: string): {
1599
+ schedule: (input: TInput, options: {
1600
+ delayMs: number;
1601
+ }) => Promise<string>;
1602
+ cancel: () => Promise<void>;
1603
+ isScheduled: boolean;
1604
+ scheduledAt: Date | null;
1605
+ requestId: string | null;
1606
+ error: Error | null;
1607
+ };
1608
+
1609
+ export { type DropzoneOptions, type DropzoneState, type MutationStatus, type QueryStatus, type UseAuthReturn, type UseChannelOptions, type UseChannelReturn, type UseCreateOptions, type UseDeleteOptions, type UseDownloadOptions, type UseDownloadReturn, type UseDropzoneReturn, type UseEmailVerificationReturn, type UseFileOptions, type UseFileReturn, type UseFilesOptions, type UseFilesReturn, type UseFunctionListOptions, type UseFunctionListReturn, type UseFunctionOptions, type UseFunctionReturn, type UseInfiniteQueryOptions, type UseInfiniteQueryReturn, type UseMFAReturn, type UseMagicLinkReturn, type UseMutationOptions, type UseMutationReturn, type UseOAuthReturn, type UsePaginatedQueryOptions, type UsePaginatedQueryReturn, type UsePasswordResetReturn, type UsePresenceOptions, type UsePresenceReturn, type UseQueryFirstReturn, type UseQueryOptions, type UseQueryReturn, type UseRealtimeOptions, type UseRpcOptions, type UseRpcReturn, type UseSubscriptionOptions, type UseSubscriptionReturn, type UseUpdateOptions, type UseUploadOptions, type UseUploadReturn, type UseUpsertOptions, type VaifContextValue, VaifProvider, type VaifProviderProps, useAuth, useBatchCreate, useBatchDelete, useBatchInvoke, useBatchUpdate, useBroadcast, useChannel, useCount, useCreate, useDelete, useDownload, useDropzone, useEmailVerification, useFile, useFiles, useFunction, useFunctionList, useInfiniteQuery, useMFA, useMagicLink, useMutation, useOAuth, useOptimisticMutation, usePaginatedQuery, usePasswordReset, usePresence, usePublicUrl, useQuery, useQueryById, useQueryFirst, useRealtimeConnection, useRpc, useScheduledFunction, useSubscription, useToken, useUpdate, useUpload, useUpsert, useUser, useVaif, useVaifClient };