nexu-app 2.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.
Files changed (106) hide show
  1. package/README.md +149 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +1192 -0
  4. package/package.json +43 -0
  5. package/templates/default/.changeset/config.json +11 -0
  6. package/templates/default/.eslintignore +16 -0
  7. package/templates/default/.eslintrc.js +67 -0
  8. package/templates/default/.github/actions/build/action.yml +35 -0
  9. package/templates/default/.github/actions/quality/action.yml +53 -0
  10. package/templates/default/.github/dependabot.yml +51 -0
  11. package/templates/default/.github/workflows/deploy-dev.yml +83 -0
  12. package/templates/default/.github/workflows/deploy-prod.yml +83 -0
  13. package/templates/default/.github/workflows/deploy-rec.yml +83 -0
  14. package/templates/default/.husky/commit-msg +1 -0
  15. package/templates/default/.husky/pre-commit +1 -0
  16. package/templates/default/.nexu-version +1 -0
  17. package/templates/default/.prettierignore +7 -0
  18. package/templates/default/.prettierrc +19 -0
  19. package/templates/default/.vscode/extensions.json +14 -0
  20. package/templates/default/.vscode/settings.json +36 -0
  21. package/templates/default/apps/gitkeep +0 -0
  22. package/templates/default/commitlint.config.js +26 -0
  23. package/templates/default/docker/docker-compose.dev.yml +49 -0
  24. package/templates/default/docker/docker-compose.prod.yml +64 -0
  25. package/templates/default/docker/docker-compose.yml +6 -0
  26. package/templates/default/docs/architecture.md +452 -0
  27. package/templates/default/docs/cli.md +330 -0
  28. package/templates/default/docs/contributing.md +462 -0
  29. package/templates/default/docs/scripts.md +460 -0
  30. package/templates/default/gitignore +44 -0
  31. package/templates/default/lintstagedrc.cjs +4 -0
  32. package/templates/default/package.json +51 -0
  33. package/templates/default/packages/auth/package.json +61 -0
  34. package/templates/default/packages/auth/src/components/ProtectedRoute.tsx +75 -0
  35. package/templates/default/packages/auth/src/components/SignInForm.tsx +153 -0
  36. package/templates/default/packages/auth/src/components/SignUpForm.tsx +179 -0
  37. package/templates/default/packages/auth/src/components/SocialButtons.tsx +147 -0
  38. package/templates/default/packages/auth/src/components/index.ts +4 -0
  39. package/templates/default/packages/auth/src/hooks/index.ts +4 -0
  40. package/templates/default/packages/auth/src/hooks/useAuth.ts +51 -0
  41. package/templates/default/packages/auth/src/hooks/useRequireAuth.ts +54 -0
  42. package/templates/default/packages/auth/src/hooks/useSession.ts +48 -0
  43. package/templates/default/packages/auth/src/hooks/useUser.ts +48 -0
  44. package/templates/default/packages/auth/src/index.ts +45 -0
  45. package/templates/default/packages/auth/src/next/index.ts +18 -0
  46. package/templates/default/packages/auth/src/next/middleware.ts +183 -0
  47. package/templates/default/packages/auth/src/next/server.ts +219 -0
  48. package/templates/default/packages/auth/src/providers/AuthContext.tsx +435 -0
  49. package/templates/default/packages/auth/src/providers/index.ts +1 -0
  50. package/templates/default/packages/auth/src/types/index.ts +284 -0
  51. package/templates/default/packages/auth/src/utils/api.ts +228 -0
  52. package/templates/default/packages/auth/src/utils/index.ts +3 -0
  53. package/templates/default/packages/auth/src/utils/oauth.ts +230 -0
  54. package/templates/default/packages/auth/src/utils/token.ts +204 -0
  55. package/templates/default/packages/auth/tsconfig.json +14 -0
  56. package/templates/default/packages/auth/tsup.config.ts +18 -0
  57. package/templates/default/packages/cache/package.json +26 -0
  58. package/templates/default/packages/cache/src/index.ts +137 -0
  59. package/templates/default/packages/cache/tsconfig.json +9 -0
  60. package/templates/default/packages/cache/tsup.config.ts +9 -0
  61. package/templates/default/packages/config/eslint/index.js +20 -0
  62. package/templates/default/packages/config/package.json +9 -0
  63. package/templates/default/packages/config/typescript/base.json +26 -0
  64. package/templates/default/packages/constants/package.json +26 -0
  65. package/templates/default/packages/constants/src/index.ts +121 -0
  66. package/templates/default/packages/constants/tsconfig.json +9 -0
  67. package/templates/default/packages/constants/tsup.config.ts +9 -0
  68. package/templates/default/packages/logger/package.json +27 -0
  69. package/templates/default/packages/logger/src/index.ts +197 -0
  70. package/templates/default/packages/logger/tsconfig.json +11 -0
  71. package/templates/default/packages/logger/tsup.config.ts +9 -0
  72. package/templates/default/packages/result/package.json +26 -0
  73. package/templates/default/packages/result/src/index.ts +142 -0
  74. package/templates/default/packages/result/tsconfig.json +9 -0
  75. package/templates/default/packages/result/tsup.config.ts +9 -0
  76. package/templates/default/packages/types/package.json +26 -0
  77. package/templates/default/packages/types/src/index.ts +78 -0
  78. package/templates/default/packages/types/tsconfig.json +9 -0
  79. package/templates/default/packages/types/tsup.config.ts +10 -0
  80. package/templates/default/packages/ui/package.json +38 -0
  81. package/templates/default/packages/ui/src/components/Button.tsx +58 -0
  82. package/templates/default/packages/ui/src/components/Card.tsx +85 -0
  83. package/templates/default/packages/ui/src/components/Input.tsx +45 -0
  84. package/templates/default/packages/ui/src/index.ts +15 -0
  85. package/templates/default/packages/ui/tsconfig.json +11 -0
  86. package/templates/default/packages/ui/tsup.config.ts +11 -0
  87. package/templates/default/packages/utils/package.json +30 -0
  88. package/templates/default/packages/utils/src/index.test.ts +130 -0
  89. package/templates/default/packages/utils/src/index.ts +154 -0
  90. package/templates/default/packages/utils/tsconfig.json +10 -0
  91. package/templates/default/packages/utils/tsup.config.ts +10 -0
  92. package/templates/default/pnpm-workspace.yaml +3 -0
  93. package/templates/default/scripts/audit.mjs +700 -0
  94. package/templates/default/scripts/deploy.mjs +40 -0
  95. package/templates/default/scripts/generate-app.mjs +808 -0
  96. package/templates/default/scripts/lib/package-manager.mjs +186 -0
  97. package/templates/default/scripts/setup.mjs +102 -0
  98. package/templates/default/services/.env.example +16 -0
  99. package/templates/default/services/docker-compose.yml +207 -0
  100. package/templates/default/services/grafana/provisioning/dashboards/dashboards.yml +11 -0
  101. package/templates/default/services/grafana/provisioning/datasources/datasources.yml +9 -0
  102. package/templates/default/services/postgres/init/gitkeep +2 -0
  103. package/templates/default/services/prometheus/prometheus.yml +13 -0
  104. package/templates/default/tsconfig.json +27 -0
  105. package/templates/default/turbo.json +40 -0
  106. package/templates/default/vitest.config.ts +15 -0
