@umituz/react-native-auth 4.3.77 → 4.3.79

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 (52) hide show
  1. package/package.json +1 -1
  2. package/src/exports/domain.ts +32 -0
  3. package/src/exports/infrastructure.ts +80 -0
  4. package/src/exports/presentation.ts +115 -0
  5. package/src/exports/shared.ts +93 -0
  6. package/src/exports/utils.ts +10 -0
  7. package/src/index.ts +7 -132
  8. package/src/infrastructure/utils/listener/anonymousSignIn/attemptAnonymousSignIn.ts +81 -0
  9. package/src/infrastructure/utils/listener/anonymousSignIn/constants.ts +7 -0
  10. package/src/infrastructure/utils/listener/anonymousSignIn/createAnonymousSignInHandler.ts +59 -0
  11. package/src/infrastructure/utils/listener/anonymousSignIn/index.ts +16 -0
  12. package/src/infrastructure/utils/listener/anonymousSignIn/types.ts +21 -0
  13. package/src/presentation/components/RegisterForm/RegisterForm.tsx +91 -0
  14. package/src/presentation/components/RegisterForm/RegisterFormFields.tsx +117 -0
  15. package/src/presentation/components/RegisterForm/index.ts +7 -0
  16. package/src/presentation/components/RegisterForm/styles.ts +11 -0
  17. package/src/presentation/components/RegisterForm/types.ts +34 -0
  18. package/src/presentation/hooks/auth/index.ts +7 -0
  19. package/src/presentation/hooks/auth/types.ts +26 -0
  20. package/src/presentation/hooks/auth/useAuthBottomSheet.ts +110 -0
  21. package/src/presentation/hooks/auth/useSocialAuthHandlers.ts +56 -0
  22. package/src/shared/error-handling/handlers/ErrorHandler.ts +70 -0
  23. package/src/shared/error-handling/handlers/FormErrorHandler.ts +99 -0
  24. package/src/shared/error-handling/handlers/index.ts +7 -0
  25. package/src/shared/error-handling/index.ts +19 -0
  26. package/src/shared/error-handling/mappers/ErrorMapper.ts +115 -0
  27. package/src/shared/error-handling/mappers/FieldErrorMapper.ts +97 -0
  28. package/src/shared/error-handling/mappers/index.ts +6 -0
  29. package/src/shared/error-handling/types/ErrorTypes.ts +23 -0
  30. package/src/shared/error-handling/types/index.ts +10 -0
  31. package/src/shared/form/builders/FieldBuilder.ts +90 -0
  32. package/src/shared/form/builders/FormBuilder.ts +126 -0
  33. package/src/shared/form/builders/index.ts +9 -0
  34. package/src/shared/form/index.ts +51 -0
  35. package/src/shared/form/state/FormState.ts +114 -0
  36. package/src/shared/form/state/index.ts +16 -0
  37. package/src/shared/form/types/FormTypes.ts +30 -0
  38. package/src/shared/form/types/index.ts +11 -0
  39. package/src/shared/form/utils/fieldUtils.ts +115 -0
  40. package/src/shared/form/utils/formUtils.ts +113 -0
  41. package/src/shared/form/utils/index.ts +6 -0
  42. package/src/shared/validation/index.ts +23 -0
  43. package/src/shared/validation/rules/ValidationRule.ts +71 -0
  44. package/src/shared/validation/rules/index.ts +5 -0
  45. package/src/shared/validation/sanitizers/EmailSanitizer.ts +22 -0
  46. package/src/shared/validation/sanitizers/PasswordSanitizer.ts +24 -0
  47. package/src/shared/validation/sanitizers/index.ts +6 -0
  48. package/src/shared/validation/types.ts +26 -0
  49. package/src/shared/validation/validators/EmailValidator.ts +61 -0
  50. package/src/shared/validation/validators/NameValidator.ts +52 -0
  51. package/src/shared/validation/validators/PasswordValidator.ts +92 -0
  52. package/src/shared/validation/validators/index.ts +8 -0
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Anonymous Sign-In Types
3
+ */
4
+
5
+ export interface AnonymousSignInCallbacks {
6
+ onSignInSuccess: () => void;
7
+ onSignInFailure: (error: Error) => void;
8
+ }
9
+
10
+ export interface AnonymousSignInOptions {
11
+ maxRetries?: number;
12
+ retryDelay?: number;
13
+ timeout?: number;
14
+ }
15
+
16
+ export interface AnonymousStore {
17
+ setFirebaseUser: (user: any) => void;
18
+ setLoading: (loading: boolean) => void;
19
+ setInitialized: (initialized: boolean) => void;
20
+ setError: (error: string | null) => void;
21
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Register Form Component
3
+ * Main registration form with error display and actions
4
+ */
5
+
6
+ import React, { memo } from 'react';
7
+ import { AtomicButton } from '@umituz/react-native-design-system/atoms';
8
+ import { useRegisterForm } from '../../hooks/useRegisterForm';
9
+ import { AuthErrorDisplay } from '../AuthErrorDisplay';
10
+ import { AuthLink } from '../AuthLink';
11
+ import { AuthLegalLinks } from '../AuthLegalLinks';
12
+ import { RegisterFormFields } from './RegisterFormFields';
13
+ import { styles } from './styles';
14
+ import type { RegisterFormProps } from './types';
15
+
16
+ export const RegisterForm = memo<RegisterFormProps>(({
17
+ translations,
18
+ onNavigateToLogin,
19
+ termsUrl,
20
+ privacyUrl,
21
+ onTermsPress,
22
+ onPrivacyPress,
23
+ }) => {
24
+ const {
25
+ displayName,
26
+ email,
27
+ password,
28
+ confirmPassword,
29
+ fieldErrors,
30
+ loading,
31
+ passwordRequirements,
32
+ passwordsMatch,
33
+ handleDisplayNameChange,
34
+ handleEmailChange,
35
+ handlePasswordChange,
36
+ handleConfirmPasswordChange,
37
+ handleSignUp,
38
+ displayError,
39
+ } = useRegisterForm();
40
+
41
+ return (
42
+ <>
43
+ <RegisterFormFields
44
+ displayName={displayName}
45
+ email={email}
46
+ password={password}
47
+ confirmPassword={confirmPassword}
48
+ fieldErrors={fieldErrors}
49
+ loading={loading}
50
+ passwordRequirements={passwordRequirements}
51
+ passwordsMatch={passwordsMatch}
52
+ translations={translations}
53
+ onDisplayNameChange={handleDisplayNameChange}
54
+ onEmailChange={handleEmailChange}
55
+ onPasswordChange={handlePasswordChange}
56
+ onConfirmPasswordChange={handleConfirmPasswordChange}
57
+ onSubmit={handleSignUp}
58
+ />
59
+
60
+ <AuthErrorDisplay error={displayError} />
61
+
62
+ <AtomicButton
63
+ variant="primary"
64
+ onPress={() => void handleSignUp()}
65
+ disabled={loading || !email.trim() || !password || !confirmPassword}
66
+ fullWidth
67
+ style={styles.signUpButton}
68
+ >
69
+ {translations.signUp}
70
+ </AtomicButton>
71
+
72
+ <AuthLink
73
+ text={translations.alreadyHaveAccount}
74
+ linkText={translations.signIn}
75
+ onPress={onNavigateToLogin}
76
+ disabled={loading}
77
+ />
78
+
79
+ <AuthLegalLinks
80
+ translations={translations.legal}
81
+ termsUrl={termsUrl}
82
+ privacyUrl={privacyUrl}
83
+ onTermsPress={onTermsPress}
84
+ onPrivacyPress={onPrivacyPress}
85
+ prefixText={translations.bySigningUp}
86
+ />
87
+ </>
88
+ );
89
+ });
90
+
91
+ RegisterForm.displayName = 'RegisterForm';
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Register Form Fields
3
+ * Individual form field components for registration
4
+ */
5
+
6
+ import React, { useRef } from 'react';
7
+ import { TextInput } from 'react-native';
8
+ import { FormTextInput } from '../form/FormTextInput';
9
+ import { FormEmailInput } from '../form/FormEmailInput';
10
+ import { FormPasswordInput } from '../form/FormPasswordInput';
11
+ import { PasswordStrengthIndicator } from '../PasswordStrengthIndicator';
12
+ import { PasswordMatchIndicator } from '../PasswordMatchIndicator';
13
+ import type { RegisterFormTranslations } from './types';
14
+
15
+ export interface RegisterFormFieldsProps {
16
+ displayName: string;
17
+ email: string;
18
+ password: string;
19
+ confirmPassword: string;
20
+ fieldErrors: Record<string, string | null>;
21
+ loading: boolean;
22
+ passwordRequirements: any;
23
+ passwordsMatch: boolean;
24
+ translations: RegisterFormTranslations;
25
+ onDisplayNameChange: (value: string) => void;
26
+ onEmailChange: (value: string) => void;
27
+ onPasswordChange: (value: string) => void;
28
+ onConfirmPasswordChange: (value: string) => void;
29
+ onSubmit: () => void;
30
+ }
31
+
32
+ export const RegisterFormFields: React.FC<RegisterFormFieldsProps> = ({
33
+ displayName,
34
+ email,
35
+ password,
36
+ confirmPassword,
37
+ fieldErrors,
38
+ loading,
39
+ passwordRequirements,
40
+ passwordsMatch,
41
+ translations,
42
+ onDisplayNameChange,
43
+ onEmailChange,
44
+ onPasswordChange,
45
+ onConfirmPasswordChange,
46
+ onSubmit,
47
+ }) => {
48
+ const emailRef = useRef<React.ElementRef<typeof TextInput>>(null);
49
+ const passwordRef = useRef<React.ElementRef<typeof TextInput>>(null);
50
+ const confirmPasswordRef = useRef<React.ElementRef<typeof TextInput>>(null);
51
+
52
+ return (
53
+ <>
54
+ <FormTextInput
55
+ value={displayName}
56
+ onChangeText={onDisplayNameChange}
57
+ label={translations.displayName}
58
+ placeholder={translations.displayNamePlaceholder}
59
+ error={fieldErrors.displayName}
60
+ disabled={loading}
61
+ autoCapitalize="words"
62
+ onSubmitEditing={() => emailRef.current?.focus()}
63
+ returnKeyType="next"
64
+ />
65
+
66
+ <FormEmailInput
67
+ ref={emailRef}
68
+ value={email}
69
+ onChangeText={onEmailChange}
70
+ label={translations.email}
71
+ placeholder={translations.emailPlaceholder}
72
+ error={fieldErrors.email}
73
+ disabled={loading}
74
+ onSubmitEditing={() => passwordRef.current?.focus()}
75
+ returnKeyType="next"
76
+ />
77
+
78
+ <FormPasswordInput
79
+ ref={passwordRef}
80
+ value={password}
81
+ onChangeText={onPasswordChange}
82
+ label={translations.password}
83
+ placeholder={translations.passwordPlaceholder}
84
+ error={fieldErrors.password}
85
+ disabled={loading}
86
+ onSubmitEditing={() => confirmPasswordRef.current?.focus()}
87
+ returnKeyType="next"
88
+ style={{ marginBottom: 4 }}
89
+ />
90
+ {password.length > 0 && (
91
+ <PasswordStrengthIndicator
92
+ translations={translations.passwordStrength}
93
+ requirements={passwordRequirements}
94
+ />
95
+ )}
96
+
97
+ <FormPasswordInput
98
+ ref={confirmPasswordRef}
99
+ value={confirmPassword}
100
+ onChangeText={onConfirmPasswordChange}
101
+ label={translations.confirmPassword}
102
+ placeholder={translations.confirmPasswordPlaceholder}
103
+ error={fieldErrors.confirmPassword}
104
+ disabled={loading}
105
+ onSubmitEditing={() => void onSubmit()}
106
+ returnKeyType="done"
107
+ style={{ marginBottom: 4 }}
108
+ />
109
+ {confirmPassword.length > 0 && (
110
+ <PasswordMatchIndicator
111
+ translations={translations.passwordMatch}
112
+ isMatch={passwordsMatch}
113
+ />
114
+ )}
115
+ </>
116
+ );
117
+ };
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Register Form Module Exports
3
+ */
4
+
5
+ export { RegisterForm } from './RegisterForm';
6
+ export { RegisterFormFields } from './RegisterFormFields';
7
+ export type { RegisterFormTranslations, RegisterFormProps } from './types';
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Register Form Styles
3
+ */
4
+
5
+ import { StyleSheet } from 'react-native';
6
+
7
+ export const styles = StyleSheet.create({
8
+ passwordInput: { marginBottom: 4 },
9
+ confirmPasswordInput: { marginBottom: 4 },
10
+ signUpButton: { minHeight: 52, marginBottom: 16, marginTop: 8 },
11
+ });
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Register Form Types
3
+ */
4
+
5
+ import type { AuthLegalLinksTranslations } from '../AuthLegalLinks';
6
+ import type { PasswordStrengthTranslations } from '../PasswordStrengthIndicator';
7
+ import type { PasswordMatchTranslations } from '../PasswordMatchIndicator';
8
+
9
+ export interface RegisterFormTranslations {
10
+ displayName: string;
11
+ displayNamePlaceholder: string;
12
+ email: string;
13
+ emailPlaceholder: string;
14
+ password: string;
15
+ passwordPlaceholder: string;
16
+ confirmPassword: string;
17
+ confirmPasswordPlaceholder: string;
18
+ signUp: string;
19
+ alreadyHaveAccount: string;
20
+ signIn: string;
21
+ bySigningUp: string;
22
+ legal: AuthLegalLinksTranslations;
23
+ passwordStrength: PasswordStrengthTranslations;
24
+ passwordMatch: PasswordMatchTranslations;
25
+ }
26
+
27
+ export interface RegisterFormProps {
28
+ translations: RegisterFormTranslations;
29
+ onNavigateToLogin: () => void;
30
+ termsUrl?: string;
31
+ privacyUrl?: string;
32
+ onTermsPress?: () => void;
33
+ onPrivacyPress?: () => void;
34
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Auth Hooks Module Exports
3
+ */
4
+
5
+ export { useAuthBottomSheet } from './useAuthBottomSheet';
6
+ export { useSocialAuthHandlers } from './useSocialAuthHandlers';
7
+ export type { UseAuthBottomSheetParams, SocialAuthConfiguration, SocialAuthHandlers } from './types';
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Auth Bottom Sheet Types
3
+ */
4
+
5
+ import type { GoogleAuthConfig } from '../useGoogleAuth';
6
+ import type { SocialAuthProvider } from '../../domain/value-objects/AuthConfig';
7
+
8
+ export interface SocialAuthConfiguration {
9
+ google?: GoogleAuthConfig;
10
+ apple?: { enabled: boolean };
11
+ }
12
+
13
+ export interface UseAuthBottomSheetParams {
14
+ socialConfig?: SocialAuthConfiguration;
15
+ onGoogleSignIn?: () => Promise<void>;
16
+ onAppleSignIn?: () => Promise<void>;
17
+ /** Called when auth completes successfully (login or register) */
18
+ onAuthSuccess?: () => void;
19
+ }
20
+
21
+ export interface SocialAuthHandlers {
22
+ handleGoogleSignIn: () => Promise<void>;
23
+ handleAppleSignIn: () => Promise<void>;
24
+ googleLoading: boolean;
25
+ appleLoading: boolean;
26
+ }
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Auth Bottom Sheet Hook (Refactored)
3
+ * Main hook for auth bottom sheet management
4
+ */
5
+
6
+ import { useCallback, useEffect, useMemo, useRef } from 'react';
7
+ import type { BottomSheetModalRef } from '@umituz/react-native-design-system/molecules';
8
+ import { useAuthModalStore } from '../../stores/authModalStore';
9
+ import { useAuth } from '../useAuth';
10
+ import { determineEnabledProviders } from '../../utils/socialAuthHandler.util';
11
+ import { useAuthTransitions, executeAfterAuth } from '../../utils/authTransition.util';
12
+ import { useSocialAuthHandlers } from './useSocialAuthHandlers';
13
+ import type { SocialAuthProvider } from '../../../domain/value-objects/AuthConfig';
14
+ import type { UseAuthBottomSheetParams } from './types';
15
+
16
+ export function useAuthBottomSheet(params: UseAuthBottomSheetParams = {}) {
17
+ const { socialConfig, onGoogleSignIn, onAppleSignIn, onAuthSuccess } = params;
18
+
19
+ const modalRef = useRef<BottomSheetModalRef>(null);
20
+
21
+ const { isVisible, mode, hideAuthModal, setMode, executePendingCallback, clearPendingCallback } =
22
+ useAuthModalStore();
23
+ const { isAuthenticated, isAnonymous } = useAuth();
24
+
25
+ // Social auth handlers
26
+ const { handleGoogleSignIn, handleAppleSignIn, googleLoading, appleLoading } =
27
+ useSocialAuthHandlers(socialConfig, onGoogleSignIn, onAppleSignIn);
28
+
29
+ // Determine enabled providers
30
+ const providers = useMemo<SocialAuthProvider[]>(() => {
31
+ return determineEnabledProviders(socialConfig, appleAvailable, googleConfigured);
32
+ }, [socialConfig, appleAvailable, googleConfigured]);
33
+
34
+ // Handle visibility sync with modalRef
35
+ useEffect(() => {
36
+ if (isVisible) {
37
+ modalRef.current?.present();
38
+ } else {
39
+ modalRef.current?.dismiss();
40
+ }
41
+ }, [isVisible]);
42
+
43
+ const handleDismiss = useCallback(() => {
44
+ hideAuthModal();
45
+ clearPendingCallback();
46
+ }, [hideAuthModal, clearPendingCallback]);
47
+
48
+ const handleClose = useCallback(() => {
49
+ modalRef.current?.dismiss();
50
+ handleDismiss();
51
+ }, [handleDismiss]);
52
+
53
+ // Handle auth transitions
54
+ useAuthTransitions(
55
+ { isAuthenticated, isAnonymous, isVisible },
56
+ (result) => {
57
+ if (result.shouldClose) {
58
+ modalRef.current?.dismiss();
59
+ hideAuthModal();
60
+ onAuthSuccess?.();
61
+
62
+ const timeoutId = executeAfterAuth(() => {
63
+ executePendingCallback();
64
+ });
65
+
66
+ return () => clearTimeout(timeoutId);
67
+ }
68
+ return undefined;
69
+ }
70
+ );
71
+
72
+ const handleNavigateToRegister = useCallback(() => {
73
+ setMode('register');
74
+ }, [setMode]);
75
+
76
+ const handleNavigateToLogin = useCallback(() => {
77
+ setMode('login');
78
+ }, [setMode]);
79
+
80
+ return useMemo(
81
+ () => ({
82
+ modalRef,
83
+ googleLoading,
84
+ appleLoading,
85
+ mode,
86
+ providers,
87
+ handleDismiss,
88
+ handleClose,
89
+ handleNavigateToRegister,
90
+ handleNavigateToLogin,
91
+ handleGoogleSignIn,
92
+ handleAppleSignIn,
93
+ }),
94
+ [
95
+ modalRef,
96
+ googleLoading,
97
+ appleLoading,
98
+ mode,
99
+ providers,
100
+ handleDismiss,
101
+ handleClose,
102
+ handleNavigateToRegister,
103
+ handleNavigateToLogin,
104
+ handleGoogleSignIn,
105
+ handleAppleSignIn,
106
+ ]
107
+ );
108
+ }
109
+
110
+ // TODO: Fix appleAvailable and googleConfigured references
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Social Auth Handlers
3
+ * Handles social authentication logic
4
+ */
5
+
6
+ import { useCallback, useState } from 'react';
7
+ import { useGoogleAuth } from '../useGoogleAuth';
8
+ import { useAppleAuth } from '../useAppleAuth';
9
+ import type { SocialAuthConfiguration, SocialAuthHandlers } from './types';
10
+
11
+ export function useSocialAuthHandlers(
12
+ socialConfig?: SocialAuthConfiguration,
13
+ onGoogleSignIn?: () => Promise<void>,
14
+ onAppleSignIn?: () => Promise<void>
15
+ ): SocialAuthHandlers {
16
+ // Social Auth Hooks
17
+ const { signInWithGoogle, googleConfigured } = useGoogleAuth(socialConfig?.google);
18
+ const { signInWithApple, appleAvailable } = useAppleAuth();
19
+
20
+ // Social auth loading states
21
+ const [googleLoading, setGoogleLoading] = useState(false);
22
+ const [appleLoading, setAppleLoading] = useState(false);
23
+
24
+ const handleGoogleSignIn = useCallback(async () => {
25
+ setGoogleLoading(true);
26
+ try {
27
+ if (onGoogleSignIn) {
28
+ await onGoogleSignIn();
29
+ } else if (signInWithGoogle) {
30
+ await signInWithGoogle();
31
+ }
32
+ } finally {
33
+ setGoogleLoading(false);
34
+ }
35
+ }, [onGoogleSignIn, signInWithGoogle]);
36
+
37
+ const handleAppleSignIn = useCallback(async () => {
38
+ setAppleLoading(true);
39
+ try {
40
+ if (onAppleSignIn) {
41
+ await onAppleSignIn();
42
+ } else if (signInWithApple) {
43
+ await signInWithApple();
44
+ }
45
+ } finally {
46
+ setAppleLoading(false);
47
+ }
48
+ }, [onAppleSignIn, signInWithApple]);
49
+
50
+ return {
51
+ handleGoogleSignIn,
52
+ handleAppleSignIn,
53
+ googleLoading,
54
+ appleLoading,
55
+ };
56
+ }
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Error Handler
3
+ * Centralized error handling logic
4
+ */
5
+
6
+ import type { ErrorMap } from '../types/ErrorTypes';
7
+ import { ErrorMapper, DEFAULT_AUTH_ERROR_MAPPINGS } from '../mappers/ErrorMapper';
8
+
9
+ export class ErrorHandler {
10
+ private mapper: ErrorMapper;
11
+ private translations?: ErrorMap;
12
+
13
+ constructor(translations?: ErrorMap, config?: typeof DEFAULT_AUTH_ERROR_MAPPINGS) {
14
+ this.mapper = new ErrorMapper(config || DEFAULT_AUTH_ERROR_MAPPINGS);
15
+ this.translations = translations;
16
+ }
17
+
18
+ /**
19
+ * Handle error and return user-friendly message
20
+ */
21
+ handle(error: unknown): string {
22
+ const key = this.mapper.getLocalizationKey(error);
23
+ return this.mapper.resolveMessage(key, this.translations);
24
+ }
25
+
26
+ /**
27
+ * Get localization key for error
28
+ */
29
+ getErrorKey(error: unknown): string {
30
+ return this.mapper.getLocalizationKey(error);
31
+ }
32
+
33
+ /**
34
+ * Update translations
35
+ */
36
+ setTranslations(translations: ErrorMap): void {
37
+ this.translations = translations;
38
+ }
39
+
40
+ /**
41
+ * Set error mappings
42
+ */
43
+ setMappings(config: Partial<typeof DEFAULT_AUTH_ERROR_MAPPINGS>): void {
44
+ this.mapper.setMappings(config);
45
+ }
46
+
47
+ /**
48
+ * Check if error is of specific type
49
+ */
50
+ isErrorType(error: unknown, errorName: string): boolean {
51
+ return error instanceof Error && error.name === errorName;
52
+ }
53
+
54
+ /**
55
+ * Log error in development
56
+ */
57
+ logError(context: string, error: unknown): void {
58
+ if (__DEV__) {
59
+ console.error(`[${context}] Error:`, error);
60
+ }
61
+ }
62
+
63
+ /**
64
+ * Handle and log error
65
+ */
66
+ handleAndLog(context: string, error: unknown): string {
67
+ this.logError(context, error);
68
+ return this.handle(error);
69
+ }
70
+ }
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Form Error Handler
3
+ * Handles form-specific error logic
4
+ */
5
+
6
+ import type { FieldError, ErrorMap } from '../types/ErrorTypes';
7
+ import { ErrorHandler } from './ErrorHandler';
8
+ import { FieldErrorMapper } from '../mappers/FieldErrorMapper';
9
+
10
+ export interface FormErrorHandlerConfig {
11
+ translations?: ErrorMap;
12
+ errorMappings?: Partial<typeof DEFAULT_AUTH_ERROR_MAPPINGS>;
13
+ }
14
+
15
+ export class FormErrorHandler extends ErrorHandler {
16
+ constructor(config: FormErrorHandlerConfig = {}) {
17
+ super(config.translations, config.errorMappings);
18
+ }
19
+
20
+ /**
21
+ * Handle validation result errors
22
+ */
23
+ handleValidationErrors(
24
+ errors: FieldError[],
25
+ translations?: ErrorMap
26
+ ): Record<string, string> {
27
+ const mappedErrors = translations
28
+ ? FieldErrorMapper.mapErrorsToMessages(errors, translations)
29
+ : errors;
30
+
31
+ return FieldErrorMapper.toRecord(mappedErrors);
32
+ }
33
+
34
+ /**
35
+ * Get form field errors with null support
36
+ */
37
+ getFormFieldErrors(
38
+ errors: FieldError[],
39
+ fields: string[]
40
+ ): Record<string, string | null> {
41
+ return FieldErrorMapper.toFormFieldErrors(errors, fields);
42
+ }
43
+
44
+ /**
45
+ * Check if form has errors
46
+ */
47
+ formHasErrors(errors: FieldError[]): boolean {
48
+ return errors.length > 0;
49
+ }
50
+
51
+ /**
52
+ * Check if specific field has error
53
+ */
54
+ fieldHasError(errors: FieldError[], field: string): boolean {
55
+ return FieldErrorMapper.extractFieldError(errors, field) !== null;
56
+ }
57
+
58
+ /**
59
+ * Get first form error message
60
+ */
61
+ getFirstFormError(errors: FieldError[]): string | null {
62
+ return FieldErrorMapper.getFirstErrorMessage(errors);
63
+ }
64
+
65
+ /**
66
+ * Handle auth error for form
67
+ */
68
+ handleAuthError(error: unknown): string {
69
+ return this.handle(error);
70
+ }
71
+
72
+ /**
73
+ * Clear field errors for specific fields
74
+ */
75
+ clearFieldErrors(errors: FieldError[], fields: string[]): FieldError[] {
76
+ return FieldErrorMapper.excludeFields(errors, fields);
77
+ }
78
+
79
+ /**
80
+ * Filter errors to only specific fields
81
+ */
82
+ filterToFields(errors: FieldError[], fields: string[]): FieldError[] {
83
+ return FieldErrorMapper.filterByFields(errors, fields);
84
+ }
85
+
86
+ /**
87
+ * Create field error
88
+ */
89
+ createFieldError(field: string, message: string): FieldError {
90
+ return FieldErrorMapper.createFieldError(field, message);
91
+ }
92
+
93
+ /**
94
+ * Merge multiple error arrays
95
+ */
96
+ mergeErrors(...errorArrays: FieldError[][]): FieldError[] {
97
+ return FieldErrorMapper.mergeFieldErrors(...errorArrays);
98
+ }
99
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Error Handlers Public API
3
+ */
4
+
5
+ export { ErrorHandler } from './ErrorHandler';
6
+ export { FormErrorHandler } from './FormErrorHandler';
7
+ export type { FormErrorHandlerConfig } from './FormErrorHandler';
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Error Handling Module Public API
3
+ * Centralized error handling system
4
+ */
5
+
6
+ // Types
7
+ export type {
8
+ FieldError,
9
+ FormFieldErrors,
10
+ ErrorMap,
11
+ ErrorMappingConfig,
12
+ } from './types';
13
+
14
+ // Mappers
15
+ export { ErrorMapper, DEFAULT_AUTH_ERROR_MAPPINGS, FieldErrorMapper } from './mappers';
16
+
17
+ // Handlers
18
+ export { ErrorHandler, FormErrorHandler } from './handlers';
19
+ export type { FormErrorHandlerConfig } from './handlers';