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.
Files changed (42) hide show
  1. package/README.md +148 -1
  2. package/dist/client/index.d.ts.map +1 -1
  3. package/dist/client/types.d.ts +2 -6
  4. package/dist/client/types.d.ts.map +1 -1
  5. package/dist/client.test.js +26 -22
  6. package/dist/client.test.js.map +1 -1
  7. package/dist/elysia-fork/context.d.ts +6 -22
  8. package/dist/elysia-fork/context.d.ts.map +1 -1
  9. package/dist/elysia-fork/error.d.ts +2 -226
  10. package/dist/elysia-fork/error.d.ts.map +1 -1
  11. package/dist/elysia-fork/error.js +13 -166
  12. package/dist/elysia-fork/error.js.map +1 -1
  13. package/dist/elysia-fork/types.d.ts +20 -30
  14. package/dist/elysia-fork/types.d.ts.map +1 -1
  15. package/dist/elysia-fork/types.js +1 -2
  16. package/dist/elysia-fork/types.js.map +1 -1
  17. package/dist/elysia-fork/utils.d.ts +1 -62
  18. package/dist/elysia-fork/utils.d.ts.map +1 -1
  19. package/dist/openapi.d.ts +67 -0
  20. package/dist/openapi.d.ts.map +1 -0
  21. package/dist/openapi.js +250 -0
  22. package/dist/openapi.js.map +1 -0
  23. package/dist/spiceflow.d.ts +35 -23
  24. package/dist/spiceflow.d.ts.map +1 -1
  25. package/dist/spiceflow.js +214 -111
  26. package/dist/spiceflow.js.map +1 -1
  27. package/dist/spiceflow.test.js +135 -31
  28. package/dist/spiceflow.test.js.map +1 -1
  29. package/dist/zod.test.d.ts +2 -0
  30. package/dist/zod.test.d.ts.map +1 -0
  31. package/dist/zod.test.js +61 -0
  32. package/dist/zod.test.js.map +1 -0
  33. package/package.json +7 -3
  34. package/src/client/types.ts +14 -19
  35. package/src/client.test.ts +34 -25
  36. package/src/elysia-fork/context.ts +19 -49
  37. package/src/elysia-fork/error.ts +6 -259
  38. package/src/elysia-fork/types.ts +115 -188
  39. package/src/openapi.ts +426 -0
  40. package/src/spiceflow.test.ts +188 -51
  41. package/src/spiceflow.ts +312 -183
  42. 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
- import { error } from './error.js'
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: 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: Request
133
+ // route: string
134
+ request: TypedRequest<Route['body']>
132
135
  store: Singleton['store']
133
136
  response?: Route['response']
134
- } & ({} extends Route['response']
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
  >
@@ -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 type SpiceflowErrors =
26
- | InternalServerError
27
- | NotFoundError
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 const mapValueError = (error: ValueError) => {
104
- const { message, path, value, type } = error
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
- }