@@ -0,0 +1,435 @@
1
+ 'use client';
2
+
3
+ import React, {
4
+ createContext,
5
+ useCallback,
6
+ useContext,
7
+ useEffect,
8
+ useMemo,
9
+ useReducer,
10
+ useRef,
11
+ } from 'react';
12
+
13
+ import type {
14
+ AuthConfig,
15
+ AuthError,
16
+ AuthProvider as AuthProviderType,
17
+ AuthResponse,
18
+ AuthSession,
19
+ AuthState,
20
+ AuthUser,
21
+ SignInCredentials,
22
+ SignUpCredentials,
23
+ UpdatePasswordRequest,
24
+ UseAuthReturn,
25
+ } from '../types';
26
+ import { AuthApiClient, createAuthApiClient } from '../utils/api';
27
+ import {
28
+ clearOAuthState,
29
+ getProviderConfig,
30
+ getStoredOAuthState,
31
+ initiateOAuthFlow,
32
+ parseOAuthCallback,
33
+ } from '../utils/oauth';
34
+ import { createTokenManager, TokenManager } from '../utils/token';
35
+
36
+ // ============================================================================
37
+ // State Management
38
+ // ============================================================================
39
+
40
+ type AuthAction =
41
+ | { type: 'LOADING' }
42
+ | { type: 'AUTHENTICATED'; user: AuthUser; session: AuthSession }
43
+ | { type: 'UNAUTHENTICATED' }
44
+ | { type: 'ERROR'; error: AuthError }
45
+ | { type: 'UPDATE_USER'; user: AuthUser }
46
+ | { type: 'CLEAR_ERROR' };
47
+
48
+ const initialState: AuthState = {
49
+ user: null,
50
+ session: null,
51
+ isAuthenticated: false,
52
+ isLoading: true,
53
+ error: null,
54
+ };
55
+
56
+ function authReducer(state: AuthState, action: AuthAction): AuthState {
57
+ switch (action.type) {
58
+ case 'LOADING':
59
+ return { ...state, isLoading: true, error: null };
60
+ case 'AUTHENTICATED':
61
+ return {
62
+ user: action.user,
63
+ session: action.session,
64
+ isAuthenticated: true,
65
+ isLoading: false,
66
+ error: null,
67
+ };
68
+ case 'UNAUTHENTICATED':
69
+ return {
70
+ user: null,
71
+ session: null,
72
+ isAuthenticated: false,
73
+ isLoading: false,
74
+ error: null,
75
+ };
76
+ case 'ERROR':
77
+ return { ...state, isLoading: false, error: action.error };
78
+ case 'UPDATE_USER':
79
+ return { ...state, user: action.user };
80
+ case 'CLEAR_ERROR':
81
+ return { ...state, error: null };
82
+ default:
83
+ return state;
84
+ }
85
+ }
86
+
87
+ // ============================================================================
88
+ // Context
89
+ // ============================================================================
90
+
91
+ interface AuthContextValue extends UseAuthReturn {
92
+ config: AuthConfig;
93
+ tokenManager: TokenManager;
94
+ apiClient: AuthApiClient;
95
+ }
96
+
97
+ const AuthContext = createContext<AuthContextValue | null>(null);
98
+
99
+ // ============================================================================
100
+ // Provider
101
+ // ============================================================================
102
+
103
+ interface AuthProviderProps {
104
+ children: React.ReactNode;
105
+ config: AuthConfig;
106
+ }
107
+
108
+ export function AuthProvider({ children, config }: AuthProviderProps) {
109
+ const [state, dispatch] = useReducer(authReducer, initialState);
110
+
111
+ // Create stable instances
112
+ const tokenManager = useMemo(
113
+ () => createTokenManager(config),
114
+ [config.tokenStorage, config.cookieOptions]
115
+ );
116
+
117
+ const apiClient = useMemo(
118
+ () => createAuthApiClient(config, tokenManager),
119
+ [config, tokenManager]
120
+ );
121
+
122
+ // Ref for auto-refresh interval
123
+ const refreshIntervalRef = useRef<NodeJS.Timeout | null>(null);
124
+
125
+ // Helper to create session from response
126
+ const createSession = useCallback(
127
+ (response: AuthResponse, provider: AuthProviderType = 'email'): AuthSession => {
128
+ return {
129
+ user: response.user,
130
+ accessToken: response.accessToken,
131
+ refreshToken: response.refreshToken,
132
+ expiresAt: Date.now() + response.expiresIn * 1000,
133
+ provider,
134
+ };
135
+ },
136
+ []
137
+ );
138
+
139
+ // Handle successful authentication
140
+ const handleAuthSuccess = useCallback(
141
+ async (response: AuthResponse, provider: AuthProviderType = 'email') => {
142
+ const session = createSession(response, provider);
143
+
144
+ tokenManager.setAccessToken(response.accessToken, response.expiresIn);
145
+ if (response.refreshToken) {
146
+ tokenManager.setRefreshToken(response.refreshToken);
147
+ }
148
+
149
+ dispatch({ type: 'AUTHENTICATED', user: response.user, session });
150
+
151
+ // Call callback if provided
152
+ if (config.callbacks?.onSignIn) {
153
+ await config.callbacks.onSignIn(response.user, session);
154
+ }
155
+ },
156
+ [config.callbacks, tokenManager, createSession]
157
+ );
158
+
159
+ // Sign in with credentials
160
+ const signIn = useCallback(
161
+ async (credentials: SignInCredentials): Promise<AuthResponse> => {
162
+ dispatch({ type: 'LOADING' });
163
+
164
+ try {
165
+ const response = await apiClient.signIn(credentials);
166
+ await handleAuthSuccess(response, 'email');
167
+ return response;
168
+ } catch (error) {
169
+ const authError = error as AuthError;
170
+ dispatch({ type: 'ERROR', error: authError });
171
+ config.callbacks?.onError?.(authError);
172
+ throw error;
173
+ }
174
+ },
175
+ [apiClient, handleAuthSuccess, config.callbacks]
176
+ );
177
+
178
+ // Sign up with credentials
179
+ const signUp = useCallback(
180
+ async (credentials: SignUpCredentials): Promise<AuthResponse> => {
181
+ dispatch({ type: 'LOADING' });
182
+
183
+ try {
184
+ const response = await apiClient.signUp(credentials);
185
+ await handleAuthSuccess(response, 'email');
186
+ return response;
187
+ } catch (error) {
188
+ const authError = error as AuthError;
189
+ dispatch({ type: 'ERROR', error: authError });
190
+ config.callbacks?.onError?.(authError);
191
+ throw error;
192
+ }
193
+ },
194
+ [apiClient, handleAuthSuccess, config.callbacks]
195
+ );
196
+
197
+ // Sign out
198
+ const signOut = useCallback(async (): Promise<void> => {
199
+ try {
200
+ await apiClient.signOut();
201
+ } catch {
202
+ // Ignore sign out errors
203
+ } finally {
204
+ tokenManager.clearTokens();
205
+ dispatch({ type: 'UNAUTHENTICATED' });
206
+ void config.callbacks?.onSignOut?.();
207
+ }
208
+ }, [apiClient, tokenManager, config.callbacks]);
209
+
210
+ // Sign in with OAuth provider
211
+ const signInWithProvider = useCallback(
212
+ async (provider: AuthProviderType): Promise<void> => {
213
+ const providerConfig = getProviderConfig(provider, config.providers);
214
+
215
+ if (!providerConfig) {
216
+ throw new Error(`Provider ${provider} is not configured`);
217
+ }
218
+
219
+ await initiateOAuthFlow(provider, providerConfig);
220
+ },
221
+ [config.providers]
222
+ );
223
+
224
+ // Refresh session
225
+ const refreshSession = useCallback(async (): Promise<AuthSession | null> => {
226
+ try {
227
+ const response = await apiClient.refreshToken();
228
+ const session = createSession(response);
229
+
230
+ tokenManager.setAccessToken(response.accessToken, response.expiresIn);
231
+ if (response.refreshToken) {
232
+ tokenManager.setRefreshToken(response.refreshToken);
233
+ }
234
+
235
+ dispatch({ type: 'AUTHENTICATED', user: response.user, session });
236
+
237
+ return session;
238
+ } catch {
239
+ tokenManager.clearTokens();
240
+ dispatch({ type: 'UNAUTHENTICATED' });
241
+ config.callbacks?.onSessionExpired?.();
242
+ return null;
243
+ }
244
+ }, [apiClient, tokenManager, createSession, config.callbacks]);
245
+
246
+ // Reset password
247
+ const resetPassword = useCallback(
248
+ async (email: string): Promise<void> => {
249
+ await apiClient.resetPassword(email);
250
+ },
251
+ [apiClient]
252
+ );
253
+
254
+ // Update password
255
+ const updatePassword = useCallback(
256
+ async (request: UpdatePasswordRequest): Promise<void> => {
257
+ await apiClient.updatePassword(request);
258
+ },
259
+ [apiClient]
260
+ );
261
+
262
+ // Update user
263
+ const updateUser = useCallback(
264
+ async (data: Partial<AuthUser>): Promise<AuthUser> => {
265
+ const user = await apiClient.updateUser(data);
266
+ dispatch({ type: 'UPDATE_USER', user });
267
+ return user;
268
+ },
269
+ [apiClient]
270
+ );
271
+
272
+ // Verify email
273
+ const verifyEmail = useCallback(
274
+ async (token: string): Promise<void> => {
275
+ await apiClient.verifyEmail(token);
276
+ },
277
+ [apiClient]
278
+ );
279
+
280
+ // Resend verification
281
+ const resendVerification = useCallback(async (): Promise<void> => {
282
+ await apiClient.resendVerification();
283
+ }, [apiClient]);
284
+
285
+ // Initialize auth state
286
+ useEffect(() => {
287
+ const initializeAuth = async () => {
288
+ // Check for OAuth callback
289
+ const callbackParams = parseOAuthCallback();
290
+ if (callbackParams?.code) {
291
+ const pathParts = window.location.pathname.split('/');
292
+ const provider = pathParts[pathParts.length - 1] as AuthProviderType;
293
+ const storedState = getStoredOAuthState(provider);
294
+
295
+ if (storedState && callbackParams.state === storedState.state) {
296
+ try {
297
+ const response = await apiClient.exchangeOAuthCode(
298
+ provider,
299
+ callbackParams.code,
300
+ callbackParams.state
301
+ );
302
+ await handleAuthSuccess(response, provider);
303
+ clearOAuthState(provider);
304
+
305
+ // Clean URL
306
+ window.history.replaceState({}, '', window.location.pathname);
307
+ return;
308
+ } catch (error) {
309
+ clearOAuthState(provider);
310
+ dispatch({ type: 'ERROR', error: error as AuthError });
311
+ return;
312
+ }
313
+ }
314
+ }
315
+
316
+ // Check for existing token
317
+ const token = tokenManager.getAccessToken();
318
+
319
+ if (!token) {
320
+ dispatch({ type: 'UNAUTHENTICATED' });
321
+ return;
322
+ }
323
+
324
+ // Validate token and get user
325
+ if (tokenManager.isTokenExpired(token)) {
326
+ // Try to refresh
327
+ const refreshToken = tokenManager.getRefreshToken();
328
+ if (refreshToken) {
329
+ await refreshSession();
330
+ } else {
331
+ tokenManager.clearTokens();
332
+ dispatch({ type: 'UNAUTHENTICATED' });
333
+ }
334
+ return;
335
+ }
336
+
337
+ // Token is valid, get user
338
+ try {
339
+ const user = await apiClient.getUser();
340
+ const session: AuthSession = {
341
+ user,
342
+ accessToken: token,
343
+ refreshToken: tokenManager.getRefreshToken() || undefined,
344
+ expiresAt: tokenManager.getTokenExpiration(token) || Date.now() + 3600000,
345
+ provider: 'email', // Will be updated from token if available
346
+ };
347
+
348
+ dispatch({ type: 'AUTHENTICATED', user, session });
349
+ } catch {
350
+ tokenManager.clearTokens();
351
+ dispatch({ type: 'UNAUTHENTICATED' });
352
+ }
353
+ };
354
+
355
+ void initializeAuth();
356
+ }, [apiClient, tokenManager, refreshSession, handleAuthSuccess]);
357
+
358
+ // Set up auto-refresh
359
+ useEffect(() => {
360
+ if (config.autoRefresh === false) return;
361
+ if (!state.isAuthenticated) return;
362
+
363
+ const threshold = (config.refreshThreshold || 300) * 1000;
364
+ const checkInterval = Math.min(threshold / 2, 60000); // Check at least every minute
365
+
366
+ refreshIntervalRef.current = setInterval(() => {
367
+ if (tokenManager.shouldRefresh(config.refreshThreshold || 300)) {
368
+ void refreshSession();
369
+ }
370
+ }, checkInterval);
371
+
372
+ return () => {
373
+ if (refreshIntervalRef.current) {
374
+ clearInterval(refreshIntervalRef.current);
375
+ }
376
+ };
377
+ }, [
378
+ config.autoRefresh,
379
+ config.refreshThreshold,
380
+ state.isAuthenticated,
381
+ tokenManager,
382
+ refreshSession,
383
+ ]);
384
+
385
+ const value: AuthContextValue = useMemo(
386
+ () => ({
387
+ ...state,
388
+ config,
389
+ tokenManager,
390
+ apiClient,
391
+ signIn,
392
+ signUp,
393
+ signOut,
394
+ signInWithProvider,
395
+ refreshSession,
396
+ resetPassword,
397
+ updatePassword,
398
+ updateUser,
399
+ verifyEmail,
400
+ resendVerification,
401
+ }),
402
+ [
403
+ state,
404
+ config,
405
+ tokenManager,
406
+ apiClient,
407
+ signIn,
408
+ signUp,
409
+ signOut,
410
+ signInWithProvider,
411
+ refreshSession,
412
+ resetPassword,
413
+ updatePassword,
414
+ updateUser,
415
+ verifyEmail,
416
+ resendVerification,
417
+ ]
418
+ );
419
+
420
+ return <AuthContext.Provider value={value}>{children}</AuthContext.Provider>;
421
+ }
422
+
423
+ // ============================================================================
424
+ // Hook
425
+ // ============================================================================
426
+
427
+ export function useAuthContext(): AuthContextValue {
428
+ const context = useContext(AuthContext);
429
+
430
+ if (!context) {
431
+ throw new Error('useAuthContext must be used within an AuthProvider');
432
+ }
433
+
434
+ return context;
435
+ }
@@ -0,0 +1 @@
1
+ export { AuthProvider, useAuthContext } from './AuthContext';
@@ -0,0 +1,284 @@
1
+ // ============================================================================
2
+ // User Types
3
+ // ============================================================================
4
+
5
+ export interface AuthUser {
6
+ id: string;
7
+ email: string;
8
+ name?: string;
9
+ avatar?: string;
10
+ emailVerified?: boolean;
11
+ provider?: AuthProvider;
12
+ metadata?: Record<string, unknown>;
13
+ createdAt?: string;
14
+ updatedAt?: string;
15
+ }
16
+
17
+ // ============================================================================
18
+ // Provider Types
19
+ // ============================================================================
20
+
21
+ export type AuthProvider =
22
+ | 'email'
23
+ | 'google'
24
+ | 'github'
25
+ | 'facebook'
26
+ | 'apple'
27
+ | 'twitter'
28
+ | 'custom';
29
+
30
+ export interface OAuthProviderConfig {
31
+ clientId: string;
32
+ redirectUri?: string;
33
+ scope?: string[];
34
+ }
35
+
36
+ export interface AuthProviderConfig {
37
+ google?: OAuthProviderConfig;
38
+ github?: OAuthProviderConfig;
39
+ facebook?: OAuthProviderConfig;
40
+ apple?: OAuthProviderConfig;
41
+ twitter?: OAuthProviderConfig;
42
+ custom?: {
43
+ name: string;
44
+ authorizationUrl: string;
45
+ tokenUrl: string;
46
+ clientId: string;
47
+ redirectUri?: string;
48
+ scope?: string[];
49
+ };
50
+ }
51
+
52
+ // ============================================================================
53
+ // Session & Token Types
54
+ // ============================================================================
55
+
56
+ export interface AuthSession {
57
+ user: AuthUser;
58
+ accessToken: string;
59
+ refreshToken?: string;
60
+ expiresAt: number;
61
+ provider: AuthProvider;
62
+ }
63
+
64
+ export interface TokenPayload {
65
+ sub: string;
66
+ email?: string;
67
+ name?: string;
68
+ exp: number;
69
+ iat: number;
70
+ [key: string]: unknown;
71
+ }
72
+
73
+ export type TokenStorage = 'localStorage' | 'cookie' | 'memory';
74
+
75
+ // ============================================================================
76
+ // Auth State Types
77
+ // ============================================================================
78
+
79
+ export interface AuthState {
80
+ user: AuthUser | null;
81
+ session: AuthSession | null;
82
+ isAuthenticated: boolean;
83
+ isLoading: boolean;
84
+ error: AuthError | null;
85
+ }
86
+
87
+ export interface AuthError {
88
+ code: AuthErrorCode;
89
+ message: string;
90
+ details?: unknown;
91
+ }
92
+
93
+ export type AuthErrorCode =
94
+ | 'INVALID_CREDENTIALS'
95
+ | 'USER_NOT_FOUND'
96
+ | 'USER_ALREADY_EXISTS'
97
+ | 'EMAIL_NOT_VERIFIED'
98
+ | 'INVALID_TOKEN'
99
+ | 'TOKEN_EXPIRED'
100
+ | 'NETWORK_ERROR'
101
+ | 'PROVIDER_ERROR'
102
+ | 'UNAUTHORIZED'
103
+ | 'FORBIDDEN'
104
+ | 'UNKNOWN_ERROR';
105
+
106
+ // ============================================================================
107
+ // Auth Config Types
108
+ // ============================================================================
109
+
110
+ export interface AuthConfig {
111
+ /**
112
+ * Base URL for API requests (e.g., '/api/auth' or 'https://api.example.com/auth')
113
+ */
114
+ apiBaseUrl: string;
115
+
116
+ /**
117
+ * OAuth providers configuration
118
+ */
119
+ providers?: AuthProviderConfig;
120
+
121
+ /**
122
+ * Token storage method
123
+ * @default 'cookie'
124
+ */
125
+ tokenStorage?: TokenStorage;
126
+
127
+ /**
128
+ * Cookie options (only used when tokenStorage is 'cookie')
129
+ */
130
+ cookieOptions?: {
131
+ secure?: boolean;
132
+ sameSite?: 'strict' | 'lax' | 'none';
133
+ domain?: string;
134
+ path?: string;
135
+ };
136
+
137
+ /**
138
+ * Auto refresh token before expiration
139
+ * @default true
140
+ */
141
+ autoRefresh?: boolean;
142
+
143
+ /**
144
+ * Refresh token before this many seconds before expiration
145
+ * @default 300 (5 minutes)
146
+ */
147
+ refreshThreshold?: number;
148
+
149
+ /**
150
+ * Callback URLs
151
+ */
152
+ callbacks?: {
153
+ onSignIn?: (user: AuthUser, session: AuthSession) => void | Promise<void>;
154
+ onSignOut?: () => void | Promise<void>;
155
+ onError?: (error: AuthError) => void;
156
+ onSessionExpired?: () => void;
157
+ };
158
+
159
+ /**
160
+ * Custom headers to include in API requests
161
+ */
162
+ headers?: Record<string, string>;
163
+
164
+ /**
165
+ * Enable debug mode
166
+ * @default false
167
+ */
168
+ debug?: boolean;
169
+ }
170
+
171
+ // ============================================================================
172
+ // API Request/Response Types
173
+ // ============================================================================
174
+
175
+ export interface SignInCredentials {
176
+ email: string;
177
+ password: string;
178
+ remember?: boolean;
179
+ }
180
+
181
+ export interface SignUpCredentials {
182
+ email: string;
183
+ password: string;
184
+ name?: string;
185
+ metadata?: Record<string, unknown>;
186
+ }
187
+
188
+ export interface ResetPasswordRequest {
189
+ email: string;
190
+ }
191
+
192
+ export interface UpdatePasswordRequest {
193
+ currentPassword?: string;
194
+ newPassword: string;
195
+ token?: string;
196
+ }
197
+
198
+ export interface AuthResponse {
199
+ user: AuthUser;
200
+ accessToken: string;
201
+ refreshToken?: string;
202
+ expiresIn: number;
203
+ }
204
+
205
+ export interface OAuthCallbackParams {
206
+ code: string;
207
+ state?: string;
208
+ error?: string;
209
+ errorDescription?: string;
210
+ }
211
+
212
+ // ============================================================================
213
+ // Hook Return Types
214
+ // ============================================================================
215
+
216
+ export interface UseAuthReturn extends AuthState {
217
+ signIn: (credentials: SignInCredentials) => Promise<AuthResponse>;
218
+ signUp: (credentials: SignUpCredentials) => Promise<AuthResponse>;
219
+ signOut: () => Promise<void>;
220
+ signInWithProvider: (provider: AuthProvider) => Promise<void>;
221
+ refreshSession: () => Promise<AuthSession | null>;
222
+ resetPassword: (email: string) => Promise<void>;
223
+ updatePassword: (request: UpdatePasswordRequest) => Promise<void>;
224
+ updateUser: (data: Partial<AuthUser>) => Promise<AuthUser>;
225
+ verifyEmail: (token: string) => Promise<void>;
226
+ resendVerification: () => Promise<void>;
227
+ }
228
+
229
+ export interface UseSessionReturn {
230
+ session: AuthSession | null;
231
+ isLoading: boolean;
232
+ refresh: () => Promise<AuthSession | null>;
233
+ isValid: () => boolean;
234
+ getToken: () => string | null;
235
+ }
236
+
237
+ export interface UseUserReturn {
238
+ user: AuthUser | null;
239
+ isLoading: boolean;
240
+ update: (data: Partial<AuthUser>) => Promise<AuthUser>;
241
+ refresh: () => Promise<AuthUser | null>;
242
+ }
243
+
244
+ // ============================================================================
245
+ // Component Props Types
246
+ // ============================================================================
247
+
248
+ export interface SignInFormProps {
249
+ onSuccess?: (response: AuthResponse) => void;
250
+ onError?: (error: AuthError) => void;
251
+ providers?: AuthProvider[];
252
+ showRememberMe?: boolean;
253
+ showForgotPassword?: boolean;
254
+ forgotPasswordUrl?: string;
255
+ signUpUrl?: string;
256
+ redirectUrl?: string;
257
+ className?: string;
258
+ }
259
+
260
+ export interface SignUpFormProps {
261
+ onSuccess?: (response: AuthResponse) => void;
262
+ onError?: (error: AuthError) => void;
263
+ providers?: AuthProvider[];
264
+ showName?: boolean;
265
+ signInUrl?: string;
266
+ redirectUrl?: string;
267
+ className?: string;
268
+ }
269
+
270
+ export interface SocialButtonsProps {
271
+ providers: AuthProvider[];
272
+ onSuccess?: (provider: AuthProvider) => void;
273
+ onError?: (error: AuthError) => void;
274
+ mode?: 'signin' | 'signup';
275
+ className?: string;
276
+ }
277
+
278
+ export interface ProtectedRouteProps {
279
+ children: React.ReactNode;
280
+ fallback?: React.ReactNode;
281
+ redirectTo?: string;
282
+ roles?: string[];
283
+ permissions?: string[];
284
+ }