1- import { StatusError } from 'itty-router'
1+ import { json , type ErrorFormatter } from 'itty-router'
2+
3+ interface ErrorDefinition {
4+ message : string
5+ status : number
6+ code : string
7+ }
8+
9+ type StatusErrorObject = {
10+ message ?: string
11+ [ key : string ] : any
12+ }
13+
14+ export class StatusError extends Error {
15+ status : number ;
16+ [ key : string ] : any
17+
18+ constructor ( status = 500 , body ?: StatusErrorObject | string ) {
19+ super ( typeof body === 'object' ? body . message : body )
20+ if ( typeof body === 'object' ) Object . assign ( this , body )
21+ this . status = status
22+ }
23+ }
224
325const errorsList = [
426 // Download errors (receiver-side)
527 {
628 code : 'DOWNLOAD_NOT_FOUND' ,
7- error : 'Download not found' ,
29+ message : 'Download not found' ,
830 status : 404 ,
931 } ,
1032 {
1133 code : 'DOWNLOAD_ERROR' ,
12- error : 'Download failed' ,
34+ message : 'Download failed' ,
1335 status : 500 ,
1436 } ,
1537 {
1638 code : 'DOWNLOAD_SHARE_CANCELED' ,
17- error : 'Download canceled by sender' ,
39+ message : 'Download canceled by sender' ,
1840 status : 409 ,
1941 } ,
2042 {
2143 code : 'DOWNLOAD_SHARE_DECLINED' ,
22- error : 'Cannot download: share was declined' ,
44+ message : 'Cannot download: share was declined' ,
2345 status : 409 ,
2446 } ,
2547 {
2648 code : 'DOWNLOAD_SHARE_NOT_PENDING' ,
27- error : 'Cannot download: share is not pending' ,
49+ message : 'Cannot download: share is not pending' ,
2850 status : 409 ,
2951 } ,
3052 {
3153 code : 'ABORT_NOT_DOWNLOADING' ,
32- error : 'Cannot abort: download is not in progress' ,
54+ message : 'Cannot abort: download is not in progress' ,
3355 status : 409 ,
3456 } ,
3557 {
3658 code : 'INVALID_SENDER_DEVICE_ID' ,
37- error : 'Invalid sender device ID' ,
59+ message : 'Invalid sender device ID' ,
3860 status : 400 ,
3961 } ,
4062
4163 // Map share errors (sender-side)
4264 {
4365 code : 'MAP_SHARE_NOT_FOUND' ,
44- error : 'Map share not found' ,
66+ message : 'Map share not found' ,
4567 status : 404 ,
4668 } ,
4769 {
4870 code : 'CANCEL_SHARE_NOT_CANCELABLE' ,
49- error : 'Cannot cancel: share is not pending or downloading' ,
71+ message : 'Cannot cancel: share is not pending or downloading' ,
5072 status : 409 ,
5173 } ,
5274 {
5375 code : 'DECLINE_SHARE_NOT_PENDING' ,
54- error : 'Cannot decline: share is not pending' ,
76+ message : 'Cannot decline: share is not pending' ,
5577 status : 409 ,
5678 } ,
5779 {
5880 code : 'DECLINE_CANNOT_CONNECT' ,
59- error : 'Cannot decline: unable to connect to sender' ,
81+ message : 'Cannot decline: unable to connect to sender' ,
6082 status : 502 ,
6183 } ,
6284
6385 // Map errors
6486 {
6587 code : 'MAP_NOT_FOUND' ,
66- error : 'Map not found' ,
88+ message : 'Map not found' ,
6789 status : 404 ,
6890 } ,
6991 {
7092 code : 'INVALID_MAP_FILE' ,
71- error : 'Invalid map file' ,
93+ message : 'Invalid map file' ,
7294 status : 400 ,
7395 } ,
7496
7597 // Generic errors
7698 {
7799 code : 'FORBIDDEN' ,
78- error : 'Forbidden' ,
100+ message : 'Forbidden' ,
79101 status : 403 ,
80102 } ,
81103 {
82104 code : 'INVALID_REQUEST' ,
83- error : 'Invalid request' ,
105+ message : 'Invalid request' ,
84106 status : 400 ,
85107 } ,
86- ] as const satisfies Array < { error : string ; status : number ; code : string } >
108+ ] as const satisfies Array < ErrorDefinition >
87109
88110export const errors = { } as Record <
89111 ( typeof errorsList ) [ number ] [ 'code' ] ,
90112 new ( body ?: { [ key : string ] : any } | string ) => StatusError
91113>
92- for ( const { code, error , status } of errorsList ) {
114+ for ( const { code, message , status } of errorsList ) {
93115 errors [ code ] = class extends StatusError {
94116 constructor ( body ?: { [ key : string ] : any } | string ) {
95- body = typeof body === 'string' ? { error : body } : body
96- super ( status , { code, error , ...body } )
117+ body = typeof body === 'string' ? { message : body } : body
118+ super ( status , { code, message , ...body } )
97119 }
98120 }
99121}
@@ -120,3 +142,41 @@ export function jsonError(err: unknown): { message: string; code: string } {
120142 }
121143 }
122144}
145+
146+ const getMessage = ( code : number ) : string =>
147+ ( {
148+ 400 : 'Bad Request' ,
149+ 401 : 'Unauthorized' ,
150+ 403 : 'Forbidden' ,
151+ 404 : 'Not Found' ,
152+ 500 : 'Internal Server Error' ,
153+ } ) [ code ] || 'Unknown Error'
154+
155+ const getCode = ( status : number ) : string =>
156+ ( {
157+ 400 : 'BAD_REQUEST' ,
158+ 401 : 'UNAUTHORIZED' ,
159+ 403 : 'FORBIDDEN' ,
160+ 404 : 'NOT_FOUND' ,
161+ 500 : 'INTERNAL_SERVER_ERROR' ,
162+ } ) [ status ] || 'UNKNOWN_ERROR'
163+
164+ export const error : ErrorFormatter = ( a = 500 , b ?) => {
165+ // handle passing an Error | StatusError directly in
166+ if ( a instanceof Error ) {
167+ const { message, code, ...err } = a
168+ a = a . status || 500
169+ b = {
170+ message : message || getMessage ( a ) ,
171+ code : code || getCode ( a ) ,
172+ ...err ,
173+ }
174+ }
175+
176+ b = {
177+ status : a ,
178+ ...( typeof b === 'object' ? b : { message : b || getMessage ( a ) } ) ,
179+ }
180+
181+ return json ( b , { status : a } )
182+ }
0 commit comments