spiceflow 1.1.8 → 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.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 +4 -2
- package/dist/spiceflow.js.map +1 -1
- package/dist/spiceflow.test.js.map +1 -1
- package/dist/stream.test.js.map +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/types.test.js +2 -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 +201 -201
- 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 -1161
- package/src/stream.test.ts +310 -310
- package/src/types.test.ts +46 -50
- 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,1051 +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
|
-
try {
|
|
1067
|
-
for await (const chunk of generator) {
|
|
1068
|
-
if (end) break
|
|
1069
|
-
if (chunk === undefined || chunk === null) continue
|
|
1070
|
-
|
|
1071
|
-
controller.enqueue(
|
|
1072
|
-
Buffer.from(
|
|
1073
|
-
`event: message\ndata: ${JSON.stringify(
|
|
1074
|
-
chunk,
|
|
1075
|
-
)}\n\n`,
|
|
1076
|
-
),
|
|
1077
|
-
)
|
|
1078
|
-
}
|
|
1079
|
-
} catch (error: any) {
|
|
1080
|
-
let res = await self.runErrorHandlers({
|
|
1081
|
-
onErrorHandlers: onErrorHandlers,
|
|
1082
|
-
error,
|
|
1083
|
-
request,
|
|
1084
|
-
})
|
|
1085
|
-
controller.enqueue(
|
|
1086
|
-
Buffer.from(
|
|
1087
|
-
`event: error\ndata: ${JSON.stringify(
|
|
1088
|
-
error.message || error.name || 'Error',
|
|
1089
|
-
)}\n\n`,
|
|
1090
|
-
),
|
|
1091
|
-
)
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
try {
|
|
1095
|
-
controller.close()
|
|
1096
|
-
} catch {
|
|
1097
|
-
// nothing
|
|
1098
|
-
}
|
|
1099
|
-
},
|
|
1100
|
-
}),
|
|
1101
|
-
{
|
|
1102
|
-
// TODO add headers somehow
|
|
1103
|
-
headers: {
|
|
1104
|
-
// Manually set transfer-encoding for direct response, eg. app.handle, eden
|
|
1105
|
-
'transfer-encoding': 'chunked',
|
|
1106
|
-
'content-type': 'text/event-stream; charset=utf-8',
|
|
1107
|
-
// ...set?.headers
|
|
1108
|
-
},
|
|
1109
|
-
},
|
|
1110
|
-
)
|
|
1111
|
-
}
|
|
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
|
+
}
|
|
1112
1066
|
}
|
|
1113
1067
|
|
|
1114
1068
|
// async function getRequestBody({
|
|
@@ -1166,119 +1120,121 @@ export class Spiceflow<
|
|
|
1166
1120
|
// }
|
|
1167
1121
|
|
|
1168
1122
|
const METHODS = [
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1123
|
+
'ALL',
|
|
1124
|
+
'CONNECT',
|
|
1125
|
+
'DELETE',
|
|
1126
|
+
'GET',
|
|
1127
|
+
'HEAD',
|
|
1128
|
+
'OPTIONS',
|
|
1129
|
+
'PATCH',
|
|
1130
|
+
'POST',
|
|
1131
|
+
'PUT',
|
|
1132
|
+
'TRACE',
|
|
1179
1133
|
] as const
|
|
1180
1134
|
|
|
1181
1135
|
/** HTTP method string */
|
|
1182
1136
|
export type Method = (typeof METHODS)[number]
|
|
1183
1137
|
|
|
1184
1138
|
function bfsFind<T>(
|
|
1185
|
-
|
|
1186
|
-
|
|
1139
|
+
tree: AnySpiceflow,
|
|
1140
|
+
onNode: (node: AnySpiceflow) => T | undefined | void,
|
|
1187
1141
|
): T | undefined {
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
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
|
|
1200
1154
|
}
|
|
1201
1155
|
export class SpiceflowRequest<T = any> extends Request {
|
|
1202
|
-
|
|
1156
|
+
validateBody?: ValidateFunction
|
|
1203
1157
|
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1158
|
+
async json(): Promise<T> {
|
|
1159
|
+
const body = (await super.json()) as Promise<T>
|
|
1160
|
+
return runValidation(body, this.validateBody)
|
|
1161
|
+
}
|
|
1208
1162
|
}
|
|
1209
1163
|
|
|
1210
1164
|
export function bfs(tree: AnySpiceflow) {
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
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
|
|
1225
1179
|
}
|
|
1226
1180
|
export async function turnHandlerResultIntoResponse(result: any) {
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
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
|
+
})
|
|
1247
1201
|
}
|
|
1248
1202
|
|
|
1249
1203
|
export type AnySpiceflow = Spiceflow<any, any, any, any, any, any>
|
|
1250
1204
|
|
|
1251
1205
|
export function isZodSchema(value: unknown): value is ZodType {
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
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
|
+
)
|
|
1261
1215
|
}
|
|
1262
1216
|
|
|
1263
1217
|
function getValidateFunction(schema: TypeSchema) {
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
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
|
+
}
|
|
1272
1228
|
}
|
|
1273
1229
|
|
|
1274
1230
|
function runValidation(value: any, validate?: ValidateFunction) {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
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
|
|
1284
1240
|
}
|