mulguard 1.1.1 → 1.1.3

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.
@@ -1,15 +1,37 @@
1
1
  import { AuthErrorCode } from './errors';
2
+ /**
3
+ * User interface representing an authenticated user.
4
+ *
5
+ * @property id - Unique user identifier
6
+ * @property email - User email address
7
+ * @property name - User display name
8
+ * @property avatar - Optional avatar URL
9
+ * @property roles - Optional user roles array
10
+ * @property emailVerified - Email verification status
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const user: User = {
15
+ * id: '123',
16
+ * email: 'user@example.com',
17
+ * name: 'John Doe',
18
+ * emailVerified: true
19
+ * }
20
+ * ```
21
+ */
2
22
  export interface User {
3
- id: string;
4
- email: string;
5
- name: string;
6
- avatar?: string;
7
- roles?: string[];
8
- emailVerified?: boolean;
9
- [key: string]: unknown;
23
+ readonly id: string;
24
+ readonly email: string;
25
+ readonly name: string;
26
+ readonly avatar?: string;
27
+ readonly roles?: readonly string[];
28
+ readonly emailVerified?: boolean;
29
+ readonly [key: string]: unknown;
10
30
  }
11
31
  /**
12
- * Session object containing user information and authentication tokens
32
+ * Session object containing user information and authentication tokens.
33
+ *
34
+ * @template TUser - User type (defaults to base User)
13
35
  *
14
36
  * @property user - User object with authentication information
15
37
  * @property expiresAt - Session expiration date/time
@@ -18,114 +40,251 @@ export interface User {
18
40
  * @property tokenType - Type of token (default: 'Bearer')
19
41
  * @property expiresIn - Token expiration time in seconds (optional)
20
42
  * @property refreshTokenExpiresAt - Refresh token expiration date/time (optional)
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * const session: Session = {
47
+ * user: { id: '123', email: 'user@example.com', name: 'John' },
48
+ * expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000),
49
+ * accessToken: 'token123',
50
+ * tokenType: 'Bearer'
51
+ * }
52
+ * ```
21
53
  */
