@vertz/errors 0.1.0 → 0.2.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.d.ts +1165 -490
- package/dist/index.js +618 -141
- package/package.json +3 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,255 +1,4 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Result type and utilities for errors-as-values pattern.
|
|
3
|
-
*
|
|
4
|
-
* This module provides a type-safe alternative to throwing exceptions.
|
|
5
|
-
* Every operation that can fail returns a Result<T, E> instead of throwing.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* import { ok, err, unwrap, map, flatMap, match, matchErr } from '@vertz/errors';
|
|
9
|
-
*
|
|
10
|
-
* // Creating results
|
|
11
|
-
* const success = ok({ name: 'Alice' });
|
|
12
|
-
* const failure = err({ code: 'NOT_FOUND', message: 'User not found' });
|
|
13
|
-
*
|
|
14
|
-
* // Transforming
|
|
15
|
-
* const doubled = map(ok(5), x => x * 2);
|
|
16
|
-
*
|
|
17
|
-
* // Chaining
|
|
18
|
-
* const result = await flatMap(ok(5), async x => ok(x * 2));
|
|
19
|
-
*
|
|
20
|
-
* // Pattern matching
|
|
21
|
-
* const message = match(result, {
|
|
22
|
-
* ok: (data) => `Success: ${data}`,
|
|
23
|
-
* err: (error) => `Error: ${error.message}`
|
|
24
|
-
* });
|
|
25
|
-
*/
|
|
26
|
-
/**
|
|
27
|
-
* Represents a successful result.
|
|
28
|
-
*
|
|
29
|
-
* @example
|
|
30
|
-
* { ok: true, data: { name: 'Alice' } }
|
|
31
|
-
*/
|
|
32
|
-
interface Ok<T> {
|
|
33
|
-
/** Always true for successful results */
|
|
34
|
-
readonly ok: true;
|
|
35
|
-
/** The successful value */
|
|
36
|
-
readonly data: T;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Represents an erroneous result.
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* { ok: false, error: { code: 'NOT_FOUND', message: 'User not found' } }
|
|
43
|
-
*/
|
|
44
|
-
interface Err<E> {
|
|
45
|
-
/** Always false for error results */
|
|
46
|
-
readonly ok: false;
|
|
47
|
-
/** The error value */
|
|
48
|
-
readonly error: E;
|
|
49
|
-
}
|
|
50
|
-
/**
|
|
51
|
-
* A discriminated union representing success or failure.
|
|
52
|
-
*
|
|
53
|
-
* @example
|
|
54
|
-
* type UserResult = Result<User, ValidationError>;
|
|
55
|
-
* type UsersResult = Result<User[], NotFoundError>;
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* const result: Result<string, Error> = ok('hello');
|
|
59
|
-
* if (result.ok) {
|
|
60
|
-
* console.log(result.data); // TypeScript knows this is string
|
|
61
|
-
* } else {
|
|
62
|
-
* console.log(result.error); // TypeScript knows this is Error
|
|
63
|
-
* }
|
|
64
|
-
*/
|
|
65
|
-
type Result<
|
|
66
|
-
T,
|
|
67
|
-
E = unknown
|
|
68
|
-
> = Ok<T> | Err<E>;
|
|
69
|
-
/**
|
|
70
|
-
* Creates a successful Result.
|
|
71
|
-
*
|
|
72
|
-
* @example
|
|
73
|
-
* const result = ok({ name: 'Alice' });
|
|
74
|
-
* // { ok: true, data: { name: 'Alice' } }
|
|
75
|
-
*
|
|
76
|
-
* @example
|
|
77
|
-
* // With type inference
|
|
78
|
-
* const result = ok(42); // Ok<number>
|
|
79
|
-
*/
|
|
80
|
-
declare const ok: <T>(data: T) => Ok<T>;
|
|
81
|
-
/**
|
|
82
|
-
* Creates an error Result.
|
|
83
|
-
*
|
|
84
|
-
* @example
|
|
85
|
-
* const result = err({ code: 'VALIDATION_FAILED', message: 'Invalid email', issues: [] });
|
|
86
|
-
* // { ok: false, error: { code: 'VALIDATION_FAILED', ... } }
|
|
87
|
-
*
|
|
88
|
-
* @example
|
|
89
|
-
* // With simple string errors
|
|
90
|
-
* const result = err('Something went wrong');
|
|
91
|
-
* // { ok: false, error: 'Something went wrong' }
|
|
92
|
-
*/
|
|
93
|
-
declare const err: <E>(error: E) => Err<E>;
|
|
94
|
-
/**
|
|
95
|
-
* Unwraps a Result, throwing if error.
|
|
96
|
-
*
|
|
97
|
-
* Use only in tests, scripts, or when failure is truly exceptional.
|
|
98
|
-
*
|
|
99
|
-
* @example
|
|
100
|
-
* // Tests
|
|
101
|
-
* const user = unwrap(await repo.findOneRequired(id));
|
|
102
|
-
*
|
|
103
|
-
* @example
|
|
104
|
-
* // Scripts
|
|
105
|
-
* const config = unwrap(parseConfig());
|
|
106
|
-
*
|
|
107
|
-
* @throws The error value if the Result is an error
|
|
108
|
-
*/
|
|
109
|
-
declare function unwrap<
|
|
110
|
-
T,
|
|
111
|
-
E
|
|
112
|
-
>(result: Result<T, E>): T;
|
|
113
|
-
/**
|
|
114
|
-
* Unwraps a Result, returning a default value if error.
|
|
115
|
-
*
|
|
116
|
-
* @example
|
|
117
|
-
* const user = await findById(id) ?? { name: 'Guest' };
|
|
118
|
-
*/
|
|
119
|
-
declare function unwrapOr<
|
|
120
|
-
T,
|
|
121
|
-
E
|
|
122
|
-
>(result: Result<T, E>, defaultValue: T): T;
|
|
123
|
-
/**
|
|
124
|
-
* Maps the success value to a new type.
|
|
125
|
-
*
|
|
126
|
-
* @example
|
|
127
|
-
* const userName = map(userResult, u => u.name);
|
|
128
|
-
* // Result<string, Error> if userResult was Result<User, Error>
|
|
129
|
-
*
|
|
130
|
-
* @example
|
|
131
|
-
* // Transform while preserving error type
|
|
132
|
-
* const id = map(ok({ userId: 5 }), u => u.userId);
|
|
133
|
-
* // Ok<number>
|
|
134
|
-
*/
|
|
135
|
-
declare function map<
|
|
136
|
-
T,
|
|
137
|
-
E,
|
|
138
|
-
U
|
|
139
|
-
>(result: Result<T, E>, fn: (data: T) => U): Result<U, E>;
|
|
140
|
-
/**
|
|
141
|
-
* Chains Result-returning functions.
|
|
142
|
-
*
|
|
143
|
-
* Allows chaining operations that can fail without nested try/catch.
|
|
144
|
-
*
|
|
145
|
-
* @example
|
|
146
|
-
* // Synchronous
|
|
147
|
-
* const profile = flatMap(
|
|
148
|
-
* await repo.findOne(userId),
|
|
149
|
-
* (user) => profileRepo.findOne(user.profileId)
|
|
150
|
-
* );
|
|
151
|
-
*
|
|
152
|
-
* @example
|
|
153
|
-
* // Asynchronous
|
|
154
|
-
* const finalResult = await flatMap(
|
|
155
|
-
* await repo.findOne(userId),
|
|
156
|
-
* async (user) => await profileRepo.findOne(user.profileId)
|
|
157
|
-
* );
|
|
158
|
-
*/
|
|
159
|
-
declare function flatMap<
|
|
160
|
-
T,
|
|
161
|
-
E,
|
|
162
|
-
U,
|
|
163
|
-
F
|
|
164
|
-
>(result: Result<T, E>, fn: (data: T) => Result<U, F>): Result<U, E | F>;
|
|
165
|
-
declare function flatMap<
|
|
166
|
-
T,
|
|
167
|
-
E,
|
|
168
|
-
U,
|
|
169
|
-
F
|
|
170
|
-
>(result: Result<T, E>, fn: (data: T) => Promise<Result<U, F>>): Promise<Result<U, E | F>>;
|
|
171
|
-
/**
|
|
172
|
-
* Pattern matching on Result.
|
|
173
|
-
*
|
|
174
|
-
* @example
|
|
175
|
-
* const message = match(result, {
|
|
176
|
-
* ok: (user) => \`Hello, \${user.name}!\`,
|
|
177
|
-
* err: (e) => \`Error: \${e.message}\`
|
|
178
|
-
* });
|
|
179
|
-
*/
|
|
180
|
-
declare function match<
|
|
181
|
-
T,
|
|
182
|
-
E,
|
|
183
|
-
OkR,
|
|
184
|
-
ErrR
|
|
185
|
-
>(result: Result<T, E>, handlers: {
|
|
186
|
-
ok: (data: T) => OkR;
|
|
187
|
-
err: (error: E) => ErrR;
|
|
188
|
-
}): OkR | ErrR;
|
|
189
|
-
/**
|
|
190
|
-
* Type for error handlers in matchErr.
|
|
191
|
-
* Extracts error codes from an error union and creates a handler map.
|
|
192
|
-
*/
|
|
193
|
-
type ErrorHandlers<
|
|
194
|
-
E,
|
|
195
|
-
R
|
|
196
|
-
> = { [K in E as K extends {
|
|
197
|
-
readonly code: infer C extends string;
|
|
198
|
-
} ? C : never] : (error: K) => R };
|
|
199
|
-
/**
|
|
200
|
-
* Exhaustive pattern matching on Result errors.
|
|
201
|
-
*
|
|
202
|
-
* Unlike `match()` which gives you a single `err` handler,
|
|
203
|
-
* `matchErr` requires a handler for every error type in the union.
|
|
204
|
-
* The compiler enforces exhaustiveness — add a new error type to the union
|
|
205
|
-
* and every callsite lights up until you handle it.
|
|
206
|
-
*
|
|
207
|
-
* Errors are discriminated by their `code` string literal field.
|
|
208
|
-
*
|
|
209
|
-
* @example
|
|
210
|
-
* const response = matchErr(result, {
|
|
211
|
-
* ok: (user) => json({ data: user }, 201),
|
|
212
|
-
* NOT_FOUND: (e) => json({ error: 'NOT_FOUND', message: e.message }, 404),
|
|
213
|
-
* UNIQUE_VIOLATION: (e) => json({ error: 'EMAIL_EXISTS', field: e.column }, 409),
|
|
214
|
-
* });
|
|
215
|
-
* // ^ Compile error if error union adds a new member you didn't handle
|
|
216
|
-
*
|
|
217
|
-
* @throws Error if an error code is not handled
|
|
218
|
-
*/
|
|
219
|
-
declare function matchErr<
|
|
220
|
-
T,
|
|
221
|
-
E extends {
|
|
222
|
-
readonly code: string;
|
|
223
|
-
},
|
|
224
|
-
R
|
|
225
|
-
>(result: Result<T, E>, handlers: {
|
|
226
|
-
ok: (data: T) => R;
|
|
227
|
-
} & ErrorHandlers<E, R>): R;
|
|
228
|
-
/**
|
|
229
|
-
* Type guard for Ok results.
|
|
230
|
-
*
|
|
231
|
-
* @example
|
|
232
|
-
* if (isOk(result)) {
|
|
233
|
-
* console.log(result.data); // TypeScript knows this is T
|
|
234
|
-
* }
|
|
235
|
-
*/
|
|
236
|
-
declare function isOk<
|
|
237
|
-
T,
|
|
238
|
-
E
|
|
239
|
-
>(result: Result<T, E>): result is Ok<T>;
|
|
240
|
-
/**
|
|
241
|
-
* Type guard for Err results.
|
|
242
|
-
*
|
|
243
|
-
* @example
|
|
244
|
-
* if (isErr(result)) {
|
|
245
|
-
* console.log(result.error); // TypeScript knows this is E
|
|
246
|
-
* }
|
|
247
|
-
*/
|
|
248
|
-
declare function isErr<
|
|
249
|
-
T,
|
|
250
|
-
E
|
|
251
|
-
>(result: Result<T, E>): result is Err<E>;
|
|
252
|
-
/**
|
|
253
2
|
* AppError - Base class for custom domain errors thrown by app developers.
|
|
254
3
|
*
|
|
255
4
|
* This is the standard way for app developers to define and throw domain errors.
|
|
@@ -293,38 +42,275 @@ declare class AppError<C extends string = string> extends Error {
|
|
|
293
42
|
toJSON(): Record<string, unknown>;
|
|
294
43
|
}
|
|
295
44
|
/**
|
|
296
|
-
*
|
|
45
|
+
* Authentication and authorization domain errors.
|
|
297
46
|
*
|
|
298
|
-
* These errors are returned
|
|
47
|
+
* These errors are returned from auth operations.
|
|
299
48
|
*/
|
|
300
49
|
/**
|
|
301
|
-
*
|
|
50
|
+
* Invalid credentials error.
|
|
51
|
+
*
|
|
52
|
+
* Returned when authentication fails due to wrong email/password.
|
|
302
53
|
*/
|
|
303
|
-
interface
|
|
304
|
-
readonly
|
|
54
|
+
interface InvalidCredentialsError {
|
|
55
|
+
readonly code: "INVALID_CREDENTIALS";
|
|
305
56
|
readonly message: string;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Creates an InvalidCredentialsError.
|
|
60
|
+
*/
|
|
61
|
+
declare function createInvalidCredentialsError(message?: string): InvalidCredentialsError;
|
|
62
|
+
/**
|
|
63
|
+
* Type guard for InvalidCredentialsError.
|
|
64
|
+
*/
|
|
65
|
+
declare function isInvalidCredentialsError(error: {
|
|
306
66
|
readonly code: string;
|
|
67
|
+
}): error is InvalidCredentialsError;
|
|
68
|
+
/**
|
|
69
|
+
* User already exists error.
|
|
70
|
+
*
|
|
71
|
+
* Returned when attempting to sign up with an existing email.
|
|
72
|
+
*/
|
|
73
|
+
interface UserExistsError {
|
|
74
|
+
readonly code: "USER_EXISTS";
|
|
75
|
+
readonly message: string;
|
|
76
|
+
readonly email?: string;
|
|
307
77
|
}
|
|
308
78
|
/**
|
|
309
|
-
*
|
|
79
|
+
* Creates a UserExistsError.
|
|
80
|
+
*/
|
|
81
|
+
declare function createUserExistsError(message?: string, email?: string): UserExistsError;
|
|
82
|
+
/**
|
|
83
|
+
* Type guard for UserExistsError.
|
|
84
|
+
*/
|
|
85
|
+
declare function isUserExistsError(error: {
|
|
86
|
+
readonly code: string;
|
|
87
|
+
}): error is UserExistsError;
|
|
88
|
+
/**
|
|
89
|
+
* Session expired error.
|
|
310
90
|
*
|
|
311
|
-
* Returned when
|
|
91
|
+
* Returned when a token is no longer valid (expired, revoked, etc.).
|
|
312
92
|
*/
|
|
313
|
-
interface
|
|
314
|
-
readonly code: "
|
|
93
|
+
interface SessionExpiredError {
|
|
94
|
+
readonly code: "SESSION_EXPIRED";
|
|
315
95
|
readonly message: string;
|
|
316
|
-
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Creates a SessionExpiredError.
|
|
99
|
+
*/
|
|
100
|
+
declare function createSessionExpiredError(message?: string): SessionExpiredError;
|
|
101
|
+
/**
|
|
102
|
+
* Type guard for SessionExpiredError.
|
|
103
|
+
*/
|
|
104
|
+
declare function isSessionExpiredError(error: {
|
|
105
|
+
readonly code: string;
|
|
106
|
+
}): error is SessionExpiredError;
|
|
107
|
+
/**
|
|
108
|
+
* Permission denied error.
|
|
109
|
+
*
|
|
110
|
+
* Returned when authenticated but not authorized to perform an action.
|
|
111
|
+
*/
|
|
112
|
+
interface PermissionDeniedError {
|
|
113
|
+
readonly code: "PERMISSION_DENIED";
|
|
114
|
+
readonly message: string;
|
|
115
|
+
readonly resource?: string;
|
|
116
|
+
readonly action?: string;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Creates a PermissionDeniedError.
|
|
120
|
+
*/
|
|
121
|
+
declare function createPermissionDeniedError(message?: string, options?: {
|
|
122
|
+
resource?: string;
|
|
123
|
+
action?: string;
|
|
124
|
+
}): PermissionDeniedError;
|
|
125
|
+
/**
|
|
126
|
+
* Type guard for PermissionDeniedError.
|
|
127
|
+
*/
|
|
128
|
+
declare function isPermissionDeniedError(error: {
|
|
129
|
+
readonly code: string;
|
|
130
|
+
}): error is PermissionDeniedError;
|
|
131
|
+
/**
|
|
132
|
+
* Rate limited error.
|
|
133
|
+
*
|
|
134
|
+
* Returned when too many attempts have been made.
|
|
135
|
+
*/
|
|
136
|
+
interface RateLimitedError {
|
|
137
|
+
readonly code: "RATE_LIMITED";
|
|
138
|
+
readonly message: string;
|
|
139
|
+
readonly retryAfter?: number;
|
|
140
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Creates a RateLimitedError.
|
|
143
|
+
*/
|
|
144
|
+
declare function createRateLimitedError(message?: string, retryAfter?: number): RateLimitedError;
|
|
145
|
+
/**
|
|
146
|
+
* Type guard for RateLimitedError.
|
|
147
|
+
*/
|
|
148
|
+
declare function isRateLimitedError(error: {
|
|
149
|
+
readonly code: string;
|
|
150
|
+
}): error is RateLimitedError;
|
|
151
|
+
/**
|
|
152
|
+
* Auth validation error.
|
|
153
|
+
*
|
|
154
|
+
* Returned when auth input fails validation (invalid email, weak password, etc.).
|
|
155
|
+
*/
|
|
156
|
+
interface AuthValidationError {
|
|
157
|
+
readonly code: "AUTH_VALIDATION_ERROR";
|
|
158
|
+
readonly message: string;
|
|
159
|
+
readonly field: "email" | "password";
|
|
160
|
+
readonly constraint?: string;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Creates an AuthValidationError.
|
|
164
|
+
*/
|
|
165
|
+
declare function createAuthValidationError(message: string, field: "email" | "password", constraint?: string): AuthValidationError;
|
|
166
|
+
/**
|
|
167
|
+
* Type guard for AuthValidationError.
|
|
168
|
+
*/
|
|
169
|
+
declare function isAuthValidationError(error: {
|
|
170
|
+
readonly code: string;
|
|
171
|
+
}): error is AuthValidationError;
|
|
172
|
+
/**
|
|
173
|
+
* Union type for all auth errors.
|
|
174
|
+
*/
|
|
175
|
+
type AuthError = InvalidCredentialsError | UserExistsError | SessionExpiredError | PermissionDeniedError | RateLimitedError | AuthValidationError;
|
|
176
|
+
/**
|
|
177
|
+
* Client domain errors.
|
|
178
|
+
*
|
|
179
|
+
* These errors are used at the HTTP boundary and use client-native vocabulary
|
|
180
|
+
* (not DB internals). They map from server HTTP responses.
|
|
181
|
+
*/
|
|
182
|
+
/**
|
|
183
|
+
* Validation error (client-facing).
|
|
184
|
+
*
|
|
185
|
+
* Maps from server's VALIDATION_FAILED.
|
|
186
|
+
*/
|
|
187
|
+
interface ValidationError2 {
|
|
188
|
+
readonly code: "ValidationError";
|
|
189
|
+
readonly message: string;
|
|
190
|
+
readonly issues?: readonly {
|
|
191
|
+
readonly path: readonly (string | number)[];
|
|
192
|
+
readonly message: string;
|
|
193
|
+
readonly code: string;
|
|
194
|
+
}[];
|
|
317
195
|
}
|
|
318
196
|
/**
|
|
319
197
|
* Creates a ValidationError.
|
|
320
198
|
*/
|
|
321
|
-
declare function createValidationError(message: string, issues
|
|
199
|
+
declare function createValidationError(message: string, issues?: readonly {
|
|
200
|
+
readonly path: readonly (string | number)[];
|
|
201
|
+
readonly message: string;
|
|
202
|
+
readonly code: string;
|
|
203
|
+
}[]): ValidationError2;
|
|
322
204
|
/**
|
|
323
205
|
* Type guard for ValidationError.
|
|
324
206
|
*/
|
|
325
207
|
declare function isValidationError(error: {
|
|
326
208
|
readonly code: string;
|
|
327
|
-
}): error is
|
|
209
|
+
}): error is ValidationError2;
|
|
210
|
+
/**
|
|
211
|
+
* Not found error (client-facing).
|
|
212
|
+
*
|
|
213
|
+
* Maps from server's NOT_FOUND.
|
|
214
|
+
*/
|
|
215
|
+
interface NotFoundError {
|
|
216
|
+
readonly code: "NotFound";
|
|
217
|
+
readonly message: string;
|
|
218
|
+
readonly resource?: string;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Creates a NotFoundError.
|
|
222
|
+
*/
|
|
223
|
+
declare function createNotFoundError(message?: string, resource?: string): NotFoundError;
|
|
224
|
+
/**
|
|
225
|
+
* Type guard for NotFoundError.
|
|
226
|
+
*/
|
|
227
|
+
declare function isNotFoundError(error: {
|
|
228
|
+
readonly code: string;
|
|
229
|
+
}): error is NotFoundError;
|
|
230
|
+
/**
|
|
231
|
+
* Conflict error (client-facing).
|
|
232
|
+
*
|
|
233
|
+
* Maps from server's UNIQUE_VIOLATION.
|
|
234
|
+
*/
|
|
235
|
+
interface ConflictError {
|
|
236
|
+
readonly code: "Conflict";
|
|
237
|
+
readonly message: string;
|
|
238
|
+
readonly field?: string;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Creates a ConflictError.
|
|
242
|
+
*/
|
|
243
|
+
declare function createConflictError(message?: string, field?: string): ConflictError;
|
|
244
|
+
/**
|
|
245
|
+
* Type guard for ConflictError.
|
|
246
|
+
*/
|
|
247
|
+
declare function isConflictError(error: {
|
|
248
|
+
readonly code: string;
|
|
249
|
+
}): error is ConflictError;
|
|
250
|
+
/**
|
|
251
|
+
* Unauthorized error.
|
|
252
|
+
*
|
|
253
|
+
* Returned when the user is not authenticated.
|
|
254
|
+
*/
|
|
255
|
+
interface UnauthorizedError {
|
|
256
|
+
readonly code: "Unauthorized";
|
|
257
|
+
readonly message: string;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Creates an UnauthorizedError.
|
|
261
|
+
*/
|
|
262
|
+
declare function createUnauthorizedError(message?: string): UnauthorizedError;
|
|
263
|
+
/**
|
|
264
|
+
* Type guard for UnauthorizedError.
|
|
265
|
+
*/
|
|
266
|
+
declare function isUnauthorizedError(error: {
|
|
267
|
+
readonly code: string;
|
|
268
|
+
}): error is UnauthorizedError;
|
|
269
|
+
/**
|
|
270
|
+
* Forbidden error.
|
|
271
|
+
*
|
|
272
|
+
* Returned when the user is authenticated but not authorized.
|
|
273
|
+
*/
|
|
274
|
+
interface ForbiddenError {
|
|
275
|
+
readonly code: "Forbidden";
|
|
276
|
+
readonly message: string;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* Creates a ForbiddenError.
|
|
280
|
+
*/
|
|
281
|
+
declare function createForbiddenError(message?: string): ForbiddenError;
|
|
282
|
+
/**
|
|
283
|
+
* Type guard for ForbiddenError.
|
|
284
|
+
*/
|
|
285
|
+
declare function isForbiddenError(error: {
|
|
286
|
+
readonly code: string;
|
|
287
|
+
}): error is ForbiddenError;
|
|
288
|
+
/**
|
|
289
|
+
* Rate limited error (client-facing).
|
|
290
|
+
*
|
|
291
|
+
* Maps from server's RATE_LIMITED.
|
|
292
|
+
*/
|
|
293
|
+
interface RateLimitedError2 {
|
|
294
|
+
readonly code: "RATE_LIMITED";
|
|
295
|
+
readonly message: string;
|
|
296
|
+
readonly retryAfter?: number;
|
|
297
|
+
}
|
|
298
|
+
/**
|
|
299
|
+
* Creates a RateLimitedError.
|
|
300
|
+
*/
|
|
301
|
+
declare function createRateLimitedError2(message?: string, retryAfter?: number): RateLimitedError2;
|
|
302
|
+
/**
|
|
303
|
+
* Type guard for RateLimitedError.
|
|
304
|
+
*/
|
|
305
|
+
declare function isRateLimitedError2(error: {
|
|
306
|
+
readonly code: string;
|
|
307
|
+
}): error is RateLimitedError2;
|
|
308
|
+
/**
|
|
309
|
+
* Union type for all client errors.
|
|
310
|
+
*
|
|
311
|
+
* These are the errors that clients receive from API calls.
|
|
312
|
+
*/
|
|
313
|
+
type ApiError = ValidationError2 | NotFoundError | ConflictError | UnauthorizedError | ForbiddenError | RateLimitedError2;
|
|
328
314
|
/**
|
|
329
315
|
* Database domain errors.
|
|
330
316
|
*
|
|
@@ -337,8 +323,8 @@ declare function isValidationError(error: {
|
|
|
337
323
|
* Returned when a required record is not found (from findOneRequired).
|
|
338
324
|
* Note: findOne() returns Result<T | null, never> - null is success.
|
|
339
325
|
*/
|
|
340
|
-
interface
|
|
341
|
-
readonly code: "
|
|
326
|
+
interface NotFoundError2 {
|
|
327
|
+
readonly code: "NotFound";
|
|
342
328
|
readonly message: string;
|
|
343
329
|
readonly table: string;
|
|
344
330
|
readonly key?: Record<string, unknown>;
|
|
@@ -346,17 +332,17 @@ interface NotFoundError {
|
|
|
346
332
|
/**
|
|
347
333
|
* Creates a NotFoundError.
|
|
348
334
|
*/
|
|
349
|
-
declare function
|
|
335
|
+
declare function createNotFoundError2(table: string, key?: Record<string, unknown>): NotFoundError2;
|
|
350
336
|
/**
|
|
351
337
|
* Type guard for NotFoundError.
|
|
352
338
|
*/
|
|
353
|
-
declare function
|
|
339
|
+
declare function isNotFoundError2(error: {
|
|
354
340
|
readonly code: string;
|
|
355
|
-
}): error is
|
|
341
|
+
}): error is NotFoundError2;
|
|
356
342
|
/**
|
|
357
343
|
* Union type for all read errors.
|
|
358
344
|
*/
|
|
359
|
-
type ReadError =
|
|
345
|
+
type ReadError = NotFoundError2;
|
|
360
346
|
/**
|
|
361
347
|
* Unique constraint violation.
|
|
362
348
|
*
|
|
@@ -464,376 +450,1065 @@ declare function isCheckViolation(error: {
|
|
|
464
450
|
*/
|
|
465
451
|
type WriteError = UniqueViolation | FKViolation | NotNullViolation | CheckViolation;
|
|
466
452
|
/**
|
|
467
|
-
*
|
|
453
|
+
* Migration domain errors.
|
|
468
454
|
*
|
|
469
|
-
* These errors are returned from
|
|
455
|
+
* These errors are returned from migration operations and represent
|
|
456
|
+
* expected runtime failures that require case-by-case handling.
|
|
470
457
|
*/
|
|
471
458
|
/**
|
|
472
|
-
*
|
|
459
|
+
* Migration query execution failed.
|
|
473
460
|
*
|
|
474
|
-
* Returned when
|
|
461
|
+
* Returned when a migration SQL statement fails to execute.
|
|
475
462
|
*/
|
|
476
|
-
interface
|
|
477
|
-
readonly code: "
|
|
463
|
+
interface MigrationQueryError {
|
|
464
|
+
readonly code: "MIGRATION_QUERY_ERROR";
|
|
478
465
|
readonly message: string;
|
|
466
|
+
readonly sql?: string;
|
|
467
|
+
readonly cause?: unknown;
|
|
479
468
|
}
|
|
480
469
|
/**
|
|
481
|
-
* Creates
|
|
470
|
+
* Creates a MigrationQueryError.
|
|
482
471
|
*/
|
|
483
|
-
declare function
|
|
472
|
+
declare function createMigrationQueryError(message: string, options?: {
|
|
473
|
+
sql?: string;
|
|
474
|
+
cause?: unknown;
|
|
475
|
+
}): MigrationQueryError;
|
|
484
476
|
/**
|
|
485
|
-
* Type guard for
|
|
477
|
+
* Type guard for MigrationQueryError.
|
|
486
478
|
*/
|
|
487
|
-
declare function
|
|
479
|
+
declare function isMigrationQueryError(error: {
|
|
488
480
|
readonly code: string;
|
|
489
|
-
}): error is
|
|
481
|
+
}): error is MigrationQueryError;
|
|
490
482
|
/**
|
|
491
|
-
*
|
|
483
|
+
* Migration checksum mismatch detected.
|
|
492
484
|
*
|
|
493
|
-
* Returned when
|
|
485
|
+
* Returned when an applied migration's checksum doesn't match the file on disk.
|
|
494
486
|
*/
|
|
495
|
-
interface
|
|
496
|
-
readonly code: "
|
|
487
|
+
interface MigrationChecksumMismatch {
|
|
488
|
+
readonly code: "MIGRATION_CHECKSUM_MISMATCH";
|
|
497
489
|
readonly message: string;
|
|
498
|
-
readonly
|
|
490
|
+
readonly migrationName: string;
|
|
491
|
+
readonly expectedChecksum: string;
|
|
492
|
+
readonly actualChecksum: string;
|
|
499
493
|
}
|
|
500
494
|
/**
|
|
501
|
-
* Creates a
|
|
495
|
+
* Creates a MigrationChecksumMismatch error.
|
|
502
496
|
*/
|
|
503
|
-
declare function
|
|
497
|
+
declare function createMigrationChecksumMismatch(migrationName: string, expectedChecksum: string, actualChecksum: string): MigrationChecksumMismatch;
|
|
504
498
|
/**
|
|
505
|
-
* Type guard for
|
|
499
|
+
* Type guard for MigrationChecksumMismatch.
|
|
506
500
|
*/
|
|
507
|
-
declare function
|
|
501
|
+
declare function isMigrationChecksumMismatch(error: {
|
|
508
502
|
readonly code: string;
|
|
509
|
-
}): error is
|
|
503
|
+
}): error is MigrationChecksumMismatch;
|
|
510
504
|
/**
|
|
511
|
-
*
|
|
505
|
+
* Migration history table not found.
|
|
512
506
|
*
|
|
513
|
-
* Returned when
|
|
507
|
+
* Returned when attempting to query migration history before the table exists.
|
|
514
508
|
*/
|
|
515
|
-
interface
|
|
516
|
-
readonly code: "
|
|
509
|
+
interface MigrationHistoryNotFound {
|
|
510
|
+
readonly code: "MIGRATION_HISTORY_NOT_FOUND";
|
|
517
511
|
readonly message: string;
|
|
518
512
|
}
|
|
519
513
|
/**
|
|
520
|
-
* Creates a
|
|
514
|
+
* Creates a MigrationHistoryNotFound error.
|
|
521
515
|
*/
|
|
522
|
-
declare function
|
|
516
|
+
declare function createMigrationHistoryNotFound(): MigrationHistoryNotFound;
|
|
523
517
|
/**
|
|
524
|
-
* Type guard for
|
|
518
|
+
* Type guard for MigrationHistoryNotFound.
|
|
525
519
|
*/
|
|
526
|
-
declare function
|
|
520
|
+
declare function isMigrationHistoryNotFound(error: {
|
|
527
521
|
readonly code: string;
|
|
528
|
-
}): error is
|
|
522
|
+
}): error is MigrationHistoryNotFound;
|
|
529
523
|
/**
|
|
530
|
-
*
|
|
524
|
+
* Union type for all migration errors.
|
|
525
|
+
*/
|
|
526
|
+
type MigrationError = MigrationQueryError | MigrationChecksumMismatch | MigrationHistoryNotFound;
|
|
527
|
+
/**
|
|
528
|
+
* Schema validation errors.
|
|
531
529
|
*
|
|
532
|
-
*
|
|
530
|
+
* These errors are returned when schema validation fails.
|
|
533
531
|
*/
|
|
534
|
-
|
|
535
|
-
|
|
532
|
+
/**
|
|
533
|
+
* Issue within a validation error.
|
|
534
|
+
*/
|
|
535
|
+
interface ValidationIssue {
|
|
536
|
+
readonly path: readonly (string | number)[];
|
|
536
537
|
readonly message: string;
|
|
537
|
-
readonly
|
|
538
|
-
readonly action?: string;
|
|
538
|
+
readonly code: string;
|
|
539
539
|
}
|
|
540
540
|
/**
|
|
541
|
-
*
|
|
541
|
+
* Validation failed error.
|
|
542
|
+
*
|
|
543
|
+
* Returned when input doesn't match schema expectations.
|
|
542
544
|
*/
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
545
|
+
interface ValidationError3 {
|
|
546
|
+
readonly code: "VALIDATION_FAILED";
|
|
547
|
+
readonly message: string;
|
|
548
|
+
readonly issues: readonly ValidationIssue[];
|
|
549
|
+
}
|
|
547
550
|
/**
|
|
548
|
-
*
|
|
551
|
+
* Creates a ValidationError.
|
|
549
552
|
*/
|
|
550
|
-
declare function
|
|
553
|
+
declare function createValidationError2(message: string, issues: readonly ValidationIssue[]): ValidationError3;
|
|
554
|
+
/**
|
|
555
|
+
* Type guard for ValidationError.
|
|
556
|
+
*/
|
|
557
|
+
declare function isValidationError2(error: {
|
|
551
558
|
readonly code: string;
|
|
552
|
-
}): error is
|
|
559
|
+
}): error is ValidationError3;
|
|
553
560
|
/**
|
|
554
|
-
*
|
|
561
|
+
* Infrastructure errors.
|
|
555
562
|
*
|
|
556
|
-
*
|
|
563
|
+
* These are operational failures that the application developer never handles
|
|
564
|
+
* in business logic. They're caught by global middleware/error boundaries
|
|
565
|
+
* and result in 500/503 responses.
|
|
557
566
|
*/
|
|
558
|
-
|
|
559
|
-
|
|
567
|
+
/**
|
|
568
|
+
* Base class for infrastructure errors.
|
|
569
|
+
*/
|
|
570
|
+
declare class InfraError extends Error {
|
|
571
|
+
constructor(message: string);
|
|
572
|
+
}
|
|
573
|
+
/**
|
|
574
|
+
* Database connection error.
|
|
575
|
+
*
|
|
576
|
+
* Thrown when the database is unreachable.
|
|
577
|
+
*/
|
|
578
|
+
declare class ConnectionError extends InfraError {
|
|
579
|
+
constructor(message?: string);
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* Connection pool exhausted error.
|
|
583
|
+
*
|
|
584
|
+
* Thrown when no connections are available in the pool.
|
|
585
|
+
*/
|
|
586
|
+
declare class PoolExhaustedError extends InfraError {
|
|
587
|
+
constructor(message?: string);
|
|
588
|
+
}
|
|
589
|
+
/**
|
|
590
|
+
* Query error.
|
|
591
|
+
*
|
|
592
|
+
* Thrown when a query fails (malformed, syntax error, etc.).
|
|
593
|
+
*/
|
|
594
|
+
declare class QueryError extends InfraError {
|
|
595
|
+
constructor(message?: string);
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* Timeout error.
|
|
599
|
+
*
|
|
600
|
+
* Thrown when an operation takes too long.
|
|
601
|
+
*/
|
|
602
|
+
declare class TimeoutError2 extends InfraError {
|
|
603
|
+
constructor(message?: string);
|
|
604
|
+
}
|
|
605
|
+
/**
|
|
606
|
+
* Network error.
|
|
607
|
+
*
|
|
608
|
+
* Thrown when HTTP client can't reach the server.
|
|
609
|
+
*/
|
|
610
|
+
declare class NetworkError2 extends InfraError {
|
|
611
|
+
constructor(message?: string);
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Serialization error.
|
|
615
|
+
*
|
|
616
|
+
* Thrown when response couldn't be decoded.
|
|
617
|
+
*/
|
|
618
|
+
declare class SerializationError extends InfraError {
|
|
619
|
+
constructor(message?: string);
|
|
620
|
+
}
|
|
621
|
+
/**
|
|
622
|
+
* Union type for all infrastructure errors.
|
|
623
|
+
*/
|
|
624
|
+
type InfraErrors = ConnectionError | PoolExhaustedError | QueryError | TimeoutError2 | NetworkError2 | SerializationError;
|
|
625
|
+
/**
|
|
626
|
+
* Maps a database error to an HTTP status code.
|
|
627
|
+
*
|
|
628
|
+
* @param error - A database domain error
|
|
629
|
+
* @returns HTTP status code
|
|
630
|
+
*
|
|
631
|
+
* @example
|
|
632
|
+
* const status = dbErrorToHttpStatus(error);
|
|
633
|
+
* // NOT_FOUND → 404
|
|
634
|
+
* // UNIQUE_VIOLATION → 409
|
|
635
|
+
* // FK_VIOLATION → 422
|
|
636
|
+
* // NOT_NULL_VIOLATION → 422
|
|
637
|
+
* // CHECK_VIOLATION → 422
|
|
638
|
+
*/
|
|
639
|
+
declare function dbErrorToHttpStatus(error: ReadError | WriteError): number;
|
|
640
|
+
/**
|
|
641
|
+
* Maps a NotFoundError to HTTP status.
|
|
642
|
+
*/
|
|
643
|
+
declare function notFoundErrorToHttpStatus(_error: NotFoundError2): number;
|
|
644
|
+
/**
|
|
645
|
+
* Maps a UniqueViolation to HTTP status.
|
|
646
|
+
*/
|
|
647
|
+
declare function uniqueViolationToHttpStatus(_error: UniqueViolation): number;
|
|
648
|
+
/**
|
|
649
|
+
* Maps a FKViolation to HTTP status.
|
|
650
|
+
*/
|
|
651
|
+
declare function fkViolationToHttpStatus(_error: FKViolation): number;
|
|
652
|
+
/**
|
|
653
|
+
* Maps a NotNullViolation to HTTP status.
|
|
654
|
+
*/
|
|
655
|
+
declare function notNullViolationToHttpStatus(_error: NotNullViolation): number;
|
|
656
|
+
/**
|
|
657
|
+
* Maps a CheckViolation to HTTP status.
|
|
658
|
+
*/
|
|
659
|
+
declare function checkViolationToHttpStatus(_error: CheckViolation): number;
|
|
660
|
+
/**
|
|
661
|
+
* Unknown error response from server.
|
|
662
|
+
*/
|
|
663
|
+
interface UnknownError {
|
|
664
|
+
readonly code: "UNKNOWN";
|
|
560
665
|
readonly message: string;
|
|
561
|
-
readonly
|
|
666
|
+
readonly status: number;
|
|
667
|
+
}
|
|
668
|
+
/**
|
|
669
|
+
* Maps an HTTP response to a client domain error.
|
|
670
|
+
*
|
|
671
|
+
* @param status - HTTP status code
|
|
672
|
+
* @param body - Response body
|
|
673
|
+
* @returns Client domain error
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* const error = httpToClientError(404, { message: 'User not found' });
|
|
677
|
+
* // { code: 'NOT_FOUND', message: 'User not found', resource: 'user' }
|
|
678
|
+
*/
|
|
679
|
+
declare function httpToClientError(status: number, body: unknown): ApiError | UnknownError;
|
|
680
|
+
/**
|
|
681
|
+
* Checks if an error is an UnknownError.
|
|
682
|
+
*/
|
|
683
|
+
declare function isUnknownError(error: ApiError | UnknownError): error is UnknownError;
|
|
684
|
+
/**
|
|
685
|
+
* Result type and utilities for errors-as-values pattern.
|
|
686
|
+
*
|
|
687
|
+
* This module provides a type-safe alternative to throwing exceptions.
|
|
688
|
+
* Every operation that can fail returns a Result<T, E> instead of throwing.
|
|
689
|
+
*
|
|
690
|
+
* @example
|
|
691
|
+
* import { ok, err, unwrap, map, flatMap, match, matchErr } from '@vertz/errors';
|
|
692
|
+
*
|
|
693
|
+
* // Creating results
|
|
694
|
+
* const success = ok({ name: 'Alice' });
|
|
695
|
+
* const failure = err({ code: 'NOT_FOUND', message: 'User not found' });
|
|
696
|
+
*
|
|
697
|
+
* // Transforming
|
|
698
|
+
* const doubled = map(ok(5), x => x * 2);
|
|
699
|
+
*
|
|
700
|
+
* // Chaining
|
|
701
|
+
* const result = await flatMap(ok(5), async x => ok(x * 2));
|
|
702
|
+
*
|
|
703
|
+
* // Pattern matching
|
|
704
|
+
* const message = match(result, {
|
|
705
|
+
* ok: (data) => `Success: ${data}`,
|
|
706
|
+
* err: (error) => `Error: ${error.message}`
|
|
707
|
+
* });
|
|
708
|
+
*/
|
|
709
|
+
/**
|
|
710
|
+
* Represents a successful result.
|
|
711
|
+
*
|
|
712
|
+
* @example
|
|
713
|
+
* { ok: true, data: { name: 'Alice' } }
|
|
714
|
+
*/
|
|
715
|
+
interface Ok<T> {
|
|
716
|
+
/** Always true for successful results */
|
|
717
|
+
readonly ok: true;
|
|
718
|
+
/** The successful value */
|
|
719
|
+
readonly data: T;
|
|
720
|
+
}
|
|
721
|
+
/**
|
|
722
|
+
* Represents an erroneous result.
|
|
723
|
+
*
|
|
724
|
+
* @example
|
|
725
|
+
* { ok: false, error: { code: 'NOT_FOUND', message: 'User not found' } }
|
|
726
|
+
*/
|
|
727
|
+
interface Err<E> {
|
|
728
|
+
/** Always false for error results */
|
|
729
|
+
readonly ok: false;
|
|
730
|
+
/** The error value */
|
|
731
|
+
readonly error: E;
|
|
732
|
+
}
|
|
733
|
+
/**
|
|
734
|
+
* A discriminated union representing success or failure.
|
|
735
|
+
*
|
|
736
|
+
* @example
|
|
737
|
+
* type UserResult = Result<User, ValidationError>;
|
|
738
|
+
* type UsersResult = Result<User[], NotFoundError>;
|
|
739
|
+
*
|
|
740
|
+
* @example
|
|
741
|
+
* const result: Result<string, Error> = ok('hello');
|
|
742
|
+
* if (result.ok) {
|
|
743
|
+
* console.log(result.data); // TypeScript knows this is string
|
|
744
|
+
* } else {
|
|
745
|
+
* console.log(result.error); // TypeScript knows this is Error
|
|
746
|
+
* }
|
|
747
|
+
*/
|
|
748
|
+
type Result<
|
|
749
|
+
T,
|
|
750
|
+
E = unknown
|
|
751
|
+
> = Ok<T> | Err<E>;
|
|
752
|
+
/**
|
|
753
|
+
* Creates a successful Result.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* const result = ok({ name: 'Alice' });
|
|
757
|
+
* // { ok: true, data: { name: 'Alice' } }
|
|
758
|
+
*
|
|
759
|
+
* @example
|
|
760
|
+
* // With type inference
|
|
761
|
+
* const result = ok(42); // Ok<number>
|
|
762
|
+
*/
|
|
763
|
+
declare const ok: <T>(data: T) => Ok<T>;
|
|
764
|
+
/**
|
|
765
|
+
* Creates an error Result.
|
|
766
|
+
*
|
|
767
|
+
* @example
|
|
768
|
+
* const result = err({ code: 'VALIDATION_FAILED', message: 'Invalid email', issues: [] });
|
|
769
|
+
* // { ok: false, error: { code: 'VALIDATION_FAILED', ... } }
|
|
770
|
+
*
|
|
771
|
+
* @example
|
|
772
|
+
* // With simple string errors
|
|
773
|
+
* const result = err('Something went wrong');
|
|
774
|
+
* // { ok: false, error: 'Something went wrong' }
|
|
775
|
+
*/
|
|
776
|
+
declare const err: <E>(error: E) => Err<E>;
|
|
777
|
+
/**
|
|
778
|
+
* Unwraps a Result, throwing if error.
|
|
779
|
+
*
|
|
780
|
+
* Use only in tests, scripts, or when failure is truly exceptional.
|
|
781
|
+
*
|
|
782
|
+
* @example
|
|
783
|
+
* // Tests
|
|
784
|
+
* const user = unwrap(await repo.findOneRequired(id));
|
|
785
|
+
*
|
|
786
|
+
* @example
|
|
787
|
+
* // Scripts
|
|
788
|
+
* const config = unwrap(parseConfig());
|
|
789
|
+
*
|
|
790
|
+
* @throws The error value if the Result is an error
|
|
791
|
+
*/
|
|
792
|
+
declare function unwrap<
|
|
793
|
+
T,
|
|
794
|
+
E
|
|
795
|
+
>(result: Result<T, E>): T;
|
|
796
|
+
/**
|
|
797
|
+
* Unwraps a Result, returning a default value if error.
|
|
798
|
+
*
|
|
799
|
+
* @example
|
|
800
|
+
* const user = await findById(id) ?? { name: 'Guest' };
|
|
801
|
+
*/
|
|
802
|
+
declare function unwrapOr<
|
|
803
|
+
T,
|
|
804
|
+
E
|
|
805
|
+
>(result: Result<T, E>, defaultValue: T): T;
|
|
806
|
+
/**
|
|
807
|
+
* Maps the success value to a new type.
|
|
808
|
+
*
|
|
809
|
+
* @example
|
|
810
|
+
* const userName = map(userResult, u => u.name);
|
|
811
|
+
* // Result<string, Error> if userResult was Result<User, Error>
|
|
812
|
+
*
|
|
813
|
+
* @example
|
|
814
|
+
* // Transform while preserving error type
|
|
815
|
+
* const id = map(ok({ userId: 5 }), u => u.userId);
|
|
816
|
+
* // Ok<number>
|
|
817
|
+
*/
|
|
818
|
+
declare function map<
|
|
819
|
+
T,
|
|
820
|
+
E,
|
|
821
|
+
U
|
|
822
|
+
>(result: Result<T, E>, fn: (data: T) => U): Result<U, E>;
|
|
823
|
+
/**
|
|
824
|
+
* Chains Result-returning functions.
|
|
825
|
+
*
|
|
826
|
+
* Allows chaining operations that can fail without nested try/catch.
|
|
827
|
+
*
|
|
828
|
+
* @example
|
|
829
|
+
* // Synchronous
|
|
830
|
+
* const profile = flatMap(
|
|
831
|
+
* await repo.findOne(userId),
|
|
832
|
+
* (user) => profileRepo.findOne(user.profileId)
|
|
833
|
+
* );
|
|
834
|
+
*
|
|
835
|
+
* @example
|
|
836
|
+
* // Asynchronous
|
|
837
|
+
* const finalResult = await flatMap(
|
|
838
|
+
* await repo.findOne(userId),
|
|
839
|
+
* async (user) => await profileRepo.findOne(user.profileId)
|
|
840
|
+
* );
|
|
841
|
+
*/
|
|
842
|
+
declare function flatMap<
|
|
843
|
+
T,
|
|
844
|
+
E,
|
|
845
|
+
U,
|
|
846
|
+
F
|
|
847
|
+
>(result: Result<T, E>, fn: (data: T) => Result<U, F>): Result<U, E | F>;
|
|
848
|
+
declare function flatMap<
|
|
849
|
+
T,
|
|
850
|
+
E,
|
|
851
|
+
U,
|
|
852
|
+
F
|
|
853
|
+
>(result: Result<T, E>, fn: (data: T) => Promise<Result<U, F>>): Promise<Result<U, E | F>>;
|
|
854
|
+
/**
|
|
855
|
+
* Pattern matching on Result.
|
|
856
|
+
*
|
|
857
|
+
* @example
|
|
858
|
+
* const message = match(result, {
|
|
859
|
+
* ok: (user) => \`Hello, \${user.name}!\`,
|
|
860
|
+
* err: (e) => \`Error: \${e.message}\`
|
|
861
|
+
* });
|
|
862
|
+
*/
|
|
863
|
+
declare function match<
|
|
864
|
+
T,
|
|
865
|
+
E,
|
|
866
|
+
OkR,
|
|
867
|
+
ErrR
|
|
868
|
+
>(result: Result<T, E>, handlers: {
|
|
869
|
+
ok: (data: T) => OkR;
|
|
870
|
+
err: (error: E) => ErrR;
|
|
871
|
+
}): OkR | ErrR;
|
|
872
|
+
/**
|
|
873
|
+
* Type for error handlers in matchErr.
|
|
874
|
+
* Extracts error codes from an error union and creates a handler map.
|
|
875
|
+
*/
|
|
876
|
+
type ErrorHandlers<
|
|
877
|
+
E,
|
|
878
|
+
R
|
|
879
|
+
> = { [K in E as K extends {
|
|
880
|
+
readonly code: infer C extends string;
|
|
881
|
+
} ? C : never] : (error: K) => R };
|
|
882
|
+
/**
|
|
883
|
+
* Exhaustive pattern matching on Result errors.
|
|
884
|
+
*
|
|
885
|
+
* Unlike `match()` which gives you a single `err` handler,
|
|
886
|
+
* `matchErr` requires a handler for every error type in the union.
|
|
887
|
+
* The compiler enforces exhaustiveness — add a new error type to the union
|
|
888
|
+
* and every callsite lights up until you handle it.
|
|
889
|
+
*
|
|
890
|
+
* Errors are discriminated by their `code` string literal field.
|
|
891
|
+
*
|
|
892
|
+
* @example
|
|
893
|
+
* const response = matchErr(result, {
|
|
894
|
+
* ok: (user) => json({ data: user }, 201),
|
|
895
|
+
* NOT_FOUND: (e) => json({ error: 'NOT_FOUND', message: e.message }, 404),
|
|
896
|
+
* UNIQUE_VIOLATION: (e) => json({ error: 'EMAIL_EXISTS', field: e.column }, 409),
|
|
897
|
+
* });
|
|
898
|
+
* // ^ Compile error if error union adds a new member you didn't handle
|
|
899
|
+
*
|
|
900
|
+
* @throws Error if an error code is not handled
|
|
901
|
+
*/
|
|
902
|
+
declare function matchErr<
|
|
903
|
+
T,
|
|
904
|
+
E extends {
|
|
905
|
+
readonly code: string;
|
|
906
|
+
},
|
|
907
|
+
R
|
|
908
|
+
>(result: Result<T, E>, handlers: {
|
|
909
|
+
ok: (data: T) => R;
|
|
910
|
+
} & ErrorHandlers<E, R>): R;
|
|
911
|
+
/**
|
|
912
|
+
* Type guard for Ok results.
|
|
913
|
+
*
|
|
914
|
+
* @example
|
|
915
|
+
* if (isOk(result)) {
|
|
916
|
+
* console.log(result.data); // TypeScript knows this is T
|
|
917
|
+
* }
|
|
918
|
+
*/
|
|
919
|
+
declare function isOk<
|
|
920
|
+
T,
|
|
921
|
+
E
|
|
922
|
+
>(result: Result<T, E>): result is Ok<T>;
|
|
923
|
+
/**
|
|
924
|
+
* Type guard for Err results.
|
|
925
|
+
*
|
|
926
|
+
* @example
|
|
927
|
+
* if (isErr(result)) {
|
|
928
|
+
* console.log(result.error); // TypeScript knows this is E
|
|
929
|
+
* }
|
|
930
|
+
*/
|
|
931
|
+
declare function isErr<
|
|
932
|
+
T,
|
|
933
|
+
E
|
|
934
|
+
>(result: Result<T, E>): result is Err<E>;
|
|
935
|
+
/**
|
|
936
|
+
* Fetch error classes.
|
|
937
|
+
*
|
|
938
|
+
* These errors are used when making HTTP requests from the client.
|
|
939
|
+
*/
|
|
940
|
+
/**
|
|
941
|
+
* Base class for fetch errors.
|
|
942
|
+
*/
|
|
943
|
+
declare abstract class FetchError extends Error {
|
|
944
|
+
/**
|
|
945
|
+
* The error code - a string literal for type-safe discrimination.
|
|
946
|
+
*/
|
|
947
|
+
readonly code: string;
|
|
948
|
+
constructor(code: string, message: string);
|
|
949
|
+
}
|
|
950
|
+
/**
|
|
951
|
+
* Network error - thrown when the request can't reach the server.
|
|
952
|
+
*
|
|
953
|
+
* @example
|
|
954
|
+
* // When the server is unreachable
|
|
955
|
+
* const result = await client.get('/users');
|
|
956
|
+
* if (!result.ok && result.error.code === 'NETWORK_ERROR') {
|
|
957
|
+
* console.log('Network failed:', result.error.message);
|
|
958
|
+
* }
|
|
959
|
+
*/
|
|
960
|
+
declare class FetchNetworkError extends FetchError {
|
|
961
|
+
readonly code: "NetworkError";
|
|
962
|
+
constructor(message?: string);
|
|
963
|
+
}
|
|
964
|
+
/**
|
|
965
|
+
* Type guard for FetchNetworkError.
|
|
966
|
+
*/
|
|
967
|
+
declare function isFetchNetworkError(error: unknown): error is FetchNetworkError;
|
|
968
|
+
/**
|
|
969
|
+
* Base HTTP error - thrown when the server returns an error response.
|
|
970
|
+
* Use specific error classes (BadRequestError, NotFoundError, etc.) when available.
|
|
971
|
+
*
|
|
972
|
+
* @example
|
|
973
|
+
* const result = await client.get('/users/123');
|
|
974
|
+
* if (!result.ok && result.error.code === 'HTTP_ERROR') {
|
|
975
|
+
* const httpError = result.error;
|
|
976
|
+
* console.log('Status:', httpError.status); // e.g., 404
|
|
977
|
+
* console.log('Server code:', httpError.serverCode); // e.g., 'NOT_FOUND'
|
|
978
|
+
* console.log('Message:', httpError.message);
|
|
979
|
+
* }
|
|
980
|
+
*/
|
|
981
|
+
declare class HttpError2 extends FetchError {
|
|
982
|
+
readonly code: "HttpError";
|
|
983
|
+
/**
|
|
984
|
+
* HTTP status code.
|
|
985
|
+
*/
|
|
986
|
+
readonly status: number;
|
|
987
|
+
/**
|
|
988
|
+
* Server error code (from response body).
|
|
989
|
+
*/
|
|
990
|
+
readonly serverCode?: string;
|
|
991
|
+
constructor(status: number, message: string, serverCode?: string);
|
|
562
992
|
}
|
|
563
993
|
/**
|
|
564
|
-
*
|
|
994
|
+
* Type guard for HttpError.
|
|
565
995
|
*/
|
|
566
|
-
declare function
|
|
996
|
+
declare function isHttpError(error: unknown): error is HttpError2;
|
|
567
997
|
/**
|
|
568
|
-
*
|
|
998
|
+
* Bad Request (400) - The request was invalid or cannot be served.
|
|
569
999
|
*/
|
|
570
|
-
declare
|
|
571
|
-
readonly code:
|
|
572
|
-
|
|
1000
|
+
declare class FetchBadRequestError extends HttpError2 {
|
|
1001
|
+
readonly code: "HttpError";
|
|
1002
|
+
readonly status: 400;
|
|
1003
|
+
constructor(message: string, serverCode?: string);
|
|
1004
|
+
}
|
|
573
1005
|
/**
|
|
574
|
-
*
|
|
1006
|
+
* Type guard for FetchBadRequestError.
|
|
575
1007
|
*/
|
|
576
|
-
|
|
1008
|
+
declare function isFetchBadRequestError(error: unknown): error is FetchBadRequestError;
|
|
577
1009
|
/**
|
|
578
|
-
*
|
|
579
|
-
*
|
|
580
|
-
* These errors are used at the HTTP boundary and use client-native vocabulary
|
|
581
|
-
* (not DB internals). They map from server HTTP responses.
|
|
1010
|
+
* Unauthorized (401) - Authentication is required or failed.
|
|
582
1011
|
*/
|
|
1012
|
+
declare class FetchUnauthorizedError extends HttpError2 {
|
|
1013
|
+
readonly code: "HttpError";
|
|
1014
|
+
readonly status: 401;
|
|
1015
|
+
constructor(message: string, serverCode?: string);
|
|
1016
|
+
}
|
|
583
1017
|
/**
|
|
584
|
-
*
|
|
585
|
-
*
|
|
586
|
-
* Maps from server's VALIDATION_FAILED.
|
|
1018
|
+
* Type guard for FetchUnauthorizedError.
|
|
587
1019
|
*/
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
1020
|
+
declare function isFetchUnauthorizedError(error: unknown): error is FetchUnauthorizedError;
|
|
1021
|
+
/**
|
|
1022
|
+
* Forbidden (403) - The request is understood but refused.
|
|
1023
|
+
*/
|
|
1024
|
+
declare class FetchForbiddenError extends HttpError2 {
|
|
1025
|
+
readonly code: "HttpError";
|
|
1026
|
+
readonly status: 403;
|
|
1027
|
+
constructor(message: string, serverCode?: string);
|
|
596
1028
|
}
|
|
597
1029
|
/**
|
|
598
|
-
*
|
|
1030
|
+
* Type guard for FetchForbiddenError.
|
|
599
1031
|
*/
|
|
600
|
-
declare function
|
|
601
|
-
readonly path: readonly (string | number)[];
|
|
602
|
-
readonly message: string;
|
|
603
|
-
readonly code: string;
|
|
604
|
-
}[]): ValidationError2;
|
|
1032
|
+
declare function isFetchForbiddenError(error: unknown): error is FetchForbiddenError;
|
|
605
1033
|
/**
|
|
606
|
-
*
|
|
1034
|
+
* Not Found (404) - The requested resource was not found.
|
|
607
1035
|
*/
|
|
608
|
-
declare
|
|
609
|
-
readonly code:
|
|
610
|
-
|
|
1036
|
+
declare class FetchNotFoundError extends HttpError2 {
|
|
1037
|
+
readonly code: "HttpError";
|
|
1038
|
+
readonly status: 404;
|
|
1039
|
+
constructor(message: string, serverCode?: string);
|
|
1040
|
+
}
|
|
611
1041
|
/**
|
|
612
|
-
*
|
|
613
|
-
*
|
|
614
|
-
* Maps from server's NOT_FOUND.
|
|
1042
|
+
* Type guard for FetchNotFoundError.
|
|
615
1043
|
*/
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
1044
|
+
declare function isFetchNotFoundError(error: unknown): error is FetchNotFoundError;
|
|
1045
|
+
/**
|
|
1046
|
+
* Conflict (409) - The request conflicts with current state.
|
|
1047
|
+
*/
|
|
1048
|
+
declare class FetchConflictError extends HttpError2 {
|
|
1049
|
+
readonly code: "HttpError";
|
|
1050
|
+
readonly status: 409;
|
|
1051
|
+
constructor(message: string, serverCode?: string);
|
|
620
1052
|
}
|
|
621
1053
|
/**
|
|
622
|
-
*
|
|
1054
|
+
* Type guard for FetchConflictError.
|
|
623
1055
|
*/
|
|
624
|
-
declare function
|
|
1056
|
+
declare function isFetchConflictError(error: unknown): error is FetchConflictError;
|
|
625
1057
|
/**
|
|
626
|
-
*
|
|
1058
|
+
* Gone (410) - The resource is no longer available.
|
|
627
1059
|
*/
|
|
628
|
-
declare
|
|
629
|
-
readonly code:
|
|
630
|
-
|
|
1060
|
+
declare class FetchGoneError extends HttpError2 {
|
|
1061
|
+
readonly code: "HttpError";
|
|
1062
|
+
readonly status: 410;
|
|
1063
|
+
constructor(message: string, serverCode?: string);
|
|
1064
|
+
}
|
|
631
1065
|
/**
|
|
632
|
-
*
|
|
633
|
-
*
|
|
634
|
-
* Maps from server's UNIQUE_VIOLATION.
|
|
1066
|
+
* Type guard for FetchGoneError.
|
|
635
1067
|
*/
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
1068
|
+
declare function isFetchGoneError(error: unknown): error is FetchGoneError;
|
|
1069
|
+
/**
|
|
1070
|
+
* Unprocessable Entity (422) - The request was well-formed but semantically invalid.
|
|
1071
|
+
*/
|
|
1072
|
+
declare class FetchUnprocessableEntityError extends HttpError2 {
|
|
1073
|
+
readonly code: "HttpError";
|
|
1074
|
+
readonly status: 422;
|
|
1075
|
+
constructor(message: string, serverCode?: string);
|
|
640
1076
|
}
|
|
641
1077
|
/**
|
|
642
|
-
*
|
|
1078
|
+
* Type guard for FetchUnprocessableEntityError.
|
|
643
1079
|
*/
|
|
644
|
-
declare function
|
|
1080
|
+
declare function isFetchUnprocessableEntityError(error: unknown): error is FetchUnprocessableEntityError;
|
|
645
1081
|
/**
|
|
646
|
-
*
|
|
1082
|
+
* Rate Limited (429) - Too many requests.
|
|
647
1083
|
*/
|
|
648
|
-
declare
|
|
649
|
-
readonly code:
|
|
650
|
-
|
|
1084
|
+
declare class FetchRateLimitError extends HttpError2 {
|
|
1085
|
+
readonly code: "HttpError";
|
|
1086
|
+
readonly status: 429;
|
|
1087
|
+
constructor(message: string, serverCode?: string);
|
|
1088
|
+
}
|
|
651
1089
|
/**
|
|
652
|
-
*
|
|
653
|
-
*
|
|
654
|
-
* Returned when the user is not authenticated.
|
|
1090
|
+
* Type guard for FetchRateLimitError.
|
|
655
1091
|
*/
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
1092
|
+
declare function isFetchRateLimitError(error: unknown): error is FetchRateLimitError;
|
|
1093
|
+
/**
|
|
1094
|
+
* Internal Server Error (500) - The server encountered an error.
|
|
1095
|
+
*/
|
|
1096
|
+
declare class FetchInternalServerError extends HttpError2 {
|
|
1097
|
+
readonly code: "HttpError";
|
|
1098
|
+
readonly status: 500;
|
|
1099
|
+
constructor(message: string, serverCode?: string);
|
|
659
1100
|
}
|
|
660
1101
|
/**
|
|
661
|
-
*
|
|
1102
|
+
* Type guard for FetchInternalServerError.
|
|
662
1103
|
*/
|
|
663
|
-
declare function
|
|
1104
|
+
declare function isFetchInternalServerError(error: unknown): error is FetchInternalServerError;
|
|
664
1105
|
/**
|
|
665
|
-
*
|
|
1106
|
+
* Service Unavailable (503) - The server is temporarily unavailable.
|
|
666
1107
|
*/
|
|
667
|
-
declare
|
|
668
|
-
readonly code:
|
|
669
|
-
|
|
1108
|
+
declare class FetchServiceUnavailableError extends HttpError2 {
|
|
1109
|
+
readonly code: "HttpError";
|
|
1110
|
+
readonly status: 503;
|
|
1111
|
+
constructor(message: string, serverCode?: string);
|
|
1112
|
+
}
|
|
670
1113
|
/**
|
|
671
|
-
*
|
|
1114
|
+
* Type guard for FetchServiceUnavailableError.
|
|
1115
|
+
*/
|
|
1116
|
+
declare function isFetchServiceUnavailableError(error: unknown): error is FetchServiceUnavailableError;
|
|
1117
|
+
/**
|
|
1118
|
+
* Timeout error - thrown when the request takes too long.
|
|
672
1119
|
*
|
|
673
|
-
*
|
|
1120
|
+
* @example
|
|
1121
|
+
* // Configure timeout in client options
|
|
1122
|
+
* const client = createClient({ baseURL: '...', timeout: 5000 });
|
|
1123
|
+
*
|
|
1124
|
+
* const result = await client.get('/slow-endpoint');
|
|
1125
|
+
* if (!result.ok && result.error.code === 'TIMEOUT_ERROR') {
|
|
1126
|
+
* console.log('Request timed out:', result.error.message);
|
|
1127
|
+
* // Retry the request
|
|
1128
|
+
* }
|
|
674
1129
|
*/
|
|
675
|
-
|
|
676
|
-
readonly code: "
|
|
677
|
-
|
|
1130
|
+
declare class FetchTimeoutError extends FetchError {
|
|
1131
|
+
readonly code: "TimeoutError";
|
|
1132
|
+
constructor(message?: string);
|
|
678
1133
|
}
|
|
679
1134
|
/**
|
|
680
|
-
*
|
|
1135
|
+
* Type guard for FetchTimeoutError.
|
|
681
1136
|
*/
|
|
682
|
-
declare function
|
|
1137
|
+
declare function isFetchTimeoutError(error: unknown): error is FetchTimeoutError;
|
|
683
1138
|
/**
|
|
684
|
-
*
|
|
1139
|
+
* Parse error - thrown when response parsing fails.
|
|
1140
|
+
*
|
|
1141
|
+
* @example
|
|
1142
|
+
* // Server returns invalid JSON
|
|
1143
|
+
* const result = await client.get('/malformed');
|
|
1144
|
+
* if (!result.ok && result.error.code === 'PARSE_ERROR') {
|
|
1145
|
+
* console.log('Parse failed at:', result.error.path); // e.g., 'users.0.name'
|
|
1146
|
+
* console.log('Invalid value:', result.error.value);
|
|
1147
|
+
* }
|
|
685
1148
|
*/
|
|
686
|
-
declare
|
|
687
|
-
readonly code:
|
|
688
|
-
|
|
1149
|
+
declare class ParseError2 extends FetchError {
|
|
1150
|
+
readonly code: "ParseError";
|
|
1151
|
+
/**
|
|
1152
|
+
* Path where parsing failed.
|
|
1153
|
+
*/
|
|
1154
|
+
readonly path: string;
|
|
1155
|
+
/**
|
|
1156
|
+
* The value that failed to parse.
|
|
1157
|
+
*/
|
|
1158
|
+
readonly value?: unknown;
|
|
1159
|
+
constructor(path: string, message: string, value?: unknown);
|
|
1160
|
+
}
|
|
689
1161
|
/**
|
|
690
|
-
*
|
|
1162
|
+
* Type guard for ParseError.
|
|
1163
|
+
*/
|
|
1164
|
+
declare function isParseError(error: unknown): error is ParseError2;
|
|
1165
|
+
/**
|
|
1166
|
+
* Validation error - thrown when request validation fails.
|
|
691
1167
|
*
|
|
692
|
-
*
|
|
1168
|
+
* @example
|
|
1169
|
+
* // Server returns validation errors
|
|
1170
|
+
* const result = await client.post('/users', { name: '' });
|
|
1171
|
+
* if (!result.ok && result.error.code === 'VALIDATION_ERROR') {
|
|
1172
|
+
* result.error.errors.forEach(err => {
|
|
1173
|
+
* console.log(`Field: ${err.path}, Error: ${err.message}`);
|
|
1174
|
+
* });
|
|
1175
|
+
* }
|
|
1176
|
+
*
|
|
1177
|
+
* @example
|
|
1178
|
+
* // Using with matchError for exhaustive handling
|
|
1179
|
+
* matchError(result.error, {
|
|
1180
|
+
* VALIDATION_ERROR: (e) => setFormErrors(e.errors),
|
|
1181
|
+
* NETWORK_ERROR: () => showNetworkError(),
|
|
1182
|
+
* // ... other handlers
|
|
1183
|
+
* });
|
|
693
1184
|
*/
|
|
694
|
-
|
|
695
|
-
readonly code: "
|
|
696
|
-
|
|
697
|
-
|
|
1185
|
+
declare class FetchValidationError extends FetchError {
|
|
1186
|
+
readonly code: "ValidationError";
|
|
1187
|
+
/**
|
|
1188
|
+
* Validation errors matching server format.
|
|
1189
|
+
*/
|
|
1190
|
+
readonly errors: readonly {
|
|
1191
|
+
readonly path: string;
|
|
1192
|
+
readonly message: string;
|
|
1193
|
+
}[];
|
|
1194
|
+
constructor(message: string, errors: readonly {
|
|
1195
|
+
readonly path: string;
|
|
1196
|
+
readonly message: string;
|
|
1197
|
+
}[]);
|
|
698
1198
|
}
|
|
699
1199
|
/**
|
|
700
|
-
*
|
|
1200
|
+
* Type guard for FetchValidationError.
|
|
701
1201
|
*/
|
|
702
|
-
declare function
|
|
1202
|
+
declare function isFetchValidationError(error: unknown): error is FetchValidationError;
|
|
703
1203
|
/**
|
|
704
|
-
*
|
|
1204
|
+
* Creates the appropriate error class based on HTTP status code.
|
|
705
1205
|
*/
|
|
706
|
-
declare function
|
|
707
|
-
readonly code: string;
|
|
708
|
-
}): error is RateLimitedError2;
|
|
1206
|
+
declare function createHttpError(status: number, message: string, serverCode?: string): FetchError;
|
|
709
1207
|
/**
|
|
710
|
-
* Union type for all
|
|
1208
|
+
* Union type for all fetch errors.
|
|
711
1209
|
*
|
|
712
|
-
*
|
|
1210
|
+
* @example
|
|
1211
|
+
* import { matchError, FetchErrorType } from '@vertz/errors';
|
|
1212
|
+
*
|
|
1213
|
+
* // Using with matchError for exhaustive handling
|
|
1214
|
+
* function handleError(error: FetchErrorType): string {
|
|
1215
|
+
* return matchError(error, {
|
|
1216
|
+
* NETWORK_ERROR: (e) => `Network failed: ${e.message}`,
|
|
1217
|
+
* HTTP_ERROR: (e) => `HTTP ${e.status}: ${e.message}`,
|
|
1218
|
+
* TIMEOUT_ERROR: (e) => `Timeout: ${e.message}`,
|
|
1219
|
+
* PARSE_ERROR: (e) => `Parse failed at ${e.path}`,
|
|
1220
|
+
* VALIDATION_ERROR: (e) => `Validation: ${e.errors.length} errors`,
|
|
1221
|
+
* });
|
|
1222
|
+
* }
|
|
1223
|
+
*
|
|
1224
|
+
* @example
|
|
1225
|
+
* // Using with Result
|
|
1226
|
+
* const result = await client.get<User>('/users/123');
|
|
1227
|
+
* const errorMessage = result.ok ? '' : handleError(result.error);
|
|
713
1228
|
*/
|
|
714
|
-
type
|
|
1229
|
+
type FetchErrorType = FetchNetworkError | HttpError2 | FetchBadRequestError | FetchUnauthorizedError | FetchForbiddenError | FetchNotFoundError | FetchConflictError | FetchGoneError | FetchUnprocessableEntityError | FetchRateLimitError | FetchInternalServerError | FetchServiceUnavailableError | FetchTimeoutError | ParseError2 | FetchValidationError;
|
|
715
1230
|
/**
|
|
716
|
-
*
|
|
1231
|
+
* Entity error classes.
|
|
717
1232
|
*
|
|
718
|
-
* These
|
|
719
|
-
* in business logic. They're caught by global middleware/error boundaries
|
|
720
|
-
* and result in 500/503 responses.
|
|
1233
|
+
* These errors mirror server HTTP error codes and are used at the HTTP boundary.
|
|
721
1234
|
*/
|
|
722
1235
|
/**
|
|
723
|
-
* Base class for
|
|
1236
|
+
* Base class for entity errors.
|
|
1237
|
+
*
|
|
1238
|
+
* @example
|
|
1239
|
+
* import { EntityError } from '@ *
|
|
1240
|
+
* // Checkvertz/errors';
|
|
1241
|
+
error type
|
|
1242
|
+
* if (error instanceof EntityError) {
|
|
1243
|
+
* console.log(error.code); // e.g., 'NOT_FOUND'
|
|
1244
|
+
* console.log(error.message);
|
|
1245
|
+
* }
|
|
724
1246
|
*/
|
|
725
|
-
declare class
|
|
726
|
-
|
|
1247
|
+
declare abstract class EntityError extends Error {
|
|
1248
|
+
/**
|
|
1249
|
+
* The error code - a string literal for type-safe discrimination.
|
|
1250
|
+
*/
|
|
1251
|
+
readonly code: string;
|
|
1252
|
+
constructor(code: string, message: string);
|
|
727
1253
|
}
|
|
728
1254
|
/**
|
|
729
|
-
*
|
|
730
|
-
*
|
|
731
|
-
* Thrown when the database is unreachable.
|
|
1255
|
+
* Bad request error - 400.
|
|
732
1256
|
*/
|
|
733
|
-
declare class
|
|
1257
|
+
declare class BadRequestError extends EntityError {
|
|
1258
|
+
readonly code: "BadRequest";
|
|
734
1259
|
constructor(message?: string);
|
|
735
1260
|
}
|
|
736
1261
|
/**
|
|
737
|
-
*
|
|
738
|
-
*
|
|
739
|
-
* Thrown when no connections are available in the pool.
|
|
1262
|
+
* Type guard for BadRequestError.
|
|
740
1263
|
*/
|
|
741
|
-
declare
|
|
1264
|
+
declare function isBadRequestError(error: unknown): error is BadRequestError;
|
|
1265
|
+
/**
|
|
1266
|
+
* Unauthorized error - 401.
|
|
1267
|
+
*/
|
|
1268
|
+
declare class EntityUnauthorizedError extends EntityError {
|
|
1269
|
+
readonly code: "Unauthorized";
|
|
742
1270
|
constructor(message?: string);
|
|
743
1271
|
}
|
|
744
1272
|
/**
|
|
745
|
-
*
|
|
746
|
-
*
|
|
747
|
-
* Thrown when a query fails (malformed, syntax error, etc.).
|
|
1273
|
+
* Type guard for EntityUnauthorizedError.
|
|
748
1274
|
*/
|
|
749
|
-
declare
|
|
1275
|
+
declare function isEntityUnauthorizedError(error: unknown): error is EntityUnauthorizedError;
|
|
1276
|
+
/**
|
|
1277
|
+
* Forbidden error - 403.
|
|
1278
|
+
*/
|
|
1279
|
+
declare class EntityForbiddenError extends EntityError {
|
|
1280
|
+
readonly code: "Forbidden";
|
|
750
1281
|
constructor(message?: string);
|
|
751
1282
|
}
|
|
752
1283
|
/**
|
|
753
|
-
*
|
|
1284
|
+
* Type guard for EntityForbiddenError.
|
|
1285
|
+
*/
|
|
1286
|
+
declare function isEntityForbiddenError(error: unknown): error is EntityForbiddenError;
|
|
1287
|
+
/**
|
|
1288
|
+
* Not found error - 404.
|
|
754
1289
|
*
|
|
755
|
-
*
|
|
1290
|
+
* @example
|
|
1291
|
+
* // Using in matchError for server-side error handling
|
|
1292
|
+
* const result = await db.users.get(userId);
|
|
1293
|
+
* if (!result.ok) {
|
|
1294
|
+
* return matchError(result.error, {
|
|
1295
|
+
* NOT_FOUND: (e) => Response.json(
|
|
1296
|
+
* { error: { code: 'NOT_FOUND', message: `User ${userId} not found` } },
|
|
1297
|
+
* { status: 404 }
|
|
1298
|
+
* ),
|
|
1299
|
+
* // ... other handlers
|
|
1300
|
+
* });
|
|
1301
|
+
* }
|
|
1302
|
+
*
|
|
1303
|
+
* @example
|
|
1304
|
+
* // With resource info
|
|
1305
|
+
* throw new EntityNotFoundError('User not found', 'User', userId);
|
|
756
1306
|
*/
|
|
757
|
-
declare class
|
|
758
|
-
|
|
1307
|
+
declare class EntityNotFoundError extends EntityError {
|
|
1308
|
+
readonly code: "NotFound";
|
|
1309
|
+
/**
|
|
1310
|
+
* The type of resource that wasn't found.
|
|
1311
|
+
*/
|
|
1312
|
+
readonly resource?: string;
|
|
1313
|
+
/**
|
|
1314
|
+
* The ID of the resource that wasn't found.
|
|
1315
|
+
*/
|
|
1316
|
+
readonly resourceId?: string;
|
|
1317
|
+
constructor(message?: string, resource?: string, resourceId?: string);
|
|
759
1318
|
}
|
|
760
1319
|
/**
|
|
761
|
-
*
|
|
762
|
-
*
|
|
763
|
-
* Thrown when HTTP client can't reach the server.
|
|
1320
|
+
* Type guard for EntityNotFoundError.
|
|
764
1321
|
*/
|
|
765
|
-
declare
|
|
766
|
-
|
|
1322
|
+
declare function isEntityNotFoundError(error: unknown): error is EntityNotFoundError;
|
|
1323
|
+
/**
|
|
1324
|
+
* Method not allowed error - 405.
|
|
1325
|
+
*/
|
|
1326
|
+
declare class MethodNotAllowedError extends EntityError {
|
|
1327
|
+
readonly code: "MethodNotAllowed";
|
|
1328
|
+
/**
|
|
1329
|
+
* Allowed HTTP methods.
|
|
1330
|
+
*/
|
|
1331
|
+
readonly allowedMethods?: string;
|
|
1332
|
+
constructor(allowedMethods?: string, message?: string);
|
|
767
1333
|
}
|
|
768
1334
|
/**
|
|
769
|
-
*
|
|
770
|
-
*
|
|
771
|
-
* Thrown when response couldn't be decoded.
|
|
1335
|
+
* Type guard for MethodNotAllowedError.
|
|
772
1336
|
*/
|
|
773
|
-
declare
|
|
774
|
-
|
|
1337
|
+
declare function isMethodNotAllowedError(error: unknown): error is MethodNotAllowedError;
|
|
1338
|
+
/**
|
|
1339
|
+
* Conflict error - 409.
|
|
1340
|
+
*/
|
|
1341
|
+
declare class EntityConflictError extends EntityError {
|
|
1342
|
+
readonly code: "Conflict";
|
|
1343
|
+
/**
|
|
1344
|
+
* The field that caused the conflict.
|
|
1345
|
+
*/
|
|
1346
|
+
readonly field?: string;
|
|
1347
|
+
constructor(message?: string, field?: string);
|
|
775
1348
|
}
|
|
776
1349
|
/**
|
|
777
|
-
*
|
|
1350
|
+
* Type guard for EntityConflictError.
|
|
778
1351
|
*/
|
|
779
|
-
|
|
1352
|
+
declare function isEntityConflictError(error: unknown): error is EntityConflictError;
|
|
780
1353
|
/**
|
|
781
|
-
*
|
|
1354
|
+
* Entity validation error - 422.
|
|
782
1355
|
*
|
|
783
|
-
* @
|
|
784
|
-
*
|
|
1356
|
+
* @example
|
|
1357
|
+
* // Server-side: throwing validation errors
|
|
1358
|
+
* throw new EntityValidationError([
|
|
1359
|
+
* { path: ['email'], message: 'Invalid email format', code: 'INVALID_FORMAT' },
|
|
1360
|
+
* { path: ['age'], message: 'Must be positive', code: 'MIN_VALUE' },
|
|
1361
|
+
* ]);
|
|
785
1362
|
*
|
|
786
1363
|
* @example
|
|
787
|
-
*
|
|
788
|
-
*
|
|
789
|
-
*
|
|
790
|
-
*
|
|
791
|
-
*
|
|
792
|
-
*
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
*
|
|
1364
|
+
* // Server-side: handling in HTTP response
|
|
1365
|
+
* if (!result.ok) {
|
|
1366
|
+
* return matchError(result.error, {
|
|
1367
|
+
* ENTITY_VALIDATION_ERROR: (e) => Response.json(
|
|
1368
|
+
* { error: { code: 'VALIDATION_ERROR', message: 'Validation failed', errors: e.errors } },
|
|
1369
|
+
* { status: 422 }
|
|
1370
|
+
* ),
|
|
1371
|
+
* // ... other handlers
|
|
1372
|
+
* });
|
|
1373
|
+
* }
|
|
797
1374
|
*/
|
|
798
|
-
declare
|
|
1375
|
+
declare class EntityValidationError extends EntityError {
|
|
1376
|
+
readonly code: "ValidationError";
|
|
1377
|
+
/**
|
|
1378
|
+
* Validation errors.
|
|
1379
|
+
*/
|
|
1380
|
+
readonly errors: readonly {
|
|
1381
|
+
readonly path: readonly (string | number)[];
|
|
1382
|
+
readonly message: string;
|
|
1383
|
+
readonly code: string;
|
|
1384
|
+
}[];
|
|
1385
|
+
constructor(errors: readonly {
|
|
1386
|
+
readonly path: readonly (string | number)[];
|
|
1387
|
+
readonly message: string;
|
|
1388
|
+
readonly code: string;
|
|
1389
|
+
}[]);
|
|
1390
|
+
}
|
|
799
1391
|
/**
|
|
800
|
-
*
|
|
1392
|
+
* Type guard for EntityValidationError.
|
|
801
1393
|
*/
|
|
802
|
-
declare function
|
|
1394
|
+
declare function isEntityValidationError(error: unknown): error is EntityValidationError;
|
|
803
1395
|
/**
|
|
804
|
-
*
|
|
1396
|
+
* Internal server error - 500.
|
|
805
1397
|
*/
|
|
806
|
-
declare
|
|
1398
|
+
declare class InternalError2 extends EntityError {
|
|
1399
|
+
readonly code: "InternalError";
|
|
1400
|
+
constructor(message?: string);
|
|
1401
|
+
}
|
|
807
1402
|
/**
|
|
808
|
-
*
|
|
1403
|
+
* Type guard for InternalError.
|
|
809
1404
|
*/
|
|
810
|
-
declare function
|
|
1405
|
+
declare function isInternalError(error: unknown): error is InternalError2;
|
|
811
1406
|
/**
|
|
812
|
-
*
|
|
1407
|
+
* Service unavailable error - 503.
|
|
813
1408
|
*/
|
|
814
|
-
declare
|
|
1409
|
+
declare class ServiceUnavailableError extends EntityError {
|
|
1410
|
+
readonly code: "ServiceUnavailable";
|
|
1411
|
+
/**
|
|
1412
|
+
* Seconds until retry.
|
|
1413
|
+
*/
|
|
1414
|
+
readonly retryAfter?: number;
|
|
1415
|
+
constructor(message?: string, retryAfter?: number);
|
|
1416
|
+
}
|
|
815
1417
|
/**
|
|
816
|
-
*
|
|
1418
|
+
* Type guard for ServiceUnavailableError.
|
|
817
1419
|
*/
|
|
818
|
-
|
|
819
|
-
readonly code: "UNKNOWN";
|
|
820
|
-
readonly message: string;
|
|
821
|
-
readonly status: number;
|
|
822
|
-
}
|
|
1420
|
+
declare function isServiceUnavailableError(error: unknown): error is ServiceUnavailableError;
|
|
823
1421
|
/**
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
* @param status - HTTP status code
|
|
827
|
-
* @param body - Response body
|
|
828
|
-
* @returns Client domain error
|
|
1422
|
+
* Union type for all entity errors.
|
|
829
1423
|
*
|
|
830
1424
|
* @example
|
|
831
|
-
*
|
|
832
|
-
*
|
|
1425
|
+
* import { matchError, EntityErrorType } from '@vertz/errors';
|
|
1426
|
+
*
|
|
1427
|
+
* // Server-side: handling database errors
|
|
1428
|
+
* const result = await db.users.create(data);
|
|
1429
|
+
* if (!result.ok) {
|
|
1430
|
+
* return matchError(result.error, {
|
|
1431
|
+
* BAD_REQUEST: (e) => Response.json({ error: e.message }, { status: 400 }),
|
|
1432
|
+
* UNAUTHORIZED: () => Response.json({ error: 'Unauthorized' }, { status: 401 }),
|
|
1433
|
+
* FORBIDDEN: () => Response.json({ error: 'Forbidden' }, { status: 403 }),
|
|
1434
|
+
* NOT_FOUND: (e) => Response.json({ error: e.message }, { status: 404 }),
|
|
1435
|
+
* METHOD_NOT_ALLOWED: () => Response.json({ error: 'Method not allowed' }, { status: 405 }),
|
|
1436
|
+
* CONFLICT: (e) => Response.json({ error: e.message }, { status: 409 }),
|
|
1437
|
+
* ENTITY_VALIDATION_ERROR: (e) => Response.json({ error: e.errors }, { status: 422 }),
|
|
1438
|
+
* INTERNAL_ERROR: () => Response.json({ error: 'Internal error' }, { status: 500 }),
|
|
1439
|
+
* SERVICE_UNAVAILABLE: () => Response.json({ error: 'Service unavailable' }, { status: 503 }),
|
|
1440
|
+
* });
|
|
1441
|
+
* }
|
|
833
1442
|
*/
|
|
834
|
-
|
|
835
|
-
/**
|
|
836
|
-
*
|
|
1443
|
+
type EntityErrorType = BadRequestError | EntityUnauthorizedError | EntityForbiddenError | EntityNotFoundError | MethodNotAllowedError | EntityConflictError | EntityValidationError | InternalError2 | ServiceUnavailableError;
|
|
1444
|
+
/**
|
|
1445
|
+
* Handler map type for FetchError.
|
|
1446
|
+
* Each key corresponds to an error code, and the value is a handler function.
|
|
1447
|
+
*/
|
|
1448
|
+
type FetchErrorHandlers<R> = {
|
|
1449
|
+
NetworkError: (error: Extract<FetchErrorType, {
|
|
1450
|
+
code: "NetworkError";
|
|
1451
|
+
}>) => R;
|
|
1452
|
+
HttpError: (error: Extract<FetchErrorType, {
|
|
1453
|
+
code: "HttpError";
|
|
1454
|
+
}>) => R;
|
|
1455
|
+
TimeoutError: (error: Extract<FetchErrorType, {
|
|
1456
|
+
code: "TimeoutError";
|
|
1457
|
+
}>) => R;
|
|
1458
|
+
ParseError: (error: Extract<FetchErrorType, {
|
|
1459
|
+
code: "ParseError";
|
|
1460
|
+
}>) => R;
|
|
1461
|
+
ValidationError: (error: Extract<FetchErrorType, {
|
|
1462
|
+
code: "ValidationError";
|
|
1463
|
+
}>) => R;
|
|
1464
|
+
};
|
|
1465
|
+
/**
|
|
1466
|
+
* Handler map type for EntityError.
|
|
1467
|
+
* Each key corresponds to an error code, and the value is a handler function.
|
|
1468
|
+
*/
|
|
1469
|
+
type EntityErrorHandlers<R> = {
|
|
1470
|
+
BadRequest: (error: Extract<EntityErrorType, {
|
|
1471
|
+
code: "BadRequest";
|
|
1472
|
+
}>) => R;
|
|
1473
|
+
Unauthorized: (error: Extract<EntityErrorType, {
|
|
1474
|
+
code: "Unauthorized";
|
|
1475
|
+
}>) => R;
|
|
1476
|
+
Forbidden: (error: Extract<EntityErrorType, {
|
|
1477
|
+
code: "Forbidden";
|
|
1478
|
+
}>) => R;
|
|
1479
|
+
NotFound: (error: Extract<EntityErrorType, {
|
|
1480
|
+
code: "NotFound";
|
|
1481
|
+
}>) => R;
|
|
1482
|
+
MethodNotAllowed: (error: Extract<EntityErrorType, {
|
|
1483
|
+
code: "MethodNotAllowed";
|
|
1484
|
+
}>) => R;
|
|
1485
|
+
Conflict: (error: Extract<EntityErrorType, {
|
|
1486
|
+
code: "Conflict";
|
|
1487
|
+
}>) => R;
|
|
1488
|
+
ValidationError: (error: Extract<EntityErrorType, {
|
|
1489
|
+
code: "ValidationError";
|
|
1490
|
+
}>) => R;
|
|
1491
|
+
InternalError: (error: Extract<EntityErrorType, {
|
|
1492
|
+
code: "InternalError";
|
|
1493
|
+
}>) => R;
|
|
1494
|
+
ServiceUnavailable: (error: Extract<EntityErrorType, {
|
|
1495
|
+
code: "ServiceUnavailable";
|
|
1496
|
+
}>) => R;
|
|
1497
|
+
};
|
|
1498
|
+
/**
|
|
1499
|
+
* Pattern matching for FetchError types.
|
|
1500
|
+
*
|
|
1501
|
+
* Provides compile-time exhaustiveness checking - you must handle all error types.
|
|
1502
|
+
*
|
|
1503
|
+
* @example
|
|
1504
|
+
* const result = matchError(error, {
|
|
1505
|
+
* NetworkError: (e) => 'Network failed',
|
|
1506
|
+
* HttpError: (e) => `HTTP ${e.status}: ${e.message}`,
|
|
1507
|
+
* TimeoutError: (e) => 'Request timed out',
|
|
1508
|
+
* ParseError: (e) => `Parse failed at ${e.path}`,
|
|
1509
|
+
* ValidationError: (e) => `Validation: ${e.errors.length} errors`,
|
|
1510
|
+
* });
|
|
837
1511
|
*/
|
|
838
|
-
declare function
|
|
839
|
-
|
|
1512
|
+
declare function matchError<R>(error: FetchErrorType, handlers: FetchErrorHandlers<R>): R;
|
|
1513
|
+
declare function matchError<R>(error: EntityErrorType, handlers: EntityErrorHandlers<R>): R;
|
|
1514
|
+
export { unwrapOr, unwrap, uniqueViolationToHttpStatus, ok, notNullViolationToHttpStatus, notFoundErrorToHttpStatus, matchError, matchErr, match, map, isUserExistsError, isUnknownError, isUniqueViolation, isUnauthorizedError, isSessionExpiredError, isServiceUnavailableError, isValidationError2 as isSchemaValidationError, isPermissionDeniedError, isParseError, isOk, isNotNullViolation, isMigrationQueryError, isMigrationHistoryNotFound, isMigrationChecksumMismatch, isMethodNotAllowedError, isInvalidCredentialsError, isInternalError, isHttpError, isForbiddenError, isFetchValidationError, isFetchUnprocessableEntityError, isFetchUnauthorizedError, isFetchTimeoutError, isFetchServiceUnavailableError, isFetchRateLimitError, isFetchNotFoundError, isFetchNetworkError, isFetchInternalServerError, isFetchGoneError, isFetchForbiddenError, isFetchConflictError, isFetchBadRequestError, isFKViolation, isErr, isEntityValidationError, isEntityUnauthorizedError, isEntityNotFoundError, isEntityForbiddenError, isEntityConflictError, isNotFoundError2 as isDBNotFoundError, isConflictError, isValidationError as isClientValidationError, isRateLimitedError2 as isClientRateLimitedError, isNotFoundError as isClientNotFoundError, isCheckViolation, isBadRequestError, isAuthValidationError, isRateLimitedError as isAuthRateLimitedError, httpToClientError, flatMap, fkViolationToHttpStatus, err, dbErrorToHttpStatus, createUserExistsError, createUniqueViolation, createUnauthorizedError, createSessionExpiredError, createValidationError2 as createSchemaValidationError, createPermissionDeniedError, createNotNullViolation, createMigrationQueryError, createMigrationHistoryNotFound, createMigrationChecksumMismatch, createInvalidCredentialsError, createHttpError, createForbiddenError, createFKViolation, createNotFoundError2 as createDBNotFoundError, createConflictError, createValidationError as createClientValidationError, createRateLimitedError2 as createClientRateLimitedError, createNotFoundError as createClientNotFoundError, createCheckViolation, createAuthValidationError, createRateLimitedError as createAuthRateLimitedError, checkViolationToHttpStatus, WriteError, ValidationIssue, UserExistsError, UnknownError, UniqueViolation, UnauthorizedError, TimeoutError2 as TimeoutError, SessionExpiredError, ServiceUnavailableError, SerializationError, ValidationError3 as SchemaValidationError, Result, ReadError, QueryError, PoolExhaustedError, PermissionDeniedError, ParseError2 as ParseError, Ok, NotNullViolation, NetworkError2 as NetworkError, MigrationQueryError, MigrationHistoryNotFound, MigrationError, MigrationChecksumMismatch, MethodNotAllowedError, InvalidCredentialsError, InternalError2 as InternalError, InfraErrors, InfraError, HttpError2 as HttpError, ForbiddenError, FetchValidationError, FetchUnprocessableEntityError, FetchUnauthorizedError, FetchTimeoutError, FetchServiceUnavailableError, FetchRateLimitError, FetchNotFoundError, FetchNetworkError, FetchInternalServerError, FetchGoneError, FetchForbiddenError, FetchErrorType, FetchError, FetchConflictError, FetchBadRequestError, FKViolation, Err, EntityValidationError, EntityUnauthorizedError, EntityNotFoundError, EntityForbiddenError, EntityErrorType, EntityError, EntityConflictError, NotFoundError2 as DBNotFoundError, ConnectionError, ConflictError, ValidationError2 as ClientValidationError, RateLimitedError2 as ClientRateLimitedError, NotFoundError as ClientNotFoundError, CheckViolation, BadRequestError, AuthValidationError, RateLimitedError as AuthRateLimitedError, AuthError, AppError, ApiError };
|