@fluxbase/sdk-react 0.1.0-rc.1 → 2026.1.1-rc.10

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,250 @@
1
+ /**
2
+ * CAPTCHA hooks for Fluxbase SDK
3
+ *
4
+ * Provides hooks to:
5
+ * - Fetch CAPTCHA configuration from the server
6
+ * - Manage CAPTCHA widget state
7
+ */
8
+
9
+ import { useQuery } from "@tanstack/react-query";
10
+ import { useFluxbaseClient } from "./context";
11
+ import type { CaptchaConfig, CaptchaProvider } from "@fluxbase/sdk";
12
+ import { useCallback, useEffect, useRef, useState } from "react";
13
+
14
+ /**
15
+ * Hook to get the CAPTCHA configuration from the server
16
+ * Use this to determine which CAPTCHA provider to load
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * function AuthPage() {
21
+ * const { data: captchaConfig, isLoading } = useCaptchaConfig();
22
+ *
23
+ * if (isLoading) return <Loading />;
24
+ *
25
+ * return captchaConfig?.enabled ? (
26
+ * <CaptchaWidget provider={captchaConfig.provider} siteKey={captchaConfig.site_key} />
27
+ * ) : null;
28
+ * }
29
+ * ```
30
+ */
31
+ export function useCaptchaConfig() {
32
+ const client = useFluxbaseClient();
33
+
34
+ return useQuery<CaptchaConfig>({
35
+ queryKey: ["fluxbase", "auth", "captcha", "config"],
36
+ queryFn: async () => {
37
+ const { data, error } = await client.auth.getCaptchaConfig();
38
+ if (error) {
39
+ throw error;
40
+ }
41
+ return data!;
42
+ },
43
+ staleTime: 1000 * 60 * 60, // Cache for 1 hour (config rarely changes)
44
+ gcTime: 1000 * 60 * 60 * 24, // Keep in cache for 24 hours
45
+ });
46
+ }
47
+
48
+ /**
49
+ * CAPTCHA widget state for managing token generation
50
+ */
51
+ export interface CaptchaState {
52
+ /** Current CAPTCHA token (null until solved) */
53
+ token: string | null;
54
+ /** Whether the CAPTCHA widget is ready */
55
+ isReady: boolean;
56
+ /** Whether a token is being generated */
57
+ isLoading: boolean;
58
+ /** Any error that occurred */
59
+ error: Error | null;
60
+ /** Reset the CAPTCHA widget */
61
+ reset: () => void;
62
+ /** Execute/trigger the CAPTCHA (for invisible CAPTCHA like reCAPTCHA v3) */
63
+ execute: () => Promise<string>;
64
+ /** Callback to be called when CAPTCHA is verified */
65
+ onVerify: (token: string) => void;
66
+ /** Callback to be called when CAPTCHA expires */
67
+ onExpire: () => void;
68
+ /** Callback to be called when CAPTCHA errors */
69
+ onError: (error: Error) => void;
70
+ }
71
+
72
+ /**
73
+ * Hook to manage CAPTCHA widget state
74
+ *
75
+ * This hook provides a standardized interface for managing CAPTCHA tokens
76
+ * across different providers (hCaptcha, reCAPTCHA v3, Turnstile, Cap).
77
+ *
78
+ * Supported providers:
79
+ * - hcaptcha: Privacy-focused visual challenge
80
+ * - recaptcha_v3: Google's invisible risk-based CAPTCHA
81
+ * - turnstile: Cloudflare's invisible CAPTCHA
82
+ * - cap: Self-hosted proof-of-work CAPTCHA (https://capjs.js.org/)
83
+ *
84
+ * @param provider - The CAPTCHA provider type
85
+ * @returns CAPTCHA state and callbacks
86
+ *
87
+ * @example
88
+ * ```tsx
89
+ * function LoginForm() {
90
+ * const captcha = useCaptcha('hcaptcha');
91
+ *
92
+ * const handleSubmit = async (e: FormEvent) => {
93
+ * e.preventDefault();
94
+ *
95
+ * // Get CAPTCHA token
96
+ * const captchaToken = captcha.token || await captcha.execute();
97
+ *
98
+ * // Sign in with CAPTCHA token
99
+ * await signIn({
100
+ * email,
101
+ * password,
102
+ * captchaToken
103
+ * });
104
+ * };
105
+ *
106
+ * return (
107
+ * <form onSubmit={handleSubmit}>
108
+ * <input name="email" />
109
+ * <input name="password" type="password" />
110
+ *
111
+ * <HCaptcha
112
+ * sitekey={siteKey}
113
+ * onVerify={captcha.onVerify}
114
+ * onExpire={captcha.onExpire}
115
+ * onError={captcha.onError}
116
+ * />
117
+ *
118
+ * <button type="submit" disabled={!captcha.isReady}>
119
+ * Sign In
120
+ * </button>
121
+ * </form>
122
+ * );
123
+ * }
124
+ * ```
125
+ *
126
+ * @example Cap provider
127
+ * ```tsx
128
+ * function LoginForm() {
129
+ * const { data: config } = useCaptchaConfig();
130
+ * const captcha = useCaptcha(config?.provider);
131
+ *
132
+ * // For Cap, load the widget from cap_server_url
133
+ * // <script src={`${config.cap_server_url}/widget.js`} />
134
+ * // <cap-widget data-cap-url={config.cap_server_url} />
135
+ * }
136
+ * ```
137
+ */
138
+ export function useCaptcha(provider?: CaptchaProvider): CaptchaState {
139
+ const [token, setToken] = useState<string | null>(null);
140
+ const [isReady, setIsReady] = useState(false);
141
+ const [isLoading, setIsLoading] = useState(false);
142
+ const [error, setError] = useState<Error | null>(null);
143
+
144
+ // Promise resolver for execute() method
145
+ const executeResolverRef = useRef<((token: string) => void) | null>(null);
146
+ const executeRejecterRef = useRef<((error: Error) => void) | null>(null);
147
+
148
+ // Callback when CAPTCHA is verified
149
+ const onVerify = useCallback((newToken: string) => {
150
+ setToken(newToken);
151
+ setIsLoading(false);
152
+ setError(null);
153
+ setIsReady(true);
154
+
155
+ // Resolve the execute() promise if waiting
156
+ if (executeResolverRef.current) {
157
+ executeResolverRef.current(newToken);
158
+ executeResolverRef.current = null;
159
+ executeRejecterRef.current = null;
160
+ }
161
+ }, []);
162
+
163
+ // Callback when CAPTCHA expires
164
+ const onExpire = useCallback(() => {
165
+ setToken(null);
166
+ setIsReady(true);
167
+ }, []);
168
+
169
+ // Callback when CAPTCHA errors
170
+ const onError = useCallback((err: Error) => {
171
+ setError(err);
172
+ setIsLoading(false);
173
+ setToken(null);
174
+
175
+ // Reject the execute() promise if waiting
176
+ if (executeRejecterRef.current) {
177
+ executeRejecterRef.current(err);
178
+ executeResolverRef.current = null;
179
+ executeRejecterRef.current = null;
180
+ }
181
+ }, []);
182
+
183
+ // Reset the CAPTCHA
184
+ const reset = useCallback(() => {
185
+ setToken(null);
186
+ setError(null);
187
+ setIsLoading(false);
188
+ }, []);
189
+
190
+ // Execute/trigger the CAPTCHA (for invisible CAPTCHA)
191
+ const execute = useCallback(async (): Promise<string> => {
192
+ // If we already have a token, return it
193
+ if (token) {
194
+ return token;
195
+ }
196
+
197
+ // If CAPTCHA is not configured, return empty string
198
+ if (!provider) {
199
+ return "";
200
+ }
201
+
202
+ setIsLoading(true);
203
+ setError(null);
204
+
205
+ // Return a promise that will be resolved by onVerify
206
+ return new Promise<string>((resolve, reject) => {
207
+ executeResolverRef.current = resolve;
208
+ executeRejecterRef.current = reject;
209
+
210
+ // For invisible CAPTCHAs, the widget should call onVerify when done
211
+ // The actual execution is handled by the CAPTCHA widget component
212
+ });
213
+ }, [token, provider]);
214
+
215
+ // Mark as ready when provider is set
216
+ useEffect(() => {
217
+ if (provider) {
218
+ setIsReady(true);
219
+ }
220
+ }, [provider]);
221
+
222
+ return {
223
+ token,
224
+ isReady,
225
+ isLoading,
226
+ error,
227
+ reset,
228
+ execute,
229
+ onVerify,
230
+ onExpire,
231
+ onError,
232
+ };
233
+ }
234
+
235
+ /**
236
+ * Check if CAPTCHA is required for a specific endpoint
237
+ *
238
+ * @param config - CAPTCHA configuration from useCaptchaConfig
239
+ * @param endpoint - The endpoint to check (e.g., 'signup', 'login', 'password_reset')
240
+ * @returns Whether CAPTCHA is required for this endpoint
241
+ */
242
+ export function isCaptchaRequiredForEndpoint(
243
+ config: CaptchaConfig | undefined,
244
+ endpoint: string
245
+ ): boolean {
246
+ if (!config?.enabled) {
247
+ return false;
248
+ }
249
+ return config.endpoints?.includes(endpoint) ?? false;
250
+ }
@@ -1,20 +1,20 @@
1
1
  import { useState, useEffect, useCallback } from 'react'
