galbe 0.2.0 → 0.4.1
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/.github/workflows/build_test.yml +2 -4
- package/.github/workflows/release.yml +2 -2
- package/bin/cli.ts +8 -104
- package/bin/commands/build.ts +95 -0
- package/bin/commands/dev.ts +53 -0
- package/bin/commands/generate/client.ts +192 -0
- package/bin/commands/generate/code/openapi.parser.ts +479 -0
- package/bin/commands/generate/code.ts +75 -0
- package/bin/commands/generate/index.ts +12 -0
- package/bin/commands/generate/spec.ts +94 -0
- package/bin/res/cli.template.js +120 -0
- package/bin/res/client.template.ts +161 -0
- package/bin/util.ts +164 -0
- package/bun.lockb +0 -0
- package/docs/getting-started.md +8 -0
- package/docs/hooks.md +4 -4
- package/docs/plugins.md +3 -9
- package/docs/routes.md +3 -3
- package/docs/schemas.md +19 -2
- package/package.json +10 -8
- package/scripts/postinstall.ts +1 -3
- package/src/extras/spec/openapi.serializer.ts +264 -0
- package/src/extras.ts +1 -0
- package/src/index.ts +122 -80
- package/src/parser.ts +29 -8
- package/src/router.ts +27 -28
- package/src/routes.ts +142 -40
- package/src/schema.ts +79 -8
- package/src/server.ts +55 -35
- package/src/types.ts +109 -53
- package/src/util.ts +85 -3
- package/src/validator.ts +27 -5
- package/test/parser.test.ts +34 -0
- package/test/requests.test.ts +5 -12
- package/test/resources/test.route.comment.ts +20 -0
- package/test/responses.test.ts +386 -13
- package/test/routeFiles.test.ts +44 -27
- package/test/router.test.ts +67 -42
- package/scripts/build.ts +0 -14
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import type { STArray, STJson, STLiteral, STObject, STProps, STSchema, STUnion } from '../../../src/schema'
|
|
2
|
+
|
|
3
|
+
import { Galbe } from '../../../src'
|
|
4
|
+
import { walkRoutes, HttpStatus } from '../../../src/util'
|
|
5
|
+
import { Kind, Optional } from '../../../src/schema'
|
|
6
|
+
|
|
7
|
+
import { OpenAPIV3 } from 'openapi-types'
|
|
8
|
+
|
|
9
|
+
type SchemaType = { type: string; format: string; isJson: boolean }
|
|
10
|
+
|
|
11
|
+
const schemaToMedia = ({ type, format, isJson }: SchemaType) =>
|
|
12
|
+
isJson || (type && ['object', 'number', 'boolean', 'array'].includes(type))
|
|
13
|
+
? 'application/json'
|
|
14
|
+
: format === 'byte'
|
|
15
|
+
? 'application/octet-stream'
|
|
16
|
+
: type === 'string'
|
|
17
|
+
? 'text/plain'
|
|
18
|
+
: '*/*'
|
|
19
|
+
|
|
20
|
+
export const OpenAPISerializer = async (g: Galbe, version = '3.0.3'): Promise<OpenAPIV3.Document> => {
|
|
21
|
+
let paths: any = {}
|
|
22
|
+
let components: OpenAPIV3.ComponentsObject = {
|
|
23
|
+
securitySchemes: {},
|
|
24
|
+
schemas: {},
|
|
25
|
+
parameters: {},
|
|
26
|
+
requestBodies: {},
|
|
27
|
+
responses: {}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const schemaToOpenapi = (
|
|
31
|
+
schema: STSchema
|
|
32
|
+
): { schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject; isJson?: boolean } => {
|
|
33
|
+
let s = {}
|
|
34
|
+
let kind = schema[Kind]
|
|
35
|
+
let isJson = false
|
|
36
|
+
|
|
37
|
+
let pattern = schema?.pattern?.toString()
|
|
38
|
+
if (pattern) pattern = pattern.substring(1, pattern.length - 1)
|
|
39
|
+
|
|
40
|
+
let minLength = schema?.minLength
|
|
41
|
+
let maxLength = schema?.maxLength
|
|
42
|
+
let minimum = schema?.min
|
|
43
|
+
let maximum = schema?.max
|
|
44
|
+
let exclusiveMinimum = schema?.exclusiveMin
|
|
45
|
+
let exclusiveMaximum = schema?.exclusiveMax
|
|
46
|
+
let minItems = schema?.minItems
|
|
47
|
+
let maxItems = schema?.maxItems
|
|
48
|
+
let uniqueItems = schema?.unique
|
|
49
|
+
|
|
50
|
+
if (components.schemas && (schema.id as string) in components.schemas) {
|
|
51
|
+
//@ts-ignore
|
|
52
|
+
return { schema: { $ref: `#/components/schemas/${schema.id}` } }
|
|
53
|
+
}
|
|
54
|
+
// TODO add constraints min, max etc.
|
|
55
|
+
if (kind === 'boolean') s = { type: 'boolean' }
|
|
56
|
+
else if (kind === 'byteArray') s = { type: 'string', format: 'byte' }
|
|
57
|
+
else if (kind === 'number')
|
|
58
|
+
s = {
|
|
59
|
+
type: 'number',
|
|
60
|
+
...(exclusiveMinimum ? { exclusiveMinimum } : {}),
|
|
61
|
+
...(exclusiveMaximum ? { exclusiveMaximum } : {}),
|
|
62
|
+
...(minimum ? { minimum } : {}),
|
|
63
|
+
...(maximum ? { maximum } : {})
|
|
64
|
+
}
|
|
65
|
+
else if (kind === 'integer')
|
|
66
|
+
s = {
|
|
67
|
+
type: 'integer',
|
|
68
|
+
...(exclusiveMinimum ? { exclusiveMinimum } : {}),
|
|
69
|
+
...(exclusiveMaximum ? { exclusiveMaximum } : {}),
|
|
70
|
+
...(minimum ? { minimum } : {}),
|
|
71
|
+
...(maximum ? { maximum } : {})
|
|
72
|
+
}
|
|
73
|
+
else if (kind === 'string')
|
|
74
|
+
s = {
|
|
75
|
+
type: 'string',
|
|
76
|
+
...(pattern ? { pattern } : {}),
|
|
77
|
+
...(minLength ? { minLength } : {}),
|
|
78
|
+
...(maxLength ? { maxLength } : {})
|
|
79
|
+
}
|
|
80
|
+
else if (kind === 'any') s = { type: 'string' }
|
|
81
|
+
else if (kind === 'literal') {
|
|
82
|
+
let value = (schema as STLiteral).value
|
|
83
|
+
s = { type: 'string', enum: [value] }
|
|
84
|
+
} else if (kind === 'array') {
|
|
85
|
+
s = {
|
|
86
|
+
type: 'array',
|
|
87
|
+
items: schemaToOpenapi((schema as STArray).items).schema,
|
|
88
|
+
...(minItems ? { minItems } : {}),
|
|
89
|
+
...(maxItems ? { maxItems } : {}),
|
|
90
|
+
...(uniqueItems ? { uniqueItems } : {})
|
|
91
|
+
}
|
|
92
|
+
} else if (kind === 'object') {
|
|
93
|
+
let props = (schema as STObject).props || {}
|
|
94
|
+
let required = Object.entries(props)
|
|
95
|
+
.filter(([_, v]) => !v?.[Optional])
|
|
96
|
+
.map(([k, _]) => k)
|
|
97
|
+
s = {
|
|
98
|
+
type: 'object',
|
|
99
|
+
properties: Object.fromEntries(Object.entries(props).map(([k, v]) => [k, schemaToOpenapi(v).schema])),
|
|
100
|
+
...(required.length ? { required } : {})
|
|
101
|
+
}
|
|
102
|
+
} else if (kind === 'json') {
|
|
103
|
+
let props = ((schema as STJson).props || {}) as STProps
|
|
104
|
+
let type = (schema as STJson).type
|
|
105
|
+
if (type === 'unknown') type = 'object'
|
|
106
|
+
let required = Object.entries(props)
|
|
107
|
+
.filter(([_, v]) => !v?.[Optional])
|
|
108
|
+
.map(([k, _]) => k)
|
|
109
|
+
isJson = true
|
|
110
|
+
s = {
|
|
111
|
+
type: type,
|
|
112
|
+
...(type === 'object'
|
|
113
|
+
? {
|
|
114
|
+
properties: Object.fromEntries(Object.entries(props).map(([k, v]) => [k, schemaToOpenapi(v).schema])),
|
|
115
|
+
...(required.length ? { required } : {})
|
|
116
|
+
}
|
|
117
|
+
: {})
|
|
118
|
+
}
|
|
119
|
+
} else if (kind === 'union') {
|
|
120
|
+
let anyOf = (schema as STUnion).anyOf
|
|
121
|
+
s = {
|
|
122
|
+
anyOf: anyOf.map(s => schemaToOpenapi(s).schema)
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
s = { title: schema.title, description: schema.description, ...s }
|
|
126
|
+
if (components.schemas && schema.id) {
|
|
127
|
+
components.schemas[schema.id] = s
|
|
128
|
+
return { schema: { $ref: `#/components/schemas/${schema.id}` } }
|
|
129
|
+
}
|
|
130
|
+
return { schema: s, isJson }
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const resolveRef = (schema: OpenAPIV3.SchemaObject | OpenAPIV3.ReferenceObject): OpenAPIV3.SchemaObject => {
|
|
134
|
+
if (!(schema as OpenAPIV3.ReferenceObject)?.$ref) return schema as OpenAPIV3.SchemaObject
|
|
135
|
+
let ref = (schema as OpenAPIV3.ReferenceObject)?.$ref
|
|
136
|
+
let match = ref.match('^#/components/(schemas|requestBodies|responses)/(.*)$')
|
|
137
|
+
if (!match) throw new Error(`Invalid schema ref ${ref}`)
|
|
138
|
+
let [_, kind, refPath] = match as [string, keyof typeof components, string]
|
|
139
|
+
//@ts-ignore
|
|
140
|
+
return refPath.split('/').reduce((c, k) => {
|
|
141
|
+
if (c && k in c) return c[k]
|
|
142
|
+
else return undefined
|
|
143
|
+
}, components[kind])
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const parseParam = (key: string, param: STSchema, kind: 'query' | 'header' | 'path' | 'cookie') => {
|
|
147
|
+
let { schema } = schemaToOpenapi({ ...param, [Optional]: false })
|
|
148
|
+
let p: OpenAPIV3.ParameterObject = {
|
|
149
|
+
name: key,
|
|
150
|
+
in: kind,
|
|
151
|
+
description: param?.description,
|
|
152
|
+
required: kind === 'path' ? true : !param[Optional] || undefined,
|
|
153
|
+
deprecated: param.deprecated,
|
|
154
|
+
schema
|
|
155
|
+
}
|
|
156
|
+
if (components.parameters && param.id) components.parameters[param.id] = p
|
|
157
|
+
return p
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const metaRoutes = g.meta?.reduce(
|
|
161
|
+
(routes, c) => ({ ...routes, ...c.routes }),
|
|
162
|
+
{} as Record<string, Record<string, Record<string, any>>>
|
|
163
|
+
)
|
|
164
|
+
walkRoutes(g.router.routes, r => {
|
|
165
|
+
let meta = metaRoutes?.[r.path]?.[r.method]
|
|
166
|
+
let path = r.path.replaceAll(/:([^\/]+)/g, '{$1}')
|
|
167
|
+
if (!(path in paths)) paths[path] = {}
|
|
168
|
+
let tags = [...(meta?.tags?.split(' ')?.map((t: string) => t.trim()) || []), ...(meta?.tag || [])]
|
|
169
|
+
let security: Record<string, any> = []
|
|
170
|
+
|
|
171
|
+
let pathParam = r.schema?.params
|
|
172
|
+
? Object.entries(r.schema?.params as Record<string, STSchema>).map(([k, v]) => parseParam(k, v, 'path'))
|
|
173
|
+
: []
|
|
174
|
+
let queryParam = r.schema?.query
|
|
175
|
+
? Object.entries(r.schema?.query as Record<string, STSchema>).map(([k, v]) => parseParam(k, v, 'query'))
|
|
176
|
+
: []
|
|
177
|
+
let headerParam = r.schema?.headers
|
|
178
|
+
? Object.entries(r.schema?.headers as Record<string, STSchema>)
|
|
179
|
+
.map(([k, v]) => {
|
|
180
|
+
let p = parseParam(k, v, 'header')
|
|
181
|
+
if (k.match(/authorization/i)) {
|
|
182
|
+
// TODO: handle other auth methods
|
|
183
|
+
if (v.pattern && v?.pattern?.toString() === '/^Bearer /') {
|
|
184
|
+
security.push({ bearerAuth: [] })
|
|
185
|
+
components.securitySchemes = { bearerAuth: { type: 'http', scheme: 'bearer' } }
|
|
186
|
+
return null
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
return p
|
|
190
|
+
})
|
|
191
|
+
.filter(p => p)
|
|
192
|
+
: []
|
|
193
|
+
// TODO cookieParam
|
|
194
|
+
let parameters = [...pathParam, ...queryParam, ...headerParam]
|
|
195
|
+
|
|
196
|
+
let requestBody
|
|
197
|
+
if (r.schema.body) {
|
|
198
|
+
let { schema, isJson } = schemaToOpenapi(r.schema.body)
|
|
199
|
+
let { type, format } = resolveRef(schema)
|
|
200
|
+
let media = schemaToMedia({ type, format, isJson } as SchemaType)
|
|
201
|
+
requestBody = {
|
|
202
|
+
description: r.schema.body.description,
|
|
203
|
+
required: !r.schema.body[Optional],
|
|
204
|
+
content: {
|
|
205
|
+
[media]: { schema }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
if (r.schema.body.id && components.requestBodies) {
|
|
209
|
+
components.requestBodies[r.schema.body.id] = requestBody
|
|
210
|
+
requestBody = { $ref: `#/components/requestBodies/${r.schema.body.id}` }
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
let responses
|
|
214
|
+
if (r.schema.response && Object.keys(r.schema.response).length) {
|
|
215
|
+
responses = Object.fromEntries(
|
|
216
|
+
Object.entries(r.schema.response).map(([status, v]) => {
|
|
217
|
+
let s = Number(status) as keyof typeof HttpStatus
|
|
218
|
+
let { schema, isJson } = schemaToOpenapi(v)
|
|
219
|
+
let { type, format } = resolveRef(schema)
|
|
220
|
+
let media = schemaToMedia({ type, format, isJson } as SchemaType)
|
|
221
|
+
let response: OpenAPIV3.ResponseObject = {
|
|
222
|
+
description: v.description || HttpStatus[Number(s) as keyof typeof HttpStatus] || 'Response',
|
|
223
|
+
content: { [media]: { schema: schema } }
|
|
224
|
+
}
|
|
225
|
+
if (components.responses && r.schema.response?.[s].id) {
|
|
226
|
+
components.responses[r.schema.response?.[s].id as string] = response
|
|
227
|
+
//@ts-ignore
|
|
228
|
+
response = { $ref: `#/components/responses/${r.schema.response?.[s].id}` }
|
|
229
|
+
}
|
|
230
|
+
return [s, response]
|
|
231
|
+
})
|
|
232
|
+
)
|
|
233
|
+
} else {
|
|
234
|
+
responses = {
|
|
235
|
+
default: { description: HttpStatus[200] }
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
paths[path][r.method] = {
|
|
239
|
+
tags: tags.length ? tags : undefined,
|
|
240
|
+
summary: meta?.head,
|
|
241
|
+
operationId: meta?.operationId,
|
|
242
|
+
parameters: parameters.length ? parameters : undefined,
|
|
243
|
+
requestBody,
|
|
244
|
+
responses,
|
|
245
|
+
...(security.length ? { security } : {}),
|
|
246
|
+
deprecated: meta?.deprecated ? true : undefined
|
|
247
|
+
}
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
//@ts-ignore
|
|
251
|
+
components = Object.entries(components).reduce((p, [k, v]) => {
|
|
252
|
+
if (Object.keys(v).length) p[k] = v
|
|
253
|
+
return p
|
|
254
|
+
}, {} as Record<string, OpenAPIV3.ComponentsObject>)
|
|
255
|
+
return {
|
|
256
|
+
openapi: version,
|
|
257
|
+
info: {
|
|
258
|
+
title: 'Galbe app',
|
|
259
|
+
version: '0.1.0'
|
|
260
|
+
},
|
|
261
|
+
paths,
|
|
262
|
+
components: Object.keys(components)?.length ? components : undefined
|
|
263
|
+
}
|
|
264
|
+
}
|
package/src/extras.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { OpenAPISerializer } from './extras/spec/openapi.serializer'
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
ErrorHandler,
|
|
12
12
|
GalbePlugin,
|
|
13
13
|
STBody,
|
|
14
|
+
STResponse,
|
|
14
15
|
STParams,
|
|
15
16
|
STHeaders,
|
|
16
17
|
STQuery
|
|
@@ -18,26 +19,28 @@ import type {
|
|
|
18
19
|
|
|
19
20
|
import server from './server'
|
|
20
21
|
import { GalbeRouter } from './router'
|
|
21
|
-
import { defineRoutes } from './routes'
|
|
22
|
-
import { logRoute } from './util'
|
|
23
22
|
import { SchemaType, type STObject, type Static } from './schema'
|
|
24
23
|
|
|
25
24
|
const overloadDiscriminer = <
|
|
25
|
+
M extends Method,
|
|
26
26
|
Path extends string,
|
|
27
27
|
H extends STHeaders,
|
|
28
28
|
P extends Partial<STParams<Path>>,
|
|
29
29
|
Q extends STQuery,
|
|
30
|
-
B extends STBody
|
|
30
|
+
B extends STBody,
|
|
31
|
+
R extends STResponse
|
|
31
32
|
>(
|
|
32
33
|
galbe: Galbe,
|
|
33
|
-
method:
|
|
34
|
+
method: M,
|
|
34
35
|
path: Path,
|
|
35
36
|
arg2:
|
|
36
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
37
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
38
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
39
|
-
arg3?:
|
|
40
|
-
|
|
37
|
+
| RequestSchema<M, Path, H, P, Q, B, R>
|
|
38
|
+
| Hook<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>[]
|
|
39
|
+
| Handler<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>,
|
|
40
|
+
arg3?:
|
|
41
|
+
| Hook<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>[]
|
|
42
|
+
| Handler<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>,
|
|
43
|
+
arg4?: Handler<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>
|
|
41
44
|
) => {
|
|
42
45
|
const defaultSchema = {}
|
|
43
46
|
if (typeof arg2 === 'function') {
|
|
@@ -53,26 +56,31 @@ const overloadDiscriminer = <
|
|
|
53
56
|
throw new Error('Undefined route signature')
|
|
54
57
|
}
|
|
55
58
|
const galbeMethod = <
|
|
59
|
+
M extends Method,
|
|
56
60
|
Path extends string,
|
|
57
61
|
H extends STHeaders,
|
|
58
62
|
P extends Partial<STParams<Path>>,
|
|
59
63
|
Q extends STQuery,
|
|
60
|
-
B extends STBody
|
|
64
|
+
B extends STBody,
|
|
65
|
+
R extends STResponse
|
|
61
66
|
>(
|
|
62
67
|
_galbe: Galbe,
|
|
63
|
-
method:
|
|
68
|
+
method: M,
|
|
64
69
|
path: Path,
|
|
65
|
-
schema: RequestSchema<Path, H, P, Q, B> | undefined,
|
|
66
|
-
hooks: Hook<Path, RequestSchema<Path, H, P, Q, B>>[] | undefined,
|
|
67
|
-
handler: Handler<Path, RequestSchema<Path, H, P, Q, B>>
|
|
70
|
+
schema: RequestSchema<M, Path, H, P, Q, B, R> | undefined,
|
|
71
|
+
hooks: Hook<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>[] | undefined,
|
|
72
|
+
handler: Handler<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>
|
|
68
73
|
) => {
|
|
69
74
|
schema = schema ?? {}
|
|
70
75
|
hooks = hooks || []
|
|
71
|
-
const context: Context<Path, typeof schema> = {
|
|
76
|
+
const context: Context<M, Path, typeof schema> = {
|
|
72
77
|
headers: {} as Static<STObject<Exclude<(typeof schema)['headers'], undefined>>>,
|
|
73
78
|
params: {} as any,
|
|
74
79
|
query: {} as Static<STObject<Exclude<(typeof schema)['query'], undefined>>>,
|
|
75
|
-
|
|
80
|
+
//@ts-ignore
|
|
81
|
+
body: ['get', 'options', 'head'].includes(method)
|
|
82
|
+
? null
|
|
83
|
+
: ({} as Static<Exclude<(typeof schema)['body'], undefined>>),
|
|
76
84
|
request: {} as Request,
|
|
77
85
|
state: {},
|
|
78
86
|
set: {} as {
|
|
@@ -92,11 +100,11 @@ const galbeMethod = <
|
|
|
92
100
|
}
|
|
93
101
|
}
|
|
94
102
|
|
|
103
|
+
/** Galbe Schema Type builder. See {@link https://galbe.dev/documentation/schemas#schema-types Schema Types} */
|
|
95
104
|
export const $T = new SchemaType()
|
|
96
105
|
|
|
97
106
|
export { RequestError } from './types'
|
|
98
107
|
|
|
99
|
-
const indexRoutes: { method: string; path: string }[] = []
|
|
100
108
|
/**
|
|
101
109
|
* #### Galbe Server
|
|
102
110
|
* Instanciate a Galbe web server
|
|
@@ -116,7 +124,6 @@ export class Galbe {
|
|
|
116
124
|
router: GalbeRouter
|
|
117
125
|
errorHandler?: ErrorHandler
|
|
118
126
|
listening: boolean = false
|
|
119
|
-
#prepare: boolean = false
|
|
120
127
|
server?: Server
|
|
121
128
|
plugins: GalbePlugin[] = []
|
|
122
129
|
constructor(config?: GalbeConfig) {
|
|
@@ -126,35 +133,32 @@ export class Galbe {
|
|
|
126
133
|
prefix: this.config?.basePath || '',
|
|
127
134
|
cacheEnabled: this.config?.router?.cacheEnabled
|
|
128
135
|
})
|
|
136
|
+
this.config.requestValidator = config?.requestValidator ?? { enabled: true }
|
|
137
|
+
this.config.responseValidator = config?.responseValidator ?? { enabled: true }
|
|
129
138
|
}
|
|
130
139
|
private add(route: any) {
|
|
131
140
|
this.router.add(route)
|
|
132
|
-
|
|
133
|
-
if (!this.#prepare) indexRoutes.push({ method: route.method, path: route.path })
|
|
134
|
-
else logRoute(route)
|
|
135
|
-
}
|
|
141
|
+
return route
|
|
136
142
|
}
|
|
137
143
|
async use(plugin: GalbePlugin) {
|
|
138
144
|
this.plugins.push(plugin)
|
|
139
145
|
}
|
|
146
|
+
async init() {
|
|
147
|
+
for (const p of this.plugins) {
|
|
148
|
+
if (p.init) await p.init(this.config?.plugin?.[p.name] || {}, this)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
140
151
|
async listen(port?: number) {
|
|
141
152
|
port = port || this.config?.port || 3000
|
|
142
153
|
this.config.port = port
|
|
143
154
|
if (this.listening) this.stop()
|
|
155
|
+
await this.init()
|
|
156
|
+
this.server = await server(this, port)
|
|
144
157
|
if (Bun.env.BUN_ENV === 'development') {
|
|
145
|
-
this.#prepare = true
|
|
146
|
-
console.log('🏗️ \x1b[1;30mConstructing routes\x1b[0m')
|
|
147
|
-
for (const r of indexRoutes) logRoute(r)
|
|
148
|
-
await defineRoutes(this.config || {}, this)
|
|
149
|
-
console.log('\n✅ \x1b[1;30mdone\x1b[0m')
|
|
150
|
-
this.server = await server(this, port)
|
|
151
158
|
const url = `http://localhost:${port}${this.config?.basePath || ''}`
|
|
152
|
-
console.log(`\
|
|
153
|
-
} else {
|
|
154
|
-
this.server = await server(this, port)
|
|
159
|
+
console.log(`\x1b[1;30m🚀 Server running at\x1b[0m \x1b[4;34m${url}\x1b[0m\n`)
|
|
155
160
|
}
|
|
156
161
|
this.listening = true
|
|
157
|
-
this.#prepare = false
|
|
158
162
|
return this.server
|
|
159
163
|
}
|
|
160
164
|
stop() {
|
|
@@ -163,96 +167,134 @@ export class Galbe {
|
|
|
163
167
|
onError(handler: ErrorHandler) {
|
|
164
168
|
this.errorHandler = handler
|
|
165
169
|
}
|
|
166
|
-
get: Endpoint = <
|
|
170
|
+
get: Endpoint<'get'> = <
|
|
167
171
|
Path extends string,
|
|
168
|
-
H extends STHeaders,
|
|
169
172
|
P extends Partial<STParams<Path>>,
|
|
173
|
+
H extends STHeaders,
|
|
170
174
|
Q extends STQuery,
|
|
171
|
-
B extends
|
|
175
|
+
B extends undefined,
|
|
176
|
+
R extends STResponse
|
|
172
177
|
>(
|
|
173
178
|
path: Path,
|
|
174
179
|
arg2:
|
|
175
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
176
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
177
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
178
|
-
arg3?:
|
|
179
|
-
|
|
180
|
+
| RequestSchema<'get', Path, H, P, Q, B, R>
|
|
181
|
+
| Hook<'get', Path, RequestSchema<'get', Path, H, P, Q, B, R>>[]
|
|
182
|
+
| Handler<'get', Path, RequestSchema<'get', Path, H, P, Q, B, R>>,
|
|
183
|
+
arg3?:
|
|
184
|
+
| Hook<'get', Path, RequestSchema<'get', Path, H, P, Q, B, R>>[]
|
|
185
|
+
| Handler<'get', Path, RequestSchema<'get', Path, H, P, Q, B, R>>,
|
|
186
|
+
arg4?: Handler<'get', Path, RequestSchema<'get', Path, H, P, Q, B, R>>
|
|
187
|
+
//@ts-ignore
|
|
180
188
|
) => this.add(overloadDiscriminer(this, 'get', path, arg2, arg3, arg4))
|
|
181
|
-
post: Endpoint = <
|
|
189
|
+
post: Endpoint<'post'> = <
|
|
182
190
|
Path extends string,
|
|
183
|
-
H extends STHeaders,
|
|
184
191
|
P extends Partial<STParams<Path>>,
|
|
192
|
+
H extends STHeaders,
|
|
185
193
|
Q extends STQuery,
|
|
186
|
-
B extends STBody
|
|
194
|
+
B extends STBody,
|
|
195
|
+
R extends STResponse
|
|
187
196
|
>(
|
|
188
197
|
path: Path,
|
|
189
198
|
arg2:
|
|
190
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
191
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
192
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
193
|
-
arg3?:
|
|
194
|
-
|
|
199
|
+
| RequestSchema<'post', Path, H, P, Q, B, R>
|
|
200
|
+
| Hook<'post', Path, RequestSchema<'post', Path, H, P, Q, B, R>>[]
|
|
201
|
+
| Handler<'post', Path, RequestSchema<'post', Path, H, P, Q, B, R>>,
|
|
202
|
+
arg3?:
|
|
203
|
+
| Hook<'post', Path, RequestSchema<'post', Path, H, P, Q, B, R>>[]
|
|
204
|
+
| Handler<'post', Path, RequestSchema<'post', Path, H, P, Q, B, R>>,
|
|
205
|
+
arg4?: Handler<'post', Path, RequestSchema<'post', Path, H, P, Q, B, R>>
|
|
206
|
+
//@ts-ignore
|
|
195
207
|
) => this.add(overloadDiscriminer(this, 'post', path, arg2, arg3, arg4))
|
|
196
|
-
put: Endpoint = <
|
|
208
|
+
put: Endpoint<'put'> = <
|
|
197
209
|
Path extends string,
|
|
198
|
-
H extends STHeaders,
|
|
199
210
|
P extends Partial<STParams<Path>>,
|
|
211
|
+
H extends STHeaders,
|
|
200
212
|
Q extends STQuery,
|
|
201
|
-
B extends STBody
|
|
213
|
+
B extends STBody,
|
|
214
|
+
R extends STResponse
|
|
202
215
|
>(
|
|
203
216
|
path: Path,
|
|
204
217
|
arg2:
|
|
205
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
206
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
207
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
208
|
-
arg3?:
|
|
209
|
-
|
|
218
|
+
| RequestSchema<'put', Path, H, P, Q, B, R>
|
|
219
|
+
| Hook<'put', Path, RequestSchema<'put', Path, H, P, Q, B, R>>[]
|
|
220
|
+
| Handler<'put', Path, RequestSchema<'put', Path, H, P, Q, B, R>>,
|
|
221
|
+
arg3?:
|
|
222
|
+
| Hook<'put', Path, RequestSchema<'put', Path, H, P, Q, B, R>>[]
|
|
223
|
+
| Handler<'put', Path, RequestSchema<'put', Path, H, P, Q, B, R>>,
|
|
224
|
+
arg4?: Handler<'put', Path, RequestSchema<'put', Path, H, P, Q, B, R>>
|
|
210
225
|
) => this.add(overloadDiscriminer(this, 'put', path, arg2, arg3, arg4))
|
|
211
|
-
patch: Endpoint = <
|
|
226
|
+
patch: Endpoint<'patch'> = <
|
|
212
227
|
Path extends string,
|
|
213
|
-
H extends STHeaders,
|
|
214
228
|
P extends Partial<STParams<Path>>,
|
|
229
|
+
H extends STHeaders,
|
|
215
230
|
Q extends STQuery,
|
|
216
|
-
B extends STBody
|
|
231
|
+
B extends STBody,
|
|
232
|
+
R extends STResponse
|
|
217
233
|
>(
|
|
218
234
|
path: Path,
|
|
219
235
|
arg2:
|
|
220
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
221
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
222
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
223
|
-
arg3?:
|
|
224
|
-
|
|
236
|
+
| RequestSchema<'patch', Path, H, P, Q, B, R>
|
|
237
|
+
| Hook<'patch', Path, RequestSchema<'patch', Path, H, P, Q, B, R>>[]
|
|
238
|
+
| Handler<'patch', Path, RequestSchema<'patch', Path, H, P, Q, B, R>>,
|
|
239
|
+
arg3?:
|
|
240
|
+
| Hook<'patch', Path, RequestSchema<'patch', Path, H, P, Q, B, R>>[]
|
|
241
|
+
| Handler<'patch', Path, RequestSchema<'patch', Path, H, P, Q, B, R>>,
|
|
242
|
+
arg4?: Handler<'patch', Path, RequestSchema<'patch', Path, H, P, Q, B, R>>
|
|
225
243
|
) => this.add(overloadDiscriminer(this, 'patch', path, arg2, arg3, arg4))
|
|
226
|
-
delete: Endpoint = <
|
|
244
|
+
delete: Endpoint<'delete'> = <
|
|
227
245
|
Path extends string,
|
|
228
|
-
H extends STHeaders,
|
|
229
246
|
P extends Partial<STParams<Path>>,
|
|
247
|
+
H extends STHeaders,
|
|
230
248
|
Q extends STQuery,
|
|
231
|
-
B extends STBody
|
|
249
|
+
B extends STBody,
|
|
250
|
+
R extends STResponse
|
|
232
251
|
>(
|
|
233
252
|
path: Path,
|
|
234
253
|
arg2:
|
|
235
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
236
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
237
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
238
|
-
arg3?:
|
|
239
|
-
|
|
254
|
+
| RequestSchema<'delete', Path, H, P, Q, B, R>
|
|
255
|
+
| Hook<'delete', Path, RequestSchema<'delete', Path, H, P, Q, B, R>>[]
|
|
256
|
+
| Handler<'delete', Path, RequestSchema<'delete', Path, H, P, Q, B, R>>,
|
|
257
|
+
arg3?:
|
|
258
|
+
| Hook<'delete', Path, RequestSchema<'delete', Path, H, P, Q, B, R>>[]
|
|
259
|
+
| Handler<'delete', Path, RequestSchema<'delete', Path, H, P, Q, B, R>>,
|
|
260
|
+
arg4?: Handler<'delete', Path, RequestSchema<'delete', Path, H, P, Q, B, R>>
|
|
240
261
|
) => this.add(overloadDiscriminer(this, 'delete', path, arg2, arg3, arg4))
|
|
241
|
-
options: Endpoint = <
|
|
262
|
+
options: Endpoint<'options'> = <
|
|
242
263
|
Path extends string,
|
|
243
|
-
H extends STHeaders,
|
|
244
264
|
P extends Partial<STParams<Path>>,
|
|
265
|
+
H extends STHeaders,
|
|
245
266
|
Q extends STQuery,
|
|
246
|
-
B extends STBody
|
|
267
|
+
B extends STBody,
|
|
268
|
+
R extends STResponse
|
|
247
269
|
>(
|
|
248
270
|
path: Path,
|
|
249
271
|
arg2:
|
|
250
|
-
| RequestSchema<Path, H, P, Q, B>
|
|
251
|
-
| Hook<Path, RequestSchema<Path, H, P, Q, B>>[]
|
|
252
|
-
| Handler<Path, RequestSchema<Path, H, P, Q, B>>,
|
|
253
|
-
arg3?:
|
|
254
|
-
|
|
272
|
+
| RequestSchema<'options', Path, H, P, Q, B, R>
|
|
273
|
+
| Hook<'options', Path, RequestSchema<'options', Path, H, P, Q, B, R>>[]
|
|
274
|
+
| Handler<'options', Path, RequestSchema<'options', Path, H, P, Q, B, R>>,
|
|
275
|
+
arg3?:
|
|
276
|
+
| Hook<'options', Path, RequestSchema<'options', Path, H, P, Q, B, R>>[]
|
|
277
|
+
| Handler<'options', Path, RequestSchema<'options', Path, H, P, Q, B, R>>,
|
|
278
|
+
arg4?: Handler<'options', Path, RequestSchema<'options', Path, H, P, Q, B, R>>
|
|
255
279
|
) => this.add(overloadDiscriminer(this, 'options', path, arg2, arg3, arg4))
|
|
280
|
+
head: Endpoint<'head'> = <
|
|
281
|
+
Path extends string,
|
|
282
|
+
P extends Partial<STParams<Path>>,
|
|
283
|
+
H extends STHeaders,
|
|
284
|
+
Q extends STQuery,
|
|
285
|
+
B extends STBody,
|
|
286
|
+
R extends STResponse
|
|
287
|
+
>(
|
|
288
|
+
path: Path,
|
|
289
|
+
arg2:
|
|
290
|
+
| RequestSchema<'head', Path, H, P, Q, B, R>
|
|
291
|
+
| Hook<'head', Path, RequestSchema<'head', Path, H, P, Q, B, R>>[]
|
|
292
|
+
| Handler<'head', Path, RequestSchema<'head', Path, H, P, Q, B, R>>,
|
|
293
|
+
arg3?:
|
|
294
|
+
| Hook<'head', Path, RequestSchema<'head', Path, H, P, Q, B, R>>[]
|
|
295
|
+
| Handler<'head', Path, RequestSchema<'head', Path, H, P, Q, B, R>>,
|
|
296
|
+
arg4?: Handler<'head', Path, RequestSchema<'head', Path, H, P, Q, B, R>>
|
|
297
|
+
) => this.add(overloadDiscriminer(this, 'head', path, arg2, arg3, arg4))
|
|
256
298
|
}
|
|
257
299
|
|
|
258
300
|
export * from './types'
|