@gately/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.
package/dist/index.mjs ADDED
@@ -0,0 +1,391 @@
1
+ import React, { createContext, useState, useEffect, useCallback, useContext } from 'react';
2
+ import { GatelyClient } from '@gately/sdk';
3
+
4
+ /**
5
+ * Gately React Context
6
+ * Provides authentication state and methods to React components
7
+ */
8
+ const GatelyContext = createContext(undefined);
9
+ function GatelyProvider({ projectId, apiUrl, children, autoRefresh = true }) {
10
+ const [client] = useState(() => {
11
+ if (typeof window === 'undefined')
12
+ return null;
13
+ try {
14
+ return new GatelyClient(projectId, { apiUrl, autoRefresh });
15
+ }
16
+ catch (error) {
17
+ console.error('Failed to initialize Gately client:', error);
18
+ return null;
19
+ }
20
+ });
21
+ const [user, setUser] = useState(null);
22
+ const [session, setSession] = useState(null);
23
+ const [isLoading, setIsLoading] = useState(true);
24
+ const [error, setError] = useState(null);
25
+ useEffect(() => {
26
+ if (!client)
27
+ return;
28
+ setIsLoading(true);
29
+ setUser(client.getUser());
30
+ setSession(client.getSession());
31
+ setIsLoading(false);
32
+ const handleAuthStateChange = (newUser, newSession) => {
33
+ setUser(newUser);
34
+ setSession(newSession);
35
+ setError(null);
36
+ };
37
+ client.onAuthStateChange(handleAuthStateChange);
38
+ return () => {
39
+ client.offAuthStateChange(handleAuthStateChange);
40
+ };
41
+ }, [client]);
42
+ const login = useCallback(async (email, password) => {
43
+ if (!client)
44
+ throw new Error('Gately client not initialized');
45
+ setError(null);
46
+ try {
47
+ return await client.login(email, password);
48
+ }
49
+ catch (err) {
50
+ const error = err instanceof Error ? err : new Error(String(err));
51
+ setError(error);
52
+ throw error;
53
+ }
54
+ }, [client]);
55
+ const signup = useCallback(async (email, password, metadata) => {
56
+ if (!client)
57
+ throw new Error('Gately client not initialized');
58
+ setError(null);
59
+ try {
60
+ return await client.signup(email, password, metadata);
61
+ }
62
+ catch (err) {
63
+ const error = err instanceof Error ? err : new Error(String(err));
64
+ setError(error);
65
+ throw error;
66
+ }
67
+ }, [client]);
68
+ const logout = useCallback(async () => {
69
+ if (!client)
70
+ throw new Error('Gately client not initialized');
71
+ setError(null);
72
+ try {
73
+ await client.logout();
74
+ }
75
+ catch (err) {
76
+ const error = err instanceof Error ? err : new Error(String(err));
77
+ setError(error);
78
+ throw error;
79
+ }
80
+ }, [client]);
81
+ const sendMagicLink = useCallback(async (email, redirectTo) => {
82
+ if (!client)
83
+ throw new Error('Gately client not initialized');
84
+ setError(null);
85
+ try {
86
+ await client.sendMagicLink(email, { redirectTo });
87
+ }
88
+ catch (err) {
89
+ const error = err instanceof Error ? err : new Error(String(err));
90
+ setError(error);
91
+ throw error;
92
+ }
93
+ }, [client]);
94
+ const resetPassword = useCallback(async (email) => {
95
+ if (!client)
96
+ throw new Error('Gately client not initialized');
97
+ setError(null);
98
+ try {
99
+ await client.resetPassword(email);
100
+ }
101
+ catch (err) {
102
+ const error = err instanceof Error ? err : new Error(String(err));
103
+ setError(error);
104
+ throw error;
105
+ }
106
+ }, [client]);
107
+ const fetchSession = useCallback(async () => {
108
+ if (!client)
109
+ throw new Error('Gately client not initialized');
110
+ setError(null);
111
+ try {
112
+ return await client.fetchSession();
113
+ }
114
+ catch (err) {
115
+ const error = err instanceof Error ? err : new Error(String(err));
116
+ setError(error);
117
+ throw error;
118
+ }
119
+ }, [client]);
120
+ const getUserProfile = useCallback(async () => {
121
+ if (!client)
122
+ throw new Error('Gately client not initialized');
123
+ setError(null);
124
+ try {
125
+ return await client.getUserProfile();
126
+ }
127
+ catch (err) {
128
+ const error = err instanceof Error ? err : new Error(String(err));
129
+ setError(error);
130
+ throw error;
131
+ }
132
+ }, [client]);
133
+ const updateUserProfile = useCallback(async (updates) => {
134
+ if (!client)
135
+ throw new Error('Gately client not initialized');
136
+ setError(null);
137
+ try {
138
+ return await client.updateUserProfile(updates);
139
+ }
140
+ catch (err) {
141
+ const error = err instanceof Error ? err : new Error(String(err));
142
+ setError(error);
143
+ throw error;
144
+ }
145
+ }, [client]);
146
+ const deleteUserAccount = useCallback(async () => {
147
+ if (!client)
148
+ throw new Error('Gately client not initialized');
149
+ setError(null);
150
+ try {
151
+ await client.deleteUserAccount();
152
+ }
153
+ catch (err) {
154
+ const error = err instanceof Error ? err : new Error(String(err));
155
+ setError(error);
156
+ throw error;
157
+ }
158
+ }, [client]);
159
+ const changePassword = useCallback(async (current, newPassword) => {
160
+ if (!client)
161
+ throw new Error('Gately client not initialized');
162
+ setError(null);
163
+ try {
164
+ await client.changePassword(current, newPassword);
165
+ }
166
+ catch (err) {
167
+ const error = err instanceof Error ? err : new Error(String(err));
168
+ setError(error);
169
+ throw error;
170
+ }
171
+ }, [client]);
172
+ const value = {
173
+ user,
174
+ session,
175
+ isLoading,
176
+ isAuthenticated: !!session && new Date(session.expires_at) > new Date(),
177
+ error,
178
+ login,
179
+ signup,
180
+ logout,
181
+ sendMagicLink,
182
+ resetPassword,
183
+ fetchSession,
184
+ getUserProfile,
185
+ updateUserProfile,
186
+ deleteUserAccount,
187
+ changePassword,
188
+ client
189
+ };
190
+ return React.createElement(GatelyContext.Provider, { value }, children);
191
+ }
192
+
193
+ /**
194
+ * useAuth Hook
195
+ * Main hook for accessing Gately auth context
196
+ */
197
+ /**
198
+ * Hook to access Gately authentication context
199
+ * Must be used inside a GatelyProvider
200
+ *
201
+ * @returns {GatelyContextValue} Auth context with user, session, and auth methods
202
+ *
203
+ * @example
204
+ * ```tsx
205
+ * function LoginForm() {
206
+ * const { login, isLoading, error } = useAuth()
207
+ *
208
+ * const handleSubmit = async (email, password) => {
209
+ * try {
210
+ * await login(email, password)
211
+ * } catch (err) {
212
+ * console.error(err)
213
+ * }
214
+ * }
215
+ *
216
+ * return (
217
+ * <>
218
+ * {error && <p>{error.message}</p>}
219
+ * <button disabled={isLoading} onClick={() => handleSubmit('test@example.com', 'pass')}>
220
+ * {isLoading ? 'Logging in...' : 'Log In'}
221
+ * </button>
222
+ * </>
223
+ * )
224
+ * }
225
+ * ```
226
+ */
227
+ function useAuth() {
228
+ const context = useContext(GatelyContext);
229
+ if (context === undefined) {
230
+ throw new Error('useAuth must be used within a GatelyProvider');
231
+ }
232
+ return context;
233
+ }
234
+
235
+ /**
236
+ * useUser Hook
237
+ * Dedicated hook for accessing user information
238
+ */
239
+ /**
240
+ * Hook to access and manage user information
241
+ *
242
+ * @returns {UseUserReturn} User data with loading and error states
243
+ *
244
+ * @example
245
+ * ```tsx
246
+ * function UserProfile() {
247
+ * const { user, profile, isLoading, error, update } = useUser()
248
+ *
249
+ * if (isLoading) return <p>Loading...</p>
250
+ * if (error) return <p>Error: {error.message}</p>
251
+ * if (!user) return <p>Not logged in</p>
252
+ *
253
+ * return (
254
+ * <div>
255
+ * <p>Email: {user.email}</p>
256
+ * <button onClick={() => update({ name: 'New Name' })}>
257
+ * Update Profile
258
+ * </button>
259
+ * </div>
260
+ * )
261
+ * }
262
+ * ```
263
+ */
264
+ function useUser() {
265
+ const { user, isAuthenticated, getUserProfile, updateUserProfile, deleteUserAccount } = useAuth();
266
+ const [profile, setProfile] = useState(null);
267
+ const [isLoading, setIsLoading] = useState(false);
268
+ const [error, setError] = useState(null);
269
+ // Fetch user profile on mount or when auth state changes
270
+ useEffect(() => {
271
+ if (!isAuthenticated || !user) {
272
+ setProfile(null);
273
+ return;
274
+ }
275
+ const fetchProfile = async () => {
276
+ setIsLoading(true);
277
+ setError(null);
278
+ try {
279
+ const profileData = await getUserProfile();
280
+ setProfile(profileData);
281
+ }
282
+ catch (err) {
283
+ setError(err instanceof Error ? err : new Error(String(err)));
284
+ setProfile(null);
285
+ }
286
+ finally {
287
+ setIsLoading(false);
288
+ }
289
+ };
290
+ fetchProfile();
291
+ }, [isAuthenticated, user, getUserProfile]);
292
+ const refetch = useCallback(async () => {
293
+ if (!isAuthenticated) {
294
+ throw new Error('User must be authenticated to fetch profile');
295
+ }
296
+ setIsLoading(true);
297
+ setError(null);
298
+ try {
299
+ const profileData = await getUserProfile();
300
+ setProfile(profileData);
301
+ }
302
+ catch (err) {
303
+ const error = err instanceof Error ? err : new Error(String(err));
304
+ setError(error);
305
+ throw error;
306
+ }
307
+ finally {
308
+ setIsLoading(false);
309
+ }
310
+ }, [isAuthenticated, getUserProfile]);
311
+ const update = useCallback(async (updates) => {
312
+ if (!isAuthenticated) {
313
+ throw new Error('User must be authenticated to update profile');
314
+ }
315
+ setIsLoading(true);
316
+ setError(null);
317
+ try {
318
+ const updatedProfile = await updateUserProfile(updates);
319
+ setProfile(updatedProfile);
320
+ return updatedProfile;
321
+ }
322
+ catch (err) {
323
+ const error = err instanceof Error ? err : new Error(String(err));
324
+ setError(error);
325
+ throw error;
326
+ }
327
+ finally {
328
+ setIsLoading(false);
329
+ }
330
+ }, [isAuthenticated, updateUserProfile]);
331
+ const deleteUser = useCallback(async () => {
332
+ if (!isAuthenticated) {
333
+ throw new Error('User must be authenticated to delete account');
334
+ }
335
+ setIsLoading(true);
336
+ setError(null);
337
+ try {
338
+ await deleteUserAccount();
339
+ setProfile(null);
340
+ }
341
+ catch (err) {
342
+ const error = err instanceof Error ? err : new Error(String(err));
343
+ setError(error);
344
+ throw error;
345
+ }
346
+ finally {
347
+ setIsLoading(false);
348
+ }
349
+ }, [isAuthenticated, deleteUserAccount]);
350
+ return {
351
+ user,
352
+ profile,
353
+ isLoading,
354
+ error,
355
+ refetch,
356
+ update,
357
+ delete: deleteUser
358
+ };
359
+ }
360
+
361
+ /**
362
+ * useProtected Hook
363
+ * Hook for protecting components that require authentication
364
+ */
365
+ /**
366
+ * Hook to check if user is authenticated and protect a component
367
+ *
368
+ * @returns {UseProtectedReturn} Authentication status
369
+ *
370
+ * @example
371
+ * ```tsx
372
+ * function ProtectedPage() {
373
+ * const { isAuthenticated, isLoading } = useProtected()
374
+ *
375
+ * if (isLoading) return <p>Loading...</p>
376
+ * if (!isAuthenticated) return <p>Please log in to access this page</p>
377
+ *
378
+ * return <div>Protected content</div>
379
+ * }
380
+ * ```
381
+ */
382
+ function useProtected() {
383
+ const { isAuthenticated, isLoading, user } = useAuth();
384
+ return {
385
+ isAuthenticated,
386
+ isLoading,
387
+ user
388
+ };
389
+ }
390
+
391
+ export { GatelyContext, GatelyProvider, useAuth, useProtected, useUser };
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@gately/react",
3
+ "version": "1.0.0",
4
+ "type": "module",
5
+ "description": "React SDK for Gately - authentication, user management, and more",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.mjs",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.mjs",
13
+ "require": "./dist/index.js"
14
+ },
15
+ "./context": {
16
+ "types": "./dist/context.d.ts",
17
+ "import": "./dist/context.mjs",
18
+ "require": "./dist/context.js"
19
+ },
20
+ "./hooks": {
21
+ "types": "./dist/hooks/index.d.ts",
22
+ "import": "./dist/hooks/index.mjs",
23
+ "require": "./dist/hooks/index.js"
24
+ }
25
+ },
26
+ "files": [
27
+ "dist/**/*"
28
+ ],
29
+ "scripts": {
30
+ "build": "rollup -c",
31
+ "build:watch": "rollup -c -w",
32
+ "type-check": "tsc --noEmit",
33
+ "lint": "eslint src/**/*.{ts,tsx}",
34
+ "test": "jest",
35
+ "clean": "rimraf dist"
36
+ },
37
+ "keywords": [
38
+ "gately",
39
+ "authentication",
40
+ "react",
41
+ "hooks",
42
+ "auth",
43
+ "user-management"
44
+ ],
45
+ "author": "Gately Team",
46
+ "license": "MIT",
47
+ "peerDependencies": {
48
+ "react": ">=16.8.0",
49
+ "react-dom": ">=16.8.0"
50
+ },
51
+ "dependencies": {
52
+ "@gately/sdk": "^2.0.0"
53
+ },
54
+ "devDependencies": {
55
+ "@rollup/plugin-commonjs": "^25.0.0",
56
+ "@rollup/plugin-node-resolve": "^15.0.0",
57
+ "@rollup/plugin-typescript": "^11.0.0",
58
+ "@types/react": "^18.0.0",
59
+ "@types/react-dom": "^18.0.0",
60
+ "@typescript-eslint/eslint-plugin": "^6.0.0",
61
+ "@typescript-eslint/parser": "^6.0.0",
62
+ "eslint": "^8.0.0",
63
+ "react": "^18.0.0",
64
+ "react-dom": "^18.0.0",
65
+ "rollup": "^3.0.0",
66
+ "rollup-plugin-dts": "^5.0.0",
67
+ "tslib": "^2.8.1",
68
+ "typescript": "^5.9.2"
69
+ },
70
+ "engines": {
71
+ "node": ">=16.0.0"
72
+ }
73
+ }