galbe 0.6.2 → 0.7.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/bin/commands/generate/code/openapi.parser.ts +43 -34
- package/docs/schemas.md +25 -1
- package/package.json +1 -1
- package/src/extras/spec/openapi.serializer.ts +21 -4
- package/src/parser.ts +4 -3
- package/src/router.ts +1 -1
- package/src/routes.ts +1 -1
- package/src/schema.ts +31 -1
- package/src/server.ts +1 -2
- package/src/validator.ts +3 -1
- package/test/parser.test.ts +29 -6
- package/test/router.test.ts +1 -12
- package/test/test.utils.ts +4 -1
|
@@ -108,16 +108,22 @@ const parseOapiSchema = (
|
|
|
108
108
|
title: os.title,
|
|
109
109
|
description: os.description
|
|
110
110
|
}
|
|
111
|
+
let resp = ''
|
|
111
112
|
let hasOptions = Object.values(options).some(v => !!v)
|
|
112
113
|
let optArg = hasOptions ? `, ${JSON.stringify(options)}` : ''
|
|
113
114
|
let anyOf = os.oneOf || os.anyOf || os.allOf
|
|
115
|
+
let required = os.required
|
|
116
|
+
let nullable = os.nullable
|
|
117
|
+
|
|
114
118
|
if (anyOf?.length) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
119
|
+
if (anyOf.length === 1) resp = parseOapiSchema(anyOf[0] as OpenAPIV3.SchemaObject, details, extra)
|
|
120
|
+
else {
|
|
121
|
+
resp = `$T.union([${anyOf.map(s => parseOapiSchema(s as OpenAPIV3.SchemaObject)).join(',')}], ${JSON.stringify(
|
|
122
|
+
options
|
|
123
|
+
)})`
|
|
124
|
+
}
|
|
125
|
+
} else if (os.type === 'boolean') resp = `$T.boolean(${hasOptions ? JSON.stringify(options) : ''})`
|
|
126
|
+
else if (os.type === 'number') {
|
|
121
127
|
let { max, min, exclusiveMax, exclusiveMin } = {
|
|
122
128
|
max: os.maximum !== undefined && !os.exclusiveMaximum ? os.maximum : undefined,
|
|
123
129
|
min: os.minimum !== undefined && !os.exclusiveMinimum ? os.minimum : undefined,
|
|
@@ -126,49 +132,52 @@ const parseOapiSchema = (
|
|
|
126
132
|
}
|
|
127
133
|
options = { ...options, min, max, exclusiveMax, exclusiveMin }
|
|
128
134
|
hasOptions = Object.values(options).some(v => !!v)
|
|
129
|
-
|
|
130
|
-
}
|
|
131
|
-
if (os.type === 'integer') {
|
|
135
|
+
resp = `$T.number(${hasOptions ? JSON.stringify(options) : ''})`
|
|
136
|
+
} else if (os.type === 'integer') {
|
|
132
137
|
let max = os.maximum !== undefined && !os.exclusiveMaximum ? os.maximum : undefined
|
|
133
138
|
let min = os.minimum !== undefined && !os.exclusiveMinimum ? os.minimum : undefined
|
|
134
139
|
let exclusiveMax = os.maximum !== undefined && os.exclusiveMaximum ? os.maximum : undefined
|
|
135
140
|
let exclusiveMin = os.minimum !== undefined && os.exclusiveMinimum ? os.minimum : undefined
|
|
136
141
|
options = { ...options, min, max, exclusiveMax, exclusiveMin }
|
|
137
142
|
hasOptions = Object.values(options).some(v => !!v)
|
|
138
|
-
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (os.type === 'array') {
|
|
143
|
+
resp = `$T.integer(${hasOptions ? JSON.stringify(options) : ''})`
|
|
144
|
+
} else if (os.type === 'string') {
|
|
145
|
+
if (os.format === 'binary') resp = `$T.byteArray(${hasOptions ? JSON.stringify(options) : ''})`
|
|
146
|
+
else {
|
|
147
|
+
let minLength = os.minLength
|
|
148
|
+
let maxLength = os.maxLength
|
|
149
|
+
let pattern = os.pattern
|
|
150
|
+
options = { ...options, minLength, maxLength, pattern }
|
|
151
|
+
hasOptions = Object.values(options).some(v => !!v)
|
|
152
|
+
resp = `$T.string(${hasOptions ? JSON.stringify(options) : ''})`
|
|
153
|
+
}
|
|
154
|
+
} else if (os.type === 'array') {
|
|
150
155
|
let minItems = os.minItems
|
|
151
156
|
let maxItems = os.maxItems
|
|
152
157
|
let unique = os.uniqueItems
|
|
153
158
|
options = { ...options, minItems, maxItems, unique }
|
|
154
|
-
|
|
155
|
-
}
|
|
156
|
-
if (os.type === 'object') {
|
|
159
|
+
resp = `$T.array(${parseOapiSchema(os?.items)}${optArg})`
|
|
160
|
+
} else if (os.type === 'object') {
|
|
157
161
|
if (extra?.media === 'multipart/form-data') {
|
|
158
|
-
|
|
162
|
+
resp = `$T.multipartForm({${Object.entries(os?.properties || {})
|
|
159
163
|
.map(([k, v]) => `${k}:${parseOapiSchema(v)}`)
|
|
160
164
|
.join(',')}}${optArg})`
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
|
|
165
|
+
} else if (extra?.media === 'application/x-www-form-urlencoded') {
|
|
166
|
+
resp = `$T.urlForm({${Object.entries(os?.properties || {})
|
|
167
|
+
.map(([k, v]) => `${k}:${parseOapiSchema(v)}`)
|
|
168
|
+
.join(',')}}${optArg})`
|
|
169
|
+
} else {
|
|
170
|
+
resp = `$T.object({${Object.entries(os?.properties || {})
|
|
164
171
|
.map(([k, v]) => `${k}:${parseOapiSchema(v)}`)
|
|
165
172
|
.join(',')}}${optArg})`
|
|
166
173
|
}
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
}
|
|
171
|
-
|
|
174
|
+
} else throw new Error(`Unknown schema type ${os.type}`)
|
|
175
|
+
|
|
176
|
+
if (!required && nullable) resp = `$T.nullish(${resp})`
|
|
177
|
+
else if (!required) resp = `$T.optional(${resp})`
|
|
178
|
+
else if (nullable) resp = `$T.nullable(${resp})`
|
|
179
|
+
|
|
180
|
+
return resp
|
|
172
181
|
}
|
|
173
182
|
|
|
174
183
|
const buildSchemaIndex = (def: OpenAPIV3.Document) => {
|
|
@@ -217,7 +226,7 @@ const parseEndpointDef = (method: string, path: string, def?: OpenAPIV3.Operatio
|
|
|
217
226
|
let imports = {}
|
|
218
227
|
let p = path.replaceAll(/\{([^\}]*)\}/g, ':$1')
|
|
219
228
|
let description = def.summary || def.description
|
|
220
|
-
let pathName = path.replaceAll(/\/\{[^\}]*\}/g, 'X').replaceAll(/[^$\w\
|
|
229
|
+
let pathName = path.replaceAll(/\/\{[^\}]*\}/g, 'X').replaceAll(/[^$\w\d_]+([$\w\d_])/g, (_, $1) => $1.toUpperCase())
|
|
221
230
|
let schemaName = `${method}${pathName}`.replace(/^\w/, c => c.toUpperCase())
|
|
222
231
|
|
|
223
232
|
let meta = '/**\n'
|
package/docs/schemas.md
CHANGED
|
@@ -44,6 +44,14 @@ Schema Type matching integer `number` values.
|
|
|
44
44
|
const intSchema = $T.integer(options)
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
+
#### Null
|
|
48
|
+
|
|
49
|
+
Schema Type matching `null` values.
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
const nullSchema = $T.null(options)
|
|
53
|
+
```
|
|
54
|
+
|
|
47
55
|
#### Any
|
|
48
56
|
|
|
49
57
|
Schema Type matching `any` of the previous Schema Types.
|
|
@@ -68,6 +76,22 @@ Makes any type optional. This allows for `undefined` values.
|
|
|
68
76
|
const optionalSchema = $T.optional($T.string())
|
|
69
77
|
```
|
|
70
78
|
|
|
79
|
+
#### Nullable
|
|
80
|
+
|
|
81
|
+
Makes any type nullable. This allows for `null` values.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
const nullableSchema = $T.nullable($T.string())
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
#### Nullish
|
|
88
|
+
|
|
89
|
+
Makes any type nullish. This allows for `undefined` and `null` values.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
const nullishSchema = $T.nullish($T.string())
|
|
93
|
+
```
|
|
94
|
+
|
|
71
95
|
#### Union
|
|
72
96
|
|
|
73
97
|
Creates an union of Schema Types.
|
|
@@ -155,7 +179,7 @@ const schema = {
|
|
|
155
179
|
body: STByteArray | STString | STBoolean | STNumber | STInteger | STLiteral | STObject | STArray | STMulripartForm | STUrlForm | STStream
|
|
156
180
|
```
|
|
157
181
|
|
|
158
|
-
####
|
|
182
|
+
#### Object
|
|
159
183
|
|
|
160
184
|
To define an `application/json` request body, use `STObject` Schema Type. Example:
|
|
161
185
|
|
package/package.json
CHANGED
|
@@ -51,8 +51,12 @@ export const OpenAPISerializer = async (g: Galbe, version = '3.0.3'): Promise<Op
|
|
|
51
51
|
//@ts-ignore
|
|
52
52
|
return { schema: { $ref: `#/components/schemas/${schema.id}` } }
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
if (kind === '
|
|
54
|
+
|
|
55
|
+
if (kind === 'null') {
|
|
56
|
+
s = {
|
|
57
|
+
anyOf: ['null']
|
|
58
|
+
}
|
|
59
|
+
} else if (kind === 'boolean') s = { type: 'boolean' }
|
|
56
60
|
else if (kind === 'byteArray') s = { type: 'string', format: 'byte' }
|
|
57
61
|
else if (kind === 'number')
|
|
58
62
|
s = {
|
|
@@ -118,10 +122,23 @@ export const OpenAPISerializer = async (g: Galbe, version = '3.0.3'): Promise<Op
|
|
|
118
122
|
}
|
|
119
123
|
} else if (kind === 'union') {
|
|
120
124
|
let anyOf = (schema as STUnion).anyOf
|
|
121
|
-
|
|
122
|
-
|
|
125
|
+
let nullable = anyOf.some(s => s[Kind] === 'null')
|
|
126
|
+
anyOf = anyOf.filter(s => s[Kind] !== 'null')
|
|
127
|
+
|
|
128
|
+
if (anyOf.length === 0) {
|
|
129
|
+
s = {}
|
|
130
|
+
} else if (anyOf.length === 1) {
|
|
131
|
+
s = schemaToOpenapi(anyOf[0]).schema
|
|
132
|
+
} else if (anyOf.length > 1) {
|
|
133
|
+
s = {
|
|
134
|
+
anyOf: anyOf.map(s => schemaToOpenapi(s).schema)
|
|
135
|
+
}
|
|
123
136
|
}
|
|
137
|
+
|
|
138
|
+
//@ts-ignore
|
|
139
|
+
if (nullable) s.nullable = nullable
|
|
124
140
|
}
|
|
141
|
+
|
|
125
142
|
s = { title: schema.title, description: schema.description, ...s }
|
|
126
143
|
if (components.schemas && schema.id) {
|
|
127
144
|
components.schemas[schema.id] = s
|
package/src/parser.ts
CHANGED
|
@@ -56,7 +56,7 @@ export const requestBodyParser = async (
|
|
|
56
56
|
}
|
|
57
57
|
})
|
|
58
58
|
} else return new Uint8Array()
|
|
59
|
-
} else if (schema?.[Optional]) {
|
|
59
|
+
} else if (schema?.[Optional] || kind === 'null') {
|
|
60
60
|
return null
|
|
61
61
|
} else {
|
|
62
62
|
throw new RequestError({ status: 400, payload: { body: `Not a valid ${kind}` } })
|
|
@@ -75,7 +75,8 @@ export const requestBodyParser = async (
|
|
|
75
75
|
} else return null
|
|
76
76
|
}
|
|
77
77
|
} else {
|
|
78
|
-
if (kind === '
|
|
78
|
+
if (kind === 'null') throw new RequestError({ status: 400, payload: { body: `Expected null body` } })
|
|
79
|
+
else if (kind === 'byteArray') {
|
|
79
80
|
if (isStream) return rsToAsyncIterator(body)
|
|
80
81
|
const bytes = await readableStreamToArrayBuffer(body)
|
|
81
82
|
return new Uint8Array(bytes)
|
|
@@ -435,7 +436,7 @@ const parseMultipartContent = (
|
|
|
435
436
|
} else {
|
|
436
437
|
throw new RequestError({
|
|
437
438
|
status: 400,
|
|
438
|
-
payload: { body: { [headers.name]: `
|
|
439
|
+
payload: { body: { [headers.name]: `Expected ${schema?.props[headers.name][Kind]} found json` } }
|
|
439
440
|
})
|
|
440
441
|
}
|
|
441
442
|
}
|
package/src/router.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { Method, Route, RouteNode } from './types'
|
|
2
2
|
import { MethodNotAllowedError, NotFoundError } from './types'
|
|
3
3
|
|
|
4
|
-
const ROUTE_REGEX = /^(\/(\*|:?\d+|:?\w+|:?[\w\d][\w
|
|
4
|
+
const ROUTE_REGEX = /^(\/(\*|:?\d+|:?\w+|:?[\w\d.][\w-.]+[\w\d]))*\/?$/
|
|
5
5
|
|
|
6
6
|
const walk = (path: string[], node: RouteNode, alts: RouteNode[] = []): RouteNode => {
|
|
7
7
|
if (path.length < 1) throw new NotFoundError()
|
package/src/routes.ts
CHANGED
|
@@ -231,7 +231,7 @@ export const defineRoutes = async (
|
|
|
231
231
|
if (!routes) return
|
|
232
232
|
const root = process.cwd()
|
|
233
233
|
if (typeof routes === 'string') {
|
|
234
|
-
for await (const path of new Glob(routes).scan({ cwd: root, absolute: true, onlyFiles: false })) {
|
|
234
|
+
for await (const path of new Glob(routes).scan({ cwd: root, absolute: true, onlyFiles: false, dot: true })) {
|
|
235
235
|
const isDir = (await lstat(path)).isDirectory()
|
|
236
236
|
|
|
237
237
|
let files: string[] = []
|
package/src/schema.ts
CHANGED
|
@@ -31,6 +31,7 @@ export interface ArrayOptions extends Options {
|
|
|
31
31
|
}
|
|
32
32
|
export interface STSchema extends Options {
|
|
33
33
|
[Kind]:
|
|
34
|
+
| 'null'
|
|
34
35
|
| 'boolean'
|
|
35
36
|
| 'byteArray'
|
|
36
37
|
| 'number'
|
|
@@ -61,6 +62,7 @@ export type STPropsValue =
|
|
|
61
62
|
| STObject
|
|
62
63
|
| STUnion
|
|
63
64
|
| STAny
|
|
65
|
+
| STNull
|
|
64
66
|
export type STProps = Record<string | number, STPropsValue>
|
|
65
67
|
|
|
66
68
|
type Evaluate<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
|
|
@@ -84,6 +86,17 @@ export type STStream<T extends STSchema = STSchema> = T & {
|
|
|
84
86
|
[Stream]: true
|
|
85
87
|
}
|
|
86
88
|
|
|
89
|
+
// Null
|
|
90
|
+
export interface STNull extends STSchema, Options {
|
|
91
|
+
[Kind]: 'null'
|
|
92
|
+
static: null
|
|
93
|
+
}
|
|
94
|
+
export function _Null(options: Options = {}): STNull {
|
|
95
|
+
return {
|
|
96
|
+
...options,
|
|
97
|
+
[Kind]: 'null'
|
|
98
|
+
} as unknown as STNull
|
|
99
|
+
}
|
|
87
100
|
// ByteArray
|
|
88
101
|
export interface STByteArray extends STSchema, ByteArrayOptions {
|
|
89
102
|
[Kind]: 'byteArray'
|
|
@@ -310,12 +323,28 @@ export function _Stream<T extends STStreamable>(schema: T): STStream<T> {
|
|
|
310
323
|
[Stream]: true
|
|
311
324
|
} as unknown as STStream<T>
|
|
312
325
|
}
|
|
326
|
+
// Nullable
|
|
327
|
+
type STNullable<T extends STSchema> = STUnion<[T, STNull]>
|
|
328
|
+
// Nullish
|
|
329
|
+
type STNullish<T extends STSchema> = STOptional<STNullable<T>>
|
|
313
330
|
|
|
314
331
|
export class SchemaType {
|
|
315
332
|
/** Creates an Optional Schema Type Wrapper*/
|
|
316
333
|
public optional<T extends STSchema>(schema: T): STOptional<T> {
|
|
317
334
|
return { ...schema, [Optional]: true }
|
|
318
335
|
}
|
|
336
|
+
/** Creates an Nullable Schema Type Wrapper*/
|
|
337
|
+
public nullable<T extends STSchema>(schema: T): STNullable<T> {
|
|
338
|
+
return { ..._Union([schema, _Null()], {}) }
|
|
339
|
+
}
|
|
340
|
+
/** Creates an Nullish Schema Type Wrapper*/
|
|
341
|
+
public nullish<T extends STSchema>(schema: T): STNullish<T> {
|
|
342
|
+
return { ..._Union([schema, _Null()], {}), [Optional]: true }
|
|
343
|
+
}
|
|
344
|
+
/** Creates a Null Schema Type */
|
|
345
|
+
public null(options: Options = {}): STNull {
|
|
346
|
+
return _Null(options)
|
|
347
|
+
}
|
|
319
348
|
/** Creates a ByteArray Schema Type */
|
|
320
349
|
public byteArray(options: ByteArrayOptions = {}): STByteArray {
|
|
321
350
|
return _ByteArray(options)
|
|
@@ -419,7 +448,8 @@ export const schemaToTypeStr = (schema: STSchema): string => {
|
|
|
419
448
|
let type = 'unknown'
|
|
420
449
|
let kind = schema[Kind]
|
|
421
450
|
|
|
422
|
-
if (kind === '
|
|
451
|
+
if (kind === 'null') type = 'null'
|
|
452
|
+
else if (kind === 'boolean') type = 'boolean'
|
|
423
453
|
else if (kind === 'byteArray') type = 'Uint8Array'
|
|
424
454
|
else if (kind === 'number') type = 'number'
|
|
425
455
|
else if (kind === 'integer') type = 'number'
|
package/src/server.ts
CHANGED
|
@@ -4,7 +4,6 @@ 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 { logger } from 'girok'
|
|
8
7
|
|
|
9
8
|
type MakeOptional<T, K extends keyof T> = Omit<T, K> & Partial<Pick<T, K>>
|
|
10
9
|
|
|
@@ -140,7 +139,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
140
139
|
callChain.push({
|
|
141
140
|
call: async () => {
|
|
142
141
|
response = await handlerWrapper(context as Context)
|
|
143
|
-
context.set.status = response instanceof Response ? response.status : 200
|
|
142
|
+
context.set.status = response instanceof Response ? response.status : context.set.status || 200
|
|
144
143
|
}
|
|
145
144
|
})
|
|
146
145
|
if (callChain.length > 1) {
|
package/src/validator.ts
CHANGED
|
@@ -8,7 +8,9 @@ export const validate = (elt: any, schema: STSchema, parse = false): any => {
|
|
|
8
8
|
const errors: ValidationError[] = []
|
|
9
9
|
const iElt = elt
|
|
10
10
|
|
|
11
|
-
if (schema[Kind] === '
|
|
11
|
+
if (schema[Kind] === 'null') {
|
|
12
|
+
if (elt !== null) throw `Expected null value got ${iElt}`
|
|
13
|
+
} else if (schema[Kind] === 'boolean') {
|
|
12
14
|
if (typeof elt === 'string') {
|
|
13
15
|
if (parse) elt = elt === 'true' ? true : elt === 'false' ? false : null
|
|
14
16
|
else throw `Expected boolean, got string.`
|
package/test/parser.test.ts
CHANGED
|
@@ -324,14 +324,28 @@ describe('parser', () => {
|
|
|
324
324
|
bool: false,
|
|
325
325
|
object: {},
|
|
326
326
|
array: [],
|
|
327
|
-
any: false
|
|
327
|
+
any: false,
|
|
328
|
+
null: null,
|
|
329
|
+
nullable: null,
|
|
330
|
+
nullish: 42
|
|
328
331
|
}),
|
|
329
332
|
type,
|
|
330
333
|
schema,
|
|
331
334
|
expected: {
|
|
332
335
|
status: 200,
|
|
333
336
|
type: 'object',
|
|
334
|
-
resp: {
|
|
337
|
+
resp: {
|
|
338
|
+
ba: '',
|
|
339
|
+
string: '',
|
|
340
|
+
number: 0,
|
|
341
|
+
bool: false,
|
|
342
|
+
object: {},
|
|
343
|
+
array: [],
|
|
344
|
+
any: false,
|
|
345
|
+
null: null,
|
|
346
|
+
nullable: null,
|
|
347
|
+
nullish: 42
|
|
348
|
+
}
|
|
335
349
|
}
|
|
336
350
|
},
|
|
337
351
|
{
|
|
@@ -343,7 +357,10 @@ describe('parser', () => {
|
|
|
343
357
|
object: { foo: 'bar' },
|
|
344
358
|
array: [false, 'one', 2],
|
|
345
359
|
any: '36',
|
|
346
|
-
optional: 'optional'
|
|
360
|
+
optional: 'optional',
|
|
361
|
+
null: null,
|
|
362
|
+
nullable: 'test',
|
|
363
|
+
nullish: null
|
|
347
364
|
}),
|
|
348
365
|
type,
|
|
349
366
|
schema,
|
|
@@ -358,7 +375,10 @@ describe('parser', () => {
|
|
|
358
375
|
object: { foo: 'bar' },
|
|
359
376
|
array: [false, 'one', 2],
|
|
360
377
|
any: '36',
|
|
361
|
-
optional: 'optional'
|
|
378
|
+
optional: 'optional',
|
|
379
|
+
null: null,
|
|
380
|
+
nullable: 'test',
|
|
381
|
+
nullish: null
|
|
362
382
|
}
|
|
363
383
|
}
|
|
364
384
|
},
|
|
@@ -370,7 +390,8 @@ describe('parser', () => {
|
|
|
370
390
|
bool: 'x',
|
|
371
391
|
object: [],
|
|
372
392
|
array: {},
|
|
373
|
-
any: {}
|
|
393
|
+
any: {},
|
|
394
|
+
null: ''
|
|
374
395
|
}),
|
|
375
396
|
type,
|
|
376
397
|
schema,
|
|
@@ -383,7 +404,9 @@ describe('parser', () => {
|
|
|
383
404
|
number: 'a is not a valid number',
|
|
384
405
|
bool: "x is not a valid boolean. Should be 'true' or 'false'",
|
|
385
406
|
object: 'Expected an object, not an array',
|
|
386
|
-
array: 'Not a valid array'
|
|
407
|
+
array: 'Not a valid array',
|
|
408
|
+
null: 'Expected null value got ',
|
|
409
|
+
nullable: 'Required'
|
|
387
410
|
}
|
|
388
411
|
}
|
|
389
412
|
}
|
package/test/router.test.ts
CHANGED
|
@@ -13,18 +13,7 @@ describe('router', () => {
|
|
|
13
13
|
test('routes, bad syntax', async () => {
|
|
14
14
|
const galbe = new Galbe()
|
|
15
15
|
|
|
16
|
-
const invalidPaths = [
|
|
17
|
-
'.',
|
|
18
|
-
'/@',
|
|
19
|
-
'/-ta',
|
|
20
|
-
'/test/-ta',
|
|
21
|
-
'/test/my.path',
|
|
22
|
-
'/hell@/w0rld',
|
|
23
|
-
'/last-',
|
|
24
|
-
'../',
|
|
25
|
-
'./x',
|
|
26
|
-
'/hello?'
|
|
27
|
-
]
|
|
16
|
+
const invalidPaths = ['.', '/@', '/-ta', '/test/-ta', '/hell@/w0rld', '/last-', '../', './x', '/hello?']
|
|
28
17
|
|
|
29
18
|
for (const p of invalidPaths) {
|
|
30
19
|
try {
|
package/test/test.utils.ts
CHANGED
|
@@ -32,7 +32,10 @@ export const schema_objectBase = {
|
|
|
32
32
|
export const schema_object = {
|
|
33
33
|
...schema_objectBase,
|
|
34
34
|
object: $T.object($T.any()),
|
|
35
|
-
array: $T.array()
|
|
35
|
+
array: $T.array(),
|
|
36
|
+
null: $T.null(),
|
|
37
|
+
nullable: $T.nullable($T.string()),
|
|
38
|
+
nullish: $T.nullish($T.number())
|
|
36
39
|
}
|
|
37
40
|
|
|
38
41
|
export const isAsyncIterator = (obj: any) => {
|