spiceflow 1.0.2 → 1.0.4
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/index.d.ts.map +1 -1
- package/dist/client/index.js +18 -21
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +3 -8
- 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/index.ts +38 -47
- package/src/client/types.ts +17 -22
- 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
|
@@ -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
|
-
}
|
package/src/elysia-fork/types.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
Static,
|
|
9
9
|
StaticDecode,
|
|
10
10
|
TAnySchema,
|
|
11
|
+
TObject,
|
|
11
12
|
TSchema,
|
|
12
13
|
} from '@sinclair/typebox'
|
|
13
14
|
import type { TypeCheck, ValueError } from '@sinclair/typebox/compiler'
|
|
@@ -20,12 +21,11 @@ import type { Context, ErrorContext, PreContext } from './context'
|
|
|
20
21
|
import {
|
|
21
22
|
ELYSIA_RESPONSE,
|
|
22
23
|
InternalServerError,
|
|
23
|
-
InvalidCookieSignature,
|
|
24
24
|
NotFoundError,
|
|
25
25
|
ParseError,
|
|
26
26
|
ValidationError,
|
|
27
27
|
} from './error'
|
|
28
|
-
import { ZodTypeAny } from 'zod'
|
|
28
|
+
import { ZodTypeAny, ZodObject } from 'zod'
|
|
29
29
|
|
|
30
30
|
export type MaybeArray<T> = T | T[]
|
|
31
31
|
export type MaybePromise<T> = T | Promise<T>
|
|
@@ -174,10 +174,10 @@ export interface MetadataBase {
|
|
|
174
174
|
|
|
175
175
|
export interface RouteSchema {
|
|
176
176
|
body?: unknown
|
|
177
|
-
headers?: unknown
|
|
177
|
+
// headers?: unknown
|
|
178
178
|
query?: unknown
|
|
179
179
|
params?: unknown
|
|
180
|
-
cookie?: unknown
|
|
180
|
+
// cookie?: unknown
|
|
181
181
|
response?: unknown
|
|
182
182
|
}
|
|
183
183
|
|
|
@@ -187,6 +187,8 @@ type OptionalField = {
|
|
|
187
187
|
|
|
188
188
|
export type TypeSchema = TSchema | ZodTypeAny
|
|
189
189
|
|
|
190
|
+
export type TypeObject = TObject | ZodObject<any, any, any>
|
|
191
|
+
|
|
190
192
|
export type UnwrapSchema<
|
|
191
193
|
Schema extends TypeSchema | string | undefined,
|
|
192
194
|
Definitions extends Record<string, unknown> = {},
|
|
@@ -210,8 +212,8 @@ export interface UnwrapRoute<
|
|
|
210
212
|
> {
|
|
211
213
|
body: UnwrapSchema<Schema['body'], Definitions>
|
|
212
214
|
// headers: UnwrapSchema<Schema['headers'], Definitions>
|
|
213
|
-
|
|
214
|
-
|
|
215
|
+
query: UnwrapSchema<Schema['query'], Definitions>
|
|
216
|
+
params: UnwrapSchema<Schema['params'], Definitions>
|
|
215
217
|
// cookie: UnwrapSchema<Schema['cookie'], Definitions>
|
|
216
218
|
response: Schema['response'] extends TypeSchema | string
|
|
217
219
|
? {
|
|
@@ -320,8 +322,8 @@ export type HTTPMethod =
|
|
|
320
322
|
export interface InputSchema<Name extends string = string> {
|
|
321
323
|
body?: TypeSchema | Name
|
|
322
324
|
// headers?: TObject | TNull | TUndefined | Name
|
|
323
|
-
|
|
324
|
-
|
|
325
|
+
query?: TypeObject | Name
|
|
326
|
+
params?: TypeObject | Name
|
|
325
327
|
// cookie?: TObject | TNull | TUndefined | Name
|
|
326
328
|
response?:
|
|
327
329
|
| TypeSchema
|
|
@@ -335,10 +337,10 @@ export interface MergeSchema<
|
|
|
335
337
|
in out B extends RouteSchema,
|
|
336
338
|
> {
|
|
337
339
|
body: undefined extends A['body'] ? B['body'] : A['body']
|
|
338
|
-
headers: undefined extends A['headers'] ? B['headers'] : A['headers']
|
|
340
|
+
// headers: undefined extends A['headers'] ? B['headers'] : A['headers']
|
|
339
341
|
query: undefined extends A['query'] ? B['query'] : A['query']
|
|
340
342
|
params: undefined extends A['params'] ? B['params'] : A['params']
|
|
341
|
-
cookie: undefined extends A['cookie'] ? B['cookie'] : A['cookie']
|
|
343
|
+
// cookie: undefined extends A['cookie'] ? B['cookie'] : A['cookie']
|
|
342
344
|
response: {} extends A['response']
|
|
343
345
|
? {} extends B['response']
|
|
344
346
|
? {}
|
|
@@ -686,22 +688,22 @@ export type ErrorHandler<
|
|
|
686
688
|
Volatile['resolve']
|
|
687
689
|
>
|
|
688
690
|
>
|
|
689
|
-
| Prettify<
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
691
|
+
// | Prettify<
|
|
692
|
+
// {
|
|
693
|
+
// request: Request
|
|
694
|
+
// code: 'INVALID_COOKIE_SIGNATURE'
|
|
695
|
+
// error: Readonly<InvalidCookieSignature>
|
|
696
|
+
// } & NeverKey<
|
|
697
|
+
// Singleton['derive'] &
|
|
698
|
+
// Ephemeral['derive'] &
|
|
699
|
+
// Volatile['derive']
|
|
700
|
+
// > &
|
|
701
|
+
// NeverKey<
|
|
702
|
+
// Singleton['derive'] &
|
|
703
|
+
// Ephemeral['resolve'] &
|
|
704
|
+
// Volatile['resolve']
|
|
705
|
+
// >
|
|
706
|
+
// >
|
|
705
707
|
| Prettify<
|
|
706
708
|
{
|
|
707
709
|
[K in keyof T]: {
|
package/src/spiceflow.test.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { test, describe, expect } from 'vitest'
|
|
2
2
|
import { Type } from '@sinclair/typebox'
|
|
3
3
|
import { bfs, Spiceflow } from './spiceflow'
|
|
4
|
+
import { z } from 'zod'
|
|
4
5
|
|
|
5
6
|
test('works', async () => {
|
|
6
7
|
const res = await new Spiceflow()
|
|
@@ -24,6 +25,87 @@ test('GET dynamic route', async () => {
|
|
|
24
25
|
expect(await res.json()).toEqual('hi')
|
|
25
26
|
})
|
|
26
27
|
|
|
28
|
+
test('GET with query, untyped', async () => {
|
|
29
|
+
const res = await new Spiceflow()
|
|
30
|
+
.get('/query', ({ query }) => {
|
|
31
|
+
return query.id
|
|
32
|
+
})
|
|
33
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
34
|
+
expect(res.status).toBe(200)
|
|
35
|
+
expect(await res.json()).toEqual('hi')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('GET with query, zod, fails validation', async () => {
|
|
39
|
+
const res = await new Spiceflow()
|
|
40
|
+
.get(
|
|
41
|
+
'/query',
|
|
42
|
+
({ query }) => {
|
|
43
|
+
return query.id
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
query: z.object({
|
|
47
|
+
id: z.number(),
|
|
48
|
+
}),
|
|
49
|
+
},
|
|
50
|
+
)
|
|
51
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
52
|
+
expect(res.status).toBe(422)
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('GET with query and zod', async () => {
|
|
56
|
+
const res = await new Spiceflow()
|
|
57
|
+
.get(
|
|
58
|
+
'/query',
|
|
59
|
+
({ query }) => {
|
|
60
|
+
return query.id
|
|
61
|
+
// @ts-expect-error
|
|
62
|
+
void query.sdfsd
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
query: z.object({
|
|
66
|
+
id: z.string(),
|
|
67
|
+
}),
|
|
68
|
+
},
|
|
69
|
+
)
|
|
70
|
+
.handle(new Request('http://localhost/query?id=hi', { method: 'GET' }))
|
|
71
|
+
expect(res.status).toBe(200)
|
|
72
|
+
expect(await res.json()).toEqual('hi')
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('GET dynamic route, params are typed', async () => {
|
|
76
|
+
const res = await new Spiceflow()
|
|
77
|
+
.get('/ids/:id', ({ params }) => {
|
|
78
|
+
let id = params.id
|
|
79
|
+
// @ts-expect-error
|
|
80
|
+
params.sdfsd
|
|
81
|
+
return id
|
|
82
|
+
})
|
|
83
|
+
.handle(new Request('http://localhost/ids/hi', { method: 'GET' }))
|
|
84
|
+
expect(res.status).toBe(200)
|
|
85
|
+
expect(await res.json()).toEqual('hi')
|
|
86
|
+
})
|
|
87
|
+
|
|
88
|
+
test('GET dynamic route, params are typed with schema', async () => {
|
|
89
|
+
const res = await new Spiceflow()
|
|
90
|
+
.get(
|
|
91
|
+
'/ids/:id',
|
|
92
|
+
({ params }) => {
|
|
93
|
+
let id = params.id
|
|
94
|
+
// @ts-expect-error
|
|
95
|
+
params.sdfsd
|
|
96
|
+
return id
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
params: z.object({
|
|
100
|
+
id: z.string(),
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
.handle(new Request('http://localhost/ids/hi', { method: 'GET' }))
|
|
105
|
+
expect(res.status).toBe(200)
|
|
106
|
+
expect(await res.json()).toEqual('hi')
|
|
107
|
+
})
|
|
108
|
+
|
|
27
109
|
test('missing route is not found', async () => {
|
|
28
110
|
const res = await new Spiceflow()
|
|
29
111
|
.get('/ids/:id', () => 'hi')
|
|
@@ -48,9 +130,9 @@ test('body is parsed as json', async () => {
|
|
|
48
130
|
const res = await new Spiceflow()
|
|
49
131
|
.state('id', '')
|
|
50
132
|
|
|
51
|
-
.post('/post', (c) => {
|
|
52
|
-
body = c.
|
|
53
|
-
// console.log({
|
|
133
|
+
.post('/post', async (c) => {
|
|
134
|
+
body = await c.request.json()
|
|
135
|
+
// console.log({request})
|
|
54
136
|
return body
|
|
55
137
|
})
|
|
56
138
|
.handle(
|
|
@@ -71,8 +153,9 @@ test('validate body works, request success', async () => {
|
|
|
71
153
|
|
|
72
154
|
.post(
|
|
73
155
|
'/post',
|
|
74
|
-
({
|
|
75
|
-
// console.log({
|
|
156
|
+
async ({ request }) => {
|
|
157
|
+
// console.log({request})
|
|
158
|
+
let body = await request.json()
|
|
76
159
|
expect(body).toEqual({ name: 'John' })
|
|
77
160
|
return 'ok'
|
|
78
161
|
},
|
|
@@ -100,8 +183,9 @@ test('validate body works, request fails', async () => {
|
|
|
100
183
|
|
|
101
184
|
.post(
|
|
102
185
|
'/post',
|
|
103
|
-
({
|
|
104
|
-
// console.log({
|
|
186
|
+
async ({ request, redirect }) => {
|
|
187
|
+
// console.log({request})
|
|
188
|
+
let body = await request.json()
|
|
105
189
|
expect(body).toEqual({ name: 'John' })
|
|
106
190
|
},
|
|
107
191
|
{
|
|
@@ -120,7 +204,7 @@ test('validate body works, request fails', async () => {
|
|
|
120
204
|
body: JSON.stringify({ name: 'John' }),
|
|
121
205
|
}),
|
|
122
206
|
)
|
|
123
|
-
expect(res.status).toBe(
|
|
207
|
+
expect(res.status).toBe(422)
|
|
124
208
|
expect(await res.text()).toMatchInlineSnapshot(
|
|
125
209
|
`"data must have required property 'requiredField'"`,
|
|
126
210
|
)
|