spiceflow 1.1.7 → 1.1.9
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 +177 -92
- package/dist/benchmark.benchmark.js.map +1 -1
- package/dist/client/errors.d.ts.map +1 -1
- package/dist/client/errors.js.map +1 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +8 -12
- package/dist/client/index.js.map +1 -1
- package/dist/client/types.d.ts.map +1 -1
- package/dist/client/utils.js.map +1 -1
- package/dist/client/ws.d.ts.map +1 -1
- package/dist/client/ws.js +1 -3
- package/dist/client/ws.js.map +1 -1
- package/dist/client.test.js.map +1 -1
- package/dist/context.d.ts.map +1 -1
- package/dist/cors.d.ts.map +1 -1
- package/dist/cors.js.map +1 -1
- package/dist/cors.test.js.map +1 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js.map +1 -1
- package/dist/middleware.test.js +65 -0
- package/dist/middleware.test.js.map +1 -1
- package/dist/openapi.d.ts.map +1 -1
- package/dist/openapi.js +1 -1
- package/dist/openapi.js.map +1 -1
- package/dist/openapi.test.js.map +1 -1
- package/dist/spiceflow.d.ts.map +1 -1
- package/dist/spiceflow.js +12 -5
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/stream.test.js +6 -6
- package/dist/stream.test.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.test.js +56 -6
- package/dist/types.test.js.map +1 -1
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js.map +1 -1
- package/dist/zod.test.js.map +1 -1
- package/package.json +1 -1
- package/src/benchmark.benchmark.ts +8 -8
- package/src/client/errors.ts +17 -17
- package/src/client/index.ts +437 -469
- package/src/client/types.ts +168 -191
- package/src/client/utils.ts +5 -5
- package/src/client/ws.ts +87 -89
- package/src/client.test.ts +176 -183
- package/src/context.ts +82 -82
- package/src/cors.test.ts +38 -38
- package/src/cors.ts +87 -92
- package/src/error.ts +13 -13
- package/src/middleware.test.ts +221 -149
- package/src/openapi.test.ts +97 -97
- package/src/openapi.ts +365 -365
- package/src/spiceflow.test.ts +461 -467
- package/src/spiceflow.ts +1117 -1157
- package/src/stream.test.ts +310 -310
- package/src/types.test.ts +59 -39
- package/src/types.ts +698 -701
- package/src/utils.ts +79 -79
- package/src/zod.test.ts +64 -64
package/src/spiceflow.ts
CHANGED
|
@@ -6,26 +6,26 @@ export { Type as t }
|
|
|
6
6
|
|
|
7
7
|
import addFormats from 'ajv-formats'
|
|
8
8
|
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
9
|
+
ComposeSpiceflowResponse,
|
|
10
|
+
CreateEden,
|
|
11
|
+
DefinitionBase,
|
|
12
|
+
ErrorHandler,
|
|
13
|
+
HTTPMethod,
|
|
14
|
+
InlineHandler,
|
|
15
|
+
InputSchema,
|
|
16
|
+
IsAny,
|
|
17
|
+
JoinPath,
|
|
18
|
+
LocalHook,
|
|
19
|
+
MaybeArray,
|
|
20
|
+
MetadataBase,
|
|
21
|
+
MiddlewareHandler,
|
|
22
|
+
Reconcile,
|
|
23
|
+
ResolvePath,
|
|
24
|
+
RouteBase,
|
|
25
|
+
RouteSchema,
|
|
26
|
+
SingletonBase,
|
|
27
|
+
TypeSchema,
|
|
28
|
+
UnwrapRoute,
|
|
29
29
|
} from './types.js'
|
|
30
30
|
let globalIndex = 0
|
|
31
31
|
|
|
@@ -38,23 +38,23 @@ import { isProduction, ValidationError } from './error.js'
|
|
|
38
38
|
import { isAsyncIterable, redirect } from './utils.js'
|
|
39
39
|
|
|
40
40
|
const ajv = (addFormats.default || addFormats)(
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
41
|
+
new (Ajv.default || Ajv)({ useDefaults: true }),
|
|
42
|
+
[
|
|
43
|
+
'date-time',
|
|
44
|
+
'time',
|
|
45
|
+
'date',
|
|
46
|
+
'email',
|
|
47
|
+
'hostname',
|
|
48
|
+
'ipv4',
|
|
49
|
+
'ipv6',
|
|
50
|
+
'uri',
|
|
51
|
+
'uri-reference',
|
|
52
|
+
'uuid',
|
|
53
|
+
'uri-template',
|
|
54
|
+
'json-pointer',
|
|
55
|
+
'relative-json-pointer',
|
|
56
|
+
'regex',
|
|
57
|
+
],
|
|
58
58
|
)
|
|
59
59
|
|
|
60
60
|
// Should be exported from `hono/router`
|
|
@@ -64,1047 +64,1005 @@ type AsyncResponse = Response | Promise<Response>
|
|
|
64
64
|
type OnError = (x: { error: any; request: Request }) => AsyncResponse
|
|
65
65
|
|
|
66
66
|
export type InternalRoute = {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
67
|
+
method: HTTPMethod
|
|
68
|
+
path: string
|
|
69
|
+
handler: InlineHandler<any, any, any>
|
|
70
|
+
hooks: LocalHook<any, any, any, any, any, any, any>
|
|
71
|
+
validateBody?: ValidateFunction
|
|
72
|
+
validateQuery?: ValidateFunction
|
|
73
|
+
validateParams?: ValidateFunction
|
|
74
|
+
// prefix: string
|
|
75
75
|
}
|
|
76
76
|
|
|
77
77
|
type MedleyRouter = {
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
78
|
+
find: (path: string) =>
|
|
79
|
+
| {
|
|
80
|
+
store: Record<string, InternalRoute> //
|
|
81
|
+
params: Record<string, any>
|
|
82
|
+
}
|
|
83
|
+
| undefined
|
|
84
|
+
register: (path: string | undefined) => Record<string, InternalRoute>
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
const notFoundHandler = (c) => {
|
|
88
|
-
|
|
88
|
+
return new Response('Not Found', { status: 404 })
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
export class Spiceflow<
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
92
|
+
const in out BasePath extends string = '',
|
|
93
|
+
const in out Scoped extends boolean = true,
|
|
94
|
+
const in out Singleton extends SingletonBase = {
|
|
95
|
+
state: {}
|
|
96
|
+
},
|
|
97
|
+
const in out Definitions extends DefinitionBase = {
|
|
98
|
+
type: {}
|
|
99
|
+
error: {}
|
|
100
|
+
},
|
|
101
|
+
const in out Metadata extends MetadataBase = {
|
|
102
|
+
schema: {}
|
|
103
|
+
macro: {}
|
|
104
|
+
macroFn: {}
|
|
105
|
+
},
|
|
106
|
+
const out Routes extends RouteBase = {},
|
|
107
107
|
> {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
controller.enqueue(
|
|
1068
|
-
Buffer.from(
|
|
1069
|
-
`event: message\ndata: ${JSON.stringify(
|
|
1070
|
-
chunk,
|
|
1071
|
-
)}\n\n`,
|
|
1072
|
-
),
|
|
1073
|
-
)
|
|
1074
|
-
}
|
|
1075
|
-
} catch (error: any) {
|
|
1076
|
-
let res = await self.runErrorHandlers({
|
|
1077
|
-
onErrorHandlers: onErrorHandlers,
|
|
1078
|
-
error,
|
|
1079
|
-
request,
|
|
1080
|
-
})
|
|
1081
|
-
controller.enqueue(
|
|
1082
|
-
Buffer.from(
|
|
1083
|
-
`event: error\ndata: ${JSON.stringify(
|
|
1084
|
-
error.message || error.name || 'Error',
|
|
1085
|
-
)}\n\n`,
|
|
1086
|
-
),
|
|
1087
|
-
)
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
try {
|
|
1091
|
-
controller.close()
|
|
1092
|
-
} catch {
|
|
1093
|
-
// nothing
|
|
1094
|
-
}
|
|
1095
|
-
},
|
|
1096
|
-
}),
|
|
1097
|
-
{
|
|
1098
|
-
// TODO add headers somehow
|
|
1099
|
-
headers: {
|
|
1100
|
-
// Manually set transfer-encoding for direct response, eg. app.handle, eden
|
|
1101
|
-
'transfer-encoding': 'chunked',
|
|
1102
|
-
'content-type': 'text/event-stream; charset=utf-8',
|
|
1103
|
-
// ...set?.headers
|
|
1104
|
-
},
|
|
1105
|
-
},
|
|
1106
|
-
)
|
|
1107
|
-
}
|
|
108
|
+
private id: number = globalIndex++
|
|
109
|
+
private router: MedleyRouter = new OriginalRouter()
|
|
110
|
+
private middlewares: Function[] = []
|
|
111
|
+
private onErrorHandlers: OnError[] = []
|
|
112
|
+
private routes: InternalRoute[] = []
|
|
113
|
+
private defaultState: Record<any, any> = {}
|
|
114
|
+
private topLevelApp?: AnySpiceflow
|
|
115
|
+
|
|
116
|
+
/** @internal */
|
|
117
|
+
prefix?: string
|
|
118
|
+
|
|
119
|
+
/** @internal */
|
|
120
|
+
childrenApps: AnySpiceflow[] = []
|
|
121
|
+
|
|
122
|
+
/** @internal */
|
|
123
|
+
getAllRoutes() {
|
|
124
|
+
let root = this.topLevelApp || this
|
|
125
|
+
const allApps = bfs(root) || []
|
|
126
|
+
const allRoutes = allApps.flatMap((x) => {
|
|
127
|
+
const prefix = this.getAppAndParents(x)
|
|
128
|
+
.map((x) => x.prefix)
|
|
129
|
+
.join('')
|
|
130
|
+
|
|
131
|
+
return x.routes.map((x) => ({ ...x, path: prefix + x.path }))
|
|
132
|
+
})
|
|
133
|
+
return allRoutes
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
private add({
|
|
137
|
+
method,
|
|
138
|
+
path,
|
|
139
|
+
hooks,
|
|
140
|
+
handler,
|
|
141
|
+
...rest
|
|
142
|
+
}: Partial<InternalRoute>) {
|
|
143
|
+
let bodySchema: TypeSchema = hooks?.body
|
|
144
|
+
let validateBody = getValidateFunction(bodySchema)
|
|
145
|
+
let validateQuery = getValidateFunction(hooks?.query)
|
|
146
|
+
let validateParams = getValidateFunction(hooks?.params)
|
|
147
|
+
|
|
148
|
+
const store = this.router.register(path)
|
|
149
|
+
let route: InternalRoute = {
|
|
150
|
+
...rest,
|
|
151
|
+
method: (method || '') as any,
|
|
152
|
+
path: path || '',
|
|
153
|
+
handler: handler!,
|
|
154
|
+
hooks,
|
|
155
|
+
validateBody,
|
|
156
|
+
validateParams,
|
|
157
|
+
validateQuery,
|
|
158
|
+
}
|
|
159
|
+
this.routes.push(route)
|
|
160
|
+
store[method!] = route
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private match(method: string, path: string) {
|
|
164
|
+
let root = this
|
|
165
|
+
let foundApp: AnySpiceflow | undefined
|
|
166
|
+
const result = bfsFind(this, (app) => {
|
|
167
|
+
app.topLevelApp = root
|
|
168
|
+
let prefix = this.getAppAndParents(app)
|
|
169
|
+
.map((x) => x.prefix)
|
|
170
|
+
.join('')
|
|
171
|
+
if (prefix && !path.startsWith(prefix)) {
|
|
172
|
+
return
|
|
173
|
+
}
|
|
174
|
+
let pathWithoutPrefix = path
|
|
175
|
+
if (prefix) {
|
|
176
|
+
pathWithoutPrefix = path.replace(prefix, '')
|
|
177
|
+
}
|
|
178
|
+
const medleyRoute = app.router.find(pathWithoutPrefix)
|
|
179
|
+
if (!medleyRoute) {
|
|
180
|
+
foundApp = app
|
|
181
|
+
return
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
let internalRoute: InternalRoute = medleyRoute.store[method]
|
|
185
|
+
|
|
186
|
+
if (internalRoute) {
|
|
187
|
+
const params = medleyRoute.params || {}
|
|
188
|
+
|
|
189
|
+
const res = {
|
|
190
|
+
app,
|
|
191
|
+
internalRoute: internalRoute,
|
|
192
|
+
params,
|
|
193
|
+
}
|
|
194
|
+
return res
|
|
195
|
+
}
|
|
196
|
+
if (method === 'HEAD') {
|
|
197
|
+
let internalRouteGet: InternalRoute = medleyRoute.store['GET']
|
|
198
|
+
if (!internalRouteGet?.handler) {
|
|
199
|
+
return
|
|
200
|
+
}
|
|
201
|
+
return {
|
|
202
|
+
app,
|
|
203
|
+
internalRoute: {
|
|
204
|
+
hooks: {},
|
|
205
|
+
handler: async (c) => {
|
|
206
|
+
const response = await internalRouteGet.handler(c)
|
|
207
|
+
if (response instanceof Response) {
|
|
208
|
+
return new Response('', {
|
|
209
|
+
status: response.status,
|
|
210
|
+
statusText: response.statusText,
|
|
211
|
+
headers: response.headers,
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
return new Response(null, { status: 200 })
|
|
215
|
+
},
|
|
216
|
+
method,
|
|
217
|
+
path,
|
|
218
|
+
} as InternalRoute,
|
|
219
|
+
params: medleyRoute.params,
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
return (
|
|
225
|
+
result || {
|
|
226
|
+
app: foundApp || root,
|
|
227
|
+
internalRoute: {
|
|
228
|
+
hooks: {},
|
|
229
|
+
handler: notFoundHandler,
|
|
230
|
+
method,
|
|
231
|
+
path,
|
|
232
|
+
} as InternalRoute,
|
|
233
|
+
params: {},
|
|
234
|
+
}
|
|
235
|
+
)
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
state<const Name extends string | number | symbol, Value>(
|
|
239
|
+
name: Name,
|
|
240
|
+
value: Value,
|
|
241
|
+
): Spiceflow<
|
|
242
|
+
BasePath,
|
|
243
|
+
Scoped,
|
|
244
|
+
{
|
|
245
|
+
state: Reconcile<
|
|
246
|
+
Singleton['state'],
|
|
247
|
+
{
|
|
248
|
+
[name in Name]: Value
|
|
249
|
+
}
|
|
250
|
+
>
|
|
251
|
+
},
|
|
252
|
+
Definitions,
|
|
253
|
+
Metadata,
|
|
254
|
+
Routes
|
|
255
|
+
> {
|
|
256
|
+
this.defaultState[name] = value
|
|
257
|
+
return this as any
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Create a new Router
|
|
262
|
+
* @param options {@link RouterOptions} {@link Platform}
|
|
263
|
+
*/
|
|
264
|
+
constructor(
|
|
265
|
+
options: {
|
|
266
|
+
name?: string
|
|
267
|
+
scoped?: Scoped
|
|
268
|
+
|
|
269
|
+
basePath?: BasePath
|
|
270
|
+
} = {},
|
|
271
|
+
) {
|
|
272
|
+
this.scoped = options.scoped
|
|
273
|
+
|
|
274
|
+
this.prefix = options.basePath
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
_routes: Routes = {} as any
|
|
278
|
+
|
|
279
|
+
_types = {
|
|
280
|
+
Prefix: '' as BasePath,
|
|
281
|
+
Scoped: false as Scoped,
|
|
282
|
+
Singleton: {} as Singleton,
|
|
283
|
+
Definitions: {} as Definitions,
|
|
284
|
+
Metadata: {} as Metadata,
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
post<
|
|
288
|
+
const Path extends string,
|
|
289
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
290
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
291
|
+
const Handle extends InlineHandler<
|
|
292
|
+
Schema,
|
|
293
|
+
Singleton,
|
|
294
|
+
JoinPath<BasePath, Path>
|
|
295
|
+
>,
|
|
296
|
+
>(
|
|
297
|
+
path: Path,
|
|
298
|
+
handler: Handle,
|
|
299
|
+
hook?: LocalHook<
|
|
300
|
+
LocalSchema,
|
|
301
|
+
Schema,
|
|
302
|
+
Singleton,
|
|
303
|
+
Definitions['error'],
|
|
304
|
+
Metadata['macro'],
|
|
305
|
+
JoinPath<BasePath, Path>
|
|
306
|
+
>,
|
|
307
|
+
): Spiceflow<
|
|
308
|
+
BasePath,
|
|
309
|
+
Scoped,
|
|
310
|
+
Singleton,
|
|
311
|
+
Definitions,
|
|
312
|
+
Metadata,
|
|
313
|
+
Routes &
|
|
314
|
+
CreateEden<
|
|
315
|
+
JoinPath<BasePath, Path>,
|
|
316
|
+
{
|
|
317
|
+
post: {
|
|
318
|
+
body: Schema['body']
|
|
319
|
+
params: undefined extends Schema['params']
|
|
320
|
+
? ResolvePath<Path>
|
|
321
|
+
: Schema['params']
|
|
322
|
+
query: Schema['query']
|
|
323
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
>
|
|
327
|
+
> {
|
|
328
|
+
this.add({ method: 'POST', path, handler: handler, hooks: hook })
|
|
329
|
+
|
|
330
|
+
return this as any
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
get<
|
|
334
|
+
const Path extends string,
|
|
335
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
336
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
337
|
+
const Macro extends Metadata['macro'],
|
|
338
|
+
const Handle extends InlineHandler<
|
|
339
|
+
Schema,
|
|
340
|
+
Singleton,
|
|
341
|
+
JoinPath<BasePath, Path>
|
|
342
|
+
>,
|
|
343
|
+
>(
|
|
344
|
+
path: Path,
|
|
345
|
+
handler: Handle,
|
|
346
|
+
hook?: LocalHook<
|
|
347
|
+
LocalSchema,
|
|
348
|
+
Schema,
|
|
349
|
+
Singleton,
|
|
350
|
+
Definitions['error'],
|
|
351
|
+
Macro,
|
|
352
|
+
JoinPath<BasePath, Path>
|
|
353
|
+
>,
|
|
354
|
+
): Spiceflow<
|
|
355
|
+
BasePath,
|
|
356
|
+
Scoped,
|
|
357
|
+
Singleton,
|
|
358
|
+
Definitions,
|
|
359
|
+
Metadata,
|
|
360
|
+
Routes &
|
|
361
|
+
CreateEden<
|
|
362
|
+
JoinPath<BasePath, Path>,
|
|
363
|
+
{
|
|
364
|
+
get: {
|
|
365
|
+
body: Schema['body']
|
|
366
|
+
params: undefined extends Schema['params']
|
|
367
|
+
? ResolvePath<Path>
|
|
368
|
+
: Schema['params']
|
|
369
|
+
query: Schema['query']
|
|
370
|
+
|
|
371
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
>
|
|
375
|
+
> {
|
|
376
|
+
this.add({ method: 'GET', path, handler: handler, hooks: hook })
|
|
377
|
+
return this as any
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
put<
|
|
381
|
+
const Path extends string,
|
|
382
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
383
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
384
|
+
const Handle extends InlineHandler<
|
|
385
|
+
Schema,
|
|
386
|
+
Singleton,
|
|
387
|
+
JoinPath<BasePath, Path>
|
|
388
|
+
>,
|
|
389
|
+
>(
|
|
390
|
+
path: Path,
|
|
391
|
+
handler: Handle,
|
|
392
|
+
hook?: LocalHook<
|
|
393
|
+
LocalSchema,
|
|
394
|
+
Schema,
|
|
395
|
+
Singleton,
|
|
396
|
+
Definitions['error'],
|
|
397
|
+
Metadata['macro'],
|
|
398
|
+
JoinPath<BasePath, Path>
|
|
399
|
+
>,
|
|
400
|
+
): Spiceflow<
|
|
401
|
+
BasePath,
|
|
402
|
+
Scoped,
|
|
403
|
+
Singleton,
|
|
404
|
+
Definitions,
|
|
405
|
+
Metadata,
|
|
406
|
+
Routes &
|
|
407
|
+
CreateEden<
|
|
408
|
+
JoinPath<BasePath, Path>,
|
|
409
|
+
{
|
|
410
|
+
put: {
|
|
411
|
+
body: Schema['body']
|
|
412
|
+
params: undefined extends Schema['params']
|
|
413
|
+
? ResolvePath<Path>
|
|
414
|
+
: Schema['params']
|
|
415
|
+
query: Schema['query']
|
|
416
|
+
|
|
417
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
>
|
|
421
|
+
> {
|
|
422
|
+
this.add({ method: 'PUT', path, handler: handler, hooks: hook })
|
|
423
|
+
|
|
424
|
+
return this as any
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
patch<
|
|
428
|
+
const Path extends string,
|
|
429
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
430
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
431
|
+
const Handle extends InlineHandler<
|
|
432
|
+
Schema,
|
|
433
|
+
Singleton,
|
|
434
|
+
JoinPath<BasePath, Path>
|
|
435
|
+
>,
|
|
436
|
+
>(
|
|
437
|
+
path: Path,
|
|
438
|
+
handler: Handle,
|
|
439
|
+
hook?: LocalHook<
|
|
440
|
+
LocalSchema,
|
|
441
|
+
Schema,
|
|
442
|
+
Singleton,
|
|
443
|
+
Definitions['error'],
|
|
444
|
+
Metadata['macro'],
|
|
445
|
+
JoinPath<BasePath, Path>
|
|
446
|
+
>,
|
|
447
|
+
): Spiceflow<
|
|
448
|
+
BasePath,
|
|
449
|
+
Scoped,
|
|
450
|
+
Singleton,
|
|
451
|
+
Definitions,
|
|
452
|
+
Metadata,
|
|
453
|
+
Routes &
|
|
454
|
+
CreateEden<
|
|
455
|
+
JoinPath<BasePath, Path>,
|
|
456
|
+
{
|
|
457
|
+
patch: {
|
|
458
|
+
body: Schema['body']
|
|
459
|
+
params: undefined extends Schema['params']
|
|
460
|
+
? ResolvePath<Path>
|
|
461
|
+
: Schema['params']
|
|
462
|
+
query: Schema['query']
|
|
463
|
+
|
|
464
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
>
|
|
468
|
+
> {
|
|
469
|
+
this.add({ method: 'PATCH', path, handler: handler, hooks: hook })
|
|
470
|
+
|
|
471
|
+
return this as any
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
delete<
|
|
475
|
+
const Path extends string,
|
|
476
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
477
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
478
|
+
const Handle extends InlineHandler<
|
|
479
|
+
Schema,
|
|
480
|
+
Singleton,
|
|
481
|
+
JoinPath<BasePath, Path>
|
|
482
|
+
>,
|
|
483
|
+
>(
|
|
484
|
+
path: Path,
|
|
485
|
+
handler: Handle,
|
|
486
|
+
hook?: LocalHook<
|
|
487
|
+
LocalSchema,
|
|
488
|
+
Schema,
|
|
489
|
+
Singleton,
|
|
490
|
+
Definitions['error'],
|
|
491
|
+
Metadata['macro'],
|
|
492
|
+
JoinPath<BasePath, Path>
|
|
493
|
+
>,
|
|
494
|
+
): Spiceflow<
|
|
495
|
+
BasePath,
|
|
496
|
+
Scoped,
|
|
497
|
+
Singleton,
|
|
498
|
+
Definitions,
|
|
499
|
+
Metadata,
|
|
500
|
+
Routes &
|
|
501
|
+
CreateEden<
|
|
502
|
+
JoinPath<BasePath, Path>,
|
|
503
|
+
{
|
|
504
|
+
delete: {
|
|
505
|
+
body: Schema['body']
|
|
506
|
+
params: undefined extends Schema['params']
|
|
507
|
+
? ResolvePath<Path>
|
|
508
|
+
: Schema['params']
|
|
509
|
+
query: Schema['query']
|
|
510
|
+
|
|
511
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
>
|
|
515
|
+
> {
|
|
516
|
+
this.add({ method: 'DELETE', path, handler: handler, hooks: hook })
|
|
517
|
+
|
|
518
|
+
return this as any
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
options<
|
|
522
|
+
const Path extends string,
|
|
523
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
524
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
525
|
+
const Handle extends InlineHandler<
|
|
526
|
+
Schema,
|
|
527
|
+
Singleton,
|
|
528
|
+
JoinPath<BasePath, Path>
|
|
529
|
+
>,
|
|
530
|
+
>(
|
|
531
|
+
path: Path,
|
|
532
|
+
handler: Handle,
|
|
533
|
+
hook?: LocalHook<
|
|
534
|
+
LocalSchema,
|
|
535
|
+
Schema,
|
|
536
|
+
Singleton,
|
|
537
|
+
Definitions['error'],
|
|
538
|
+
Metadata['macro'],
|
|
539
|
+
JoinPath<BasePath, Path>
|
|
540
|
+
>,
|
|
541
|
+
): Spiceflow<
|
|
542
|
+
BasePath,
|
|
543
|
+
Scoped,
|
|
544
|
+
Singleton,
|
|
545
|
+
Definitions,
|
|
546
|
+
Metadata,
|
|
547
|
+
Routes &
|
|
548
|
+
CreateEden<
|
|
549
|
+
JoinPath<BasePath, Path>,
|
|
550
|
+
{
|
|
551
|
+
options: {
|
|
552
|
+
body: Schema['body']
|
|
553
|
+
params: undefined extends Schema['params']
|
|
554
|
+
? ResolvePath<Path>
|
|
555
|
+
: Schema['params']
|
|
556
|
+
query: Schema['query']
|
|
557
|
+
|
|
558
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
>
|
|
562
|
+
> {
|
|
563
|
+
this.add({ method: 'OPTIONS', path, handler: handler, hooks: hook })
|
|
564
|
+
|
|
565
|
+
return this as any
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
all<
|
|
569
|
+
const Path extends string,
|
|
570
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
571
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
572
|
+
const Handle extends InlineHandler<
|
|
573
|
+
Schema,
|
|
574
|
+
Singleton,
|
|
575
|
+
JoinPath<BasePath, Path>
|
|
576
|
+
>,
|
|
577
|
+
>(
|
|
578
|
+
path: Path,
|
|
579
|
+
handler: Handle,
|
|
580
|
+
hook?: LocalHook<
|
|
581
|
+
LocalSchema,
|
|
582
|
+
Schema,
|
|
583
|
+
Singleton,
|
|
584
|
+
Definitions['error'],
|
|
585
|
+
Metadata['macro'],
|
|
586
|
+
JoinPath<BasePath, Path>
|
|
587
|
+
>,
|
|
588
|
+
): Spiceflow<
|
|
589
|
+
BasePath,
|
|
590
|
+
Scoped,
|
|
591
|
+
Singleton,
|
|
592
|
+
Definitions,
|
|
593
|
+
Metadata,
|
|
594
|
+
Routes &
|
|
595
|
+
CreateEden<
|
|
596
|
+
JoinPath<BasePath, Path>,
|
|
597
|
+
{
|
|
598
|
+
[method in string]: {
|
|
599
|
+
body: Schema['body']
|
|
600
|
+
params: undefined extends Schema['params']
|
|
601
|
+
? ResolvePath<Path>
|
|
602
|
+
: Schema['params']
|
|
603
|
+
query: Schema['query']
|
|
604
|
+
|
|
605
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
>
|
|
609
|
+
> {
|
|
610
|
+
for (const method of METHODS) {
|
|
611
|
+
this.add({ method, path, handler: handler, hooks: hook })
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
return this as any
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
head<
|
|
618
|
+
const Path extends string,
|
|
619
|
+
const LocalSchema extends InputSchema<keyof Definitions['type'] & string>,
|
|
620
|
+
const Schema extends UnwrapRoute<LocalSchema, Definitions['type']>,
|
|
621
|
+
const Handle extends InlineHandler<
|
|
622
|
+
Schema,
|
|
623
|
+
Singleton,
|
|
624
|
+
JoinPath<BasePath, Path>
|
|
625
|
+
>,
|
|
626
|
+
>(
|
|
627
|
+
path: Path,
|
|
628
|
+
handler: Handle,
|
|
629
|
+
hook?: LocalHook<
|
|
630
|
+
LocalSchema,
|
|
631
|
+
Schema,
|
|
632
|
+
Singleton,
|
|
633
|
+
Definitions['error'],
|
|
634
|
+
Metadata['macro'],
|
|
635
|
+
JoinPath<BasePath, Path>
|
|
636
|
+
>,
|
|
637
|
+
): Spiceflow<
|
|
638
|
+
BasePath,
|
|
639
|
+
Scoped,
|
|
640
|
+
Singleton,
|
|
641
|
+
Definitions,
|
|
642
|
+
Metadata,
|
|
643
|
+
Routes &
|
|
644
|
+
CreateEden<
|
|
645
|
+
JoinPath<BasePath, Path>,
|
|
646
|
+
{
|
|
647
|
+
head: {
|
|
648
|
+
body: Schema['body']
|
|
649
|
+
params: undefined extends Schema['params']
|
|
650
|
+
? ResolvePath<Path>
|
|
651
|
+
: Schema['params']
|
|
652
|
+
query: Schema['query']
|
|
653
|
+
|
|
654
|
+
response: ComposeSpiceflowResponse<Schema['response'], Handle>
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
>
|
|
658
|
+
> {
|
|
659
|
+
this.add({ method: 'HEAD', path, handler: handler, hooks: hook })
|
|
660
|
+
|
|
661
|
+
return this as any
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
private scoped?: Scoped = true as Scoped
|
|
665
|
+
|
|
666
|
+
use<const NewSpiceflow extends AnySpiceflow>(
|
|
667
|
+
instance: NewSpiceflow,
|
|
668
|
+
): IsAny<NewSpiceflow> extends true
|
|
669
|
+
? this
|
|
670
|
+
: Spiceflow<
|
|
671
|
+
BasePath,
|
|
672
|
+
Scoped,
|
|
673
|
+
Singleton,
|
|
674
|
+
Definitions,
|
|
675
|
+
Metadata,
|
|
676
|
+
BasePath extends ``
|
|
677
|
+
? Routes & NewSpiceflow['_routes']
|
|
678
|
+
: Routes & CreateEden<BasePath, NewSpiceflow['_routes']>
|
|
679
|
+
>
|
|
680
|
+
use<const Schema extends RouteSchema>(
|
|
681
|
+
handler: MiddlewareHandler<
|
|
682
|
+
Schema,
|
|
683
|
+
{
|
|
684
|
+
state: Singleton['state']
|
|
685
|
+
}
|
|
686
|
+
>,
|
|
687
|
+
): this
|
|
688
|
+
|
|
689
|
+
use(appOrHandler) {
|
|
690
|
+
if (appOrHandler instanceof Spiceflow) {
|
|
691
|
+
this.childrenApps.push(appOrHandler)
|
|
692
|
+
} else if (typeof appOrHandler === 'function') {
|
|
693
|
+
this.middlewares ??= []
|
|
694
|
+
this.middlewares.push(appOrHandler)
|
|
695
|
+
}
|
|
696
|
+
return this
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
onError<const Schema extends RouteSchema>(
|
|
700
|
+
handler: MaybeArray<ErrorHandler<Definitions['error'], Schema, Singleton>>,
|
|
701
|
+
): this {
|
|
702
|
+
this.onErrorHandlers ??= []
|
|
703
|
+
this.onErrorHandlers.push(handler as any)
|
|
704
|
+
|
|
705
|
+
return this
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
async handle(request: Request): Promise<Response> {
|
|
709
|
+
let u = new URL(request.url, 'http://localhost')
|
|
710
|
+
let path = u.pathname + u.search
|
|
711
|
+
const defaultContext = {
|
|
712
|
+
redirect,
|
|
713
|
+
error: null,
|
|
714
|
+
path,
|
|
715
|
+
}
|
|
716
|
+
const root = this.topLevelApp || this
|
|
717
|
+
let onErrorHandlers: OnError[] = []
|
|
718
|
+
try {
|
|
719
|
+
// Get all middleware and method specific routes in order
|
|
720
|
+
|
|
721
|
+
const route = this.match(request.method, path)
|
|
722
|
+
|
|
723
|
+
const appsInScope = this.getAppsInScope(route.app)
|
|
724
|
+
onErrorHandlers = appsInScope.flatMap((x) => x.onErrorHandlers)
|
|
725
|
+
let {
|
|
726
|
+
params: _params,
|
|
727
|
+
app: { defaultState },
|
|
728
|
+
} = route
|
|
729
|
+
const middlewares = appsInScope.flatMap((x) => x.middlewares)
|
|
730
|
+
// console.log({ onReqHandlers })
|
|
731
|
+
let state = structuredClone(defaultState)
|
|
732
|
+
|
|
733
|
+
let content = route?.internalRoute?.hooks?.content
|
|
734
|
+
|
|
735
|
+
if (route.internalRoute?.validateBody) {
|
|
736
|
+
// TODO don't clone the request
|
|
737
|
+
let typedRequest =
|
|
738
|
+
request instanceof SpiceflowRequest
|
|
739
|
+
? request
|
|
740
|
+
: new SpiceflowRequest(u, request)
|
|
741
|
+
typedRequest.validateBody = route.internalRoute?.validateBody
|
|
742
|
+
request = typedRequest
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
let index = 0
|
|
746
|
+
let context = {
|
|
747
|
+
...defaultContext,
|
|
748
|
+
request,
|
|
749
|
+
state,
|
|
750
|
+
path,
|
|
751
|
+
query: parseQuery.parse((u.search || '').slice(1)),
|
|
752
|
+
params: _params,
|
|
753
|
+
redirect,
|
|
754
|
+
} satisfies MiddlewareContext<any>
|
|
755
|
+
let handlerResponse: Response | undefined
|
|
756
|
+
const next = async () => {
|
|
757
|
+
if (index < middlewares.length) {
|
|
758
|
+
const middleware = middlewares[index]
|
|
759
|
+
index++
|
|
760
|
+
|
|
761
|
+
const result = await middleware(context, next)
|
|
762
|
+
if (result instanceof Response) {
|
|
763
|
+
handlerResponse = result
|
|
764
|
+
}
|
|
765
|
+
if (!result && index < middlewares.length) {
|
|
766
|
+
return await next()
|
|
767
|
+
} else if (result) {
|
|
768
|
+
return await turnHandlerResultIntoResponse(result)
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (handlerResponse) {
|
|
772
|
+
return handlerResponse
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
context.query = runValidation(
|
|
776
|
+
context.query,
|
|
777
|
+
route.internalRoute?.validateQuery,
|
|
778
|
+
)
|
|
779
|
+
context.params = runValidation(
|
|
780
|
+
context.params,
|
|
781
|
+
route.internalRoute?.validateParams,
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
const res = route.internalRoute?.handler(context)
|
|
785
|
+
if (isAsyncIterable(res)) {
|
|
786
|
+
handlerResponse = await this.handleStream({
|
|
787
|
+
generator: res,
|
|
788
|
+
request,
|
|
789
|
+
onErrorHandlers,
|
|
790
|
+
})
|
|
791
|
+
return handlerResponse
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
handlerResponse = await turnHandlerResultIntoResponse(res)
|
|
795
|
+
return handlerResponse
|
|
796
|
+
}
|
|
797
|
+
const response = await next()
|
|
798
|
+
|
|
799
|
+
return response
|
|
800
|
+
} catch (err: any) {
|
|
801
|
+
if (err instanceof Response) return err
|
|
802
|
+
let res = await this.runErrorHandlers({
|
|
803
|
+
onErrorHandlers,
|
|
804
|
+
error: err,
|
|
805
|
+
request,
|
|
806
|
+
})
|
|
807
|
+
if (res instanceof Response) return res
|
|
808
|
+
|
|
809
|
+
let status = err?.status ?? 500
|
|
810
|
+
res ||= new Response(err?.message || 'Internal Server Error', {
|
|
811
|
+
status,
|
|
812
|
+
})
|
|
813
|
+
return res
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
|
|
817
|
+
private async runErrorHandlers({
|
|
818
|
+
onErrorHandlers = [] as OnError[],
|
|
819
|
+
error: err,
|
|
820
|
+
request,
|
|
821
|
+
}) {
|
|
822
|
+
if (onErrorHandlers.length === 0) {
|
|
823
|
+
console.error(`Spiceflow unhandled error:`, err)
|
|
824
|
+
} else {
|
|
825
|
+
for (const errHandler of onErrorHandlers) {
|
|
826
|
+
const res = errHandler({ error: err, request })
|
|
827
|
+
if (res instanceof Response) {
|
|
828
|
+
return res
|
|
829
|
+
}
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
private getAppAndParents(currentApp?: AnySpiceflow) {
|
|
835
|
+
let root = this.topLevelApp || this
|
|
836
|
+
|
|
837
|
+
if (!root.childrenApps.length) {
|
|
838
|
+
return [root]
|
|
839
|
+
}
|
|
840
|
+
const parents: AnySpiceflow[] = []
|
|
841
|
+
let current = currentApp
|
|
842
|
+
|
|
843
|
+
const parentMap = new Map<number, AnySpiceflow>()
|
|
844
|
+
bfsFind(root, (node) => {
|
|
845
|
+
for (const child of node.childrenApps) {
|
|
846
|
+
parentMap.set(child.id, node)
|
|
847
|
+
}
|
|
848
|
+
})
|
|
849
|
+
|
|
850
|
+
// Traverse the parent map to get the parents
|
|
851
|
+
while (current) {
|
|
852
|
+
parents.unshift(current)
|
|
853
|
+
current = parentMap.get(current.id)
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
return parents.filter((x) => x !== undefined)
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
private getAppsInScope(currentApp?: AnySpiceflow) {
|
|
860
|
+
let root = this.topLevelApp || this
|
|
861
|
+
if (!root.childrenApps.length) {
|
|
862
|
+
return [root]
|
|
863
|
+
}
|
|
864
|
+
const withParents = this.getAppAndParents(currentApp)
|
|
865
|
+
|
|
866
|
+
const wantedOrder = bfs(root)
|
|
867
|
+
const scopeFalseApps = wantedOrder.filter((x) => x.scoped === false)
|
|
868
|
+
let appsInScope = [] as AnySpiceflow[]
|
|
869
|
+
for (const app of wantedOrder) {
|
|
870
|
+
if (scopeFalseApps.includes(app)) {
|
|
871
|
+
appsInScope.push(app)
|
|
872
|
+
continue
|
|
873
|
+
}
|
|
874
|
+
if (withParents.includes(app)) {
|
|
875
|
+
appsInScope.push(app)
|
|
876
|
+
continue
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
return appsInScope
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
async listen(port: number, hostname: string = '127.0.0.1') {
|
|
883
|
+
if (typeof Bun !== 'undefined') {
|
|
884
|
+
const server = Bun.serve({
|
|
885
|
+
port,
|
|
886
|
+
development: !isProduction,
|
|
887
|
+
hostname,
|
|
888
|
+
reusePort: true,
|
|
889
|
+
error(error) {
|
|
890
|
+
console.error(error)
|
|
891
|
+
return new Response('Internal Server Error', {
|
|
892
|
+
status: 500,
|
|
893
|
+
})
|
|
894
|
+
},
|
|
895
|
+
|
|
896
|
+
fetch: async (request) => {
|
|
897
|
+
const res = await this.handle(request)
|
|
898
|
+
return res
|
|
899
|
+
},
|
|
900
|
+
})
|
|
901
|
+
process.on('beforeExit', () => {
|
|
902
|
+
server.stop()
|
|
903
|
+
})
|
|
904
|
+
console.log(`Listening on http://localhost:${port}`)
|
|
905
|
+
return server
|
|
906
|
+
}
|
|
907
|
+
return this.listenNode(port, hostname)
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
async listenNode(port: number, hostname: string = '0.0.0.0') {
|
|
911
|
+
const { Readable } = await import('stream')
|
|
912
|
+
const { createServer } = await import('http')
|
|
913
|
+
|
|
914
|
+
const server = createServer(async (req, res) => {
|
|
915
|
+
const abortController = new AbortController()
|
|
916
|
+
const { signal } = abortController
|
|
917
|
+
|
|
918
|
+
req.on('error', (err) => {
|
|
919
|
+
abortController.abort()
|
|
920
|
+
})
|
|
921
|
+
req.on('aborted', (err) => {
|
|
922
|
+
abortController.abort()
|
|
923
|
+
})
|
|
924
|
+
// this is how you see when a request is aborted in Node.js, laughable
|
|
925
|
+
res.on('close', function () {
|
|
926
|
+
let aborted = !res.writableFinished
|
|
927
|
+
if (aborted) {
|
|
928
|
+
abortController.abort()
|
|
929
|
+
}
|
|
930
|
+
})
|
|
931
|
+
|
|
932
|
+
const url = new URL(
|
|
933
|
+
req.url || '',
|
|
934
|
+
`http://${req.headers.host || hostname || 'localhost'}`,
|
|
935
|
+
)
|
|
936
|
+
const typedRequest = new SpiceflowRequest(url.toString(), {
|
|
937
|
+
method: req.method,
|
|
938
|
+
headers: req.headers as HeadersInit,
|
|
939
|
+
body:
|
|
940
|
+
req.method !== 'GET' && req.method !== 'HEAD'
|
|
941
|
+
? (Readable.toWeb(req) as any)
|
|
942
|
+
: null,
|
|
943
|
+
signal,
|
|
944
|
+
// @ts-ignore
|
|
945
|
+
duplex: 'half',
|
|
946
|
+
// keepalive: true,
|
|
947
|
+
})
|
|
948
|
+
|
|
949
|
+
try {
|
|
950
|
+
const response = await this.handle(typedRequest)
|
|
951
|
+
|
|
952
|
+
res.statusCode = response.status
|
|
953
|
+
for (const [key, value] of response.headers) {
|
|
954
|
+
res.setHeader(key, value)
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
if (response.body) {
|
|
958
|
+
const reader = response.body.getReader()
|
|
959
|
+
while (true) {
|
|
960
|
+
const { done, value } = await reader.read()
|
|
961
|
+
if (done) break
|
|
962
|
+
res.write(value)
|
|
963
|
+
}
|
|
964
|
+
}
|
|
965
|
+
res.end()
|
|
966
|
+
} catch (error) {
|
|
967
|
+
console.error('Error handling request:', error)
|
|
968
|
+
res.statusCode = 500
|
|
969
|
+
res.end('Internal Server Error')
|
|
970
|
+
}
|
|
971
|
+
})
|
|
972
|
+
|
|
973
|
+
await new Promise((resolve, reject) => {
|
|
974
|
+
server.listen(port, hostname, () => {
|
|
975
|
+
console.log(`Listening on http://localhost:${port}`)
|
|
976
|
+
resolve(null)
|
|
977
|
+
})
|
|
978
|
+
})
|
|
979
|
+
|
|
980
|
+
return server
|
|
981
|
+
}
|
|
982
|
+
|
|
983
|
+
private async handleStream({
|
|
984
|
+
onErrorHandlers,
|
|
985
|
+
generator,
|
|
986
|
+
request,
|
|
987
|
+
}: {
|
|
988
|
+
generator: Generator | AsyncGenerator
|
|
989
|
+
onErrorHandlers: OnError[]
|
|
990
|
+
request: Request
|
|
991
|
+
}) {
|
|
992
|
+
let init = generator.next()
|
|
993
|
+
if (init instanceof Promise) init = await init
|
|
994
|
+
|
|
995
|
+
if (init?.done) {
|
|
996
|
+
return await turnHandlerResultIntoResponse(init.value)
|
|
997
|
+
}
|
|
998
|
+
// let errorHandlers = this.routerTree.onErrorHandlers
|
|
999
|
+
let self = this
|
|
1000
|
+
return new Response(
|
|
1001
|
+
new ReadableStream({
|
|
1002
|
+
async start(controller) {
|
|
1003
|
+
let end = false
|
|
1004
|
+
|
|
1005
|
+
request?.signal.addEventListener('abort', () => {
|
|
1006
|
+
end = true
|
|
1007
|
+
|
|
1008
|
+
try {
|
|
1009
|
+
controller.close()
|
|
1010
|
+
} catch {
|
|
1011
|
+
// nothing
|
|
1012
|
+
}
|
|
1013
|
+
})
|
|
1014
|
+
|
|
1015
|
+
if (init?.value !== undefined && init?.value !== null)
|
|
1016
|
+
controller.enqueue(
|
|
1017
|
+
Buffer.from(
|
|
1018
|
+
`event: message\ndata: ${JSON.stringify(init.value)}\n\n`,
|
|
1019
|
+
),
|
|
1020
|
+
)
|
|
1021
|
+
|
|
1022
|
+
try {
|
|
1023
|
+
for await (const chunk of generator) {
|
|
1024
|
+
if (end) break
|
|
1025
|
+
if (chunk === undefined || chunk === null) continue
|
|
1026
|
+
|
|
1027
|
+
controller.enqueue(
|
|
1028
|
+
Buffer.from(
|
|
1029
|
+
`event: message\ndata: ${JSON.stringify(chunk)}\n\n`,
|
|
1030
|
+
),
|
|
1031
|
+
)
|
|
1032
|
+
}
|
|
1033
|
+
} catch (error: any) {
|
|
1034
|
+
let res = await self.runErrorHandlers({
|
|
1035
|
+
onErrorHandlers: onErrorHandlers,
|
|
1036
|
+
error,
|
|
1037
|
+
request,
|
|
1038
|
+
})
|
|
1039
|
+
controller.enqueue(
|
|
1040
|
+
Buffer.from(
|
|
1041
|
+
`event: error\ndata: ${JSON.stringify(
|
|
1042
|
+
error.message || error.name || 'Error',
|
|
1043
|
+
)}\n\n`,
|
|
1044
|
+
),
|
|
1045
|
+
)
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
try {
|
|
1049
|
+
controller.close()
|
|
1050
|
+
} catch {
|
|
1051
|
+
// nothing
|
|
1052
|
+
}
|
|
1053
|
+
},
|
|
1054
|
+
}),
|
|
1055
|
+
{
|
|
1056
|
+
// TODO add headers somehow
|
|
1057
|
+
headers: {
|
|
1058
|
+
// Manually set transfer-encoding for direct response, eg. app.handle, eden
|
|
1059
|
+
'transfer-encoding': 'chunked',
|
|
1060
|
+
'content-type': 'text/event-stream; charset=utf-8',
|
|
1061
|
+
// ...set?.headers
|
|
1062
|
+
},
|
|
1063
|
+
},
|
|
1064
|
+
)
|
|
1065
|
+
}
|
|
1108
1066
|
}
|
|
1109
1067
|
|
|
1110
1068
|
// async function getRequestBody({
|
|
@@ -1162,119 +1120,121 @@ export class Spiceflow<
|
|
|
1162
1120
|
// }
|
|
1163
1121
|
|
|
1164
1122
|
const METHODS = [
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1123
|
+
'ALL',
|
|
1124
|
+
'CONNECT',
|
|
1125
|
+
'DELETE',
|
|
1126
|
+
'GET',
|
|
1127
|
+
'HEAD',
|
|
1128
|
+
'OPTIONS',
|
|
1129
|
+
'PATCH',
|
|
1130
|
+
'POST',
|
|
1131
|
+
'PUT',
|
|
1132
|
+
'TRACE',
|
|
1175
1133
|
] as const
|
|
1176
1134
|
|
|
1177
1135
|
/** HTTP method string */
|
|
1178
1136
|
export type Method = (typeof METHODS)[number]
|
|
1179
1137
|
|
|
1180
1138
|
function bfsFind<T>(
|
|
1181
|
-
|
|
1182
|
-
|
|
1139
|
+
tree: AnySpiceflow,
|
|
1140
|
+
onNode: (node: AnySpiceflow) => T | undefined | void,
|
|
1183
1141
|
): T | undefined {
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1142
|
+
const queue = [tree]
|
|
1143
|
+
|
|
1144
|
+
while (queue.length > 0) {
|
|
1145
|
+
const node = queue.shift()!
|
|
1146
|
+
|
|
1147
|
+
const result = onNode(node)
|
|
1148
|
+
if (result) {
|
|
1149
|
+
return result
|
|
1150
|
+
}
|
|
1151
|
+
queue.push(...node.childrenApps)
|
|
1152
|
+
}
|
|
1153
|
+
return
|
|
1196
1154
|
}
|
|
1197
1155
|
export class SpiceflowRequest<T = any> extends Request {
|
|
1198
|
-
|
|
1156
|
+
validateBody?: ValidateFunction
|
|
1199
1157
|
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1158
|
+
async json(): Promise<T> {
|
|
1159
|
+
const body = (await super.json()) as Promise<T>
|
|
1160
|
+
return runValidation(body, this.validateBody)
|
|
1161
|
+
}
|
|
1204
1162
|
}
|
|
1205
1163
|
|
|
1206
1164
|
export function bfs(tree: AnySpiceflow) {
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1165
|
+
const queue = [tree]
|
|
1166
|
+
let nodes: AnySpiceflow[] = []
|
|
1167
|
+
while (queue.length > 0) {
|
|
1168
|
+
const node = queue.shift()!
|
|
1169
|
+
if (node) {
|
|
1170
|
+
nodes.push(node)
|
|
1171
|
+
}
|
|
1172
|
+
// const result = onNode(node)
|
|
1173
|
+
|
|
1174
|
+
if (node?.childrenApps?.length) {
|
|
1175
|
+
queue.push(...node.childrenApps)
|
|
1176
|
+
}
|
|
1177
|
+
}
|
|
1178
|
+
return nodes
|
|
1221
1179
|
}
|
|
1222
1180
|
export async function turnHandlerResultIntoResponse(result: any) {
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1181
|
+
// if (result === undefined) return new Response('', { status: 404 })
|
|
1182
|
+
// if user returns not a response, convert to json
|
|
1183
|
+
if (result instanceof Response) {
|
|
1184
|
+
return result
|
|
1185
|
+
}
|
|
1186
|
+
// if user returns a promise, await it
|
|
1187
|
+
if (result instanceof Promise) {
|
|
1188
|
+
result = await result
|
|
1189
|
+
}
|
|
1190
|
+
// // if user returns a string, convert to json
|
|
1191
|
+
// if (typeof result === 'string') {
|
|
1192
|
+
// result = new Response(result)
|
|
1193
|
+
// }
|
|
1194
|
+
// if user returns an object, convert to json
|
|
1195
|
+
|
|
1196
|
+
return new Response(JSON.stringify(result ?? null, null, 2), {
|
|
1197
|
+
headers: {
|
|
1198
|
+
'content-type': 'application/json',
|
|
1199
|
+
},
|
|
1200
|
+
})
|
|
1243
1201
|
}
|
|
1244
1202
|
|
|
1245
1203
|
export type AnySpiceflow = Spiceflow<any, any, any, any, any, any>
|
|
1246
1204
|
|
|
1247
1205
|
export function isZodSchema(value: unknown): value is ZodType {
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1206
|
+
return (
|
|
1207
|
+
value instanceof z.ZodType ||
|
|
1208
|
+
(typeof value === 'object' &&
|
|
1209
|
+
value !== null &&
|
|
1210
|
+
'parse' in value &&
|
|
1211
|
+
'safeParse' in value &&
|
|
1212
|
+
'optional' in value &&
|
|
1213
|
+
'nullable' in value)
|
|
1214
|
+
)
|
|
1257
1215
|
}
|
|
1258
1216
|
|
|
1259
1217
|
function getValidateFunction(schema: TypeSchema) {
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1218
|
+
if (isZodSchema(schema)) {
|
|
1219
|
+
let jsonSchema = zodToJsonSchema(schema, {
|
|
1220
|
+
removeAdditionalStrategy: 'strict',
|
|
1221
|
+
})
|
|
1222
|
+
return ajv.compile(jsonSchema)
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
if (schema) {
|
|
1226
|
+
return ajv.compile(schema)
|
|
1227
|
+
}
|
|
1268
1228
|
}
|
|
1269
1229
|
|
|
1270
1230
|
function runValidation(value: any, validate?: ValidateFunction) {
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1231
|
+
if (!validate) return value
|
|
1232
|
+
const valid = validate(value)
|
|
1233
|
+
if (!valid) {
|
|
1234
|
+
const error = ajv.errorsText(validate.errors, {
|
|
1235
|
+
separator: '\n',
|
|
1236
|
+
})
|
|
1237
|
+
throw new ValidationError(error)
|
|
1238
|
+
}
|
|
1239
|
+
return value
|
|
1280
1240
|
}
|