spiceflow 1.0.0 → 1.0.2
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 +147 -0
- package/dist/client/index.d.ts +4 -3
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +5 -5
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts +4 -5
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/ws.d.ts +4 -4
- package/dist/client/ws.d.ts.map +1 -1
- package/dist/client/ws.js.map +1 -1
- package/dist/client.test.js +9 -8
- package/dist/client.test.js.map +1 -1
- package/dist/elysia-fork/error.d.ts +5 -65
- package/dist/elysia-fork/error.d.ts.map +1 -1
- package/dist/elysia-fork/error.js +2 -2
- package/dist/elysia-fork/error.js.map +1 -1
- package/dist/elysia-fork/types.d.ts +27 -116
- 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/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/openapi.d.ts +68 -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 +48 -52
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +148 -87
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js +80 -43
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/stream.test.js +14 -14
- package/dist/stream.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 +59 -0
- package/dist/zod.test.js.map +1 -0
- package/package.json +7 -4
- package/src/client/index.ts +10 -8
- package/src/client/types.ts +4 -4
- package/src/client/ws.ts +4 -4
- package/src/client.test.ts +9 -8
- package/src/elysia-fork/context.ts +2 -2
- package/src/elysia-fork/error.ts +3 -3
- package/src/elysia-fork/types.ts +108 -284
- package/src/index.ts +2 -0
- package/src/openapi.ts +426 -0
- package/src/spiceflow.test.ts +117 -64
- package/src/spiceflow.ts +261 -179
- package/src/stream.test.ts +14 -14
- package/src/zod.test.ts +71 -0
package/src/spiceflow.ts
CHANGED
|
@@ -5,11 +5,13 @@ import { Type } from '@sinclair/typebox'
|
|
|
5
5
|
export { Type as t }
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
|
-
|
|
8
|
+
ComposeSpiceflowResponse,
|
|
9
9
|
CreateEden,
|
|
10
10
|
DefinitionBase,
|
|
11
11
|
EphemeralType,
|
|
12
12
|
ErrorHandler,
|
|
13
|
+
Handler,
|
|
14
|
+
HTTPMethod,
|
|
13
15
|
InlineHandler,
|
|
14
16
|
InputSchema,
|
|
15
17
|
JoinPath,
|
|
@@ -24,20 +26,41 @@ import {
|
|
|
24
26
|
RouteBase,
|
|
25
27
|
RouteSchema,
|
|
26
28
|
SingletonBase,
|
|
27
|
-
|
|
29
|
+
TypeSchema,
|
|
30
|
+
UnwrapRoute,
|
|
28
31
|
} from './elysia-fork/types.js'
|
|
32
|
+
import addFormats from 'ajv-formats'
|
|
33
|
+
let globalIndex = 0
|
|
29
34
|
|
|
30
35
|
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
31
36
|
// @ts-ignore
|
|
32
37
|
import OriginalRouter from '@medley/router'
|
|
33
38
|
import { TSchema } from '@sinclair/typebox'
|
|
34
|
-
import Ajv from 'ajv'
|
|
39
|
+
import Ajv, { ValidateFunction } from 'ajv'
|
|
35
40
|
import { Context } from './elysia-fork/context.js'
|
|
36
41
|
import { isAsyncIterable } from './utils.js'
|
|
37
42
|
import { redirect } from './elysia-fork/utils.js'
|
|
38
43
|
import { ValidationError } from './elysia-fork/error.js'
|
|
44
|
+
import { zodToJsonSchema } from 'zod-to-json-schema'
|
|
45
|
+
import { z, ZodType } from 'zod'
|
|
46
|
+
|
|
47
|
+
const ajv = addFormats(new Ajv({ useDefaults: true }), [
|
|
48
|
+
'date-time',
|
|
49
|
+
'time',
|
|
50
|
+
'date',
|
|
51
|
+
'email',
|
|
52
|
+
'hostname',
|
|
53
|
+
'ipv4',
|
|
54
|
+
'ipv6',
|
|
55
|
+
'uri',
|
|
56
|
+
'uri-reference',
|
|
57
|
+
'uuid',
|
|
58
|
+
'uri-template',
|
|
59
|
+
'json-pointer',
|
|
60
|
+
'relative-json-pointer',
|
|
61
|
+
'regex',
|
|
62
|
+
])
|
|
39
63
|
|
|
40
|
-
const ajv = new Ajv()
|
|
41
64
|
// Should be exported from `hono/router`
|
|
42
65
|
|
|
43
66
|
type P = any
|
|
@@ -47,25 +70,34 @@ type AsyncResponse = Response | Promise<Response>
|
|
|
47
70
|
type OnError = (x: { error: any; request: Request }) => AsyncResponse
|
|
48
71
|
|
|
49
72
|
type RouterTree = {
|
|
73
|
+
id: number
|
|
50
74
|
router: OriginalRouter
|
|
51
75
|
prefix?: string
|
|
52
76
|
onRequestHandlers: Function[]
|
|
53
77
|
onErrorHandlers: OnError[]
|
|
54
78
|
children: RouterTree[]
|
|
79
|
+
routes: InternalRoute[]
|
|
80
|
+
// default store for the router, used as default for context.store
|
|
55
81
|
store: Record<any, any>
|
|
82
|
+
currentRoot?: RouterTree
|
|
56
83
|
}
|
|
57
84
|
|
|
58
85
|
type OnNoMatch = (request: Request, platform: P) => AsyncResponse
|
|
59
86
|
|
|
60
|
-
type
|
|
61
|
-
|
|
62
|
-
|
|
87
|
+
export type InternalRoute = {
|
|
88
|
+
method: HTTPMethod
|
|
89
|
+
path: string
|
|
90
|
+
handler: InlineHandler<any, any, any>
|
|
91
|
+
hooks: LocalHook<any, any, any, any, any, any, any>
|
|
92
|
+
validate?: ValidateFunction
|
|
93
|
+
prefix: string
|
|
94
|
+
|
|
63
95
|
// store: Record<any, any>
|
|
64
96
|
}
|
|
65
97
|
/**
|
|
66
98
|
* Router class
|
|
67
99
|
*/
|
|
68
|
-
export class
|
|
100
|
+
export class Spiceflow<
|
|
69
101
|
const in out BasePath extends string = '',
|
|
70
102
|
const in out Scoped extends boolean = true,
|
|
71
103
|
const in out Singleton extends SingletonBase = {
|
|
@@ -95,40 +127,82 @@ export class Elysia<
|
|
|
95
127
|
derive: {}
|
|
96
128
|
resolve: {}
|
|
97
129
|
schema: {}
|
|
98
|
-
}
|
|
130
|
+
},
|
|
99
131
|
> {
|
|
100
132
|
private onNoMatch: OnNoMatch
|
|
101
133
|
// prefix: BasePath | undefined
|
|
102
|
-
routerTree: RouterTree
|
|
134
|
+
private routerTree: RouterTree
|
|
135
|
+
|
|
136
|
+
getAllRoutes() {
|
|
137
|
+
let root = this.routerTree.currentRoot || this.routerTree
|
|
138
|
+
const allApps = bfs(root) || []
|
|
139
|
+
const allRoutes = allApps.flatMap((x) => {
|
|
140
|
+
const prefix = this.getRouteAndParents(x)
|
|
141
|
+
.map((x) => x.prefix)
|
|
142
|
+
.reverse()
|
|
143
|
+
.join('')
|
|
144
|
+
|
|
145
|
+
return x.routes.map((x) => ({ ...x, path: prefix + x.path }))
|
|
146
|
+
})
|
|
147
|
+
return allRoutes
|
|
148
|
+
}
|
|
103
149
|
|
|
104
|
-
add({
|
|
150
|
+
private add({
|
|
105
151
|
method,
|
|
106
152
|
path,
|
|
153
|
+
hooks,
|
|
154
|
+
handler,
|
|
107
155
|
...rest
|
|
108
|
-
}:
|
|
109
|
-
method: string
|
|
110
|
-
path: string
|
|
111
|
-
}) {
|
|
156
|
+
}: Partial<InternalRoute>) {
|
|
112
157
|
const router = this.routerTree
|
|
113
158
|
// if (router.prefix) {
|
|
114
159
|
// path = router.prefix + path
|
|
115
160
|
// }
|
|
116
161
|
|
|
162
|
+
let bodySchema: TypeSchema = hooks?.body
|
|
163
|
+
let validate: ValidateFunction | undefined
|
|
164
|
+
|
|
165
|
+
if (isZodSchema(bodySchema)) {
|
|
166
|
+
let jsonSchema = zodToJsonSchema(bodySchema, {})
|
|
167
|
+
validate = ajv.compile(jsonSchema)
|
|
168
|
+
} else if (bodySchema) {
|
|
169
|
+
// console.log(bodySchema)
|
|
170
|
+
validate = ajv.compile(bodySchema)
|
|
171
|
+
}
|
|
172
|
+
|
|
117
173
|
const store = router.router.register(path)
|
|
118
|
-
|
|
174
|
+
let route: InternalRoute = {
|
|
175
|
+
...rest,
|
|
176
|
+
|
|
177
|
+
prefix: router.prefix || '',
|
|
178
|
+
method: (method || '') as any,
|
|
179
|
+
path: path || '',
|
|
180
|
+
// prefix,
|
|
181
|
+
handler: handler!,
|
|
182
|
+
hooks,
|
|
183
|
+
validate,
|
|
184
|
+
}
|
|
185
|
+
router.routes.push(route)
|
|
186
|
+
store[method] = route
|
|
119
187
|
}
|
|
120
188
|
|
|
121
189
|
private match(method: string, path: string) {
|
|
122
|
-
|
|
123
|
-
|
|
190
|
+
let root = this.routerTree
|
|
191
|
+
const result = bfsFind(this.routerTree, (router) => {
|
|
192
|
+
router.currentRoot = root
|
|
193
|
+
let prefix = this.getRouteAndParents(router)
|
|
194
|
+
.map((x) => x.prefix)
|
|
195
|
+
.reverse()
|
|
196
|
+
.join('')
|
|
197
|
+
if (prefix && !path.startsWith(prefix)) {
|
|
124
198
|
// console.log(
|
|
125
199
|
// `router prefix: ${router.prefix} does not match path: ${path}`
|
|
126
200
|
// )
|
|
127
201
|
return
|
|
128
202
|
}
|
|
129
203
|
let pathWithoutPrefix = path
|
|
130
|
-
if (
|
|
131
|
-
pathWithoutPrefix = path.replace(
|
|
204
|
+
if (prefix) {
|
|
205
|
+
pathWithoutPrefix = path.replace(prefix, '')
|
|
132
206
|
}
|
|
133
207
|
// console.log(`router prefix: ${router.prefix} matches path: ${path}`)
|
|
134
208
|
const route = router.router.find(pathWithoutPrefix)
|
|
@@ -136,7 +210,7 @@ export class Elysia<
|
|
|
136
210
|
return
|
|
137
211
|
}
|
|
138
212
|
|
|
139
|
-
let data:
|
|
213
|
+
let data: InternalRoute = route['store'][method]
|
|
140
214
|
if (data) {
|
|
141
215
|
// console.log(`route found: ${method} ${path}`, route)
|
|
142
216
|
|
|
@@ -148,7 +222,7 @@ export class Elysia<
|
|
|
148
222
|
store: router.store,
|
|
149
223
|
onErrorHandlers,
|
|
150
224
|
onRequestHandlers,
|
|
151
|
-
params
|
|
225
|
+
params,
|
|
152
226
|
}
|
|
153
227
|
}
|
|
154
228
|
})
|
|
@@ -158,8 +232,8 @@ export class Elysia<
|
|
|
158
232
|
|
|
159
233
|
state<const Name extends string | number | symbol, Value>(
|
|
160
234
|
name: Name,
|
|
161
|
-
value: Value
|
|
162
|
-
):
|
|
235
|
+
value: Value,
|
|
236
|
+
): Spiceflow<
|
|
163
237
|
BasePath,
|
|
164
238
|
Scoped,
|
|
165
239
|
{
|
|
@@ -189,24 +263,25 @@ export class Elysia<
|
|
|
189
263
|
*/
|
|
190
264
|
constructor(
|
|
191
265
|
options: {
|
|
192
|
-
|
|
193
|
-
// onError?: OnError
|
|
266
|
+
name?: string
|
|
194
267
|
scoped?: Scoped
|
|
195
268
|
onNoMatch?: (request: Request, platform: P) => AsyncResponse
|
|
196
269
|
basePath?: BasePath
|
|
197
|
-
} = {}
|
|
270
|
+
} = {},
|
|
198
271
|
) {
|
|
199
272
|
this.scoped = options.scoped
|
|
200
273
|
|
|
201
274
|
this.onNoMatch =
|
|
202
275
|
options.onNoMatch ?? (() => new Response(null, { status: 404 }))
|
|
203
276
|
this.routerTree = {
|
|
277
|
+
id: globalIndex++,
|
|
204
278
|
router: new OriginalRouter(),
|
|
205
279
|
prefix: options.basePath,
|
|
206
280
|
onRequestHandlers: [],
|
|
207
281
|
onErrorHandlers: [],
|
|
208
282
|
children: [],
|
|
209
|
-
store: {}
|
|
283
|
+
store: {},
|
|
284
|
+
routes: [],
|
|
210
285
|
}
|
|
211
286
|
|
|
212
287
|
// Bind router methods
|
|
@@ -224,28 +299,12 @@ export class Elysia<
|
|
|
224
299
|
Scoped: false as Scoped,
|
|
225
300
|
Singleton: {} as Singleton,
|
|
226
301
|
Definitions: {} as Definitions,
|
|
227
|
-
Metadata: {} as Metadata
|
|
302
|
+
Metadata: {} as Metadata,
|
|
228
303
|
}
|
|
229
304
|
|
|
230
305
|
_ephemeral = {} as Ephemeral
|
|
231
306
|
_volatile = {} as Volatile
|
|
232
307
|
|
|
233
|
-
/**
|
|
234
|
-
* ### post
|
|
235
|
-
* Register handler for path with method [POST]
|
|
236
|
-
*
|
|
237
|
-
* ---
|
|
238
|
-
* @example
|
|
239
|
-
* ```typescript
|
|
240
|
-
* import { Elysia, t } from 'elysia'
|
|
241
|
-
*
|
|
242
|
-
* new Elysia()
|
|
243
|
-
* .post('/', () => 'hi')
|
|
244
|
-
* .post('/with-hook', () => 'hi', {
|
|
245
|
-
* response: t.String()
|
|
246
|
-
* })
|
|
247
|
-
* ```
|
|
248
|
-
*/
|
|
249
308
|
post<
|
|
250
309
|
const Path extends string,
|
|
251
310
|
const LocalSchema extends InputSchema<
|
|
@@ -265,7 +324,7 @@ export class Elysia<
|
|
|
265
324
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
266
325
|
},
|
|
267
326
|
JoinPath<BasePath, Path>
|
|
268
|
-
|
|
327
|
+
>,
|
|
269
328
|
>(
|
|
270
329
|
path: Path,
|
|
271
330
|
handler: Handle,
|
|
@@ -279,8 +338,8 @@ export class Elysia<
|
|
|
279
338
|
Definitions['error'],
|
|
280
339
|
Metadata['macro'],
|
|
281
340
|
JoinPath<BasePath, Path>
|
|
282
|
-
|
|
283
|
-
):
|
|
341
|
+
>,
|
|
342
|
+
): Spiceflow<
|
|
284
343
|
BasePath,
|
|
285
344
|
Scoped,
|
|
286
345
|
Singleton,
|
|
@@ -297,7 +356,7 @@ export class Elysia<
|
|
|
297
356
|
: Schema['params']
|
|
298
357
|
query: Schema['query']
|
|
299
358
|
headers: Schema['headers']
|
|
300
|
-
response:
|
|
359
|
+
response: ComposeSpiceflowResponse<
|
|
301
360
|
Schema['response'],
|
|
302
361
|
Handle
|
|
303
362
|
>
|
|
@@ -307,7 +366,7 @@ export class Elysia<
|
|
|
307
366
|
Ephemeral,
|
|
308
367
|
Volatile
|
|
309
368
|
> {
|
|
310
|
-
this.add({ method: 'POST', path, handler: handler, hook })
|
|
369
|
+
this.add({ method: 'POST', path, handler: handler, hooks: hook })
|
|
311
370
|
|
|
312
371
|
return this as any
|
|
313
372
|
}
|
|
@@ -332,7 +391,7 @@ export class Elysia<
|
|
|
332
391
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
333
392
|
},
|
|
334
393
|
JoinPath<BasePath, Path>
|
|
335
|
-
|
|
394
|
+
>,
|
|
336
395
|
>(
|
|
337
396
|
path: Path,
|
|
338
397
|
handler: Handle,
|
|
@@ -346,8 +405,8 @@ export class Elysia<
|
|
|
346
405
|
Definitions['error'],
|
|
347
406
|
Macro,
|
|
348
407
|
JoinPath<BasePath, Path>
|
|
349
|
-
|
|
350
|
-
):
|
|
408
|
+
>,
|
|
409
|
+
): Spiceflow<
|
|
351
410
|
BasePath,
|
|
352
411
|
Scoped,
|
|
353
412
|
Singleton,
|
|
@@ -364,7 +423,7 @@ export class Elysia<
|
|
|
364
423
|
: Schema['params']
|
|
365
424
|
query: Schema['query']
|
|
366
425
|
headers: Schema['headers']
|
|
367
|
-
response:
|
|
426
|
+
response: ComposeSpiceflowResponse<
|
|
368
427
|
Schema['response'],
|
|
369
428
|
Handle
|
|
370
429
|
>
|
|
@@ -374,7 +433,7 @@ export class Elysia<
|
|
|
374
433
|
Ephemeral,
|
|
375
434
|
Volatile
|
|
376
435
|
> {
|
|
377
|
-
this.add({ method: 'GET', path, handler: handler, hook })
|
|
436
|
+
this.add({ method: 'GET', path, handler: handler, hooks: hook })
|
|
378
437
|
return this as any
|
|
379
438
|
}
|
|
380
439
|
|
|
@@ -397,7 +456,7 @@ export class Elysia<
|
|
|
397
456
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
398
457
|
},
|
|
399
458
|
JoinPath<BasePath, Path>
|
|
400
|
-
|
|
459
|
+
>,
|
|
401
460
|
>(
|
|
402
461
|
path: Path,
|
|
403
462
|
handler: Handle,
|
|
@@ -411,8 +470,8 @@ export class Elysia<
|
|
|
411
470
|
Definitions['error'],
|
|
412
471
|
Metadata['macro'],
|
|
413
472
|
JoinPath<BasePath, Path>
|
|
414
|
-
|
|
415
|
-
):
|
|
473
|
+
>,
|
|
474
|
+
): Spiceflow<
|
|
416
475
|
BasePath,
|
|
417
476
|
Scoped,
|
|
418
477
|
Singleton,
|
|
@@ -429,7 +488,7 @@ export class Elysia<
|
|
|
429
488
|
: Schema['params']
|
|
430
489
|
query: Schema['query']
|
|
431
490
|
headers: Schema['headers']
|
|
432
|
-
response:
|
|
491
|
+
response: ComposeSpiceflowResponse<
|
|
433
492
|
Schema['response'],
|
|
434
493
|
Handle
|
|
435
494
|
>
|
|
@@ -439,7 +498,7 @@ export class Elysia<
|
|
|
439
498
|
Ephemeral,
|
|
440
499
|
Volatile
|
|
441
500
|
> {
|
|
442
|
-
this.add({ method: 'PUT', path, handler: handler, hook })
|
|
501
|
+
this.add({ method: 'PUT', path, handler: handler, hooks: hook })
|
|
443
502
|
|
|
444
503
|
return this as any
|
|
445
504
|
}
|
|
@@ -463,7 +522,7 @@ export class Elysia<
|
|
|
463
522
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
464
523
|
},
|
|
465
524
|
JoinPath<BasePath, Path>
|
|
466
|
-
|
|
525
|
+
>,
|
|
467
526
|
>(
|
|
468
527
|
path: Path,
|
|
469
528
|
handler: Handle,
|
|
@@ -477,8 +536,8 @@ export class Elysia<
|
|
|
477
536
|
Definitions['error'],
|
|
478
537
|
Metadata['macro'],
|
|
479
538
|
JoinPath<BasePath, Path>
|
|
480
|
-
|
|
481
|
-
):
|
|
539
|
+
>,
|
|
540
|
+
): Spiceflow<
|
|
482
541
|
BasePath,
|
|
483
542
|
Scoped,
|
|
484
543
|
Singleton,
|
|
@@ -495,7 +554,7 @@ export class Elysia<
|
|
|
495
554
|
: Schema['params']
|
|
496
555
|
query: Schema['query']
|
|
497
556
|
headers: Schema['headers']
|
|
498
|
-
response:
|
|
557
|
+
response: ComposeSpiceflowResponse<
|
|
499
558
|
Schema['response'],
|
|
500
559
|
Handle
|
|
501
560
|
>
|
|
@@ -505,7 +564,7 @@ export class Elysia<
|
|
|
505
564
|
Ephemeral,
|
|
506
565
|
Volatile
|
|
507
566
|
> {
|
|
508
|
-
this.add({ method: 'PATCH', path, handler: handler, hook })
|
|
567
|
+
this.add({ method: 'PATCH', path, handler: handler, hooks: hook })
|
|
509
568
|
|
|
510
569
|
return this as any
|
|
511
570
|
}
|
|
@@ -529,7 +588,7 @@ export class Elysia<
|
|
|
529
588
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
530
589
|
},
|
|
531
590
|
JoinPath<BasePath, Path>
|
|
532
|
-
|
|
591
|
+
>,
|
|
533
592
|
>(
|
|
534
593
|
path: Path,
|
|
535
594
|
handler: Handle,
|
|
@@ -543,8 +602,8 @@ export class Elysia<
|
|
|
543
602
|
Definitions['error'],
|
|
544
603
|
Metadata['macro'],
|
|
545
604
|
JoinPath<BasePath, Path>
|
|
546
|
-
|
|
547
|
-
):
|
|
605
|
+
>,
|
|
606
|
+
): Spiceflow<
|
|
548
607
|
BasePath,
|
|
549
608
|
Scoped,
|
|
550
609
|
Singleton,
|
|
@@ -561,7 +620,7 @@ export class Elysia<
|
|
|
561
620
|
: Schema['params']
|
|
562
621
|
query: Schema['query']
|
|
563
622
|
headers: Schema['headers']
|
|
564
|
-
response:
|
|
623
|
+
response: ComposeSpiceflowResponse<
|
|
565
624
|
Schema['response'],
|
|
566
625
|
Handle
|
|
567
626
|
>
|
|
@@ -571,7 +630,7 @@ export class Elysia<
|
|
|
571
630
|
Ephemeral,
|
|
572
631
|
Volatile
|
|
573
632
|
> {
|
|
574
|
-
this.add({ method: 'DELETE', path, handler: handler, hook })
|
|
633
|
+
this.add({ method: 'DELETE', path, handler: handler, hooks: hook })
|
|
575
634
|
|
|
576
635
|
return this as any
|
|
577
636
|
}
|
|
@@ -595,7 +654,7 @@ export class Elysia<
|
|
|
595
654
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
596
655
|
},
|
|
597
656
|
JoinPath<BasePath, Path>
|
|
598
|
-
|
|
657
|
+
>,
|
|
599
658
|
>(
|
|
600
659
|
path: Path,
|
|
601
660
|
handler: Handle,
|
|
@@ -609,8 +668,8 @@ export class Elysia<
|
|
|
609
668
|
Definitions['error'],
|
|
610
669
|
Metadata['macro'],
|
|
611
670
|
JoinPath<BasePath, Path>
|
|
612
|
-
|
|
613
|
-
):
|
|
671
|
+
>,
|
|
672
|
+
): Spiceflow<
|
|
614
673
|
BasePath,
|
|
615
674
|
Scoped,
|
|
616
675
|
Singleton,
|
|
@@ -627,7 +686,7 @@ export class Elysia<
|
|
|
627
686
|
: Schema['params']
|
|
628
687
|
query: Schema['query']
|
|
629
688
|
headers: Schema['headers']
|
|
630
|
-
response:
|
|
689
|
+
response: ComposeSpiceflowResponse<
|
|
631
690
|
Schema['response'],
|
|
632
691
|
Handle
|
|
633
692
|
>
|
|
@@ -637,7 +696,7 @@ export class Elysia<
|
|
|
637
696
|
Ephemeral,
|
|
638
697
|
Volatile
|
|
639
698
|
> {
|
|
640
|
-
this.add({ method: 'OPTIONS', path, handler: handler, hook })
|
|
699
|
+
this.add({ method: 'OPTIONS', path, handler: handler, hooks: hook })
|
|
641
700
|
|
|
642
701
|
return this as any
|
|
643
702
|
}
|
|
@@ -661,7 +720,7 @@ export class Elysia<
|
|
|
661
720
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
662
721
|
},
|
|
663
722
|
JoinPath<BasePath, Path>
|
|
664
|
-
|
|
723
|
+
>,
|
|
665
724
|
>(
|
|
666
725
|
path: Path,
|
|
667
726
|
handler: Handle,
|
|
@@ -675,8 +734,8 @@ export class Elysia<
|
|
|
675
734
|
Definitions['error'],
|
|
676
735
|
Metadata['macro'],
|
|
677
736
|
JoinPath<BasePath, Path>
|
|
678
|
-
|
|
679
|
-
):
|
|
737
|
+
>,
|
|
738
|
+
): Spiceflow<
|
|
680
739
|
BasePath,
|
|
681
740
|
Scoped,
|
|
682
741
|
Singleton,
|
|
@@ -693,7 +752,7 @@ export class Elysia<
|
|
|
693
752
|
: Schema['params']
|
|
694
753
|
query: Schema['query']
|
|
695
754
|
headers: Schema['headers']
|
|
696
|
-
response:
|
|
755
|
+
response: ComposeSpiceflowResponse<
|
|
697
756
|
Schema['response'],
|
|
698
757
|
Handle
|
|
699
758
|
>
|
|
@@ -704,7 +763,7 @@ export class Elysia<
|
|
|
704
763
|
Volatile
|
|
705
764
|
> {
|
|
706
765
|
for (const method of METHODS) {
|
|
707
|
-
this.add({ method, path, handler: handler, hook })
|
|
766
|
+
this.add({ method, path, handler: handler, hooks: hook })
|
|
708
767
|
}
|
|
709
768
|
|
|
710
769
|
return this as any
|
|
@@ -729,7 +788,7 @@ export class Elysia<
|
|
|
729
788
|
resolve: Ephemeral['resolve'] & Volatile['resolve']
|
|
730
789
|
},
|
|
731
790
|
JoinPath<BasePath, Path>
|
|
732
|
-
|
|
791
|
+
>,
|
|
733
792
|
>(
|
|
734
793
|
path: Path,
|
|
735
794
|
handler: Handle,
|
|
@@ -743,8 +802,8 @@ export class Elysia<
|
|
|
743
802
|
Definitions['error'],
|
|
744
803
|
Metadata['macro'],
|
|
745
804
|
JoinPath<BasePath, Path>
|
|
746
|
-
|
|
747
|
-
):
|
|
805
|
+
>,
|
|
806
|
+
): Spiceflow<
|
|
748
807
|
BasePath,
|
|
749
808
|
Scoped,
|
|
750
809
|
Singleton,
|
|
@@ -761,7 +820,7 @@ export class Elysia<
|
|
|
761
820
|
: Schema['params']
|
|
762
821
|
query: Schema['query']
|
|
763
822
|
headers: Schema['headers']
|
|
764
|
-
response:
|
|
823
|
+
response: ComposeSpiceflowResponse<
|
|
765
824
|
Schema['response'],
|
|
766
825
|
Handle
|
|
767
826
|
>
|
|
@@ -771,17 +830,17 @@ export class Elysia<
|
|
|
771
830
|
Ephemeral,
|
|
772
831
|
Volatile
|
|
773
832
|
> {
|
|
774
|
-
this.add({ method: 'HEAD', path, handler: handler, hook })
|
|
833
|
+
this.add({ method: 'HEAD', path, handler: handler, hooks: hook })
|
|
775
834
|
|
|
776
835
|
return this as any
|
|
777
836
|
}
|
|
778
837
|
|
|
779
838
|
/**
|
|
780
|
-
* If set to true, other
|
|
839
|
+
* If set to true, other Spiceflow handler will not inherits global life-cycle, store, decorators from the current instance
|
|
781
840
|
*
|
|
782
841
|
* @default false
|
|
783
842
|
*/
|
|
784
|
-
scoped?: Scoped
|
|
843
|
+
private scoped?: Scoped
|
|
785
844
|
get _scoped() {
|
|
786
845
|
return this.scoped as Scoped
|
|
787
846
|
}
|
|
@@ -789,11 +848,11 @@ export class Elysia<
|
|
|
789
848
|
// group is not needed, you can add another prefixed app instead
|
|
790
849
|
// group<
|
|
791
850
|
// const Prefix extends string,
|
|
792
|
-
// const
|
|
851
|
+
// const NewSpiceflow extends Spiceflow<any, any, any, any, any, any, any, any>
|
|
793
852
|
// >(
|
|
794
853
|
// prefix: Prefix,
|
|
795
854
|
// run: (
|
|
796
|
-
// group:
|
|
855
|
+
// group: Spiceflow<
|
|
797
856
|
// `${BasePath}${Prefix}`,
|
|
798
857
|
// Scoped,
|
|
799
858
|
// Singleton,
|
|
@@ -803,14 +862,14 @@ export class Elysia<
|
|
|
803
862
|
// Ephemeral,
|
|
804
863
|
// Volatile
|
|
805
864
|
// >
|
|
806
|
-
// ) =>
|
|
807
|
-
// ):
|
|
865
|
+
// ) => NewSpiceflow
|
|
866
|
+
// ): Spiceflow<
|
|
808
867
|
// BasePath,
|
|
809
868
|
// Scoped,
|
|
810
869
|
// Singleton,
|
|
811
870
|
// Definitions,
|
|
812
871
|
// Metadata,
|
|
813
|
-
// Prettify<Routes &
|
|
872
|
+
// Prettify<Routes & NewSpiceflow['_routes']>,
|
|
814
873
|
// Ephemeral,
|
|
815
874
|
// Volatile
|
|
816
875
|
// > {
|
|
@@ -825,44 +884,37 @@ export class Elysia<
|
|
|
825
884
|
// return this
|
|
826
885
|
// }
|
|
827
886
|
|
|
828
|
-
use<const
|
|
829
|
-
instance:
|
|
830
|
-
):
|
|
831
|
-
?
|
|
887
|
+
use<const NewSpiceflow extends AnySpiceflow>(
|
|
888
|
+
instance: NewSpiceflow,
|
|
889
|
+
): NewSpiceflow['_scoped'] extends false
|
|
890
|
+
? Spiceflow<
|
|
832
891
|
BasePath,
|
|
833
892
|
Scoped,
|
|
834
893
|
// @ts-expect-error - This is truly ideal
|
|
835
|
-
Prettify2<Singleton &
|
|
836
|
-
Prettify2<Definitions &
|
|
837
|
-
Prettify2<Metadata &
|
|
894
|
+
Prettify2<Singleton & NewSpiceflow['_types']['Singleton']>,
|
|
895
|
+
Prettify2<Definitions & NewSpiceflow['_types']['Definitions']>,
|
|
896
|
+
Prettify2<Metadata & NewSpiceflow['_types']['Metadata']>,
|
|
838
897
|
BasePath extends ``
|
|
839
|
-
? Routes &
|
|
840
|
-
: Routes & CreateEden<BasePath,
|
|
898
|
+
? Routes & NewSpiceflow['_routes']
|
|
899
|
+
: Routes & CreateEden<BasePath, NewSpiceflow['_routes']>,
|
|
841
900
|
Ephemeral,
|
|
842
|
-
Prettify2<Volatile &
|
|
901
|
+
Prettify2<Volatile & NewSpiceflow['_ephemeral']>
|
|
843
902
|
>
|
|
844
|
-
:
|
|
903
|
+
: Spiceflow<
|
|
845
904
|
BasePath,
|
|
846
905
|
Scoped,
|
|
847
906
|
Singleton,
|
|
848
907
|
Definitions,
|
|
849
908
|
Metadata,
|
|
850
909
|
BasePath extends ``
|
|
851
|
-
? Routes &
|
|
852
|
-
: Routes & CreateEden<BasePath,
|
|
910
|
+
? Routes & NewSpiceflow['_routes']
|
|
911
|
+
: Routes & CreateEden<BasePath, NewSpiceflow['_routes']>,
|
|
853
912
|
Ephemeral,
|
|
854
913
|
Volatile
|
|
855
914
|
> {
|
|
856
915
|
const thisRouter = this.routerTree
|
|
857
916
|
// TODO use scoped logic to add onRequest and onError on all routers if necessary, add them first
|
|
858
|
-
this.routerTree.children.push(
|
|
859
|
-
mapBfs(instance.routerTree, (r) => {
|
|
860
|
-
return {
|
|
861
|
-
...r,
|
|
862
|
-
prefix: (thisRouter.prefix || '') + r.prefix
|
|
863
|
-
}
|
|
864
|
-
})
|
|
865
|
-
)
|
|
917
|
+
this.routerTree.children.push(instance.routerTree)
|
|
866
918
|
return this as any
|
|
867
919
|
}
|
|
868
920
|
|
|
@@ -881,7 +933,7 @@ export class Elysia<
|
|
|
881
933
|
Ephemeral,
|
|
882
934
|
Volatile
|
|
883
935
|
>
|
|
884
|
-
|
|
936
|
+
>,
|
|
885
937
|
): this {
|
|
886
938
|
const router = this.routerTree
|
|
887
939
|
|
|
@@ -908,7 +960,7 @@ export class Elysia<
|
|
|
908
960
|
resolve: {}
|
|
909
961
|
}
|
|
910
962
|
>
|
|
911
|
-
|
|
963
|
+
>,
|
|
912
964
|
) {
|
|
913
965
|
const router = this.routerTree
|
|
914
966
|
router.onRequestHandlers ??= []
|
|
@@ -929,7 +981,7 @@ export class Elysia<
|
|
|
929
981
|
const defaultContext = {
|
|
930
982
|
redirect,
|
|
931
983
|
error: null,
|
|
932
|
-
path
|
|
984
|
+
path,
|
|
933
985
|
}
|
|
934
986
|
let onErrorHandlers: OnError[] = []
|
|
935
987
|
try {
|
|
@@ -940,41 +992,44 @@ export class Elysia<
|
|
|
940
992
|
if (!route) {
|
|
941
993
|
return this.onNoMatch(request, platform)
|
|
942
994
|
}
|
|
943
|
-
onErrorHandlers = this.getRouteAndParents(route.router)
|
|
944
|
-
(
|
|
945
|
-
|
|
946
|
-
const { params, store } = route
|
|
947
|
-
const
|
|
948
|
-
(
|
|
949
|
-
|
|
995
|
+
onErrorHandlers = this.getRouteAndParents(route.router)
|
|
996
|
+
.reverse()
|
|
997
|
+
.flatMap((x) => x.onErrorHandlers)
|
|
998
|
+
const { params, store: defaultStore } = route
|
|
999
|
+
const onReqHandlers = this.getRouteAndParents(route.router)
|
|
1000
|
+
.reverse()
|
|
1001
|
+
.flatMap((x) => x.onRequestHandlers)
|
|
1002
|
+
// console.log({ onReqHandlers })
|
|
1003
|
+
let store = { ...defaultStore }
|
|
950
1004
|
// TODO add content type
|
|
951
1005
|
|
|
952
|
-
let content = route?.
|
|
1006
|
+
let content = route?.hooks?.content
|
|
953
1007
|
let body = await getRequestBody({ request, content })
|
|
954
|
-
|
|
955
|
-
if (
|
|
956
|
-
|
|
957
|
-
|
|
1008
|
+
|
|
1009
|
+
if (route.validate) {
|
|
1010
|
+
// TODO move compile to the router
|
|
1011
|
+
|
|
1012
|
+
const valid = route.validate(body)
|
|
958
1013
|
if (!valid) {
|
|
959
|
-
const error = ajv.errorsText(validate.errors, {
|
|
960
|
-
separator: '\n'
|
|
1014
|
+
const error = ajv.errorsText(route.validate.errors, {
|
|
1015
|
+
separator: '\n',
|
|
961
1016
|
})
|
|
962
1017
|
|
|
963
1018
|
return new Response(error, {
|
|
964
1019
|
status: 400,
|
|
965
1020
|
headers: {
|
|
966
|
-
'content-type': 'text/plain'
|
|
967
|
-
}
|
|
1021
|
+
'content-type': 'text/plain',
|
|
1022
|
+
},
|
|
968
1023
|
})
|
|
969
1024
|
}
|
|
970
1025
|
}
|
|
971
|
-
if (
|
|
972
|
-
for (const handler of
|
|
1026
|
+
if (onReqHandlers.length > 0) {
|
|
1027
|
+
for (const handler of onReqHandlers) {
|
|
973
1028
|
const res = await handler({
|
|
974
1029
|
request,
|
|
975
1030
|
response,
|
|
976
1031
|
store,
|
|
977
|
-
path
|
|
1032
|
+
path,
|
|
978
1033
|
} satisfies Context<any, any, any>)
|
|
979
1034
|
if (res) {
|
|
980
1035
|
return await turnHandlerResultIntoResponse(res)
|
|
@@ -991,7 +1046,7 @@ export class Elysia<
|
|
|
991
1046
|
params: params as any,
|
|
992
1047
|
store,
|
|
993
1048
|
body,
|
|
994
|
-
path
|
|
1049
|
+
path,
|
|
995
1050
|
|
|
996
1051
|
// platform
|
|
997
1052
|
} satisfies Context<any, any, string>)
|
|
@@ -999,7 +1054,7 @@ export class Elysia<
|
|
|
999
1054
|
return await this.handleStream({
|
|
1000
1055
|
generator: res,
|
|
1001
1056
|
request,
|
|
1002
|
-
onErrorHandlers
|
|
1057
|
+
onErrorHandlers,
|
|
1003
1058
|
})
|
|
1004
1059
|
}
|
|
1005
1060
|
|
|
@@ -1008,11 +1063,11 @@ export class Elysia<
|
|
|
1008
1063
|
let res = await this.runErrorHandlers({
|
|
1009
1064
|
onErrorHandlers,
|
|
1010
1065
|
error: err,
|
|
1011
|
-
request
|
|
1066
|
+
request,
|
|
1012
1067
|
})
|
|
1013
1068
|
if (res) return res
|
|
1014
1069
|
return new Response(err?.message || 'Internal Server Error', {
|
|
1015
|
-
status: 500
|
|
1070
|
+
status: 500,
|
|
1016
1071
|
})
|
|
1017
1072
|
}
|
|
1018
1073
|
}
|
|
@@ -1020,7 +1075,7 @@ export class Elysia<
|
|
|
1020
1075
|
private async runErrorHandlers({
|
|
1021
1076
|
onErrorHandlers = [] as OnError[],
|
|
1022
1077
|
error: err,
|
|
1023
|
-
request
|
|
1078
|
+
request,
|
|
1024
1079
|
}) {
|
|
1025
1080
|
if (onErrorHandlers.length === 0) {
|
|
1026
1081
|
console.error(`Spiceflow unhandled error:`, err)
|
|
@@ -1034,31 +1089,33 @@ export class Elysia<
|
|
|
1034
1089
|
}
|
|
1035
1090
|
}
|
|
1036
1091
|
|
|
1092
|
+
// get the route parents, the order is starting from the current router and going up to the root
|
|
1037
1093
|
private getRouteAndParents(currentRouter?: RouterTree) {
|
|
1038
1094
|
const parents: RouterTree[] = []
|
|
1039
1095
|
let current = currentRouter
|
|
1040
1096
|
|
|
1097
|
+
let root = this.routerTree.currentRoot || this.routerTree
|
|
1041
1098
|
// Perform BFS once to build a parent map
|
|
1042
|
-
const parentMap = new Map<
|
|
1043
|
-
|
|
1099
|
+
const parentMap = new Map<number, RouterTree>()
|
|
1100
|
+
bfsFind(root, (node) => {
|
|
1044
1101
|
for (const child of node.children) {
|
|
1045
|
-
parentMap.set(child, node)
|
|
1102
|
+
parentMap.set(child.id, node)
|
|
1046
1103
|
}
|
|
1047
1104
|
})
|
|
1048
1105
|
|
|
1049
1106
|
// Traverse the parent map to get the parents
|
|
1050
1107
|
while (current) {
|
|
1051
|
-
parents.
|
|
1052
|
-
current = parentMap.get(current)
|
|
1108
|
+
parents.push(current)
|
|
1109
|
+
current = parentMap.get(current.id)
|
|
1053
1110
|
}
|
|
1054
1111
|
|
|
1055
|
-
return parents.
|
|
1112
|
+
return parents.filter((x) => x !== undefined)
|
|
1056
1113
|
}
|
|
1057
1114
|
|
|
1058
|
-
async handleStream({
|
|
1115
|
+
private async handleStream({
|
|
1059
1116
|
onErrorHandlers,
|
|
1060
1117
|
generator,
|
|
1061
|
-
request
|
|
1118
|
+
request,
|
|
1062
1119
|
}: {
|
|
1063
1120
|
generator: Generator | AsyncGenerator
|
|
1064
1121
|
onErrorHandlers: OnError[]
|
|
@@ -1091,9 +1148,9 @@ export class Elysia<
|
|
|
1091
1148
|
controller.enqueue(
|
|
1092
1149
|
Buffer.from(
|
|
1093
1150
|
`event: message\ndata: ${JSON.stringify(
|
|
1094
|
-
init.value
|
|
1095
|
-
)}\n\n
|
|
1096
|
-
)
|
|
1151
|
+
init.value,
|
|
1152
|
+
)}\n\n`,
|
|
1153
|
+
),
|
|
1097
1154
|
)
|
|
1098
1155
|
|
|
1099
1156
|
try {
|
|
@@ -1104,23 +1161,23 @@ export class Elysia<
|
|
|
1104
1161
|
controller.enqueue(
|
|
1105
1162
|
Buffer.from(
|
|
1106
1163
|
`event: message\ndata: ${JSON.stringify(
|
|
1107
|
-
chunk
|
|
1108
|
-
)}\n\n
|
|
1109
|
-
)
|
|
1164
|
+
chunk,
|
|
1165
|
+
)}\n\n`,
|
|
1166
|
+
),
|
|
1110
1167
|
)
|
|
1111
1168
|
}
|
|
1112
1169
|
} catch (error: any) {
|
|
1113
1170
|
let res = await self.runErrorHandlers({
|
|
1114
1171
|
onErrorHandlers: onErrorHandlers,
|
|
1115
1172
|
error,
|
|
1116
|
-
request
|
|
1173
|
+
request,
|
|
1117
1174
|
})
|
|
1118
1175
|
controller.enqueue(
|
|
1119
1176
|
Buffer.from(
|
|
1120
1177
|
`event: error\ndata: ${JSON.stringify(
|
|
1121
|
-
error.message || error.name || 'Error'
|
|
1122
|
-
)}\n\n
|
|
1123
|
-
)
|
|
1178
|
+
error.message || error.name || 'Error',
|
|
1179
|
+
)}\n\n`,
|
|
1180
|
+
),
|
|
1124
1181
|
)
|
|
1125
1182
|
}
|
|
1126
1183
|
|
|
@@ -1129,24 +1186,24 @@ export class Elysia<
|
|
|
1129
1186
|
} catch {
|
|
1130
1187
|
// nothing
|
|
1131
1188
|
}
|
|
1132
|
-
}
|
|
1189
|
+
},
|
|
1133
1190
|
}),
|
|
1134
1191
|
{
|
|
1135
1192
|
// TODO add headers somehow
|
|
1136
1193
|
headers: {
|
|
1137
1194
|
// Manually set transfer-encoding for direct response, eg. app.handle, eden
|
|
1138
1195
|
'transfer-encoding': 'chunked',
|
|
1139
|
-
'content-type': 'text/event-stream; charset=utf-8'
|
|
1196
|
+
'content-type': 'text/event-stream; charset=utf-8',
|
|
1140
1197
|
// ...set?.headers
|
|
1141
|
-
}
|
|
1142
|
-
}
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1143
1200
|
)
|
|
1144
1201
|
}
|
|
1145
1202
|
}
|
|
1146
1203
|
|
|
1147
1204
|
async function getRequestBody({
|
|
1148
1205
|
request,
|
|
1149
|
-
content
|
|
1206
|
+
content,
|
|
1150
1207
|
}: {
|
|
1151
1208
|
content
|
|
1152
1209
|
request: Request
|
|
@@ -1208,41 +1265,50 @@ const METHODS = [
|
|
|
1208
1265
|
'PATCH',
|
|
1209
1266
|
'POST',
|
|
1210
1267
|
'PUT',
|
|
1211
|
-
'TRACE'
|
|
1268
|
+
'TRACE',
|
|
1212
1269
|
] as const
|
|
1213
1270
|
|
|
1214
1271
|
/** HTTP method string */
|
|
1215
1272
|
export type Method = (typeof METHODS)[number]
|
|
1216
1273
|
|
|
1217
|
-
function
|
|
1274
|
+
function bfsFind<T>(
|
|
1218
1275
|
tree: RouterTree,
|
|
1219
|
-
onNode: (node: RouterTree) => T | undefined | void
|
|
1276
|
+
onNode: (node: RouterTree) => T | undefined | void,
|
|
1220
1277
|
): T | undefined {
|
|
1221
1278
|
const queue = [tree]
|
|
1279
|
+
|
|
1222
1280
|
while (queue.length > 0) {
|
|
1223
1281
|
const node = queue.shift()!
|
|
1282
|
+
|
|
1224
1283
|
const result = onNode(node)
|
|
1225
1284
|
if (result) {
|
|
1226
1285
|
return result
|
|
1227
1286
|
}
|
|
1228
1287
|
queue.push(...node.children)
|
|
1229
1288
|
}
|
|
1230
|
-
return
|
|
1289
|
+
return
|
|
1231
1290
|
}
|
|
1232
|
-
|
|
1233
|
-
function mapBfs(
|
|
1234
|
-
tree: RouterTree,
|
|
1235
|
-
mapper: (node: RouterTree) => RouterTree
|
|
1236
|
-
): RouterTree {
|
|
1291
|
+
export function bfs(tree: RouterTree) {
|
|
1237
1292
|
const queue = [tree]
|
|
1238
|
-
|
|
1293
|
+
let nodes: RouterTree[] = []
|
|
1239
1294
|
while (queue.length > 0) {
|
|
1240
1295
|
const node = queue.shift()!
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1296
|
+
if (node) {
|
|
1297
|
+
nodes.push(node)
|
|
1298
|
+
}
|
|
1299
|
+
// const result = onNode(node)
|
|
1300
|
+
|
|
1301
|
+
queue.push(...node.children)
|
|
1244
1302
|
}
|
|
1245
|
-
return
|
|
1303
|
+
return nodes
|
|
1304
|
+
}
|
|
1305
|
+
function mapTree<T>(
|
|
1306
|
+
tree: RouterTree,
|
|
1307
|
+
mapper: (node: RouterTree) => T,
|
|
1308
|
+
): T & { children: (T & { children: any[] })[] } {
|
|
1309
|
+
const mappedNode = mapper(tree) as T & { children: any[] }
|
|
1310
|
+
mappedNode.children = tree.children.map((child) => mapTree(child, mapper))
|
|
1311
|
+
return mappedNode
|
|
1246
1312
|
}
|
|
1247
1313
|
|
|
1248
1314
|
export async function turnHandlerResultIntoResponse(result: any) {
|
|
@@ -1260,7 +1326,23 @@ export async function turnHandlerResultIntoResponse(result: any) {
|
|
|
1260
1326
|
// }
|
|
1261
1327
|
// if user returns an object, convert to json
|
|
1262
1328
|
|
|
1263
|
-
return new Response(JSON.stringify(result)
|
|
1329
|
+
return new Response(JSON.stringify(result, null, 2), {
|
|
1330
|
+
headers: {
|
|
1331
|
+
'content-type': 'application/json',
|
|
1332
|
+
},
|
|
1333
|
+
})
|
|
1264
1334
|
}
|
|
1265
1335
|
|
|
1266
|
-
export type
|
|
1336
|
+
export type AnySpiceflow = Spiceflow<any, any, any, any, any, any, any, any>
|
|
1337
|
+
|
|
1338
|
+
export function isZodSchema(value: unknown): value is ZodType {
|
|
1339
|
+
return (
|
|
1340
|
+
value instanceof z.ZodType ||
|
|
1341
|
+
(typeof value === 'object' &&
|
|
1342
|
+
value !== null &&
|
|
1343
|
+
'parse' in value &&
|
|
1344
|
+
'safeParse' in value &&
|
|
1345
|
+
'optional' in value &&
|
|
1346
|
+
'nullable' in value)
|
|
1347
|
+
)
|
|
1348
|
+
}
|