spiceflow 1.0.1 → 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 +148 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/types.d.ts +2 -6
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client.test.js +26 -22
- 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 -226
- 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 +20 -30
- package/dist/elysia-fork/types.d.ts.map +1 -1
- package/dist/elysia-fork/types.js +1 -2
- package/dist/elysia-fork/types.js.map +1 -1
- package/dist/elysia-fork/utils.d.ts +1 -62
- package/dist/elysia-fork/utils.d.ts.map +1 -1
- package/dist/openapi.d.ts +67 -0
- package/dist/openapi.d.ts.map +1 -0
- package/dist/openapi.js +250 -0
- package/dist/openapi.js.map +1 -0
- package/dist/spiceflow.d.ts +35 -23
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +214 -111
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js +135 -31
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/zod.test.d.ts +2 -0
- package/dist/zod.test.d.ts.map +1 -0
- package/dist/zod.test.js +61 -0
- package/dist/zod.test.js.map +1 -0
- package/package.json +7 -3
- package/src/client/types.ts +14 -19
- package/src/client.test.ts +34 -25
- package/src/elysia-fork/context.ts +19 -49
- package/src/elysia-fork/error.ts +6 -259
- package/src/elysia-fork/types.ts +115 -188
- package/src/openapi.ts +426 -0
- package/src/spiceflow.test.ts +188 -51
- package/src/spiceflow.ts +312 -183
- package/src/zod.test.ts +73 -0
|
@@ -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
|
-
}
|