galbe 0.11.0 → 0.12.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/.prettierrc +1 -1
- package/bin/commands/generate/client.ts +58 -30
- package/bin/commands/generate/code/openapi.parser.ts +72 -45
- package/bin/res/cli.template.js +20 -18
- package/bin/res/client.template.ts +52 -26
- package/bin/util.ts +27 -7
- package/docs/context.md +1 -1
- package/docs/schemas.md +63 -26
- package/package.json +1 -1
- package/src/extras/spec/openapi.serializer.ts +83 -56
- package/src/index.ts +16 -11
- package/src/parser.ts +198 -146
- package/src/schema.ts +156 -66
- package/src/server.ts +18 -16
- package/src/types.ts +54 -42
- package/src/util.ts +37 -9
- package/src/validator.ts +23 -19
- package/test/parser.test.ts +369 -281
- package/test/requests.test.ts +242 -176
- package/test/resources/static/chameleon.png +0 -0
- package/test/resources/static/index.html +13 -0
- package/test/resources/static/sub/index.html +13 -0
- package/test/resources/static/sub/other.html +13 -0
- package/test/responses.test.ts +49 -49
- package/test/test.utils.ts +4 -2
- package/test/types.test.ts +909 -0
package/src/schema.ts
CHANGED
|
@@ -45,10 +45,12 @@ export interface STSchema extends Options {
|
|
|
45
45
|
| 'multipartForm'
|
|
46
46
|
| 'any'
|
|
47
47
|
| 'union'
|
|
48
|
+
| 'intersection'
|
|
48
49
|
[Optional]?: boolean
|
|
49
50
|
[Stream]?: boolean
|
|
50
51
|
params: unknown[]
|
|
51
52
|
static: unknown
|
|
53
|
+
props?: STProps
|
|
52
54
|
[key: string]: any
|
|
53
55
|
}
|
|
54
56
|
export type STPropsValue =
|
|
@@ -67,6 +69,10 @@ export type STProps = Record<string | number, STPropsValue>
|
|
|
67
69
|
|
|
68
70
|
type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
|
|
69
71
|
|
|
72
|
+
type Entries<T extends object> = {
|
|
73
|
+
[K in keyof T]-?: [K, T[K]]
|
|
74
|
+
}[keyof T]
|
|
75
|
+
|
|
70
76
|
/**
|
|
71
77
|
* Infer the static TypeScript type from a {@link https://galbe.dev/documentation/schemas#schema-types Schema Type}
|
|
72
78
|
* @example
|
|
@@ -76,7 +82,9 @@ type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
|
|
|
76
82
|
* // ^? type T = { foo: string }
|
|
77
83
|
* ```
|
|
78
84
|
*/
|
|
79
|
-
export type Static<T extends STSchema, P extends unknown[] = unknown[]> =
|
|
85
|
+
export type Static<T extends STSchema, P extends unknown[] = unknown[]> = T[typeof Optional] extends true
|
|
86
|
+
? (T & { params: P })['static'] | undefined
|
|
87
|
+
: (T & { params: P })['static']
|
|
80
88
|
|
|
81
89
|
// Utils
|
|
82
90
|
export type STOptional<T extends STSchema> = T & {
|
|
@@ -94,7 +102,7 @@ export interface STNull extends STSchema, Options {
|
|
|
94
102
|
export function _Null(options: Options = {}): STNull {
|
|
95
103
|
return {
|
|
96
104
|
...options,
|
|
97
|
-
[Kind]: 'null'
|
|
105
|
+
[Kind]: 'null',
|
|
98
106
|
} as unknown as STNull
|
|
99
107
|
}
|
|
100
108
|
// ByteArray
|
|
@@ -105,7 +113,7 @@ export interface STByteArray extends STSchema, ByteArrayOptions {
|
|
|
105
113
|
export function _ByteArray(options: ByteArrayOptions = {}): STByteArray {
|
|
106
114
|
return {
|
|
107
115
|
...options,
|
|
108
|
-
[Kind]: 'byteArray'
|
|
116
|
+
[Kind]: 'byteArray',
|
|
109
117
|
} as unknown as STByteArray
|
|
110
118
|
}
|
|
111
119
|
// Boolean
|
|
@@ -116,7 +124,7 @@ export interface STBoolean extends STSchema, Options {
|
|
|
116
124
|
export function _Bool(options: Options = {}): STBoolean {
|
|
117
125
|
return {
|
|
118
126
|
...options,
|
|
119
|
-
[Kind]: 'boolean'
|
|
127
|
+
[Kind]: 'boolean',
|
|
120
128
|
} as unknown as STBoolean
|
|
121
129
|
}
|
|
122
130
|
// String
|
|
@@ -127,7 +135,7 @@ export interface STString extends STSchema, NumberOptions {
|
|
|
127
135
|
export function _String(options: StringOptions = {}): STString {
|
|
128
136
|
return {
|
|
129
137
|
...options,
|
|
130
|
-
[Kind]: 'string'
|
|
138
|
+
[Kind]: 'string',
|
|
131
139
|
} as unknown as STString
|
|
132
140
|
}
|
|
133
141
|
// Number
|
|
@@ -138,7 +146,7 @@ export interface STNumber extends STSchema, NumberOptions {
|
|
|
138
146
|
export function _Number(options: NumberOptions = {}): STNumber {
|
|
139
147
|
return {
|
|
140
148
|
...options,
|
|
141
|
-
[Kind]: 'number'
|
|
149
|
+
[Kind]: 'number',
|
|
142
150
|
} as unknown as STNumber
|
|
143
151
|
}
|
|
144
152
|
// Integer
|
|
@@ -149,7 +157,7 @@ export interface STInteger extends STSchema, NumberOptions {
|
|
|
149
157
|
export function _Integer(options: NumberOptions = {}): STInteger {
|
|
150
158
|
return {
|
|
151
159
|
...options,
|
|
152
|
-
[Kind]: 'integer'
|
|
160
|
+
[Kind]: 'integer',
|
|
153
161
|
} as unknown as STInteger
|
|
154
162
|
}
|
|
155
163
|
// Literal
|
|
@@ -164,7 +172,7 @@ export function _Literal<T extends STLiteralValue>(value: T, options: Options =
|
|
|
164
172
|
...options,
|
|
165
173
|
[Kind]: 'literal',
|
|
166
174
|
static: value,
|
|
167
|
-
value
|
|
175
|
+
value,
|
|
168
176
|
} as unknown as STLiteral<T>
|
|
169
177
|
}
|
|
170
178
|
// Any
|
|
@@ -176,7 +184,7 @@ export interface STAny extends STSchema, Options {
|
|
|
176
184
|
export function _Any(options: Options = {}): STAny {
|
|
177
185
|
return {
|
|
178
186
|
...options,
|
|
179
|
-
[Kind]: 'any'
|
|
187
|
+
[Kind]: 'any',
|
|
180
188
|
} as unknown as STAny
|
|
181
189
|
}
|
|
182
190
|
|
|
@@ -218,36 +226,36 @@ function _Json<T extends STBoolean | STNumber | STString | STObject>(value?: T,
|
|
|
218
226
|
}
|
|
219
227
|
|
|
220
228
|
// UrlForm
|
|
221
|
-
export type STUrlFormValues =
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
export type STUrlFormProps = Record<string, STUrlFormValues>
|
|
233
|
-
export interface STUrlForm<T extends STUrlFormProps = STUrlFormProps> extends STSchema {
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
}
|
|
238
|
-
function _UrlForm<T extends STUrlFormProps>(properties?: T, options: Options = {}): STUrlForm<T> {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
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
|
+
// }
|
|
251
259
|
|
|
252
260
|
// MultipartForm
|
|
253
261
|
export type STMultipartFormValues = STSchema
|
|
@@ -284,6 +292,7 @@ function _MultipartForm<T extends STProps>(properties?: T, options: Options = {}
|
|
|
284
292
|
}
|
|
285
293
|
|
|
286
294
|
// Array
|
|
295
|
+
type NonEmptyArray<T> = [T, ...T[]]
|
|
287
296
|
export interface STArray<T extends STSchema = STSchema> extends STSchema {
|
|
288
297
|
[Kind]: 'array'
|
|
289
298
|
static: Static<T>[]
|
|
@@ -293,7 +302,7 @@ export function _Array<T extends STSchema>(schema?: T, options: ArrayOptions = {
|
|
|
293
302
|
return {
|
|
294
303
|
...options,
|
|
295
304
|
[Kind]: 'array',
|
|
296
|
-
items: schema ?? _Any()
|
|
305
|
+
items: schema ?? _Any(),
|
|
297
306
|
} as unknown as STArray<T>
|
|
298
307
|
}
|
|
299
308
|
|
|
@@ -301,45 +310,86 @@ export function _Array<T extends STSchema>(schema?: T, options: ArrayOptions = {
|
|
|
301
310
|
type UnionStatic<T extends STSchema[], P extends unknown[]> = {
|
|
302
311
|
[K in keyof T]: T[K] extends STSchema ? Static<T[K], P> : never
|
|
303
312
|
}[number]
|
|
304
|
-
export interface STUnion<T extends STSchema
|
|
313
|
+
export interface STUnion<T extends NonEmptyArray<STSchema> = NonEmptyArray<STSchema>> extends STSchema {
|
|
305
314
|
[Kind]: 'union'
|
|
306
315
|
static: UnionStatic<T, this['params']>
|
|
316
|
+
props: T[number]['props']
|
|
307
317
|
anyOf: T
|
|
308
318
|
}
|
|
309
|
-
export function _Union<T extends STSchema
|
|
319
|
+
export function _Union<T extends NonEmptyArray<STSchema>>(schemas: [...T], options: Options): STUnion<T> {
|
|
310
320
|
const s = {
|
|
311
321
|
...options,
|
|
312
322
|
[Kind]: 'union',
|
|
313
|
-
anyOf: schemas
|
|
314
|
-
optional: () => ({ ...s, [Optional]: true })
|
|
323
|
+
anyOf: schemas as T,
|
|
324
|
+
optional: () => ({ ...s, [Optional]: true }),
|
|
315
325
|
}
|
|
316
326
|
return s as unknown as STUnion<T>
|
|
317
327
|
}
|
|
328
|
+
|
|
329
|
+
// Intersection
|
|
330
|
+
type IntersectionStatic<T extends readonly STSchema[], P extends unknown[]> = T extends readonly [infer H, ...infer R]
|
|
331
|
+
? H extends STSchema
|
|
332
|
+
? R extends readonly STSchema[]
|
|
333
|
+
? Static<H, P> & IntersectionStatic<R, P>
|
|
334
|
+
: never
|
|
335
|
+
: never
|
|
336
|
+
: unknown
|
|
337
|
+
export interface STIntersection<T extends NonEmptyArray<STObject | STUnion | STIntersection> = NonEmptyArray<STObject>>
|
|
338
|
+
extends STSchema {
|
|
339
|
+
[Kind]: 'intersection'
|
|
340
|
+
static: IntersectionStatic<T, this['params']>
|
|
341
|
+
props: T[number]['props']
|
|
342
|
+
allOf: T
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
export function _Intersection<T extends NonEmptyArray<STObject | STUnion | STIntersection>>(
|
|
346
|
+
schemas: [...T],
|
|
347
|
+
options: Options
|
|
348
|
+
): STIntersection<T> {
|
|
349
|
+
const s = {
|
|
350
|
+
...options,
|
|
351
|
+
[Kind]: 'intersection',
|
|
352
|
+
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
|
+
optional: () => ({ ...s, [Optional]: true }),
|
|
365
|
+
}
|
|
366
|
+
return s as unknown as STIntersection<T>
|
|
367
|
+
}
|
|
368
|
+
|
|
318
369
|
// Stream
|
|
319
|
-
type STStreamable = STByteArray | STString |
|
|
370
|
+
type STStreamable = STByteArray | STString | STMultipartForm | STObject | STUnion | STIntersection
|
|
320
371
|
export function _Stream<T extends STStreamable>(schema: T): STStream<T> {
|
|
321
372
|
return {
|
|
322
373
|
...schema,
|
|
323
|
-
[Stream]: true
|
|
374
|
+
[Stream]: true,
|
|
324
375
|
} as unknown as STStream<T>
|
|
325
376
|
}
|
|
326
377
|
// Nullable
|
|
327
378
|
type STNullable<T extends STSchema> = STUnion<[T, STNull]>
|
|
328
379
|
// Nullish
|
|
329
|
-
type STNullish<T extends STSchema> =
|
|
330
|
-
|
|
380
|
+
type STNullish<T extends STSchema> = (T | STNull) & { [Kind]: 'union'; anyOf: [T, STNull]; [Optional]: true }
|
|
331
381
|
export class SchemaType {
|
|
332
|
-
/** Creates an Optional Schema Type Wrapper*/
|
|
382
|
+
/** Creates an Optional Schema Type Wrapper */
|
|
333
383
|
public optional<T extends STSchema>(schema: T): STOptional<T> {
|
|
334
384
|
return { ...schema, [Optional]: true }
|
|
335
385
|
}
|
|
336
|
-
/** Creates an Nullable Schema Type Wrapper*/
|
|
386
|
+
/** Creates an Nullable Schema Type Wrapper */
|
|
337
387
|
public nullable<T extends STSchema>(schema: T): STNullable<T> {
|
|
338
388
|
return { ..._Union([schema, _Null()], {}) }
|
|
339
389
|
}
|
|
340
|
-
/** Creates an Nullish Schema Type Wrapper*/
|
|
341
|
-
public nullish<T extends STSchema>(schema: T):
|
|
342
|
-
return
|
|
390
|
+
/** Creates an Nullish Schema Type Wrapper */
|
|
391
|
+
public nullish<T extends STSchema>(schema: T): STOptional<STNullable<T>> {
|
|
392
|
+
return this.optional(this.nullable(schema)) as STOptional<STNullable<T>>
|
|
343
393
|
}
|
|
344
394
|
/** Creates a Null Schema Type */
|
|
345
395
|
public null(options: Options = {}): STNull {
|
|
@@ -385,29 +435,64 @@ export class SchemaType {
|
|
|
385
435
|
return _Json(value, options)
|
|
386
436
|
}
|
|
387
437
|
/** Creates an UrlForm Schema Type */
|
|
388
|
-
public urlForm<T extends STUrlFormProps>(properties?: T, options: Options = {}): STUrlForm<T> {
|
|
389
|
-
|
|
390
|
-
}
|
|
391
|
-
/** Creates
|
|
438
|
+
// public urlForm<T extends STUrlFormProps>(properties?: T, options: Options = {}): STUrlForm<T> {
|
|
439
|
+
// return _UrlForm(properties, options)
|
|
440
|
+
// }
|
|
441
|
+
/** Creates a MultipartForm Schema Type */
|
|
392
442
|
public multipartForm<T extends STProps>(properties?: T, options: Options = {}): STMultipartForm<T> {
|
|
393
443
|
return _MultipartForm(properties, options)
|
|
394
444
|
}
|
|
395
|
-
/**
|
|
445
|
+
/** Creates an Array Schema Type */
|
|
396
446
|
public array<T extends STSchema>(schema?: T, options: Options = {}): STArray<T> {
|
|
397
447
|
return _Array(schema, options)
|
|
398
448
|
}
|
|
399
|
-
/**
|
|
400
|
-
public union<T extends STSchema
|
|
449
|
+
/** Creates an Union Schema Type */
|
|
450
|
+
public union<T extends NonEmptyArray<STSchema>>(schemas: [...T], options: Options = {}): STUnion<T> {
|
|
401
451
|
return _Union(schemas, options)
|
|
402
452
|
}
|
|
403
|
-
/**
|
|
404
|
-
public
|
|
453
|
+
/** Creates an Intersection Schema Type */
|
|
454
|
+
public intersection<T extends NonEmptyArray<STObject | STUnion | STIntersection>>(
|
|
455
|
+
schemas: [...T],
|
|
456
|
+
options: Options = {}
|
|
457
|
+
): STIntersection<T> {
|
|
458
|
+
return _Intersection(schemas, options)
|
|
459
|
+
}
|
|
460
|
+
/** Creates a Stream Schema Type */
|
|
461
|
+
public stream<T extends STObject>(
|
|
462
|
+
schema: T
|
|
463
|
+
): Omit<STStream<T>, 'static'> & {
|
|
464
|
+
static: AsyncGenerator<
|
|
465
|
+
T['props'] extends undefined
|
|
466
|
+
? { [k: string]: Static<STPropsValue> }
|
|
467
|
+
: T['props'] extends STProps
|
|
468
|
+
? { [K in keyof T['props']]: [K, Static<T['props'][K]>] }[keyof T['props']]
|
|
469
|
+
: never
|
|
470
|
+
>
|
|
471
|
+
params: unknown[]
|
|
472
|
+
}
|
|
473
|
+
public stream<T extends STUnion>(
|
|
474
|
+
schema: T
|
|
475
|
+
): Omit<STStream<T>, 'static'> & {
|
|
476
|
+
static: AsyncGenerator<
|
|
477
|
+
Entries<{
|
|
478
|
+
[P in KeysOfUnion<T['props']> as ValueAt<T['props'], P> extends STSchema ? P : never]: Static<
|
|
479
|
+
ValueAt<T['props'], P>
|
|
480
|
+
>
|
|
481
|
+
}>
|
|
482
|
+
>
|
|
483
|
+
params: unknown[]
|
|
484
|
+
}
|
|
485
|
+
public stream<T extends STIntersection>(
|
|
405
486
|
schema: T
|
|
406
487
|
): Omit<STStream<T>, 'static'> & {
|
|
407
488
|
static: AsyncGenerator<
|
|
408
489
|
T['props'] extends undefined
|
|
409
|
-
?
|
|
410
|
-
:
|
|
490
|
+
? never
|
|
491
|
+
: T['props'] extends STProps
|
|
492
|
+
? Entries<{
|
|
493
|
+
[K in keyof Static<T>]: Static<T>[K]
|
|
494
|
+
}>
|
|
495
|
+
: never
|
|
411
496
|
>
|
|
412
497
|
params: unknown[]
|
|
413
498
|
}
|
|
@@ -443,6 +528,8 @@ export class SchemaType {
|
|
|
443
528
|
return _Stream(schema)
|
|
444
529
|
}
|
|
445
530
|
}
|
|
531
|
+
type KeysOfUnion<U> = U extends unknown ? keyof U : never
|
|
532
|
+
type ValueAt<U, K extends PropertyKey> = U extends unknown ? (K extends keyof U ? U[K] : never) : never
|
|
446
533
|
|
|
447
534
|
export const schemaToTypeStr = (schema: STSchema): string => {
|
|
448
535
|
let type = 'unknown'
|
|
@@ -464,16 +551,19 @@ export const schemaToTypeStr = (schema: STSchema): string => {
|
|
|
464
551
|
} else if (kind === 'object') {
|
|
465
552
|
let props = (schema as STObject).props
|
|
466
553
|
type = `{${Object.entries(props)
|
|
467
|
-
.map(([k, v]) => `${typeof k === 'string' ? `'${k}'` : k}:${schemaToTypeStr(v)}`)
|
|
554
|
+
.map(([k, v]) => `${typeof k === 'string' ? `'${k}'` : k}${v?.[Optional] ? '?' : ''}:${schemaToTypeStr(v)}`)
|
|
468
555
|
.join(';')}}`
|
|
469
556
|
} else if (kind === 'json') {
|
|
470
557
|
type = `Json<${schemaToTypeStr({ ...schema, [Kind]: schema.type })}>`
|
|
471
558
|
} else if (kind === 'union') {
|
|
472
559
|
let anyOf = (schema as STUnion).anyOf
|
|
473
560
|
type = anyOf.map(s => schemaToTypeStr(s)).join('|')
|
|
561
|
+
} else if (kind === 'intersection') {
|
|
562
|
+
let allOf = (schema as STIntersection).allOf
|
|
563
|
+
type = allOf.map(s => schemaToTypeStr(s)).join('&')
|
|
474
564
|
}
|
|
475
565
|
|
|
476
|
-
if (schema[Optional]) type = `${type}|undefined`
|
|
566
|
+
// if (schema[Optional]) type = `${type}|undefined`
|
|
477
567
|
|
|
478
568
|
return type
|
|
479
569
|
}
|
package/src/server.ts
CHANGED
|
@@ -4,6 +4,7 @@ import { InternalError, 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
|
+
import { inferBodyType } from './util'
|
|
7
8
|
|
|
8
9
|
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
|
|
9
10
|
|
|
@@ -19,7 +20,7 @@ const setupPluginCallbacks = (galbe: Galbe) => ({
|
|
|
19
20
|
onFetch: galbe.plugins.filter(p => p.onFetch),
|
|
20
21
|
onRoute: galbe.plugins.filter(p => p.onRoute),
|
|
21
22
|
beforeHandle: galbe.plugins.filter(p => p.beforeHandle),
|
|
22
|
-
afterHandle: galbe.plugins.filter(p => p.afterHandle)
|
|
23
|
+
afterHandle: galbe.plugins.filter(p => p.afterHandle),
|
|
23
24
|
})
|
|
24
25
|
|
|
25
26
|
export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
@@ -38,9 +39,12 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
38
39
|
if (!METHODS.includes(req.method)) return new Response('', { status: 501 })
|
|
39
40
|
const context = {
|
|
40
41
|
request: req,
|
|
42
|
+
contentType: !EMPTY_BODY_METHODS.includes(req.method)
|
|
43
|
+
? inferBodyType(req.headers.get('content-type'))
|
|
44
|
+
: undefined,
|
|
41
45
|
remoteAddress: server.requestIP(req),
|
|
42
46
|
set: { headers: { 'set-cookie': [] } },
|
|
43
|
-
state: {}
|
|
47
|
+
state: {},
|
|
44
48
|
} as MakeOptional<Context, 'headers' | 'params' | 'query' | 'body'>
|
|
45
49
|
for (const p of pluginsCb.onFetch) {
|
|
46
50
|
//@ts-ignore
|
|
@@ -80,9 +84,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
80
84
|
}
|
|
81
85
|
let inParams = requestPathParser(url.pathname, route.path)
|
|
82
86
|
|
|
83
|
-
context.body =
|
|
84
|
-
? await requestBodyParser(req.body, inHeaders, schema.body)
|
|
85
|
-
: null
|
|
87
|
+
context.body = await requestBodyParser(req.body, inHeaders, schema.body, context.contentType)
|
|
86
88
|
context.headers = inHeaders
|
|
87
89
|
context.query = inQuery
|
|
88
90
|
context.params = inParams
|
|
@@ -94,7 +96,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
94
96
|
if (schema?.headers)
|
|
95
97
|
context.headers = {
|
|
96
98
|
...context.headers,
|
|
97
|
-
...parseEntry(context.headers, schema.headers, { name: 'headers', i: true })
|
|
99
|
+
...parseEntry(context.headers, schema.headers, { name: 'headers', i: true }),
|
|
98
100
|
}
|
|
99
101
|
} catch (error) {
|
|
100
102
|
if (error instanceof RequestError) errors.push(error)
|
|
@@ -142,13 +144,13 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
142
144
|
let r = await hook(context as Context, next)
|
|
143
145
|
if (r) return r
|
|
144
146
|
if (!nextCalled && !handlerCalled) return await next()
|
|
145
|
-
}
|
|
147
|
+
},
|
|
146
148
|
}))
|
|
147
149
|
callChain.push({
|
|
148
150
|
call: async () => {
|
|
149
151
|
response = await handlerWrapper(context as Context)
|
|
150
152
|
context.set.status = response instanceof Response ? response.status : context.set.status || 200
|
|
151
|
-
}
|
|
153
|
+
},
|
|
152
154
|
})
|
|
153
155
|
if (callChain.length > 1) {
|
|
154
156
|
let r = await callChain[0].call()
|
|
@@ -176,7 +178,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
176
178
|
console.log(`Internal Error`, error?.payload || '')
|
|
177
179
|
return new Response('Internal Server Error', {
|
|
178
180
|
status: error.status,
|
|
179
|
-
headers: { '
|
|
181
|
+
headers: { 'content-type': 'application/json' },
|
|
180
182
|
})
|
|
181
183
|
} else if (error instanceof RequestError) {
|
|
182
184
|
let payload = error.payload
|
|
@@ -187,19 +189,19 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
187
189
|
headers.set('content-type', 'application/json')
|
|
188
190
|
try {
|
|
189
191
|
payload = JSON.stringify(error.payload)
|
|
190
|
-
} catch (err) {
|
|
192
|
+
} catch (err) {}
|
|
191
193
|
}
|
|
192
194
|
}
|
|
193
195
|
return new Response(payload, {
|
|
194
196
|
status: error.status,
|
|
195
|
-
headers
|
|
197
|
+
headers,
|
|
196
198
|
})
|
|
197
199
|
} else console.log(error)
|
|
198
200
|
return new Response('"Internal Server Error"', {
|
|
199
201
|
status: 500,
|
|
200
202
|
headers: {
|
|
201
|
-
'content-type': 'application/json'
|
|
202
|
-
}
|
|
203
|
+
'content-type': 'application/json',
|
|
204
|
+
},
|
|
203
205
|
})
|
|
204
206
|
}
|
|
205
207
|
},
|
|
@@ -208,10 +210,10 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
208
210
|
return new Response('"Internal Server Error"', {
|
|
209
211
|
status: 500,
|
|
210
212
|
headers: {
|
|
211
|
-
'content-type': 'application/json'
|
|
212
|
-
}
|
|
213
|
+
'content-type': 'application/json',
|
|
214
|
+
},
|
|
213
215
|
})
|
|
214
|
-
}
|
|
216
|
+
},
|
|
215
217
|
})
|
|
216
218
|
return server
|
|
217
219
|
}
|
package/src/types.ts
CHANGED
|
@@ -16,28 +16,11 @@ import type {
|
|
|
16
16
|
STStream,
|
|
17
17
|
STString,
|
|
18
18
|
STUnion,
|
|
19
|
-
|
|
20
|
-
Static
|
|
19
|
+
Static,
|
|
21
20
|
} from './schema'
|
|
22
21
|
import type { Galbe } from './index'
|
|
23
22
|
import { HttpStatus } from './util'
|
|
24
23
|
|
|
25
|
-
export type STBody =
|
|
26
|
-
| STByteArray
|
|
27
|
-
| STString
|
|
28
|
-
| STBoolean
|
|
29
|
-
| STNumber
|
|
30
|
-
| STInteger
|
|
31
|
-
| STLiteral
|
|
32
|
-
| STObject
|
|
33
|
-
| STArray
|
|
34
|
-
| STUrlForm
|
|
35
|
-
| STMultipartForm
|
|
36
|
-
| STUnion
|
|
37
|
-
| STStream
|
|
38
|
-
| STAny
|
|
39
|
-
| undefined
|
|
40
|
-
|
|
41
24
|
export type STResponseValue =
|
|
42
25
|
| STByteArray
|
|
43
26
|
| STString
|
|
@@ -52,6 +35,19 @@ export type STResponseValue =
|
|
|
52
35
|
| STStream
|
|
53
36
|
| STAny
|
|
54
37
|
| STNull
|
|
38
|
+
export type STBody =
|
|
39
|
+
| STNull
|
|
40
|
+
| Partial<{
|
|
41
|
+
byteArray?: STByteArray | STStream
|
|
42
|
+
text?: STString | STLiteral | STBoolean | STNumber | STInteger | STUnion | STStream
|
|
43
|
+
json?: STJson | STObject | STBoolean | STInteger | STNumber | STString | STArray | STUnion
|
|
44
|
+
urlForm?: STObject | STStream | STUnion
|
|
45
|
+
multipart?: STMultipartForm | STStream | STUnion
|
|
46
|
+
default?: STString | STByteArray | STStream | STAny
|
|
47
|
+
}>
|
|
48
|
+
export type STBodyType = keyof STBody
|
|
49
|
+
export type STBodyValue = STBody[STBodyType]
|
|
50
|
+
|
|
55
51
|
export type STResponse = Partial<Record<number | 'default', STResponseValue>>
|
|
56
52
|
|
|
57
53
|
export type MaybeArray<T> = T | T[]
|
|
@@ -159,9 +155,9 @@ type OmitNotDefined<S extends RequestSchema> = {
|
|
|
159
155
|
[K in keyof Exclude<S['params'], undefined> as Exclude<S['params'], undefined>[K] extends Required<
|
|
160
156
|
Exclude<S['params'], undefined>
|
|
161
157
|
>[K]
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
158
|
+
? K
|
|
159
|
+
: //@ts-ignore
|
|
160
|
+
never]: Static<STObject<Exclude<S['params'], undefined>>>[K]
|
|
165
161
|
}
|
|
166
162
|
type StaticBody<T extends STSchema> = T extends STOptional<STSchema> ? Static<T> | null : Static<T>
|
|
167
163
|
export type Context<
|
|
@@ -169,24 +165,35 @@ export type Context<
|
|
|
169
165
|
Path extends string = string,
|
|
170
166
|
S extends RequestSchema = RequestSchema
|
|
171
167
|
> = {
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
168
|
+
[K in STBodyType]: K extends keyof Exclude<S['body'], undefined>
|
|
169
|
+
? {
|
|
170
|
+
headers: Static<STObject<Exclude<S['headers'], undefined>>>
|
|
171
|
+
params: {
|
|
172
|
+
[P in ExtractParams<Path>]: P extends keyof OmitNotDefined<S> ? OmitNotDefined<S>[P] : string
|
|
173
|
+
}
|
|
174
|
+
query: Static<STObject<Exclude<S['query'], undefined>>>
|
|
175
|
+
contentType: M extends 'get' | 'options' | 'head' ? undefined : K
|
|
176
|
+
body: M extends 'get' | 'options' | 'head'
|
|
177
|
+
? null
|
|
178
|
+
: Exclude<S['body'], undefined> extends STNull
|
|
179
|
+
? null
|
|
180
|
+
: K extends STBodyType
|
|
181
|
+
? StaticBody<Exclude<Exclude<S['body'], undefined>[K], undefined>>
|
|
182
|
+
: never
|
|
183
|
+
request: Request
|
|
184
|
+
remoteAddress: SocketAddress | null
|
|
185
|
+
route?: Route
|
|
186
|
+
state: Record<string, any>
|
|
187
|
+
set: {
|
|
188
|
+
headers: {
|
|
189
|
+
'set-cookie': string[]
|
|
190
|
+
[header: string]: string | string[]
|
|
191
|
+
}
|
|
192
|
+
status?: number
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
: never
|
|
196
|
+
}[STBodyType]
|
|
190
197
|
export type Next = () => void | Promise<any>
|
|
191
198
|
export type Hook<M extends Method = Method, Path extends string = string, S extends RequestSchema = RequestSchema> = (
|
|
192
199
|
ctx: Context<M, Path, S>,
|
|
@@ -251,13 +258,17 @@ export type Endpoint<M extends Method> = {
|
|
|
251
258
|
export type StaticEndpointOptions = {
|
|
252
259
|
resolve?: (path: string, target: string) => string | null | undefined | void
|
|
253
260
|
}
|
|
254
|
-
export type StaticEndpoint<P extends string = string, T extends string = string> = (
|
|
261
|
+
export type StaticEndpoint<P extends string = string, T extends string = string> = (
|
|
262
|
+
path: P,
|
|
263
|
+
target: T,
|
|
264
|
+
options?: StaticEndpointOptions
|
|
265
|
+
) => Route<'get', P, {}, {}, {}, STBody, STResponse, T>
|
|
255
266
|
|
|
256
267
|
export class RequestError {
|
|
257
268
|
status: number
|
|
258
269
|
payload?: any
|
|
259
270
|
headers?: Record<string, string>
|
|
260
|
-
constructor(options: { status?: number; payload?: any
|
|
271
|
+
constructor(options: { status?: number; payload?: any; headers?: Record<string, string> }) {
|
|
261
272
|
this.status = options.status ?? 500
|
|
262
273
|
this.payload = options.payload
|
|
263
274
|
this.headers = options.headers
|
|
@@ -289,7 +300,7 @@ export type Route<
|
|
|
289
300
|
context: Context<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>
|
|
290
301
|
hooks: Hook<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>[]
|
|
291
302
|
handler: Handler<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>
|
|
292
|
-
static?: { path: SP
|
|
303
|
+
static?: { path: SP; root: SR }
|
|
293
304
|
}
|
|
294
305
|
|
|
295
306
|
export class NotFoundError extends RequestError {
|
|
@@ -349,6 +360,7 @@ export type GalbePlugin = {
|
|
|
349
360
|
|
|
350
361
|
export type GalbeCLICommand = {
|
|
351
362
|
name: string
|
|
363
|
+
tags: string[]
|
|
352
364
|
description?: string
|
|
353
365
|
route: Route
|
|
354
366
|
arguments?: { name: string; type: string; description: string }[]
|