spiceflow 1.0.2 → 1.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -3
- package/dist/client/types.d.ts +2 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client.test.js +20 -17
- package/dist/client.test.js.map +1 -1
- package/dist/elysia-fork/context.d.ts +6 -22
- package/dist/elysia-fork/context.d.ts.map +1 -1
- package/dist/elysia-fork/error.d.ts +2 -166
- package/dist/elysia-fork/error.d.ts.map +1 -1
- package/dist/elysia-fork/error.js +13 -166
- package/dist/elysia-fork/error.js.map +1 -1
- package/dist/elysia-fork/types.d.ts +8 -11
- package/dist/elysia-fork/types.d.ts.map +1 -1
- package/dist/elysia-fork/types.js.map +1 -1
- package/dist/openapi.d.ts +0 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/spiceflow.d.ts +10 -10
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +94 -68
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js +75 -8
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/zod.test.js +10 -8
- package/dist/zod.test.js.map +1 -1
- package/package.json +1 -1
- package/src/client/types.ts +14 -19
- package/src/client.test.ts +28 -20
- package/src/elysia-fork/context.ts +19 -49
- package/src/elysia-fork/error.ts +6 -259
- package/src/elysia-fork/types.ts +28 -26
- package/src/spiceflow.test.ts +92 -8
- package/src/spiceflow.ts +112 -80
- package/src/zod.test.ts +10 -8
package/src/client/types.ts
CHANGED
|
@@ -32,7 +32,7 @@ type And<A extends boolean, B extends boolean> = A extends true
|
|
|
32
32
|
: false
|
|
33
33
|
|
|
34
34
|
type ReplaceGeneratorWithAsyncGenerator<
|
|
35
|
-
in out RecordType extends Record<string, unknown
|
|
35
|
+
in out RecordType extends Record<string, unknown>,
|
|
36
36
|
> = {
|
|
37
37
|
[K in keyof RecordType]: RecordType[K] extends Generator<
|
|
38
38
|
infer A,
|
|
@@ -62,7 +62,7 @@ export namespace SpiceflowClient {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
export type Create<
|
|
65
|
-
App extends Spiceflow<any, any, any, any, any, any, any, any
|
|
65
|
+
App extends Spiceflow<any, any, any, any, any, any, any, any>,
|
|
66
66
|
> = App extends {
|
|
67
67
|
_routes: infer Schema extends Record<string, any>
|
|
68
68
|
}
|
|
@@ -89,24 +89,19 @@ export namespace SpiceflowClient {
|
|
|
89
89
|
: never
|
|
90
90
|
: Route[K] extends {
|
|
91
91
|
body: infer Body
|
|
92
|
-
headers: infer Headers
|
|
92
|
+
// headers: infer Headers
|
|
93
93
|
params: any
|
|
94
94
|
query: infer Query
|
|
95
95
|
response: infer Response extends Record<number, unknown>
|
|
96
96
|
}
|
|
97
|
-
? (undefined extends
|
|
98
|
-
? {
|
|
99
|
-
: {
|
|
100
|
-
headers: Headers
|
|
101
|
-
}) &
|
|
102
|
-
(undefined extends Query
|
|
103
|
-
? { query?: Record<string, unknown> }
|
|
104
|
-
: { query: Query }) extends infer Param
|
|
97
|
+
? { headers?: Record<string, unknown> } & (undefined extends Query
|
|
98
|
+
? { query?: Record<string, unknown> }
|
|
99
|
+
: { query: Query }) extends infer Param
|
|
105
100
|
? {} extends Param
|
|
106
101
|
? undefined extends Body
|
|
107
102
|
? K extends 'get' | 'head'
|
|
108
103
|
? (
|
|
109
|
-
options?: Prettify<Param & TreatyParam
|
|
104
|
+
options?: Prettify<Param & TreatyParam>,
|
|
110
105
|
) => Promise<
|
|
111
106
|
TreatyResponse<
|
|
112
107
|
ReplaceGeneratorWithAsyncGenerator<Response>
|
|
@@ -114,7 +109,7 @@ export namespace SpiceflowClient {
|
|
|
114
109
|
>
|
|
115
110
|
: (
|
|
116
111
|
body?: Body,
|
|
117
|
-
options?: Prettify<Param & TreatyParam
|
|
112
|
+
options?: Prettify<Param & TreatyParam>,
|
|
118
113
|
) => Promise<
|
|
119
114
|
TreatyResponse<
|
|
120
115
|
ReplaceGeneratorWithAsyncGenerator<Response>
|
|
@@ -124,7 +119,7 @@ export namespace SpiceflowClient {
|
|
|
124
119
|
body: Body extends Record<string, unknown>
|
|
125
120
|
? ReplaceBlobWithFiles<Body>
|
|
126
121
|
: Body,
|
|
127
|
-
options?: Prettify<Param & TreatyParam
|
|
122
|
+
options?: Prettify<Param & TreatyParam>,
|
|
128
123
|
) => Promise<
|
|
129
124
|
TreatyResponse<
|
|
130
125
|
ReplaceGeneratorWithAsyncGenerator<Response>
|
|
@@ -132,7 +127,7 @@ export namespace SpiceflowClient {
|
|
|
132
127
|
>
|
|
133
128
|
: K extends 'get' | 'head'
|
|
134
129
|
? (
|
|
135
|
-
options: Prettify<Param & TreatyParam
|
|
130
|
+
options: Prettify<Param & TreatyParam>,
|
|
136
131
|
) => Promise<
|
|
137
132
|
TreatyResponse<
|
|
138
133
|
ReplaceGeneratorWithAsyncGenerator<Response>
|
|
@@ -142,7 +137,7 @@ export namespace SpiceflowClient {
|
|
|
142
137
|
body: Body extends Record<string, unknown>
|
|
143
138
|
? ReplaceBlobWithFiles<Body>
|
|
144
139
|
: Body,
|
|
145
|
-
options: Prettify<Param & TreatyParam
|
|
140
|
+
options: Prettify<Param & TreatyParam>,
|
|
146
141
|
) => Promise<
|
|
147
142
|
TreatyResponse<
|
|
148
143
|
ReplaceGeneratorWithAsyncGenerator<Response>
|
|
@@ -179,13 +174,13 @@ export namespace SpiceflowClient {
|
|
|
179
174
|
| RequestInit['headers']
|
|
180
175
|
| ((
|
|
181
176
|
path: string,
|
|
182
|
-
options: RequestInit
|
|
177
|
+
options: RequestInit,
|
|
183
178
|
) => RequestInit['headers'] | void)
|
|
184
179
|
>
|
|
185
180
|
onRequest?: MaybeArray<
|
|
186
181
|
(
|
|
187
182
|
path: string,
|
|
188
|
-
options: RequestInit
|
|
183
|
+
options: RequestInit,
|
|
189
184
|
) => MaybePromise<RequestInit | void>
|
|
190
185
|
>
|
|
191
186
|
onResponse?: MaybeArray<(response: Response) => MaybePromise<unknown>>
|
|
@@ -226,7 +221,7 @@ export namespace SpiceflowClient {
|
|
|
226
221
|
|
|
227
222
|
export type WSEvent<
|
|
228
223
|
K extends keyof WebSocketEventMap,
|
|
229
|
-
Data = unknown
|
|
224
|
+
Data = unknown,
|
|
230
225
|
> = K extends 'message' ? OnMessage<Data> : WebSocketEventMap[K]
|
|
231
226
|
}
|
|
232
227
|
|
package/src/client.test.ts
CHANGED
|
@@ -9,7 +9,7 @@ const randomObject = {
|
|
|
9
9
|
c: true,
|
|
10
10
|
d: false,
|
|
11
11
|
e: null,
|
|
12
|
-
f: new Date(0)
|
|
12
|
+
f: new Date(0),
|
|
13
13
|
}
|
|
14
14
|
const randomArray = [
|
|
15
15
|
'a',
|
|
@@ -18,7 +18,7 @@ const randomArray = [
|
|
|
18
18
|
false,
|
|
19
19
|
null,
|
|
20
20
|
new Date(0),
|
|
21
|
-
{ a: 'a', b: 2, c: true, d: false, e: null, f: new Date(0) }
|
|
21
|
+
{ a: 'a', b: 2, c: true, d: false, e: null, f: new Date(0) },
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
const app = new Spiceflow()
|
|
@@ -27,22 +27,30 @@ const app = new Spiceflow()
|
|
|
27
27
|
.get('/number', () => 1)
|
|
28
28
|
.get('/true', () => true)
|
|
29
29
|
.get('/false', () => false)
|
|
30
|
-
.post('/array', ({
|
|
31
|
-
body: t.Array(t.String())
|
|
30
|
+
.post('/array', async ({ request }) => await request.json(), {
|
|
31
|
+
body: t.Array(t.String()),
|
|
32
32
|
})
|
|
33
|
-
.post('/mirror', ({
|
|
34
|
-
.post('/body', ({
|
|
35
|
-
body: t.String()
|
|
33
|
+
.post('/mirror', async ({ request }) => await request.json())
|
|
34
|
+
.post('/body', async ({ request }) => await request.text(), {
|
|
35
|
+
body: t.String(),
|
|
36
36
|
})
|
|
37
|
-
.delete('/empty', ({
|
|
38
|
-
|
|
37
|
+
.delete('/empty', async ({ request }) => {
|
|
38
|
+
const body = await request.text()
|
|
39
|
+
return { body: body || null }
|
|
40
|
+
})
|
|
41
|
+
.post('/deep/nested/mirror', async ({ request }) => await request.json(), {
|
|
39
42
|
body: t.Object({
|
|
40
43
|
username: t.String(),
|
|
41
|
-
password: t.String()
|
|
42
|
-
})
|
|
44
|
+
password: t.String(),
|
|
45
|
+
}),
|
|
43
46
|
})
|
|
44
47
|
|
|
45
|
-
.use(
|
|
48
|
+
.use(
|
|
49
|
+
new Spiceflow({ basePath: '/nested' }).get(
|
|
50
|
+
'/data',
|
|
51
|
+
({ params }) => 'hi',
|
|
52
|
+
),
|
|
53
|
+
)
|
|
46
54
|
// .get('/error', ({ error }) => error("I'm a teapot", 'Kirifuji Nagisa'), {
|
|
47
55
|
// response: {
|
|
48
56
|
// 200: t.Void(),
|
|
@@ -59,10 +67,10 @@ const app = new Spiceflow()
|
|
|
59
67
|
{
|
|
60
68
|
response: {
|
|
61
69
|
200: t.Object({
|
|
62
|
-
x: t.String()
|
|
63
|
-
})
|
|
64
|
-
}
|
|
65
|
-
}
|
|
70
|
+
x: t.String(),
|
|
71
|
+
}),
|
|
72
|
+
},
|
|
73
|
+
},
|
|
66
74
|
)
|
|
67
75
|
|
|
68
76
|
// TODO ajv does not accept dates for some reason
|
|
@@ -78,9 +86,9 @@ const app = new Spiceflow()
|
|
|
78
86
|
({ redirect }) => redirect('http://localhost:8083/true'),
|
|
79
87
|
{
|
|
80
88
|
body: t.Object({
|
|
81
|
-
username: t.String()
|
|
82
|
-
})
|
|
83
|
-
}
|
|
89
|
+
username: t.String(),
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
84
92
|
)
|
|
85
93
|
// .get('/formdata', () => ({
|
|
86
94
|
// image: Bun.file('./test/kyuukurarin.mp4')
|
|
@@ -108,7 +116,7 @@ const client = createSpiceflowClient(app)
|
|
|
108
116
|
|
|
109
117
|
describe('client', () => {
|
|
110
118
|
it('get index', async () => {
|
|
111
|
-
const { data, error } = await client.index.get()
|
|
119
|
+
const { data, error } = await client.index.get({})
|
|
112
120
|
|
|
113
121
|
expect(data).toBe('a')
|
|
114
122
|
expect(error).toBeNull()
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
StatusMap,
|
|
3
3
|
InvertedStatusMap,
|
|
4
|
-
redirect as Redirect
|
|
4
|
+
redirect as Redirect,
|
|
5
5
|
} from './utils.js'
|
|
6
6
|
|
|
7
7
|
import type {
|
|
@@ -9,9 +9,10 @@ import type {
|
|
|
9
9
|
Prettify,
|
|
10
10
|
ResolvePath,
|
|
11
11
|
SingletonBase,
|
|
12
|
-
HTTPHeaders
|
|
12
|
+
HTTPHeaders,
|
|
13
13
|
} from './types.js'
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
import { TypedRequest } from '../spiceflow.js'
|
|
15
16
|
|
|
16
17
|
type InvertedStatusMapKey = keyof InvertedStatusMap
|
|
17
18
|
|
|
@@ -27,10 +28,10 @@ export type ErrorContext<
|
|
|
27
28
|
derive: {}
|
|
28
29
|
resolve: {}
|
|
29
30
|
},
|
|
30
|
-
Path extends string = ''
|
|
31
|
+
Path extends string = '',
|
|
31
32
|
> = Prettify<
|
|
32
33
|
{
|
|
33
|
-
body: Route['body']
|
|
34
|
+
// body: Route['body']
|
|
34
35
|
query: undefined extends Route['query']
|
|
35
36
|
? Record<string, string | undefined>
|
|
36
37
|
: Route['query']
|
|
@@ -58,10 +59,10 @@ export type ErrorContext<
|
|
|
58
59
|
*
|
|
59
60
|
* @example '/id/:id'
|
|
60
61
|
*/
|
|
61
|
-
route: string
|
|
62
|
-
request:
|
|
62
|
+
// route: string
|
|
63
|
+
request: TypedRequest<Route['body']>
|
|
63
64
|
store: Singleton['store']
|
|
64
|
-
response: Route['response']
|
|
65
|
+
// response: Route['response']
|
|
65
66
|
} & Singleton['decorator'] &
|
|
66
67
|
Singleton['derive'] &
|
|
67
68
|
Singleton['resolve']
|
|
@@ -75,11 +76,13 @@ export type Context<
|
|
|
75
76
|
derive: {}
|
|
76
77
|
resolve: {}
|
|
77
78
|
},
|
|
78
|
-
Path extends string = ''
|
|
79
|
+
Path extends string = '',
|
|
79
80
|
> = Prettify<
|
|
80
81
|
{
|
|
81
|
-
body: Route['body']
|
|
82
|
-
|
|
82
|
+
// body: Route['body']
|
|
83
|
+
query: undefined extends Route['query']
|
|
84
|
+
? Record<string, string | undefined>
|
|
85
|
+
: Route['query']
|
|
83
86
|
params: undefined extends Route['params']
|
|
84
87
|
? Path extends `${string}/${':' | '*'}${string}`
|
|
85
88
|
? ResolvePath<Path>
|
|
@@ -127,44 +130,11 @@ export type Context<
|
|
|
127
130
|
*
|
|
128
131
|
* @example '/id/:id'
|
|
129
132
|
*/
|
|
130
|
-
route: string
|
|
131
|
-
request:
|
|
133
|
+
// route: string
|
|
134
|
+
request: TypedRequest<Route['body']>
|
|
132
135
|
store: Singleton['store']
|
|
133
136
|
response?: Route['response']
|
|
134
|
-
} &
|
|
135
|
-
? {
|
|
136
|
-
error: typeof error
|
|
137
|
-
}
|
|
138
|
-
: {
|
|
139
|
-
error: <
|
|
140
|
-
const Code extends
|
|
141
|
-
| keyof Route['response']
|
|
142
|
-
| InvertedStatusMap[Extract<
|
|
143
|
-
InvertedStatusMapKey,
|
|
144
|
-
keyof Route['response']
|
|
145
|
-
>],
|
|
146
|
-
const T extends Code extends keyof Route['response']
|
|
147
|
-
? Route['response'][Code]
|
|
148
|
-
: Code extends keyof StatusMap
|
|
149
|
-
? // @ts-ignore StatusMap[Code] always valid because Code generic check
|
|
150
|
-
Route['response'][StatusMap[Code]]
|
|
151
|
-
: never
|
|
152
|
-
>(
|
|
153
|
-
code: Code,
|
|
154
|
-
response: T
|
|
155
|
-
) => {
|
|
156
|
-
// [ELYSIA_RESPONSE]: Code extends keyof StatusMap
|
|
157
|
-
// ? StatusMap[Code]
|
|
158
|
-
// : Code
|
|
159
|
-
response: T
|
|
160
|
-
_type: {
|
|
161
|
-
[ERROR_CODE in Code extends keyof StatusMap
|
|
162
|
-
? StatusMap[Code]
|
|
163
|
-
: Code]: T
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}) &
|
|
167
|
-
Singleton['decorator'] &
|
|
137
|
+
} & Singleton['decorator'] &
|
|
168
138
|
Singleton['derive'] &
|
|
169
139
|
Singleton['resolve']
|
|
170
140
|
>
|
|
@@ -176,7 +146,7 @@ export type PreContext<
|
|
|
176
146
|
store: {}
|
|
177
147
|
derive: {}
|
|
178
148
|
resolve: {}
|
|
179
|
-
}
|
|
149
|
+
},
|
|
180
150
|
> = Prettify<
|
|
181
151
|
{
|
|
182
152
|
store: Singleton['store']
|
|
@@ -191,6 +161,6 @@ export type PreContext<
|
|
|
191
161
|
// redirect?: string
|
|
192
162
|
// }
|
|
193
163
|
|
|
194
|
-
error: typeof error
|
|
164
|
+
// error: typeof error
|
|
195
165
|
} & Singleton['decorator']
|
|
196
166
|
>
|
package/src/elysia-fork/error.ts
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type { TSchema } from '@sinclair/typebox'
|
|
2
|
-
import { Value } from '@sinclair/typebox/value'
|
|
3
|
-
import type { TypeCheck, ValueError } from '@sinclair/typebox/compiler'
|
|
4
1
|
|
|
5
|
-
import { StatusMap, InvertedStatusMap } from './utils'
|
|
6
2
|
|
|
7
3
|
// ? Cloudflare worker support
|
|
8
4
|
const env =
|
|
@@ -22,272 +18,23 @@ export type ELYSIA_RESPONSE = typeof ELYSIA_RESPONSE
|
|
|
22
18
|
|
|
23
19
|
export const isProduction = (env?.NODE_ENV ?? env?.ENV) === 'production'
|
|
24
20
|
|
|
25
|
-
export
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
| ParseError
|
|
29
|
-
| ValidationError
|
|
30
|
-
| InvalidCookieSignature
|
|
31
|
-
|
|
32
|
-
export const error = <
|
|
33
|
-
const Code extends number | keyof StatusMap,
|
|
34
|
-
const T = Code extends keyof InvertedStatusMap
|
|
35
|
-
? InvertedStatusMap[Code]
|
|
36
|
-
: Code,
|
|
37
|
-
const Status extends Code extends keyof StatusMap
|
|
38
|
-
? StatusMap[Code]
|
|
39
|
-
: Code = Code extends keyof StatusMap ? StatusMap[Code] : Code
|
|
40
|
-
>(
|
|
41
|
-
code: Code,
|
|
42
|
-
response?: T
|
|
43
|
-
): {
|
|
44
|
-
[ELYSIA_RESPONSE]: Status
|
|
45
|
-
response: T
|
|
46
|
-
_type: {
|
|
47
|
-
[ERROR_CODE in Status]: T
|
|
48
|
-
}
|
|
49
|
-
error: Error
|
|
50
|
-
} => {
|
|
51
|
-
const res =
|
|
52
|
-
response ??
|
|
53
|
-
(code in InvertedStatusMap
|
|
54
|
-
? // @ts-expect-error Always correct
|
|
55
|
-
InvertedStatusMap[code]
|
|
56
|
-
: code)
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
// @ts-expect-error trust me bro
|
|
60
|
-
[ELYSIA_RESPONSE]: StatusMap[code] ?? code,
|
|
61
|
-
response: res,
|
|
62
|
-
_type: undefined as any,
|
|
63
|
-
error: new Error(res)
|
|
64
|
-
} as const
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export class InternalServerError extends Error {
|
|
68
|
-
code = 'INTERNAL_SERVER_ERROR'
|
|
69
|
-
status = 500
|
|
70
|
-
|
|
71
|
-
constructor(message?: string) {
|
|
72
|
-
super(message ?? 'INTERNAL_SERVER_ERROR')
|
|
73
|
-
}
|
|
21
|
+
export class ValidationError extends Error {
|
|
22
|
+
code = 'VALIDATION'
|
|
23
|
+
status = 422
|
|
74
24
|
}
|
|
75
25
|
|
|
76
26
|
export class NotFoundError extends Error {
|
|
77
27
|
code = 'NOT_FOUND'
|
|
78
28
|
status = 404
|
|
79
|
-
|
|
80
|
-
constructor(message?: string) {
|
|
81
|
-
super(message ?? 'NOT_FOUND')
|
|
82
|
-
}
|
|
83
29
|
}
|
|
84
30
|
|
|
85
31
|
export class ParseError extends Error {
|
|
86
32
|
code = 'PARSE'
|
|
87
33
|
status = 400
|
|
88
|
-
|
|
89
|
-
constructor() {
|
|
90
|
-
super('Failed to parse body')
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
export class InvalidCookieSignature extends Error {
|
|
95
|
-
code = 'INVALID_COOKIE_SIGNATURE'
|
|
96
|
-
status = 400
|
|
97
|
-
|
|
98
|
-
constructor(public key: string, message?: string) {
|
|
99
|
-
super(message ?? `"${key}" has invalid cookie signature`)
|
|
100
|
-
}
|
|
101
34
|
}
|
|
102
35
|
|
|
103
|
-
export
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const property = path.slice(1).replaceAll('/', '.')
|
|
107
|
-
const isRoot = path === ''
|
|
108
|
-
|
|
109
|
-
switch (type) {
|
|
110
|
-
case 42:
|
|
111
|
-
return {
|
|
112
|
-
...error,
|
|
113
|
-
summary: isRoot
|
|
114
|
-
? `Value should not be provided`
|
|
115
|
-
: `Property '${property}' should not be provided`
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
case 45:
|
|
119
|
-
return {
|
|
120
|
-
...error,
|
|
121
|
-
summary: isRoot
|
|
122
|
-
? `Value is missing`
|
|
123
|
-
: `Property '${property}' is missing`
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
case 50:
|
|
127
|
-
// Expected string to match 'email' format
|
|
128
|
-
const quoteIndex = message.indexOf("'")!
|
|
129
|
-
const format = message.slice(
|
|
130
|
-
quoteIndex + 1,
|
|
131
|
-
message.indexOf("'", quoteIndex + 1)
|
|
132
|
-
)
|
|
133
|
-
|
|
134
|
-
return {
|
|
135
|
-
...error,
|
|
136
|
-
summary: isRoot
|
|
137
|
-
? `Value should be an email`
|
|
138
|
-
: `Property '${property}' should be ${format}`
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
case 54:
|
|
142
|
-
return {
|
|
143
|
-
...error,
|
|
144
|
-
summary: `${message.slice(
|
|
145
|
-
0,
|
|
146
|
-
9
|
|
147
|
-
)} property '${property}' to be ${message.slice(
|
|
148
|
-
8
|
|
149
|
-
)} but found: ${value}`
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
case 62:
|
|
153
|
-
const union = error.schema.anyOf
|
|
154
|
-
.map((x: Record<string, unknown>) => `'${x?.format ?? x.type}'`)
|
|
155
|
-
.join(', ')
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
...error,
|
|
159
|
-
summary: isRoot
|
|
160
|
-
? `Value should be one of ${union}`
|
|
161
|
-
: `Property '${property}' should be one of: ${union}`
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
default:
|
|
165
|
-
return { summary: message, ...error }
|
|
166
|
-
}
|
|
36
|
+
export class InternalServerError extends Error {
|
|
37
|
+
code = 'INTERNAL_SERVER_ERROR'
|
|
38
|
+
status = 500
|
|
167
39
|
}
|
|
168
40
|
|
|
169
|
-
export class ValidationError extends Error {
|
|
170
|
-
code = 'VALIDATION'
|
|
171
|
-
status = 422
|
|
172
|
-
|
|
173
|
-
constructor(
|
|
174
|
-
public type: string,
|
|
175
|
-
public validator: TSchema | TypeCheck<any>,
|
|
176
|
-
public value: unknown
|
|
177
|
-
) {
|
|
178
|
-
if (value && typeof value === 'object' && ELYSIA_RESPONSE in value)
|
|
179
|
-
// @ts-expect-error
|
|
180
|
-
value = value.response
|
|
181
|
-
|
|
182
|
-
const error = isProduction
|
|
183
|
-
? undefined
|
|
184
|
-
: 'Errors' in validator
|
|
185
|
-
? validator.Errors(value).First()
|
|
186
|
-
: Value.Errors(validator, value).First()
|
|
187
|
-
|
|
188
|
-
const customError =
|
|
189
|
-
error?.schema.error !== undefined
|
|
190
|
-
? typeof error.schema.error === 'function'
|
|
191
|
-
? error.schema.error({
|
|
192
|
-
type,
|
|
193
|
-
validator,
|
|
194
|
-
value,
|
|
195
|
-
get errors() {
|
|
196
|
-
return [...validator.Errors(value)].map(
|
|
197
|
-
mapValueError
|
|
198
|
-
)
|
|
199
|
-
}
|
|
200
|
-
})
|
|
201
|
-
: error.schema.error
|
|
202
|
-
: undefined
|
|
203
|
-
|
|
204
|
-
const accessor = error?.path || 'root'
|
|
205
|
-
let message = ''
|
|
206
|
-
|
|
207
|
-
if (customError !== undefined) {
|
|
208
|
-
message =
|
|
209
|
-
typeof customError === 'object'
|
|
210
|
-
? JSON.stringify(customError)
|
|
211
|
-
: customError + ''
|
|
212
|
-
} else if (isProduction) {
|
|
213
|
-
message = JSON.stringify({
|
|
214
|
-
type: 'validation',
|
|
215
|
-
on: type,
|
|
216
|
-
summary: mapValueError(error).summary,
|
|
217
|
-
message: error?.message,
|
|
218
|
-
found: value
|
|
219
|
-
})
|
|
220
|
-
} else {
|
|
221
|
-
// @ts-ignore private field
|
|
222
|
-
const schema = validator?.schema ?? validator
|
|
223
|
-
const errors =
|
|
224
|
-
'Errors' in validator
|
|
225
|
-
? [...validator.Errors(value)].map(mapValueError)
|
|
226
|
-
: [...Value.Errors(validator, value)].map(mapValueError)
|
|
227
|
-
|
|
228
|
-
let expected
|
|
229
|
-
|
|
230
|
-
try {
|
|
231
|
-
expected = Value.Create(schema)
|
|
232
|
-
} catch (error) {
|
|
233
|
-
expected = {
|
|
234
|
-
type: 'Could not create expected value',
|
|
235
|
-
// @ts-expect-error
|
|
236
|
-
message: error?.message,
|
|
237
|
-
error
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
message = JSON.stringify(
|
|
242
|
-
{
|
|
243
|
-
type: 'validation',
|
|
244
|
-
on: type,
|
|
245
|
-
summary: errors[0]?.summary,
|
|
246
|
-
property: accessor,
|
|
247
|
-
message: error?.message,
|
|
248
|
-
expected,
|
|
249
|
-
found: value,
|
|
250
|
-
errors
|
|
251
|
-
},
|
|
252
|
-
null,
|
|
253
|
-
2
|
|
254
|
-
)
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
super(message)
|
|
258
|
-
|
|
259
|
-
Object.setPrototypeOf(this, ValidationError.prototype)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
get all() {
|
|
263
|
-
return 'Errors' in this.validator
|
|
264
|
-
? [...this.validator.Errors(this.value)].map(mapValueError)
|
|
265
|
-
: // @ts-ignore
|
|
266
|
-
[...Value.Errors(this.validator, this.value)].map(mapValueError)
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
static simplifyModel(validator: TSchema | TypeCheck<any>) {
|
|
270
|
-
// @ts-ignore
|
|
271
|
-
const model = 'schema' in validator ? validator.schema : validator
|
|
272
|
-
|
|
273
|
-
try {
|
|
274
|
-
return Value.Create(model)
|
|
275
|
-
} catch {
|
|
276
|
-
return model
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
get model() {
|
|
281
|
-
return ValidationError.simplifyModel(this.validator)
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
toResponse(headers?: Record<string, any>) {
|
|
285
|
-
return new Response(this.message, {
|
|
286
|
-
status: 400,
|
|
287
|
-
headers: {
|
|
288
|
-
...headers,
|
|
289
|
-
'content-type': 'application/json'
|
|
290
|
-
}
|
|
291
|
-
})
|
|
292
|
-
}
|
|
293
|
-
}
|