22
- export interface Session {
23
- user: User;
24
- expiresAt: Date | string;
25
- /** Access token for API authentication (optional) */
26
- accessToken?: string;
27
- /** Refresh token for token refresh (optional) */
28
- refreshToken?: string;
29
- /** Type of token (default: 'Bearer') */
30
- tokenType?: 'Bearer' | 'Basic';
31
- /** Token expiration time in seconds (optional) */
32
- expiresIn?: number;
33
- /** Refresh token expiration date/time (optional) */
34
- refreshTokenExpiresAt?: Date | string;
35
- [key: string]: unknown;
54
+ export interface Session<TUser extends User = User> {
55
+ readonly user: TUser;
56
+ readonly expiresAt: Date | string;
57
+ readonly accessToken?: string;
58
+ readonly refreshToken?: string;
59
+ readonly tokenType?: 'Bearer' | 'Basic';
60
+ readonly expiresIn?: number;
61
+ readonly refreshTokenExpiresAt?: Date | string;
62
+ readonly [key: string]: unknown;
36
63
  }
37
64
  /**
38
- * Authentication result returned by sign-in, sign-up, and other auth actions
65
+ * Base authentication result type.
66
+ *
67
+ * @template TUser - User type (defaults to base User)
68
+ * @template TSession - Session type (defaults to base Session)
39
69
  *
40
70
  * @property success - Whether the authentication was successful
41
71
  * @property user - User object if authentication succeeded
42
72
  * @property session - Session object if authentication succeeded
43
73
  * @property error - Error message if authentication failed
44
74
  * @property errorCode - Specific error code for programmatic error handling
45
- * @property requires2FA - Whether 2FA verification is required (true when 2FA is needed)
75
+ * @property requires2FA - Whether 2FA verification is required
46
76
  * @property email - Email address (required when requires2FA is true)
47
77
  * @property userId - User ID (required when requires2FA is true)
48
78
  *
49
79
  * @example
50
80
  * ```typescript
51
- * const result = await auth.signIn.email({ email: 'user@example.com', password: 'password' })
81
+ * const result = await auth.signIn.email(credentials)
52
82
  * if (result.success) {
53
- * // Authentication successful
54
- * console.log('User:', result.user)
83
+ * // TypeScript knows result.user and result.session exist
84
+ * console.log(result.user.email)
55
85
  * } else if (result.requires2FA) {
56
- * // 2FA required
57
- * console.log('2FA required for:', result.email)
58
- * } else {
59
- * // Authentication failed
60
- * console.error('Error:', result.error, result.errorCode)
86
+ * // TypeScript knows result.email and result.userId exist
87
+ * await auth.verify2FA({ email: result.email, userId: result.userId, code: '123456' })
61
88
  * }
62
89
  * ```
63
90
  */
64
- export interface AuthResult {
65
- success: boolean;
66
- user?: User;
67
- session?: Session;
68
- error?: string;
69
- /** Error code for programmatic error handling */
70
- errorCode?: AuthErrorCode;
71
- /** Indicates if 2FA is required */
72
- requires2FA?: boolean;
73
- /** Email for 2FA flow */
74
- email?: string;
75
- /** User ID for 2FA flow */
76
- userId?: string;
91
+ export interface AuthResult<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>> {
92
+ readonly success: boolean;
93
+ readonly user?: TUser;
94
+ readonly session?: TSession;
95
+ readonly error?: string;
96
+ readonly errorCode?: AuthErrorCode;
97
+ readonly requires2FA?: boolean;
98
+ readonly email?: string;
99
+ readonly userId?: string;
77
100
  }
78
101
  /**
79
- * Two-factor authentication result
102
+ * Successful authentication result type.
103
+ *
104
+ * @template TUser - User type
105
+ * @template TSession - Session type
106
+ */
107
+ export type SuccessfulAuthResult<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>> = AuthResult<TUser, TSession> & {
108
+ readonly success: true;
109
+ readonly user: TUser;
110
+ readonly session: TSession;
111
+ };
112
+ /**
113
+ * Failed authentication result type.
114
+ *
115
+ * @template TUser - User type
116
+ * @template TSession - Session type
117
+ */
118
+ export type FailedAuthResult<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>> = AuthResult<TUser, TSession> & {
119
+ readonly success: false;
120
+ readonly error: string;
121
+ };
122
+ /**
123
+ * Two-factor authentication required result type.
80
124
  *
81
125
  * This type is returned when 2FA verification is required after initial authentication.
82
- * Use the `isTwoFactorRequired()` helper function to check if a result is of this type.
126
+ * Use the `isTwoFactorRequired()` type guard to narrow to this type.
127
+ *
128
+ * @template TUser - User type
129
+ * @template TSession - Session type
83
130
  *
84
- * @property success - Always false for 2FA required results
131
+ * @property success - Always false
85
132
  * @property requires2FA - Always true
86
133
  * @property email - Email address of the user requiring 2FA
87
134
  * @property userId - User ID requiring 2FA
88
135
  * @property errorCode - Always AuthErrorCode.TWO_FA_REQUIRED
89
- * @property twoFactorMethod - Method of 2FA (totp, sms, email) - optional
90
- * @property challengeToken - Challenge token for 2FA verification - optional
136
+ * @property twoFactorMethod - Method of 2FA (optional)
137
+ * @property challengeToken - Challenge token for 2FA verification (optional)
91
138
  *
92
139
  * @example
93
140
  * ```typescript
94
- * const result = await auth.signIn.email({ email: 'user@example.com', password: 'password' })
141
+ * const result = await auth.signIn.email(credentials)
95
142
  * if (isTwoFactorRequired(result)) {
96
143
  * // TypeScript knows result is TwoFactorAuthResult here
97
144
  * console.log('2FA required for:', result.email)
98
- * console.log('Method:', result.twoFactorMethod) // 'totp' | 'sms' | 'email'
145
+ * console.log('Method:', result.twoFactorMethod)
99
146
  * }
100
147
  * ```
101
148
  */
102
- export interface TwoFactorAuthResult extends AuthResult {
103
- success: false;
104
- requires2FA: true;
105
- email: string;
106
- userId: string;
107
- error: '2FA_REQUIRED';
108
- errorCode: AuthErrorCode.TWO_FA_REQUIRED;
109
- /** Method of 2FA (totp, sms, email) - optional */
110
- twoFactorMethod?: 'totp' | 'sms' | 'email';
111
- /** Challenge token for 2FA verification - optional */
112
- challengeToken?: string;
149
+ export interface TwoFactorAuthResult<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>> extends AuthResult<TUser, TSession> {
150
+ readonly success: false;
151
+ readonly requires2FA: true;
152
+ readonly email: string;
153
+ readonly userId: string;
154
+ readonly error: '2FA_REQUIRED';
155
+ readonly errorCode: AuthErrorCode.TWO_FA_REQUIRED;
156
+ readonly twoFactorMethod?: 'totp' | 'sms' | 'email';
157
+ readonly challengeToken?: string;
113
158
  }
114
159
  /**
115
- * Data for 2FA verification
160
+ * Email and password credentials for authentication.
161
+ *
162
+ * @property email - User email address
163
+ * @property password - User password
116
164
  */
117
- export interface Verify2FAData {
118
- email: string;
119
- userId: string;
120
- code: string;
121
- }
122
165
  export interface EmailCredentials {
123
- email: string;
124
- password: string;
166
+ readonly email: string;
167
+ readonly password: string;
125
168
  }
169
+ /**
170
+ * User registration data.
171
+ *
172
+ * @property email - User email address
173
+ * @property password - User password
174
+ * @property name - User display name
175
+ */
126
176
  export interface RegisterData {
127
- email: string;
128
- password: string;
129
- name: string;
130
- [key: string]: unknown;
177
+ readonly email: string;
178
+ readonly password: string;
179
+ readonly name: string;
180
+ readonly [key: string]: unknown;
181
+ }
182
+ /**
183
+ * Data required for 2FA verification.
184
+ *
185
+ * @property email - User email address
186
+ * @property userId - User ID
187
+ * @property code - 2FA verification code
188
+ */
189
+ export interface Verify2FAData {
190
+ readonly email: string;
191
+ readonly userId: string;
192
+ readonly code: string;
131
193
  }
194
+ /**
195
+ * Type predicate to check if AuthResult indicates success.
196
+ *
197
+ * @template TUser - User type
198
+ * @template TSession - Session type
199
+ * @param result - AuthResult to check
200
+ * @returns True if result is successful
201
+ *
202
+ * @example
203
+ * ```typescript
204
+ * const result = await auth.signIn.email(credentials)
205
+ * if (isAuthSuccess(result)) {
206
+ * // TypeScript narrows to SuccessfulAuthResult
207
+ * console.log(result.user.email) // ✅ Type-safe
208
+ * console.log(result.session.expiresAt) // ✅ Type-safe
209
+ * }
210
+ * ```
211
+ */
212
+ export declare function isAuthSuccess<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>>(result: AuthResult<TUser, TSession>): result is SuccessfulAuthResult<TUser, TSession>;
213
+ /**
214
+ * Type predicate to check if AuthResult indicates failure.
215
+ *
216
+ * @template TUser - User type
217
+ * @template TSession - Session type
218
+ * @param result - AuthResult to check
219
+ * @returns True if result indicates failure
220
+ *
221
+ * @example
222
+ * ```typescript
223
+ * const result = await auth.signIn.email(credentials)
224
+ * if (isAuthFailure(result)) {
225
+ * // TypeScript narrows to FailedAuthResult
226
+ * console.error(result.error) // ✅ Type-safe
227
+ * }
228
+ * ```
229
+ */
230
+ export declare function isAuthFailure<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>>(result: AuthResult<TUser, TSession>): result is FailedAuthResult<TUser, TSession>;
231
+ /**
232
+ * Type predicate to check if AuthResult indicates 2FA is required.
233
+ *
234
+ * @template TUser - User type
235
+ * @template TSession - Session type
236
+ * @param result - AuthResult to check
237
+ * @returns True if 2FA is required
238
+ *
239
+ * @example
240
+ * ```typescript
241
+ * const result = await auth.signIn.email(credentials)
242
+ * if (isTwoFactorRequired(result)) {
243
+ * // TypeScript narrows to TwoFactorAuthResult
244
+ * await auth.verify2FA({
245
+ * email: result.email, // ✅ Type-safe
246
+ * userId: result.userId, // ✅ Type-safe
247
+ * code: '123456'
248
+ * })
249
+ * }
250
+ * ```
251
+ */
252
+ export declare function isTwoFactorRequired<TUser extends User = User, TSession extends Session<TUser> = Session<TUser>>(result: AuthResult<TUser, TSession>): result is TwoFactorAuthResult<TUser, TSession>;
253
+ /**
254
+ * TODO: Performance
255
+ * - [ ] Consider using branded types for User.id to prevent ID mixing
256
+ * - [ ] Add type-level optimizations for discriminated unions
257
+ * - [ ] Implement compile-time validation for email format
258
+ * - [ ] Add type-level session expiration checking
259
+ *
260
+ * TODO: Features
261
+ * - [ ] Add conditional types for provider-specific result variants
262
+ * - [ ] Implement type-safe error handling with exhaustiveness checking
263
+ * - [ ] Create type-level validation for credentials strength
264
+ * - [ ] Add generic constraints for custom User/Session extensions
265
+ * - [ ] Implement type-safe middleware chain types
266
+ *
267
+ * TODO: Type Safety
268
+ * - [ ] Add branded types for sensitive data (tokens, passwords)
269
+ * - [ ] Create type guards for all public interfaces
270
+ * - [ ] Add type-level validation for registration data
271
+ * - [ ] Implement type-safe error codes with const assertions
272
+ *
273
+ * TODO: Testing
274
+ * - [ ] Add type-level tests using ts-expect
275
+ * - [ ] Test type inference in various scenarios
276
+ * - [ ] Verify type narrowing with type predicates
277
+ * - [ ] Test generic constraints and conditional types
278
+ * - [ ] Add tests for discriminated union narrowing
279
+ *
280
+ * TODO: Documentation
281
+ * - [ ] Add more JSDoc examples for generic types
282
+ * - [ ] Document type-level patterns and best practices
283
+ * - [ ] Create migration guide for custom User/Session types
284
+ *
285
+ * TODO: Limitations
286
+ * - [ ] Type inference may be limited with very deep generic chains
287
+ * - [ ] Branded types add minimal runtime overhead
288
+ * - [ ] Deep readonly types may impact performance with large objects
289
+ * - [ ] Discriminated unions require explicit type guards for narrowing
290
+ */
@@ -1,44 +1,200 @@
1
1
  /**
2
- * Authentication error codes
2
+ * Authentication error codes and error handling types.
3
+ *
4
+ * @module @mulguard/core/types/errors
5
+ */
6
+ /**
7
+ * Authentication error code enumeration.
8
+ *
9
+ * Provides specific error codes for programmatic error handling.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * if (result.errorCode === AuthErrorCode.INVALID_CREDENTIALS) {
14
+ * // Handle invalid credentials
15
+ * } else if (result.errorCode === AuthErrorCode.TWO_FA_REQUIRED) {
16
+ * // Handle 2FA requirement
17
+ * }
18
+ * ```
3
19
  */
4
20
  export declare enum AuthErrorCode {
5
- /** Invalid email or password */
21
+ /** Invalid email or password credentials */
6
22
  INVALID_CREDENTIALS = "INVALID_CREDENTIALS",
7
- /** Account is temporarily locked */
23
+ /** Account is temporarily locked due to failed attempts */
8
24
  ACCOUNT_LOCKED = "ACCOUNT_LOCKED",
9
25
  /** Account is inactive or disabled */
10
26
  ACCOUNT_INACTIVE = "ACCOUNT_INACTIVE",
11
- /** Two-factor authentication required */
27
+ /** Two-factor authentication is required */
12
28
  TWO_FA_REQUIRED = "TWO_FA_REQUIRED",
13
29
  /** Invalid two-factor authentication code */
14
30
  INVALID_TWO_FA_CODE = "INVALID_TWO_FA_CODE",
15
31
  /** Session has expired */
16
32
  SESSION_EXPIRED = "SESSION_EXPIRED",
17
- /** User is not authorized */
33
+ /** User is not authorized for this operation */
18
34
  UNAUTHORIZED = "UNAUTHORIZED",
19
- /** Network or API error */
35
+ /** Network or API communication error */
20
36
  NETWORK_ERROR = "NETWORK_ERROR",
21
- /** Validation error */
37
+ /** Input validation error */
22
38
  VALIDATION_ERROR = "VALIDATION_ERROR",
23
39
  /** Rate limit exceeded */
24
40
  RATE_LIMITED = "RATE_LIMITED",
25
- /** Unknown error */
41
+ /** Unknown or unexpected error */
26
42
  UNKNOWN_ERROR = "UNKNOWN_ERROR"
27
43
  }
28
44
  /**
29
- * Authentication error interface
45
+ * Authentication error interface.
46
+ *
47
+ * Provides structured error information with code, message, and optional metadata.
48
+ *
49
+ * @property code - Specific error code
50
+ * @property message - Human-readable error message
51
+ * @property statusCode - HTTP status code (if applicable)
52
+ * @property details - Additional error details (optional)
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * const error: AuthError = {
57
+ * code: AuthErrorCode.INVALID_CREDENTIALS,
58
+ * message: 'Invalid email or password',
59
+ * statusCode: 401
60
+ * }
61
+ * ```
30
62
  */
31
63
  export interface AuthError {
32
- /** Error code */
33
- code: AuthErrorCode;
34
- /** Human-readable error message */
35
- message: string;
36
- /** HTTP status code (if applicable) */
37
- statusCode?: number;
38
- /** Additional error details */
39
- details?: unknown;
64
+ readonly code: AuthErrorCode;
65
+ readonly message: string;
66
+ readonly statusCode?: number;
67
+ readonly details?: unknown;
40
68
  }
41
69
  /**
42
- * Create an authentication error
70
+ * Error result type for failed operations.
71
+ *
72
+ * @template TCode - Error code type (defaults to AuthErrorCode)
73
+ */
74
+ export type ErrorResult<TCode extends AuthErrorCode = AuthErrorCode> = {
75
+ readonly success: false;
76
+ readonly error: string;
77
+ readonly errorCode: TCode;
78
+ readonly details?: unknown;
79
+ };
80
+ /**
81
+ * Creates an authentication error object.
82
+ *
83
+ * @param code - Error code
84
+ * @param message - Human-readable error message
85
+ * @param statusCode - HTTP status code (optional)
86
+ * @param details - Additional error details (optional)
87
+ * @returns AuthError object
88
+ *
89
+ * @example
90
+ * ```typescript
91
+ * const error = createAuthError(
92
+ * AuthErrorCode.INVALID_CREDENTIALS,
93
+ * 'Invalid email or password',
94
+ * 401
95
+ * )
96
+ * ```
43
97
  */
44
98
  export declare function createAuthError(code: AuthErrorCode, message: string, statusCode?: number, details?: unknown): AuthError;
99
+ /**
100
+ * Creates an error result for failed authentication operations.
101
+ *
102
+ * @template TCode - Error code type
103
+ * @param code - Error code
104
+ * @param message - Error message
105
+ * @param details - Additional error details (optional)
106
+ * @returns ErrorResult object
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * const result = createErrorResult(
111
+ * AuthErrorCode.INVALID_CREDENTIALS,
112
+ * 'Invalid email or password'
113
+ * )
114
+ * ```
115
+ */
116
+ export declare function createErrorResult<TCode extends AuthErrorCode = AuthErrorCode>(code: TCode, message: string, details?: unknown): ErrorResult<TCode>;
117
+ /**
118
+ * HTTP status code mapping for error codes.
119
+ *
120
+ * Maps authentication error codes to appropriate HTTP status codes.
121
+ */
122
+ export declare const ERROR_STATUS_MAP: Readonly<Record<AuthErrorCode, number>>;
123
+ /**
124
+ * Gets the HTTP status code for an error code.
125
+ *
126
+ * @param code - Error code
127
+ * @returns HTTP status code
128
+ *
129
+ * @example
130
+ * ```typescript
131
+ * const statusCode = getErrorStatusCode(AuthErrorCode.INVALID_CREDENTIALS)
132
+ * // Returns 401
133
+ * ```
134
+ */
135
+ export declare function getErrorStatusCode(code: AuthErrorCode): number;
136
+ /**
137
+ * Type predicate to check if a value is an AuthError.
138
+ *
139
+ * @param value - Value to check
140
+ * @returns True if value is an AuthError
141
+ *
142
+ * @example
143
+ * ```typescript
144
+ * if (isAuthError(error)) {
145
+ * // TypeScript knows error is AuthError here
146
+ * console.log(error.code, error.message)
147
+ * }
148
+ * ```
149
+ */
150
+ export declare function isAuthError(value: unknown): value is AuthError;
151
+ /**
152
+ * Type predicate to check if a value is an ErrorResult.
153
+ *
154
+ * @param value - Value to check
155
+ * @returns True if value is an ErrorResult
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * if (isErrorResult(result)) {
160
+ * // TypeScript knows result is ErrorResult here
161
+ * console.log(result.errorCode, result.error)
162
+ * }
163
+ * ```
164
+ */
165
+ export declare function isErrorResult(value: unknown): value is ErrorResult;
166
+ /**
167
+ * TODO: Performance
168
+ * - [ ] Consider using const assertions for error code strings
169
+ * - [ ] Add type-level validation for error code mappings
170
+ * - [ ] Implement compile-time error code exhaustiveness checking
171
+ *
172
+ * TODO: Features
173
+ * - [ ] Add error recovery strategies
174
+ * - [ ] Implement error code hierarchies/categories
175
+ * - [ ] Add localized error messages support
176
+ * - [ ] Create error code to user-friendly message mapping
177
+ * - [ ] Add error code metadata (retryable, transient, etc.)
178
+ *
179
+ * TODO: Type Safety
180
+ * - [ ] Add branded types for error codes
181
+ * - [ ] Implement type-safe error code unions
182
+ * - [ ] Add exhaustiveness checking for error handling
183
+ * - [ ] Create type-safe error code validation
184
+ *
185
+ * TODO: Testing
186
+ * - [ ] Add tests for error code mappings
187
+ * - [ ] Test type guards with various inputs
188
+ * - [ ] Verify error result type narrowing
189
+ * - [ ] Test error factory functions
190
+ *
191
+ * TODO: Documentation
192
+ * - [ ] Add examples for each error code
193
+ * - [ ] Document error handling best practices
194
+ * - [ ] Create error code reference guide
195
+ *
196
+ * TODO: Limitations
197
+ * - [ ] Error code enum may need extension for custom errors
198
+ * - [ ] Status code mapping is fixed (consider configurable)
199
+ * - [ ] Error details type is unknown (consider generic)
200
+ */