galbe 0.13.0 → 0.14.0
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 +18 -1
- package/bin/commands/build.ts +10 -4
- package/bin/commands/generate/cli/index.ts +156 -0
- package/bin/commands/generate/cli/targets/cac.ts +535 -0
- package/bin/commands/generate/client.ts +32 -109
- package/bin/commands/generate/code/openapi.parser.ts +428 -133
- package/bin/commands/generate/code/route-merge.ts +248 -0
- package/bin/commands/generate/code.ts +110 -23
- package/bin/commands/generate/index.ts +2 -0
- package/package.json +4 -1
- package/src/cookies.ts +87 -0
- package/src/extras/spec/openapi.serializer.ts +236 -101
- package/src/extras.ts +1 -0
- package/src/index.ts +4 -7
- package/src/parser.ts +104 -63
- package/src/router.ts +34 -20
- package/src/routes.ts +14 -10
- package/src/schema.ts +82 -95
- package/src/server.ts +35 -21
- package/src/types.ts +180 -61
- package/src/util.ts +10 -18
- package/src/validator.ts +62 -32
- package/bin/res/cli.template.js +0 -122
package/src/schema.ts
CHANGED
|
@@ -7,7 +7,24 @@ export interface Options {
|
|
|
7
7
|
title?: string
|
|
8
8
|
description?: string
|
|
9
9
|
default?: any
|
|
10
|
+
example?: any
|
|
11
|
+
/**
|
|
12
|
+
* On a response schema: a map of named examples (OpenAPI content-level
|
|
13
|
+
* `examples`). On any other schema: a single example value.
|
|
14
|
+
*/
|
|
10
15
|
examples?: any
|
|
16
|
+
/**
|
|
17
|
+
* Response-only: declares response headers. Each value is a Galbe schema
|
|
18
|
+
* describing the header's value type.
|
|
19
|
+
*/
|
|
20
|
+
headers?: Record<string, any>
|
|
21
|
+
/** Marks the schema as deprecated. Surfaced by spec generators (e.g. OpenAPI). */
|
|
22
|
+
deprecated?: boolean
|
|
23
|
+
/**
|
|
24
|
+
* Response-only: declares response headers emitted in the OpenAPI `responses` object.
|
|
25
|
+
* Distinct from the request-level `headers` field.
|
|
26
|
+
*/
|
|
27
|
+
responseHeaders?: Record<string, any>
|
|
11
28
|
}
|
|
12
29
|
export interface ByteArrayOptions extends Options {
|
|
13
30
|
minLength?: number
|
|
@@ -17,6 +34,7 @@ export interface StringOptions extends Options {
|
|
|
17
34
|
minLength?: number
|
|
18
35
|
maxLength?: number
|
|
19
36
|
pattern?: RegExp
|
|
37
|
+
format?: string
|
|
20
38
|
}
|
|
21
39
|
export interface NumberOptions extends Options {
|
|
22
40
|
min?: number
|
|
@@ -44,14 +62,13 @@ export interface STSchema extends Options {
|
|
|
44
62
|
| 'urlForm'
|
|
45
63
|
| 'multipartForm'
|
|
46
64
|
| 'any'
|
|
47
|
-
| '
|
|
65
|
+
| 'anyOf'
|
|
66
|
+
| 'oneOf'
|
|
48
67
|
| 'intersection'
|
|
49
68
|
[Optional]?: boolean
|
|
50
69
|
[Stream]?: boolean
|
|
51
70
|
params: unknown[]
|
|
52
71
|
static: unknown
|
|
53
|
-
props?: STProps
|
|
54
|
-
[key: string]: any
|
|
55
72
|
}
|
|
56
73
|
export type STPropsValue =
|
|
57
74
|
| STBoolean
|
|
@@ -128,7 +145,7 @@ export function _Bool(options: Options = {}): STBoolean {
|
|
|
128
145
|
} as unknown as STBoolean
|
|
129
146
|
}
|
|
130
147
|
// String
|
|
131
|
-
export interface STString extends STSchema,
|
|
148
|
+
export interface STString extends STSchema, StringOptions {
|
|
132
149
|
[Kind]: 'string'
|
|
133
150
|
static: string
|
|
134
151
|
}
|
|
@@ -179,7 +196,6 @@ export function _Literal<T extends STLiteralValue>(value: T, options: Options =
|
|
|
179
196
|
export interface STAny extends STSchema, Options {
|
|
180
197
|
[Kind]: 'any'
|
|
181
198
|
static: any
|
|
182
|
-
[key: string]: any
|
|
183
199
|
}
|
|
184
200
|
export function _Any(options: Options = {}): STAny {
|
|
185
201
|
return {
|
|
@@ -196,8 +212,8 @@ export interface STObject<T extends STProps = STProps> extends STSchema {
|
|
|
196
212
|
}
|
|
197
213
|
export interface STJson<T extends STBoolean | STNumber | STString | STObject = any> extends STSchema {
|
|
198
214
|
[Kind]: 'json'
|
|
199
|
-
type: 'boolean' | 'number' | 'string' | 'object' | 'unknown'
|
|
200
215
|
static: Static<T>
|
|
216
|
+
value: T
|
|
201
217
|
}
|
|
202
218
|
type ObjectStatic<T extends STProps, P extends unknown[]> = ObjectStaticProps<T, { [K in keyof T]: Static<T[K], P> }>
|
|
203
219
|
type OptionalPropertyKeys<T extends STProps> = {
|
|
@@ -217,46 +233,18 @@ function _Object<T extends STProps>(properties?: T, options: Options = {}): STOb
|
|
|
217
233
|
? { ...options, [Kind]: 'object', props: clonedProperties, required: requiredKeys }
|
|
218
234
|
: { ...options, [Kind]: 'object', props: clonedProperties }) as unknown as STObject<T>
|
|
219
235
|
}
|
|
220
|
-
function _Json<T extends STBoolean | STNumber | STString | STObject>(value
|
|
221
|
-
|
|
222
|
-
if (
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
236
|
+
function _Json<T extends STBoolean | STNumber | STString | STObject>(value: T, options: Options = {}): STJson<T> {
|
|
237
|
+
const k = value?.[Kind]
|
|
238
|
+
if (k !== 'boolean' && k !== 'number' && k !== 'string' && k !== 'object') {
|
|
239
|
+
throw new Error('Invalid Json type definition')
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
...options,
|
|
243
|
+
[Kind]: 'json',
|
|
244
|
+
value,
|
|
245
|
+
} as unknown as STJson<T>
|
|
226
246
|
}
|
|
227
247
|
|
|
228
|
-
// UrlForm
|
|
229
|
-
// export type STUrlFormValues =
|
|
230
|
-
// | STByteArray
|
|
231
|
-
// | STBoolean
|
|
232
|
-
// | STNumber
|
|
233
|
-
// | STInteger
|
|
234
|
-
// | STString
|
|
235
|
-
// | STLiteral
|
|
236
|
-
// | STObject
|
|
237
|
-
// | STUnion
|
|
238
|
-
// | STAny
|
|
239
|
-
// | STArray
|
|
240
|
-
// export type STUrlFormProps = Record<string, STUrlFormValues>
|
|
241
|
-
// export interface STUrlForm<T extends STUrlFormProps = STUrlFormProps> extends STSchema {
|
|
242
|
-
// [Kind]: 'urlForm'
|
|
243
|
-
// static: T extends undefined ? Record<string, any> : ObjectStatic<T, this['params']>
|
|
244
|
-
// props: T
|
|
245
|
-
// }
|
|
246
|
-
// function _UrlForm<T extends STUrlFormProps>(properties?: T, options: Options = {}): STUrlForm<T> {
|
|
247
|
-
// if (!properties) return { ...options, [Kind]: 'urlForm' } as unknown as STUrlForm<T>
|
|
248
|
-
// const propertyKeys = globalThis.Object.getOwnPropertyNames(properties)
|
|
249
|
-
// const optionalKeys = propertyKeys.filter(key => properties[key]?.[Optional])
|
|
250
|
-
// const requiredKeys = propertyKeys.filter(name => !optionalKeys.includes(name))
|
|
251
|
-
// const clonedProperties = propertyKeys.reduce(
|
|
252
|
-
// (acc, key) => ({ ...acc, [key]: { ...properties[key] } }),
|
|
253
|
-
// {} as STUrlFormProps
|
|
254
|
-
// )
|
|
255
|
-
// return (requiredKeys.length > 0
|
|
256
|
-
// ? { ...options, [Kind]: 'urlForm', props: clonedProperties, required: requiredKeys }
|
|
257
|
-
// : { ...options, [Kind]: 'urlForm', props: clonedProperties }) as unknown as STUrlForm<T>
|
|
258
|
-
// }
|
|
259
|
-
|
|
260
248
|
// MultipartForm
|
|
261
249
|
export type STMultipartFormValues = STSchema
|
|
262
250
|
export interface MultipartFormData<K extends string = string, V extends Static<STMultipartForm> = any> {
|
|
@@ -293,7 +281,7 @@ function _MultipartForm<T extends STProps>(properties?: T, options: Options = {}
|
|
|
293
281
|
|
|
294
282
|
// Array
|
|
295
283
|
type NonEmptyArray<T> = [T, ...T[]]
|
|
296
|
-
export interface STArray<T extends STSchema = STSchema> extends STSchema {
|
|
284
|
+
export interface STArray<T extends STSchema = STSchema> extends STSchema, ArrayOptions {
|
|
297
285
|
[Kind]: 'array'
|
|
298
286
|
static: Static<T>[]
|
|
299
287
|
items: T
|
|
@@ -311,16 +299,19 @@ type UnionStatic<T extends STSchema[], P extends unknown[]> = {
|
|
|
311
299
|
[K in keyof T]: T[K] extends STSchema ? Static<T[K], P> : never
|
|
312
300
|
}[number]
|
|
313
301
|
export interface STUnion<T extends NonEmptyArray<STSchema> = NonEmptyArray<STSchema>> extends STSchema {
|
|
314
|
-
[Kind]: '
|
|
302
|
+
[Kind]: 'anyOf' | 'oneOf'
|
|
315
303
|
static: UnionStatic<T, this['params']>
|
|
316
|
-
|
|
317
|
-
anyOf: T
|
|
304
|
+
members: T
|
|
318
305
|
}
|
|
319
|
-
export function _Union<T extends NonEmptyArray<STSchema>>(
|
|
306
|
+
export function _Union<T extends NonEmptyArray<STSchema>>(
|
|
307
|
+
kind: 'anyOf' | 'oneOf',
|
|
308
|
+
schemas: [...T],
|
|
309
|
+
options: Options
|
|
310
|
+
): STUnion<T> {
|
|
320
311
|
const s = {
|
|
321
312
|
...options,
|
|
322
|
-
[Kind]:
|
|
323
|
-
|
|
313
|
+
[Kind]: kind,
|
|
314
|
+
members: schemas as T,
|
|
324
315
|
optional: () => ({ ...s, [Optional]: true }),
|
|
325
316
|
}
|
|
326
317
|
return s as unknown as STUnion<T>
|
|
@@ -334,15 +325,14 @@ type IntersectionStatic<T extends readonly STSchema[], P extends unknown[]> = T
|
|
|
334
325
|
: never
|
|
335
326
|
: never
|
|
336
327
|
: unknown
|
|
337
|
-
|
|
338
|
-
|
|
328
|
+
// type Intersecs = NonEmptyArray<STObject | STUnion | STIntersection>
|
|
329
|
+
export interface STIntersection<T extends NonEmptyArray<STObject | STUnion | STIntersection<any>>> extends STSchema {
|
|
339
330
|
[Kind]: 'intersection'
|
|
340
331
|
static: IntersectionStatic<T, this['params']>
|
|
341
|
-
props: T[number]['props']
|
|
342
332
|
allOf: T
|
|
343
333
|
}
|
|
344
334
|
|
|
345
|
-
export function _Intersection<T extends NonEmptyArray<STObject | STUnion | STIntersection
|
|
335
|
+
export function _Intersection<T extends NonEmptyArray<STObject | STUnion | STIntersection<any>>>(
|
|
346
336
|
schemas: [...T],
|
|
347
337
|
options: Options
|
|
348
338
|
): STIntersection<T> {
|
|
@@ -350,24 +340,13 @@ export function _Intersection<T extends NonEmptyArray<STObject | STUnion | STInt
|
|
|
350
340
|
...options,
|
|
351
341
|
[Kind]: 'intersection',
|
|
352
342
|
allOf: schemas as T,
|
|
353
|
-
props: schemas.reduce(
|
|
354
|
-
(b, c) => ({
|
|
355
|
-
...b,
|
|
356
|
-
...(c.props || {}),
|
|
357
|
-
...Object.fromEntries(
|
|
358
|
-
Object.entries(b).filter(([_, v]) => (v as STSchema)[Kind] === 'literal' || !(v as STSchema)[Optional])
|
|
359
|
-
// TODO: handle cases where left != right
|
|
360
|
-
),
|
|
361
|
-
}),
|
|
362
|
-
{}
|
|
363
|
-
) as STProps,
|
|
364
343
|
optional: () => ({ ...s, [Optional]: true }),
|
|
365
344
|
}
|
|
366
345
|
return s as unknown as STIntersection<T>
|
|
367
346
|
}
|
|
368
347
|
|
|
369
348
|
// Stream
|
|
370
|
-
type STStreamable = STByteArray | STString | STMultipartForm | STObject | STUnion | STIntersection
|
|
349
|
+
type STStreamable = STByteArray | STString | STMultipartForm | STObject | STUnion | STIntersection<any>
|
|
371
350
|
export function _Stream<T extends STStreamable>(schema: T): STStream<T> {
|
|
372
351
|
return {
|
|
373
352
|
...schema,
|
|
@@ -377,7 +356,7 @@ export function _Stream<T extends STStreamable>(schema: T): STStream<T> {
|
|
|
377
356
|
// Nullable
|
|
378
357
|
type STNullable<T extends STSchema> = STUnion<[T, STNull]>
|
|
379
358
|
// Nullish
|
|
380
|
-
type STNullish<T extends STSchema> = (T | STNull) & { [Kind]: '
|
|
359
|
+
type STNullish<T extends STSchema> = (T | STNull) & { [Kind]: 'anyOf'; members: [T, STNull]; [Optional]: true }
|
|
381
360
|
export class SchemaType {
|
|
382
361
|
/** Creates an Optional Schema Type Wrapper */
|
|
383
362
|
public optional<T extends STSchema>(schema: T): STOptional<T> {
|
|
@@ -385,7 +364,7 @@ export class SchemaType {
|
|
|
385
364
|
}
|
|
386
365
|
/** Creates an Nullable Schema Type Wrapper */
|
|
387
366
|
public nullable<T extends STSchema>(schema: T): STNullable<T> {
|
|
388
|
-
return { ..._Union([schema, _Null()], {}) }
|
|
367
|
+
return { ..._Union('anyOf', [schema, _Null()], {}) }
|
|
389
368
|
}
|
|
390
369
|
/** Creates an Nullish Schema Type Wrapper */
|
|
391
370
|
public nullish<T extends STSchema>(schema: T): STOptional<STNullable<T>> {
|
|
@@ -434,24 +413,28 @@ export class SchemaType {
|
|
|
434
413
|
): STJson<T> {
|
|
435
414
|
return _Json(value, options)
|
|
436
415
|
}
|
|
437
|
-
/** Creates an UrlForm Schema Type */
|
|
438
|
-
// public urlForm<T extends STUrlFormProps>(properties?: T, options: Options = {}): STUrlForm<T> {
|
|
439
|
-
// return _UrlForm(properties, options)
|
|
440
|
-
// }
|
|
441
416
|
/** Creates a MultipartForm Schema Type */
|
|
442
417
|
public multipartForm<T extends STProps>(properties?: T, options: Options = {}): STMultipartForm<T> {
|
|
443
418
|
return _MultipartForm(properties, options)
|
|
444
419
|
}
|
|
445
420
|
/** Creates an Array Schema Type */
|
|
446
|
-
public array<T extends STSchema>(schema?: T, options:
|
|
421
|
+
public array<T extends STSchema>(schema?: T, options: ArrayOptions = {}): STArray<T> {
|
|
447
422
|
return _Array(schema, options)
|
|
448
423
|
}
|
|
449
|
-
/** Creates an
|
|
424
|
+
/** Creates an anyOf Schema Type (alias: `union`) */
|
|
425
|
+
public anyOf<T extends NonEmptyArray<STSchema>>(schemas: [...T], options: Options = {}): STUnion<T> {
|
|
426
|
+
return _Union('anyOf', schemas, options)
|
|
427
|
+
}
|
|
428
|
+
/** Creates a oneOf Schema Type */
|
|
429
|
+
public oneOf<T extends NonEmptyArray<STSchema>>(schemas: [...T], options: Options = {}): STUnion<T> {
|
|
430
|
+
return _Union('oneOf', schemas, options)
|
|
431
|
+
}
|
|
432
|
+
/** @deprecated Use `anyOf` instead */
|
|
450
433
|
public union<T extends NonEmptyArray<STSchema>>(schemas: [...T], options: Options = {}): STUnion<T> {
|
|
451
|
-
return _Union(schemas, options)
|
|
434
|
+
return _Union('anyOf', schemas, options)
|
|
452
435
|
}
|
|
453
436
|
/** Creates an Intersection Schema Type */
|
|
454
|
-
public intersection<T extends NonEmptyArray<STObject | STUnion | STIntersection
|
|
437
|
+
public intersection<T extends NonEmptyArray<STObject | STUnion | STIntersection<any>>>(
|
|
455
438
|
schemas: [...T],
|
|
456
439
|
options: Options = {}
|
|
457
440
|
): STIntersection<T> {
|
|
@@ -465,8 +448,8 @@ export class SchemaType {
|
|
|
465
448
|
T['props'] extends undefined
|
|
466
449
|
? { [k: string]: Static<STPropsValue> }
|
|
467
450
|
: T['props'] extends STProps
|
|
468
|
-
|
|
469
|
-
|
|
451
|
+
? { [K in keyof T['props']]: [K, Static<T['props'][K]>] }[keyof T['props']]
|
|
452
|
+
: never
|
|
470
453
|
>
|
|
471
454
|
params: unknown[]
|
|
472
455
|
}
|
|
@@ -475,24 +458,27 @@ export class SchemaType {
|
|
|
475
458
|
): Omit<STStream<T>, 'static'> & {
|
|
476
459
|
static: AsyncGenerator<
|
|
477
460
|
Entries<{
|
|
478
|
-
[P in KeysOfUnion<T['
|
|
479
|
-
|
|
480
|
-
|
|
461
|
+
[P in KeysOfUnion<MemberProps<T['members'][number]>> as ValueAt<
|
|
462
|
+
MemberProps<T['members'][number]>,
|
|
463
|
+
P
|
|
464
|
+
> extends STSchema
|
|
465
|
+
? P
|
|
466
|
+
: never]: Static<ValueAt<MemberProps<T['members'][number]>, P>>
|
|
481
467
|
}>
|
|
482
468
|
>
|
|
483
469
|
params: unknown[]
|
|
484
470
|
}
|
|
485
|
-
public stream<T extends STIntersection
|
|
471
|
+
public stream<T extends STIntersection<any>>(
|
|
486
472
|
schema: T
|
|
487
473
|
): Omit<STStream<T>, 'static'> & {
|
|
488
474
|
static: AsyncGenerator<
|
|
489
|
-
T['
|
|
475
|
+
MemberProps<T['allOf'][number]> extends undefined
|
|
490
476
|
? never
|
|
491
|
-
: T['
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
477
|
+
: MemberProps<T['allOf'][number]> extends STProps
|
|
478
|
+
? Entries<{
|
|
479
|
+
[K in keyof Static<T>]: Static<T>[K]
|
|
480
|
+
}>
|
|
481
|
+
: never
|
|
496
482
|
>
|
|
497
483
|
params: unknown[]
|
|
498
484
|
}
|
|
@@ -530,6 +516,7 @@ export class SchemaType {
|
|
|
530
516
|
}
|
|
531
517
|
type KeysOfUnion<U> = U extends unknown ? keyof U : never
|
|
532
518
|
type ValueAt<U, K extends PropertyKey> = U extends unknown ? (K extends keyof U ? U[K] : never) : never
|
|
519
|
+
type MemberProps<U> = U extends { props: infer P } ? P : never
|
|
533
520
|
|
|
534
521
|
export const schemaToTypeStr = (schema: STSchema): string => {
|
|
535
522
|
let type = 'unknown'
|
|
@@ -554,13 +541,13 @@ export const schemaToTypeStr = (schema: STSchema): string => {
|
|
|
554
541
|
.map(([k, v]) => `${typeof k === 'string' ? `'${k}'` : k}${v?.[Optional] ? '?' : ''}:${schemaToTypeStr(v)}`)
|
|
555
542
|
.join(';')}}`
|
|
556
543
|
} else if (kind === 'json') {
|
|
557
|
-
type = `Json<${schemaToTypeStr(
|
|
558
|
-
} else if (kind === '
|
|
559
|
-
let
|
|
560
|
-
type =
|
|
544
|
+
type = `Json<${schemaToTypeStr((schema as STJson).value)}>`
|
|
545
|
+
} else if (kind === 'anyOf' || kind === 'oneOf') {
|
|
546
|
+
let members = (schema as STUnion).members
|
|
547
|
+
type = members.map(s => schemaToTypeStr(s)).join('|')
|
|
561
548
|
} else if (kind === 'intersection') {
|
|
562
|
-
let allOf = (schema as STIntersection).allOf
|
|
563
|
-
type = allOf.map(s => schemaToTypeStr(s)).join('&')
|
|
549
|
+
let allOf = (schema as STIntersection<any>).allOf
|
|
550
|
+
type = allOf.map((s: STSchema) => schemaToTypeStr(s)).join('&')
|
|
564
551
|
}
|
|
565
552
|
|
|
566
553
|
// if (schema[Optional]) type = `${type}|undefined`
|
package/src/server.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import type { Context, Method, Route } from './types'
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { InternalServerError, RequestError } from './types'
|
|
4
4
|
import { parseEntry, requestBodyParser, requestPathParser, responseParser } from './parser'
|
|
5
5
|
import { Galbe } from './index'
|
|
6
6
|
import { validateResponse } from './validator'
|
|
7
|
-
|
|
7
|
+
const normalizeContentType = (ct: string | null): string | undefined =>
|
|
8
|
+
ct ? ct.split(';')[0].trim() || undefined : undefined
|
|
9
|
+
import { readCookies, stringifyCookie } from './cookies'
|
|
8
10
|
|
|
9
11
|
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
|
|
10
12
|
|
|
@@ -13,7 +15,7 @@ const EMPTY_BODY_METHODS = ['GET', 'OPTIONS', 'HEAD']
|
|
|
13
15
|
|
|
14
16
|
const handleInternalError = (error: any) => {
|
|
15
17
|
console.error(error)
|
|
16
|
-
return new
|
|
18
|
+
return new InternalServerError()
|
|
17
19
|
}
|
|
18
20
|
|
|
19
21
|
const setupPluginCallbacks = (galbe: Galbe) => ({
|
|
@@ -37,24 +39,29 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
37
39
|
|
|
38
40
|
async fetch(req) {
|
|
39
41
|
if (!METHODS.includes(req.method)) return new Response('', { status: 501 })
|
|
42
|
+
const cookies: string[] = []
|
|
40
43
|
const context = {
|
|
41
44
|
request: req,
|
|
42
45
|
contentType: !EMPTY_BODY_METHODS.includes(req.method)
|
|
43
|
-
?
|
|
46
|
+
? normalizeContentType(req.headers.get('content-type'))
|
|
44
47
|
: undefined,
|
|
45
48
|
remoteAddress: server.requestIP(req),
|
|
46
|
-
set: {
|
|
49
|
+
set: {
|
|
50
|
+
headers: { 'set-cookie': [] },
|
|
51
|
+
cookie: (name, value, opt = { path: '/' }) => cookies.push(stringifyCookie(name, value, opt)),
|
|
52
|
+
},
|
|
47
53
|
state: {},
|
|
54
|
+
cookies: readCookies(req.headers.get('cookie')),
|
|
48
55
|
} as MakeOptional<Context, 'headers' | 'params' | 'query' | 'body'>
|
|
49
|
-
for (const p of pluginsCb.onFetch) {
|
|
50
|
-
//@ts-ignore
|
|
51
|
-
const r = await p.onFetch(context)
|
|
52
|
-
if (r) return r
|
|
53
|
-
}
|
|
54
56
|
const url = new URL(req.url)
|
|
55
57
|
let route: Route
|
|
56
58
|
let response: any = ''
|
|
57
59
|
try {
|
|
60
|
+
for (const p of pluginsCb.onFetch) {
|
|
61
|
+
//@ts-ignore
|
|
62
|
+
const r = await p.onFetch(context)
|
|
63
|
+
if (r) return r
|
|
64
|
+
}
|
|
58
65
|
// find route
|
|
59
66
|
try {
|
|
60
67
|
route = router.find(req.method.toLowerCase() as Method, url.pathname)
|
|
@@ -84,7 +91,12 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
84
91
|
}
|
|
85
92
|
let inParams = requestPathParser(url.pathname, route.path)
|
|
86
93
|
|
|
87
|
-
context.body = await requestBodyParser(
|
|
94
|
+
context.body = await requestBodyParser(
|
|
95
|
+
req.body,
|
|
96
|
+
inHeaders,
|
|
97
|
+
EMPTY_BODY_METHODS.includes(req.method) ? undefined : schema.body,
|
|
98
|
+
context.contentType
|
|
99
|
+
)
|
|
88
100
|
context.headers = inHeaders
|
|
89
101
|
context.query = inQuery
|
|
90
102
|
context.params = inParams
|
|
@@ -152,12 +164,12 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
152
164
|
context.set.status = response instanceof Response ? response.status : context.set.status || 200
|
|
153
165
|
},
|
|
154
166
|
})
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
const r = await callChain[0].call()
|
|
168
|
+
if (r) response = r
|
|
169
|
+
if (context.set.status === undefined)
|
|
170
|
+
context.set.status = response instanceof Response ? response.status : 200
|
|
159
171
|
|
|
160
|
-
const parsedResponse = responseParser(response, context as Context, schema.response)
|
|
172
|
+
const parsedResponse = responseParser(response, context as Context, cookies, schema.response)
|
|
161
173
|
|
|
162
174
|
if (galbe.config?.responseValidator?.enabled !== false && schema.response)
|
|
163
175
|
validateResponse(response, schema.response, parsedResponse.status || 200)
|
|
@@ -172,17 +184,19 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
172
184
|
} catch (error) {
|
|
173
185
|
context.set.status = error instanceof RequestError ? error.status : 500
|
|
174
186
|
let customError
|
|
175
|
-
for (let eh of galbe.errorCb)
|
|
187
|
+
for (let eh of galbe.errorCb)
|
|
188
|
+
customError = responseParser(eh(error, context as Context), context as Context, cookies)
|
|
176
189
|
if (customError) return customError
|
|
177
|
-
if (error instanceof
|
|
178
|
-
|
|
179
|
-
|
|
190
|
+
if (error instanceof InternalServerError) {
|
|
191
|
+
let internalPayload = 'Internal Server Error'
|
|
192
|
+
try { internalPayload = JSON.stringify(error?.payload || internalPayload) } catch {}
|
|
193
|
+
return new Response(internalPayload, {
|
|
180
194
|
status: error.status,
|
|
181
195
|
headers: { 'content-type': 'application/json' },
|
|
182
196
|
})
|
|
183
197
|
} else if (error instanceof RequestError) {
|
|
184
198
|
let payload = error.payload
|
|
185
|
-
let headers = new Headers(error?.headers
|
|
199
|
+
let headers = new Headers({ ...context.set.headers, ...error?.headers })
|
|
186
200
|
if (!headers.has('content-type')) {
|
|
187
201
|
if (typeof error.payload === 'string') headers.set('content-type', 'text/plain')
|
|
188
202
|
else {
|