2
2
  import { useFluxbaseClient } from './context'
3
- import type { APIKey, CreateAPIKeyRequest } from '@fluxbase/sdk'
3
+ import type { ClientKey, CreateClientKeyRequest } from '@fluxbase/sdk'
4
4
 
5
- export interface UseAPIKeysOptions {
5
+ export interface UseClientKeysOptions {
6
6
  /**
7
- * Whether to automatically fetch API keys on mount
7
+ * Whether to automatically fetch client keys on mount
8
8
  * @default true
9
9
  */
10
10
  autoFetch?: boolean
11
11
  }
12
12
 
13
- export interface UseAPIKeysReturn {
13
+ export interface UseClientKeysReturn {
14
14
  /**
15
- * Array of API keys
15
+ * Array of client keys
16
16
  */
17
- keys: APIKey[]
17
+ keys: ClientKey[]
18
18
 
19
19
  /**
20
20
  * Whether keys are being fetched
@@ -27,45 +27,45 @@ export interface UseAPIKeysReturn {
27
27
  error: Error | null
28
28
 
29
29
  /**
30
- * Refetch API keys
30
+ * Refetch client keys
31
31
  */
32
32
  refetch: () => Promise<void>
33
33
 
34
34
  /**
35
- * Create a new API key
35
+ * Create a new client key
36
36
  */
37
- createKey: (request: CreateAPIKeyRequest) => Promise<{ key: string; keyData: APIKey }>
37
+ createKey: (request: CreateClientKeyRequest) => Promise<{ key: string; keyData: ClientKey }>
38
38
 
39
39
  /**
40
- * Update an API key
40
+ * Update a client key
41
41
  */
42
42
  updateKey: (keyId: string, update: { name?: string; description?: string }) => Promise<void>
43
43
 
44
44
  /**
45
- * Revoke an API key
45
+ * Revoke a client key
46
46
  */
47
47
  revokeKey: (keyId: string) => Promise<void>
48
48
 
49
49
  /**
50
- * Delete an API key
50
+ * Delete a client key
51
51
  */
52
52
  deleteKey: (keyId: string) => Promise<void>
53
53
  }
54
54
 
55
55
  /**
56
- * Hook for managing API keys
56
+ * Hook for managing client keys
57
57
  *
58
- * Provides API key list and management functions.
58
+ * Provides client key list and management functions.
59
59
  *
60
60
  * @example
61
61
  * ```tsx
62
- * function APIKeyManager() {
63
- * const { keys, isLoading, createKey, revokeKey } = useAPIKeys()
62
+ * function ClientKeyManager() {
63
+ * const { keys, isLoading, createKey, revokeKey } = useClientKeys()
64
64
  *
65
65
  * const handleCreate = async () => {
66
66
  * const { key, keyData } = await createKey({
67
67
  * name: 'Backend Service',
68
- * description: 'API key for backend',
68
+ * description: 'Client key for backend',
69
69
  * expires_at: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000).toISOString()
70
70
  * })
71
71
  * alert(`Key created: ${key}`)
@@ -85,23 +85,23 @@ export interface UseAPIKeysReturn {
85
85
  * }
86
86
  * ```
87
87
  */
88
- export function useAPIKeys(options: UseAPIKeysOptions = {}): UseAPIKeysReturn {
88
+ export function useClientKeys(options: UseClientKeysOptions = {}): UseClientKeysReturn {
89
89
  const { autoFetch = true } = options
90
90
  const client = useFluxbaseClient()
91
91
 
92
- const [keys, setKeys] = useState<APIKey[]>([])
92
+ const [keys, setKeys] = useState<ClientKey[]>([])
93
93
  const [isLoading, setIsLoading] = useState(autoFetch)
94
94
  const [error, setError] = useState<Error | null>(null)
95
95
 
96
96
  /**
97
- * Fetch API keys from API
97
+ * Fetch client keys from API
98
98
  */
99
99
  const fetchKeys = useCallback(async () => {
100
100
  try {
101
101
  setIsLoading(true)
102
102
  setError(null)
103
- const response = await client.admin.management.apiKeys.list()
104
- setKeys(response.api_keys)
103
+ const response = await client.admin.management.clientKeys.list()
104
+ setKeys(response.client_keys)
105
105
  } catch (err) {
106
106
  setError(err as Error)
107
107
  } finally {
@@ -110,45 +110,45 @@ export function useAPIKeys(options: UseAPIKeysOptions = {}): UseAPIKeysReturn {
110
110
  }, [client])
111
111
 
112
112
  /**
113
- * Create a new API key
113
+ * Create a new client key
114
114
  */
115
115
  const createKey = useCallback(
116
- async (request: CreateAPIKeyRequest): Promise<{ key: string; keyData: APIKey }> => {
117
- const response = await client.admin.management.apiKeys.create(request)
116
+ async (request: CreateClientKeyRequest): Promise<{ key: string; keyData: ClientKey }> => {
117
+ const response = await client.admin.management.clientKeys.create(request)
118
118
  await fetchKeys() // Refresh list
119
- return { key: response.key, keyData: response.api_key }
119
+ return { key: response.key, keyData: response.client_key }
120
120
  },
121
121
  [client, fetchKeys]
122
122
  )
123
123
 
124
124
  /**
125
- * Update an API key
125
+ * Update a client key
126
126
  */
127
127
  const updateKey = useCallback(
128
128
  async (keyId: string, update: { name?: string; description?: string }): Promise<void> => {
129
- await client.admin.management.apiKeys.update(keyId, update)
129
+ await client.admin.management.clientKeys.update(keyId, update)
130
130
  await fetchKeys() // Refresh list
131
131
  },
132
132
  [client, fetchKeys]
133
133
  )
134
134
 
135
135
  /**
136
- * Revoke an API key
136
+ * Revoke a client key
137
137
  */
138
138
  const revokeKey = useCallback(
139
139
  async (keyId: string): Promise<void> => {
140
- await client.admin.management.apiKeys.revoke(keyId)
140
+ await client.admin.management.clientKeys.revoke(keyId)
141
141
  await fetchKeys() // Refresh list
142
142
  },
143
143
  [client, fetchKeys]
144
144
  )
145
145
 
146
146
  /**
147
- * Delete an API key
147
+ * Delete a client key
148
148
  */
149
149
  const deleteKey = useCallback(
150
150
  async (keyId: string): Promise<void> => {
151
- await client.admin.management.apiKeys.delete(keyId)
151
+ await client.admin.management.clientKeys.delete(keyId)
152
152
  await fetchKeys() // Refresh list
153
153
  },
154
154
  [client, fetchKeys]
@@ -172,3 +172,14 @@ export function useAPIKeys(options: UseAPIKeysOptions = {}): UseAPIKeysReturn {
172
172
  deleteKey
173
173
  }
174
174
  }
175
+
176
+ /**
177
+ * @deprecated Use useClientKeys instead
178
+ */
179
+ export const useAPIKeys = useClientKeys
180
+
181
+ /** @deprecated Use UseClientKeysOptions instead */
182
+ export type UseAPIKeysOptions = UseClientKeysOptions
183
+
184
+ /** @deprecated Use UseClientKeysReturn instead */
185
+ export type UseAPIKeysReturn = UseClientKeysReturn