@geekmidas/errors 9.0.2 → 10.0.0-alpha.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geekmidas/errors",
3
- "version": "9.0.2",
3
+ "version": "10.0.0-alpha.1",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -14,6 +14,9 @@
14
14
  }
15
15
  }
16
16
  },
17
+ "files": [
18
+ "dist"
19
+ ],
17
20
  "repository": {
18
21
  "type": "git",
19
22
  "url": "https://github.com/geekmidas/toolbox"
package/CHANGELOG.md DELETED
@@ -1,40 +0,0 @@
1
- # @geekmidas/errors
2
-
3
- ## 9.0.2
4
-
5
- ### Patch Changes
6
-
7
- - [#12](https://github.com/geekmidas/toolbox/pull/12) [`d53863a`](https://github.com/geekmidas/toolbox/commit/d53863a84db2e4ab5420e08f79128b637043fc42) Thanks [@geekmidas](https://github.com/geekmidas)! - Align every published package on a single version and keep them in step.
8
-
9
- All packages now share one version, enforced by a changesets `fixed` group. The
10
- baseline is 9.0.1 — @geekmidas/client's published version — so nothing moves
11
- backwards; this release takes the whole set to 9.0.2 together.
12
-
13
- Independent versions made "which version of the docs applies to me"
14
- unanswerable: a reader on constructs@7 and cli@2 was on no version at all. One
15
- number per release makes versioned documentation possible, and lets 9 freeze as
16
- the current paradigm while the constructs rework is developed against it.
17
-
18
- Every release now publishes every package, and a major anywhere is a major
19
- everywhere. Peer ranges get simpler in return.
20
-
21
- ## 1.0.2
22
-
23
- ### Patch Changes
24
-
25
- - 🐛 [#11](https://github.com/geekmidas/toolbox/pull/11) [`40f4dc0`](https://github.com/geekmidas/toolbox/commit/40f4dc095911b2223a255029d8f776caf7781309) Thanks [@geekmidas](https://github.com/geekmidas)! - Patch release across all packages to realign published versions with the
26
- registry. The previous release only published the four packages that had
27
- version bumps; the remaining packages failed with "cannot publish over the
28
- previously published versions" because their versions were unchanged.
29
-
30
- ## 1.0.1
31
-
32
- ### Patch Changes
33
-
34
- - 🐛 [`d70c6c0`](https://github.com/geekmidas/toolbox/commit/d70c6c0aeb8a79da2473ac77dbd8255a4a2f5651) Thanks [@geekmidas](https://github.com/geekmidas)! - Fix `package.json` exports so TypeScript declarations resolve correctly under NodeNext/Bundler module resolution. Each subpath export now nests `types` inside its `import`/`require` condition, pointing at the `.d.mts` and `.d.cts` files that `tsdown` actually emits (previously the exports referenced non-existent `.d.ts` files, causing type-resolution failures for consumers). Both ESM (`.mjs`) and CJS (`.cjs`) runtime entry points are preserved. Additionally, `@geekmidas/ui` had `import` paths pointing at `.js` files that were never emitted — those are corrected to `.mjs`.
35
-
36
- ## 1.0.0
37
-
38
- ### Major Changes
39
-
40
- - [`ff7b115`](https://github.com/geekmidas/toolbox/commit/ff7b11599f60f84ac6cdc73714c853ecf786b2e8) Thanks [@geekmidas](https://github.com/geekmidas)! - Version 1 Stable release
@@ -1,591 +0,0 @@
1
- import { describe, expect, it } from 'vitest';
2
- import {
3
- BadGatewayError,
4
- BadRequestError,
5
- ConflictError,
6
- createError,
7
- createHttpError,
8
- ForbiddenError,
9
- GatewayTimeoutError,
10
- HttpError,
11
- HttpStatusCode,
12
- InternalServerError,
13
- isClientError,
14
- isHttpError,
15
- isServerError,
16
- MethodNotAllowedError,
17
- NotFoundError,
18
- NotImplementedError,
19
- ServiceUnavailableError,
20
- TooManyRequestsError,
21
- UnauthorizedError,
22
- UnprocessableEntityError,
23
- wrapError,
24
- } from '../index';
25
-
26
- describe('HttpError', () => {
27
- it('should create basic HTTP error', () => {
28
- const error = new HttpError(400, 'Bad request');
29
-
30
- expect(error).toBeInstanceOf(Error);
31
- expect(error).toBeInstanceOf(HttpError);
32
- expect(error.statusCode).toBe(400);
33
- expect(error.statusMessage).toBe('Bad Request');
34
- expect(error.message).toBe('Bad request');
35
- expect(error.name).toBe('HttpError');
36
- expect(error.isHttpError).toBe(true);
37
- });
38
-
39
- it('should use default status message when not provided', () => {
40
- const error = new HttpError(404);
41
-
42
- expect(error.statusMessage).toBe('Not Found');
43
- expect(error.message).toBe('HTTP Error'); // Default from constructor
44
- });
45
-
46
- it('should include error details', () => {
47
- const error = new HttpError(400, 'Invalid input', {
48
- details: { field: 'email', value: 'invalid' },
49
- });
50
-
51
- expect(error.details).toEqual({ field: 'email', value: 'invalid' });
52
- });
53
-
54
- it('should include error code', () => {
55
- const error = new HttpError(400, 'Invalid input', {
56
- code: 'VALIDATION_ERROR',
57
- });
58
-
59
- expect(error.code).toBe('VALIDATION_ERROR');
60
- });
61
-
62
- it('should support error cause chaining', () => {
63
- const originalError = new Error('Database connection failed');
64
- const error = new HttpError(500, 'Internal error', {
65
- cause: originalError,
66
- });
67
-
68
- expect(error.cause).toBe(originalError);
69
- });
70
-
71
- it('should serialize to JSON', () => {
72
- const error = new HttpError(404, 'Not found', {
73
- details: { id: '123' },
74
- code: 'NOT_FOUND',
75
- });
76
-
77
- const json = error.toJSON();
78
-
79
- expect(json).toMatchObject({
80
- name: 'HttpError',
81
- message: 'Not found',
82
- statusCode: 404,
83
- statusMessage: 'Not Found',
84
- code: 'NOT_FOUND',
85
- details: { id: '123' },
86
- });
87
- expect(json.stack).toBeDefined();
88
- });
89
-
90
- it('should generate error body', () => {
91
- const error = new HttpError(400, 'Bad request', {
92
- details: { field: 'email' },
93
- code: 'INVALID_EMAIL',
94
- });
95
-
96
- const body = JSON.parse(error.body);
97
-
98
- expect(body).toEqual({
99
- message: 'Bad request',
100
- code: 'INVALID_EMAIL',
101
- error: { field: 'email' },
102
- });
103
- });
104
-
105
- it('should handle unknown status codes', () => {
106
- const error = new HttpError(418);
107
-
108
- expect(error.statusCode).toBe(418);
109
- expect(error.statusMessage).toBe('Unknown Error');
110
- });
111
- });
112
-
113
- describe('Client Error Classes (4xx)', () => {
114
- describe('BadRequestError', () => {
115
- it('should create 400 error', () => {
116
- const error = new BadRequestError('Invalid input');
117
-
118
- expect(error.statusCode).toBe(400);
119
- expect(error.statusMessage).toBe('Bad Request');
120
- expect(error.message).toBe('Invalid input');
121
- });
122
-
123
- it('should include details', () => {
124
- const error = new BadRequestError('Invalid input', { field: 'email' });
125
-
126
- expect(error.details).toEqual({ field: 'email' });
127
- });
128
- });
129
-
130
- describe('UnauthorizedError', () => {
131
- it('should create 401 error', () => {
132
- const error = new UnauthorizedError('Invalid token');
133
-
134
- expect(error.statusCode).toBe(401);
135
- expect(error.statusMessage).toBe('Unauthorized');
136
- expect(error.message).toBe('Invalid token');
137
- });
138
- });
139
-
140
- describe('ForbiddenError', () => {
141
- it('should create 403 error', () => {
142
- const error = new ForbiddenError('Access denied');
143
-
144
- expect(error.statusCode).toBe(403);
145
- expect(error.statusMessage).toBe('Forbidden');
146
- expect(error.message).toBe('Access denied');
147
- });
148
- });
149
-
150
- describe('NotFoundError', () => {
151
- it('should create 404 error', () => {
152
- const error = new NotFoundError('User not found');
153
-
154
- expect(error.statusCode).toBe(404);
155
- expect(error.statusMessage).toBe('Not Found');
156
- expect(error.message).toBe('User not found');
157
- });
158
- });
159
-
160
- describe('MethodNotAllowedError', () => {
161
- it('should create 405 error', () => {
162
- const error = new MethodNotAllowedError('DELETE not allowed');
163
-
164
- expect(error.statusCode).toBe(405);
165
- expect(error.statusMessage).toBe('Method Not Allowed');
166
- expect(error.message).toBe('DELETE not allowed');
167
- });
168
-
169
- it('should include allowed methods', () => {
170
- const error = new MethodNotAllowedError('DELETE not allowed', [
171
- 'GET',
172
- 'POST',
173
- 'PUT',
174
- ]);
175
-
176
- expect(error.details).toEqual({
177
- allowedMethods: ['GET', 'POST', 'PUT'],
178
- });
179
- });
180
- });
181
-
182
- describe('ConflictError', () => {
183
- it('should create 409 error', () => {
184
- const error = new ConflictError('Email already exists');
185
-
186
- expect(error.statusCode).toBe(409);
187
- expect(error.statusMessage).toBe('Conflict');
188
- expect(error.message).toBe('Email already exists');
189
- });
190
- });
191
-
192
- describe('UnprocessableEntityError', () => {
193
- it('should create 422 error', () => {
194
- const error = new UnprocessableEntityError('Validation failed');
195
-
196
- expect(error.statusCode).toBe(422);
197
- expect(error.statusMessage).toBe('Unprocessable Entity');
198
- expect(error.message).toBe('Validation failed');
199
- });
200
-
201
- it('should include validation errors', () => {
202
- const error = new UnprocessableEntityError('Validation failed', {
203
- email: 'Invalid format',
204
- age: 'Must be 18+',
205
- });
206
-
207
- expect(error.details).toEqual({
208
- validationErrors: {
209
- email: 'Invalid format',
210
- age: 'Must be 18+',
211
- },
212
- });
213
- });
214
- });
215
-
216
- describe('TooManyRequestsError', () => {
217
- it('should create 429 error', () => {
218
- const error = new TooManyRequestsError('Rate limit exceeded');
219
-
220
- expect(error.statusCode).toBe(429);
221
- expect(error.statusMessage).toBe('Too Many Requests');
222
- expect(error.message).toBe('Rate limit exceeded');
223
- });
224
-
225
- it('should include retry after', () => {
226
- const error = new TooManyRequestsError('Rate limit exceeded', 60);
227
-
228
- expect(error.details).toEqual({ retryAfter: 60 });
229
- });
230
- });
231
- });
232
-
233
- describe('Server Error Classes (5xx)', () => {
234
- describe('InternalServerError', () => {
235
- it('should create 500 error', () => {
236
- const error = new InternalServerError('Database error');
237
-
238
- expect(error.statusCode).toBe(500);
239
- expect(error.statusMessage).toBe('Internal Server Error');
240
- expect(error.message).toBe('Database error');
241
- });
242
- });
243
-
244
- describe('NotImplementedError', () => {
245
- it('should create 501 error', () => {
246
- const error = new NotImplementedError('Feature not implemented');
247
-
248
- expect(error.statusCode).toBe(501);
249
- expect(error.statusMessage).toBe('Not Implemented');
250
- expect(error.message).toBe('Feature not implemented');
251
- });
252
- });
253
-
254
- describe('BadGatewayError', () => {
255
- it('should create 502 error', () => {
256
- const error = new BadGatewayError('Upstream error');
257
-
258
- expect(error.statusCode).toBe(502);
259
- expect(error.statusMessage).toBe('Bad Gateway');
260
- expect(error.message).toBe('Upstream error');
261
- });
262
- });
263
-
264
- describe('ServiceUnavailableError', () => {
265
- it('should create 503 error', () => {
266
- const error = new ServiceUnavailableError('Service down');
267
-
268
- expect(error.statusCode).toBe(503);
269
- expect(error.statusMessage).toBe('Service Unavailable');
270
- expect(error.message).toBe('Service down');
271
- });
272
-
273
- it('should include retry after', () => {
274
- const error = new ServiceUnavailableError('Service down', 300);
275
-
276
- expect(error.details).toEqual({ retryAfter: 300 });
277
- });
278
- });
279
-
280
- describe('GatewayTimeoutError', () => {
281
- it('should create 504 error', () => {
282
- const error = new GatewayTimeoutError('Upstream timeout');
283
-
284
- expect(error.statusCode).toBe(504);
285
- expect(error.statusMessage).toBe('Gateway Timeout');
286
- expect(error.message).toBe('Upstream timeout');
287
- });
288
- });
289
- });
290
-
291
- describe('createError factory', () => {
292
- it('should create badRequest error', () => {
293
- const error = createError.badRequest('Invalid input');
294
-
295
- expect(error).toBeInstanceOf(BadRequestError);
296
- expect(error.statusCode).toBe(400);
297
- });
298
-
299
- it('should create unauthorized error', () => {
300
- const error = createError.unauthorized('Invalid token');
301
-
302
- expect(error).toBeInstanceOf(UnauthorizedError);
303
- expect(error.statusCode).toBe(401);
304
- });
305
-
306
- it('should create forbidden error', () => {
307
- const error = createError.forbidden('Access denied');
308
-
309
- expect(error).toBeInstanceOf(ForbiddenError);
310
- expect(error.statusCode).toBe(403);
311
- });
312
-
313
- it('should create notFound error', () => {
314
- const error = createError.notFound('Resource not found');
315
-
316
- expect(error).toBeInstanceOf(NotFoundError);
317
- expect(error.statusCode).toBe(404);
318
- });
319
-
320
- it('should create methodNotAllowed error', () => {
321
- const error = createError.methodNotAllowed('Method not allowed', [
322
- 'GET',
323
- 'POST',
324
- ]);
325
-
326
- expect(error).toBeInstanceOf(MethodNotAllowedError);
327
- expect(error.statusCode).toBe(405);
328
- });
329
-
330
- it('should create conflict error', () => {
331
- const error = createError.conflict('Resource exists');
332
-
333
- expect(error).toBeInstanceOf(ConflictError);
334
- expect(error.statusCode).toBe(409);
335
- });
336
-
337
- it('should create unprocessableEntity error', () => {
338
- const error = createError.unprocessableEntity('Validation failed');
339
-
340
- expect(error).toBeInstanceOf(UnprocessableEntityError);
341
- expect(error.statusCode).toBe(422);
342
- });
343
-
344
- it('should create tooManyRequests error', () => {
345
- const error = createError.tooManyRequests('Rate limited', 60);
346
-
347
- expect(error).toBeInstanceOf(TooManyRequestsError);
348
- expect(error.statusCode).toBe(429);
349
- });
350
-
351
- it('should create internalServerError error', () => {
352
- const error = createError.internalServerError('Server error');
353
-
354
- expect(error).toBeInstanceOf(InternalServerError);
355
- expect(error.statusCode).toBe(500);
356
- });
357
-
358
- it('should create notImplemented error', () => {
359
- const error = createError.notImplemented('Not implemented');
360
-
361
- expect(error).toBeInstanceOf(NotImplementedError);
362
- expect(error.statusCode).toBe(501);
363
- });
364
-
365
- it('should create badGateway error', () => {
366
- const error = createError.badGateway('Gateway error');
367
-
368
- expect(error).toBeInstanceOf(BadGatewayError);
369
- expect(error.statusCode).toBe(502);
370
- });
371
-
372
- it('should create serviceUnavailable error', () => {
373
- const error = createError.serviceUnavailable('Service down', 300);
374
-
375
- expect(error).toBeInstanceOf(ServiceUnavailableError);
376
- expect(error.statusCode).toBe(503);
377
- });
378
-
379
- it('should create gatewayTimeout error', () => {
380
- const error = createError.gatewayTimeout('Timeout');
381
-
382
- expect(error).toBeInstanceOf(GatewayTimeoutError);
383
- expect(error.statusCode).toBe(504);
384
- });
385
- });
386
-
387
- describe('createHttpError function', () => {
388
- it('should create error with valid status code', () => {
389
- const error = createHttpError(404, 'Not found');
390
-
391
- expect(error).toBeInstanceOf(NotFoundError);
392
- expect(error.statusCode).toBe(404);
393
- expect(error.message).toBe('Not found');
394
- });
395
-
396
- it('should create error with type-safe options', () => {
397
- const error = createHttpError(429, 'Rate limited', { retryAfter: 60 });
398
-
399
- expect(error).toBeInstanceOf(TooManyRequestsError);
400
- expect(error.details).toEqual({ retryAfter: 60 });
401
- });
402
-
403
- it('should create error with validation errors', () => {
404
- const error = createHttpError(422, 'Validation failed', {
405
- validationErrors: { email: 'Invalid' },
406
- });
407
-
408
- expect(error).toBeInstanceOf(UnprocessableEntityError);
409
- expect(error.details).toEqual({
410
- validationErrors: { email: 'Invalid' },
411
- });
412
- });
413
-
414
- it('should create error with allowed methods', () => {
415
- const error = createHttpError(405, 'Method not allowed', {
416
- allowedMethods: ['GET', 'POST'],
417
- });
418
-
419
- expect(error).toBeInstanceOf(MethodNotAllowedError);
420
- expect(error.details).toEqual({ allowedMethods: ['GET', 'POST'] });
421
- });
422
-
423
- it('should create generic error for unknown status code', () => {
424
- const error = createHttpError(418, 'I am a teapot');
425
-
426
- expect(error).toBeInstanceOf(HttpError);
427
- expect(error.statusCode).toBe(418);
428
- expect(error.message).toBe('I am a teapot');
429
- });
430
- });
431
-
432
- describe('Type guards', () => {
433
- describe('isHttpError', () => {
434
- it('should return true for HttpError instances', () => {
435
- const error = new HttpError(400);
436
-
437
- expect(isHttpError(error)).toBe(true);
438
- });
439
-
440
- it('should return true for HttpError subclasses', () => {
441
- const error = new NotFoundError();
442
-
443
- expect(isHttpError(error)).toBe(true);
444
- });
445
-
446
- it('should return true for duck-typed errors', () => {
447
- const error = {
448
- statusCode: 400,
449
- statusMessage: 'Bad Request',
450
- message: 'Error',
451
- isHttpError: true,
452
- };
453
-
454
- expect(isHttpError(error)).toBe(true);
455
- });
456
-
457
- it('should return false for regular errors', () => {
458
- const error = new Error('Regular error');
459
-
460
- expect(isHttpError(error)).toBe(false);
461
- });
462
-
463
- it('should return false for non-errors', () => {
464
- expect(isHttpError(null)).toBe(false);
465
- expect(isHttpError(undefined)).toBe(false);
466
- expect(isHttpError('string')).toBe(false);
467
- expect(isHttpError(123)).toBe(false);
468
- expect(isHttpError({})).toBe(false);
469
- });
470
- });
471
-
472
- describe('isClientError', () => {
473
- it('should return true for 4xx errors', () => {
474
- const error = new BadRequestError();
475
-
476
- expect(isClientError(error)).toBe(true);
477
- });
478
-
479
- it('should return false for 5xx errors', () => {
480
- const error = new InternalServerError();
481
-
482
- expect(isClientError(error)).toBe(false);
483
- });
484
-
485
- it('should return false for non-http errors', () => {
486
- const error = new Error('Regular error');
487
-
488
- expect(isClientError(error)).toBe(false);
489
- });
490
- });
491
-
492
- describe('isServerError', () => {
493
- it('should return true for 5xx errors', () => {
494
- const error = new InternalServerError();
495
-
496
- expect(isServerError(error)).toBe(true);
497
- });
498
-
499
- it('should return false for 4xx errors', () => {
500
- const error = new NotFoundError();
501
-
502
- expect(isServerError(error)).toBe(false);
503
- });
504
-
505
- it('should return false for non-http errors', () => {
506
- const error = new Error('Regular error');
507
-
508
- expect(isServerError(error)).toBe(false);
509
- });
510
- });
511
- });
512
-
513
- describe('wrapError', () => {
514
- it('should return HttpError unchanged', () => {
515
- const original = new NotFoundError('Not found');
516
- const wrapped = wrapError(original);
517
-
518
- expect(wrapped).toBe(original);
519
- });
520
-
521
- it('should wrap unknown errors as 500', () => {
522
- const original = new Error('Something went wrong');
523
- const wrapped = wrapError(original);
524
-
525
- expect(wrapped).toBeInstanceOf(HttpError);
526
- expect(wrapped.statusCode).toBe(500);
527
- expect(wrapped.message).toBe('An unknown error occurred');
528
- expect(wrapped.details?.originalError).toBe(original);
529
- });
530
-
531
- it('should wrap with custom status code', () => {
532
- const original = new Error('Service unavailable');
533
- const wrapped = wrapError(original, 503);
534
-
535
- expect(wrapped.statusCode).toBe(503);
536
- });
537
-
538
- it('should wrap with custom message', () => {
539
- const original = new Error('Database error');
540
- const wrapped = wrapError(original, 500, 'Failed to query database');
541
-
542
- expect(wrapped.statusCode).toBe(500);
543
- expect(wrapped.message).toBe('Failed to query database');
544
- expect(wrapped.details?.originalError).toBe(original);
545
- });
546
-
547
- it('should handle non-error values', () => {
548
- const wrapped = wrapError('string error');
549
-
550
- expect(wrapped).toBeInstanceOf(HttpError);
551
- expect(wrapped.statusCode).toBe(500);
552
- expect(wrapped.details?.originalError).toBe('string error');
553
- });
554
- });
555
-
556
- describe('HttpStatusCode enum', () => {
557
- it('should have 2xx success codes', () => {
558
- expect(HttpStatusCode.OK).toBe(200);
559
- expect(HttpStatusCode.CREATED).toBe(201);
560
- expect(HttpStatusCode.ACCEPTED).toBe(202);
561
- expect(HttpStatusCode.NO_CONTENT).toBe(204);
562
- });
563
-
564
- it('should have 3xx redirect codes', () => {
565
- expect(HttpStatusCode.MOVED_PERMANENTLY).toBe(301);
566
- expect(HttpStatusCode.FOUND).toBe(302);
567
- expect(HttpStatusCode.NOT_MODIFIED).toBe(304);
568
- });
569
-
570
- it('should have 4xx client error codes', () => {
571
- expect(HttpStatusCode.BAD_REQUEST).toBe(400);
572
- expect(HttpStatusCode.UNAUTHORIZED).toBe(401);
573
- expect(HttpStatusCode.FORBIDDEN).toBe(403);
574
- expect(HttpStatusCode.NOT_FOUND).toBe(404);
575
- expect(HttpStatusCode.METHOD_NOT_ALLOWED).toBe(405);
576
- expect(HttpStatusCode.NOT_ACCEPTABLE).toBe(406);
577
- expect(HttpStatusCode.REQUEST_TIMEOUT).toBe(408);
578
- expect(HttpStatusCode.CONFLICT).toBe(409);
579
- expect(HttpStatusCode.GONE).toBe(410);
580
- expect(HttpStatusCode.UNPROCESSABLE_ENTITY).toBe(422);
581
- expect(HttpStatusCode.TOO_MANY_REQUESTS).toBe(429);
582
- });
583
-
584
- it('should have 5xx server error codes', () => {
585
- expect(HttpStatusCode.INTERNAL_SERVER_ERROR).toBe(500);
586
- expect(HttpStatusCode.NOT_IMPLEMENTED).toBe(501);
587
- expect(HttpStatusCode.BAD_GATEWAY).toBe(502);
588
- expect(HttpStatusCode.SERVICE_UNAVAILABLE).toBe(503);
589
- expect(HttpStatusCode.GATEWAY_TIMEOUT).toBe(504);
590
- });
591
- });