galbe 0.10.1 → 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/README.md +3 -0
- 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/cli.md +25 -22
- package/docs/configuration.md +80 -0
- package/docs/context.md +38 -52
- package/docs/error-handler.md +20 -31
- package/docs/getting-started.md +40 -157
- package/docs/handler.md +35 -21
- package/docs/hooks.md +16 -16
- package/docs/plugins.md +50 -57
- package/docs/router.md +2 -2
- package/docs/routes.md +56 -39
- package/docs/schemas.md +115 -69
- package/package.json +1 -1
- package/src/extras/spec/openapi.serializer.ts +83 -56
- package/src/index.ts +16 -11
- package/src/parser.ts +199 -147
- package/src/router.ts +2 -2
- package/src/schema.ts +156 -66
- package/src/server.ts +25 -17
- package/src/types.ts +54 -42
- package/src/util.ts +37 -9
- package/src/validator.ts +23 -19
- package/test/parser.test.ts +375 -272
- 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/router.test.ts +14 -0
- package/test/test.utils.ts +4 -2
- package/test/types.test.ts +909 -0
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
export type GalbeClientMode = 'response' | 'direct'
|
|
2
1
|
export type GalbeClientConfig = {
|
|
3
2
|
server?: { url?: string }
|
|
4
3
|
}
|
|
@@ -7,7 +6,7 @@ export const Kind = Symbol.for('json.string')
|
|
|
7
6
|
type Json<T> = { T: T }
|
|
8
7
|
|
|
9
8
|
interface GR<S extends number | 'default' = 'default', B = any, OKS extends number = OKStatusCode> {
|
|
10
|
-
status: Exclude<S,
|
|
9
|
+
status: Exclude<S, 'default'>
|
|
11
10
|
ok: S extends OKS ? true : false
|
|
12
11
|
redirected: boolean
|
|
13
12
|
statusText: string
|
|
@@ -18,16 +17,16 @@ interface GR<S extends number | 'default' = 'default', B = any, OKS extends numb
|
|
|
18
17
|
stream?: ST
|
|
19
18
|
) => B extends Uint8Array
|
|
20
19
|
? ST extends true
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
? Promise<AsyncGenerator<Uint8Array, void, unknown>>
|
|
21
|
+
: B extends Json<infer T>
|
|
22
|
+
? Promise<T>
|
|
23
|
+
: Promise<B>
|
|
25
24
|
: B extends string
|
|
26
25
|
? ST extends true
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
? Promise<AsyncGenerator<string, void, unknown>>
|
|
27
|
+
: B extends Json<infer T>
|
|
28
|
+
? Promise<T>
|
|
29
|
+
: Promise<B>
|
|
31
30
|
: B extends Json<infer T>
|
|
32
31
|
? Promise<T>
|
|
33
32
|
: Promise<B>
|
|
@@ -42,16 +41,32 @@ type PGR<
|
|
|
42
41
|
O extends number = 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 226
|
|
43
42
|
> = Promise<GR<S, B, O>>
|
|
44
43
|
|
|
45
|
-
type
|
|
44
|
+
type ContentType = 'byteArray' | 'text' | 'json' | 'urlForm' | 'multipart' | 'default'
|
|
45
|
+
type RequestOptions<
|
|
46
|
+
H = any,
|
|
47
|
+
Q = any,
|
|
48
|
+
B extends Partial<Record<ContentType, any>> = Partial<Record<ContentType, any>>,
|
|
49
|
+
C extends keyof B = keyof B
|
|
50
|
+
> = {
|
|
51
|
+
method?: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD'
|
|
46
52
|
headers?: H
|
|
47
53
|
query?: Q
|
|
48
|
-
|
|
49
|
-
|
|
54
|
+
contentType?: C
|
|
55
|
+
body?: B[C]
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
const decoder = new TextDecoder()
|
|
53
59
|
const DEFAULT_HEADERS = {
|
|
54
|
-
'user-agent': 'Galbe//*%(()=>version)()%*/'
|
|
60
|
+
'user-agent': 'Galbe//*%(()=>version)()%*/',
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const formdata = (data: Record<string, string | string[] | Blob>): FormData => {
|
|
64
|
+
const form = new FormData()
|
|
65
|
+
for (const [k, v] of Object.entries(data)) {
|
|
66
|
+
if (Array.isArray(v)) for (const v2 of v) form.append(String(k), String(v2))
|
|
67
|
+
else form.append(String(k), String(v))
|
|
68
|
+
}
|
|
69
|
+
return form
|
|
55
70
|
}
|
|
56
71
|
|
|
57
72
|
// Typescript types
|
|
@@ -62,27 +77,23 @@ Object.entries(types).map(([tk, t])=>{
|
|
|
62
77
|
%*/
|
|
63
78
|
|
|
64
79
|
export default class GalbeClient {
|
|
65
|
-
config?: GalbeClientConfig
|
|
66
80
|
/*%
|
|
67
81
|
Object.entries(routes).map(([method, list])=>{
|
|
68
82
|
return`${method} = {\n${list.map( r => {
|
|
69
83
|
let p = Object.entries(r.params)
|
|
70
84
|
let schemas = Object.keys(r.schemas).length ?
|
|
71
|
-
`<${r.schemas.headers??'any'},${r.schemas.query??'any'},${r.schemas.body??'any'}>`:
|
|
85
|
+
`<${r.schemas.headers??'any'},${r.schemas.query??'any'},${r.schemas.body??'any'},CT>`:
|
|
72
86
|
''
|
|
73
87
|
let oks = Object.keys(r.schemas?.response||{}).filter(s=>s>=200&&s<300)
|
|
74
88
|
let responses = Object.keys(r.schemas?.response||{}).length ?
|
|
75
89
|
`${Object.entries(r.schemas.response).filter(([s,_])=>s!=='"default"').map(([k,v])=>`PGR<${k},${v}${oks?.length?`,${oks.join('|')}`:''}>`).join('|')}|PGR<Exclude<HttpStatusCode,${Object.keys(r.schemas.response).filter(s=>s!=='"default"').join('|')}>,${'"default"' in r.schemas.response ? r.schemas.response['"default"'] : 'any'}${oks?.length?`,${oks.join('|')}`:''}>`:
|
|
76
90
|
`PGR<HttpStatusCode,any${oks?.length?`,${oks.join('|')}`:',any'}>`
|
|
77
|
-
return ` '${r.path}'
|
|
91
|
+
return ` '${r.path}':<CT extends ${r.contentTypes}>(${p.length?p.map(([k,v])=>`${k}:${v.type}`).join(',')+', ':''}options:RequestOptions${schemas}={})=>this.fetch(\`${r.pathT}\`,{...options,method:'${r.method.toUpperCase()}'}) as ${responses}`
|
|
78
92
|
}).join(',\n')}\n}`
|
|
79
93
|
}).join('\n')
|
|
80
94
|
%*/
|
|
81
95
|
|
|
82
|
-
constructor(config?: GalbeClientConfig) {
|
|
83
|
-
//@ts-ignore
|
|
84
|
-
this.config = { mode: 'response', ...config }
|
|
85
|
-
}
|
|
96
|
+
constructor(private readonly config?: GalbeClientConfig) {}
|
|
86
97
|
|
|
87
98
|
async fetch(path: string, options: RequestOptions) {
|
|
88
99
|
let url = `${this?.config?.server?.url ?? ''}${path}`
|
|
@@ -90,8 +101,23 @@ export default class GalbeClient {
|
|
|
90
101
|
url = `${url}?${params.toString()}`
|
|
91
102
|
let res = await fetch(url, {
|
|
92
103
|
method: options?.method || 'GET',
|
|
93
|
-
headers: {
|
|
94
|
-
|
|
104
|
+
headers: {
|
|
105
|
+
...DEFAULT_HEADERS,
|
|
106
|
+
...(options?.contentType && ['byteArray', 'text', 'json', 'urlForm'].includes(options.contentType)
|
|
107
|
+
? {
|
|
108
|
+
'content-type': {
|
|
109
|
+
byteArray: 'application/octet-stream',
|
|
110
|
+
text: 'text/plain',
|
|
111
|
+
json: 'application/json',
|
|
112
|
+
urlForm: 'application/x-www-form-urlencoded',
|
|
113
|
+
}[options.contentType],
|
|
114
|
+
}
|
|
115
|
+
: {}),
|
|
116
|
+
...(options?.headers || {}),
|
|
117
|
+
},
|
|
118
|
+
...(options?.body
|
|
119
|
+
? { body: options?.contentType === 'multipart' ? formdata(options?.body) : JSON.stringify(options.body) }
|
|
120
|
+
: {}),
|
|
95
121
|
})
|
|
96
122
|
return {
|
|
97
123
|
headers: res.headers,
|
|
@@ -147,7 +173,7 @@ export default class GalbeClient {
|
|
|
147
173
|
}
|
|
148
174
|
}
|
|
149
175
|
return res.body
|
|
150
|
-
}
|
|
176
|
+
},
|
|
151
177
|
}
|
|
152
178
|
}
|
|
153
179
|
|
|
@@ -157,7 +183,7 @@ export default class GalbeClient {
|
|
|
157
183
|
return list.filter(r=>r.alias).map(r => {
|
|
158
184
|
let p = Object.entries(r.params)
|
|
159
185
|
let schemas = Object.keys(r.schemas).length ?
|
|
160
|
-
`<${r.schemas.headers??'any'},${r.schemas.query??'any'},${r.schemas.body??'any'}>`:
|
|
186
|
+
`<${r.schemas.headers??'any'},${r.schemas.query??'any'},${r.schemas.body??'any'},CT>`:
|
|
161
187
|
''
|
|
162
188
|
let oks = Object.keys(r.schemas?.response||{}).filter(s=>s>=200&&s<300)
|
|
163
189
|
let responses = Object.keys(r.schemas?.response||{}).length ?
|
|
@@ -167,7 +193,7 @@ export default class GalbeClient {
|
|
|
167
193
|
let description = r.description ? ` * ${r.description.replace(/\n/g,'\n * ')}` : ''
|
|
168
194
|
let params = Object.entries(r.schema.params || {}).map( ([k,v])=>`\n * @param ${k} - ${v.description?.replace(/\n/g,'\n ')}` ).join('')
|
|
169
195
|
let query = Object.entries(r.schema.query || {}).map( ([k,v])=>`\n * @param options.query.${k} - ${v.description?.replace(/\n/g,'\n ')}` ).join('')
|
|
170
|
-
return `/**\n${summary}${description}\n *${params}${query}\n *\/\n ${r.alias}(${p.length ? p.map(([k,v])=>`${k}: ${v.type}`).join(', ')+', ':''}options: RequestOptions${schemas} = {}){return this.fetch(\`${r.pathT}\`, {...options, method: '${r.method.toUpperCase()}'}) as ${responses}}\n`
|
|
196
|
+
return `/**\n${summary}${description}\n *${params}${query}\n *\/\n ${r.alias}<CT extends ${r.contentTypes}>(${p.length ? p.map(([k,v])=>`${k}: ${v.type}`).join(', ')+', ':''}options: RequestOptions${schemas} = {}){return this.fetch(\`${r.pathT}\`, {...options, method: '${r.method.toUpperCase()}'}) as ${responses}}\n`
|
|
171
197
|
}).join(' ')
|
|
172
198
|
})
|
|
173
199
|
%*/
|
package/bin/util.ts
CHANGED
|
@@ -15,14 +15,14 @@ export const fmtVal = (v: any) => {
|
|
|
15
15
|
if (typeof v === 'number') return `\x1b[36m${v}\x1b[0m`
|
|
16
16
|
return v
|
|
17
17
|
}
|
|
18
|
-
export const fmtList = (l: any) => `[${l.map((v:any) => fmtVal(v)).join(', ')}]`
|
|
18
|
+
export const fmtList = (l: any) => `[${l.map((v: any) => fmtVal(v)).join(', ')}]`
|
|
19
19
|
export const fmtInterval = (a: any, b: any) => `[${fmtVal(a)}-${fmtVal(b)}]`
|
|
20
20
|
|
|
21
21
|
export const silentExec = async (fn: () => any) => {
|
|
22
22
|
let consoleMock = Object.fromEntries(
|
|
23
23
|
Object.entries(console)
|
|
24
24
|
.filter(([_, v]) => typeof v === 'function')
|
|
25
|
-
.map(([k, _]) => [k, () => {
|
|
25
|
+
.map(([k, _]) => [k, () => {}])
|
|
26
26
|
)
|
|
27
27
|
const _console = console
|
|
28
28
|
const _processStdoutWrite = process.stdout.write
|
|
@@ -30,9 +30,9 @@ export const silentExec = async (fn: () => any) => {
|
|
|
30
30
|
//@ts-ignore
|
|
31
31
|
global.console = consoleMock
|
|
32
32
|
//@ts-ignore
|
|
33
|
-
process.stdout.write = function () {
|
|
33
|
+
process.stdout.write = function () {}
|
|
34
34
|
//@ts-ignore
|
|
35
|
-
process.stderr.write = function () {
|
|
35
|
+
process.stderr.write = function () {}
|
|
36
36
|
const r = await fn()
|
|
37
37
|
global.console = _console
|
|
38
38
|
process.stdout.write = _processStdoutWrite
|
|
@@ -50,7 +50,7 @@ export const watchDir = async (
|
|
|
50
50
|
let watcher = watch(path, {
|
|
51
51
|
persistent: false,
|
|
52
52
|
ignored: options?.ignore,
|
|
53
|
-
ignoreInitial: true
|
|
53
|
+
ignoreInitial: true,
|
|
54
54
|
})
|
|
55
55
|
watcher.on('all', async (eventType, filename) => {
|
|
56
56
|
if (filename.match(WATCH_IGNORE)) return
|
|
@@ -90,11 +90,31 @@ export const instanciateRoutes = async (g: Galbe) => {
|
|
|
90
90
|
console.log(`\x1b\[0;31m Error:\x1b[0m`)
|
|
91
91
|
console.log(errors?.[fp])
|
|
92
92
|
}
|
|
93
|
-
console.log(
|
|
93
|
+
console.log('')
|
|
94
94
|
}
|
|
95
95
|
console.log('\x1b[1;30m\x1b[32mdone\x1b[0m\n')
|
|
96
96
|
}
|
|
97
97
|
|
|
98
|
+
export function abbreviateVar(input: string): string {
|
|
99
|
+
if (!input) return ''
|
|
100
|
+
const normalized = input
|
|
101
|
+
.replace(/[_\-\s]+/g, ' ')
|
|
102
|
+
.replace(/([a-z0-9])([A-Z])/g, '$1 $2')
|
|
103
|
+
.replace(/([A-Z])([A-Z][a-z])/g, '$1 $2')
|
|
104
|
+
|
|
105
|
+
const tokens = normalized
|
|
106
|
+
.trim()
|
|
107
|
+
.split(/[^\p{L}\p{N}]+/u)
|
|
108
|
+
.filter(Boolean)
|
|
109
|
+
|
|
110
|
+
if (tokens.length === 0) return ''
|
|
111
|
+
|
|
112
|
+
return tokens
|
|
113
|
+
.map(t => t[0])
|
|
114
|
+
.join('')
|
|
115
|
+
.toLowerCase()
|
|
116
|
+
}
|
|
117
|
+
|
|
98
118
|
export const killPort = async (port: number) => {
|
|
99
119
|
let getProcCmd: string[], killCmd: (port: string) => string[]
|
|
100
120
|
|
|
@@ -172,5 +192,5 @@ export const HttpStatus = {
|
|
|
172
192
|
504: 'Gateway Timeout',
|
|
173
193
|
505: 'HTTP Version Not Supported',
|
|
174
194
|
507: 'Insufficient Storage',
|
|
175
|
-
511: 'Network Authentication Required'
|
|
195
|
+
511: 'Network Authentication Required',
|
|
176
196
|
}
|
package/docs/cli.md
CHANGED
|
@@ -9,7 +9,7 @@ However, if you want to use it directly from your terminal, you must either:
|
|
|
9
9
|
Install it globally using the following command:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
$ bun
|
|
12
|
+
$ bun i -g galbe
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Or run it with `bunx`:
|
|
@@ -26,15 +26,16 @@ Start a dev server running your Galbe application.
|
|
|
26
26
|
|
|
27
27
|
| Name | Description |
|
|
28
28
|
| ----- | -------------------------------------------------------- |
|
|
29
|
-
| index | The js or ts file that export
|
|
29
|
+
| index | The js or ts file that export your Galbe server instance. |
|
|
30
30
|
|
|
31
31
|
#### Options
|
|
32
32
|
|
|
33
|
-
| Short | Long
|
|
34
|
-
| ----- |
|
|
35
|
-
| -p | --port
|
|
36
|
-
| -w | --watch
|
|
37
|
-
| -
|
|
33
|
+
| Short | Long | Descritpion | Default |
|
|
34
|
+
| ----- | --------------- | --------------------------- | ------- |
|
|
35
|
+
| -p | --port | port number [1-65535] | 3000 |
|
|
36
|
+
| -w | --watch | watch file changes dir | false |
|
|
37
|
+
| -wi | --watchignore | ignored watch files regex | |
|
|
38
|
+
| -nc | --noclear | don't clear on file changes | false |
|
|
38
39
|
|
|
39
40
|
#### Example
|
|
40
41
|
|
|
@@ -51,6 +52,13 @@ export default g
|
|
|
51
52
|
|
|
52
53
|
```bash
|
|
53
54
|
$ galbe dev index.js -p 7357 -w
|
|
55
|
+
🏗️ Constructing routes
|
|
56
|
+
|
|
57
|
+
[GET] /example
|
|
58
|
+
|
|
59
|
+
done
|
|
60
|
+
|
|
61
|
+
🚀 Server running at http://localhost:7357
|
|
54
62
|
```
|
|
55
63
|
|
|
56
64
|
## build
|
|
@@ -61,11 +69,11 @@ Bundle your Galbe application.
|
|
|
61
69
|
|
|
62
70
|
| Name | Description |
|
|
63
71
|
| ----- | -------------------------------------------------------- |
|
|
64
|
-
| index | The js or ts file that export
|
|
72
|
+
| index | The js or ts file that export your Galbe server instance. |
|
|
65
73
|
|
|
66
74
|
#### Options
|
|
67
75
|
|
|
68
|
-
| Short | Long |
|
|
76
|
+
| Short | Long | Description | Default |
|
|
69
77
|
| ----- | --------- | ------------------------------ | -------- |
|
|
70
78
|
| -o | --out | output directory | dist/app |
|
|
71
79
|
| -C | --compile | create a standalone executable | false |
|
|
@@ -74,7 +82,6 @@ Bundle your Galbe application.
|
|
|
74
82
|
#### Example
|
|
75
83
|
|
|
76
84
|
index.js
|
|
77
|
-
|
|
78
85
|
```js
|
|
79
86
|
import { Galbe } from 'galbe'
|
|
80
87
|
|
|
@@ -87,7 +94,7 @@ $ galbe build index.js
|
|
|
87
94
|
|
|
88
95
|
## generate
|
|
89
96
|
|
|
90
|
-
Generate resources
|
|
97
|
+
Generate resources around your Galbe application.
|
|
91
98
|
|
|
92
99
|
### client
|
|
93
100
|
|
|
@@ -97,11 +104,11 @@ Generate a client for your Galbe application.
|
|
|
97
104
|
|
|
98
105
|
| Name | Description |
|
|
99
106
|
| ----- | -------------------------------------------------------- |
|
|
100
|
-
| index | The js or ts file that export
|
|
107
|
+
| index | The js or ts file that export your Galbe server instance. |
|
|
101
108
|
|
|
102
109
|
#### Options
|
|
103
110
|
|
|
104
|
-
| Short | Long |
|
|
111
|
+
| Short | Long | Description | Default |
|
|
105
112
|
| ----- | -------- | -------------------------- | ------------------------------------ |
|
|
106
113
|
| -o | --out | output file | dist/(client.ts \| client.js \| cli) |
|
|
107
114
|
| -t | --target | build target [ts, js, cli] | ts |
|
|
@@ -116,10 +123,6 @@ $ cd galbe-example
|
|
|
116
123
|
$ bun install
|
|
117
124
|
```
|
|
118
125
|
|
|
119
|
-
> [!NOTE]
|
|
120
|
-
> In order for the following examples to work, you must ensure that an instance of you galbe app is running on port 3000.
|
|
121
|
-
> You can do that by running `bun run dev`.
|
|
122
|
-
|
|
123
126
|
##### JS or TS client
|
|
124
127
|
|
|
125
128
|
To generate a JS or TS client of that application, you can run the following command:
|
|
@@ -195,7 +198,7 @@ Hello Pierre! You're 29 y.o.
|
|
|
195
198
|
```
|
|
196
199
|
|
|
197
200
|
> [!IMPORTANT]
|
|
198
|
-
> A `GCLI_SERVER_URL` environment variable must be defined. It should
|
|
201
|
+
> A `GCLI_SERVER_URL` environment variable must be defined. It should indicate the url of the Galbe server you want to target.
|
|
199
202
|
> In that specific case `http://localhost:3000`.
|
|
200
203
|
|
|
201
204
|
### spec
|
|
@@ -206,11 +209,11 @@ Generate the spec of your Galbe application.
|
|
|
206
209
|
|
|
207
210
|
| Name | Description |
|
|
208
211
|
| ----- | -------------------------------------------------------- |
|
|
209
|
-
| index | The js or ts file that export
|
|
212
|
+
| index | The js or ts file that export your Galbe server instance. |
|
|
210
213
|
|
|
211
214
|
#### Options
|
|
212
215
|
|
|
213
|
-
| Short | Long |
|
|
216
|
+
| Short | Long | Description | Default |
|
|
214
217
|
| ----- | -------- | ------------------------------------------------ | ----------------------- |
|
|
215
218
|
| -t | --target | spec target [openapi:3.0:json, openapi:3.0:yaml] | openapi:3.0:yaml |
|
|
216
219
|
| -b | --base | base spec file | |
|
|
@@ -218,7 +221,7 @@ Generate the spec of your Galbe application.
|
|
|
218
221
|
|
|
219
222
|
#### Example
|
|
220
223
|
|
|
221
|
-
Let's try to generate the
|
|
224
|
+
Let's try to generate the spec of the project defined in the previous client section. You can then run:
|
|
222
225
|
|
|
223
226
|
```bash
|
|
224
227
|
$ galbe generate spec index.ts
|
|
@@ -263,7 +266,7 @@ Generate the code and project structure from spec.
|
|
|
263
266
|
|
|
264
267
|
#### Options
|
|
265
268
|
|
|
266
|
-
| Short | Long |
|
|
269
|
+
| Short | Long | Description | Default |
|
|
267
270
|
| ----- | -------- | ------------------------------------------------- | -------------------------- |
|
|
268
271
|
| -f | --format | input format [openapi:3.0:yaml, openapi:3.0:json] | openapi:3.0:(yaml \| json) |
|
|
269
272
|
| -t | --target | source target [ts, js] | ts |
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
## Configuring Galbe
|
|
4
|
+
|
|
5
|
+
By default, Galbe automatically attempts to resolve a configuration file named `galbe.config.{js,ts}` located in the same directory as your entry file.
|
|
6
|
+
|
|
7
|
+
The configuration file should export a default object containing your settings:
|
|
8
|
+
|
|
9
|
+
```js
|
|
10
|
+
export default {
|
|
11
|
+
// config properties
|
|
12
|
+
}
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
Alternatively, you can pass your configuration directly to your Galbe server during instantiation, as shown below:
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import { Galbe } from "galbe"
|
|
19
|
+
|
|
20
|
+
const galbe = new Galbe({
|
|
21
|
+
// config properties
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export default galbe
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> [!NOTE]
|
|
28
|
+
> You can use both configuration methods simultaneously. Galbe will first apply the settings from `galbe.config.{js,ts}`, and any properties passed during instantiation will override the corresponding ones from the configuration file.
|
|
29
|
+
|
|
30
|
+
## Configuration Properties
|
|
31
|
+
|
|
32
|
+
### hostname
|
|
33
|
+
The hostname of the server. Default: `localhost`.
|
|
34
|
+
|
|
35
|
+
### port
|
|
36
|
+
The port number the server will listen on. Default: `3000`.
|
|
37
|
+
|
|
38
|
+
### basePath
|
|
39
|
+
A base path added as a prefix to all routes.
|
|
40
|
+
|
|
41
|
+
### routes
|
|
42
|
+
A glob pattern or list of glob patterns defining the route files to be analyzed by the [Automatic Route Analyzer](routes.md#automatic-route-analyzer). Default: `src/**/*.route.{js,ts}`.
|
|
43
|
+
|
|
44
|
+
### plugin
|
|
45
|
+
A property used by plugins to add specific configurations. Each key should correspond to a [Unique Plugin Identifier](plugins.md).
|
|
46
|
+
|
|
47
|
+
### tls
|
|
48
|
+
Enables or disables TLS support. Default: `false`.
|
|
49
|
+
- **tls.key**: Path to the private key file.
|
|
50
|
+
- **tls.cert**: Path to the certificate file.
|
|
51
|
+
- **tls.ca**: Path to the certificate authority file.
|
|
52
|
+
|
|
53
|
+
### requestValidator.enabled
|
|
54
|
+
Enables or disables _request_ schema validation (see [Request Schema Definition](schemas.md#request-schema-definition)). Default: `true`.
|
|
55
|
+
|
|
56
|
+
### responseValidator.enabled
|
|
57
|
+
Enables or disables _response_ schema validation (see [Response Schema Definition](schemas.md#request-schema-definition#response)). Default: `true`.
|
|
58
|
+
|
|
59
|
+
## Config Type Safety
|
|
60
|
+
|
|
61
|
+
To ensure type safety for your configuration, use the `config` helper method, which leverages your IDE’s IntelliSense:
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
import { config } from "galbe"
|
|
65
|
+
|
|
66
|
+
export default config({
|
|
67
|
+
// ...
|
|
68
|
+
})
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Alternatively, if you are using TypeScript, you can apply the `GalbeConfig` type to enforce type consistency:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import type { GalbeConfig } from "galbe"
|
|
75
|
+
|
|
76
|
+
export default {
|
|
77
|
+
// ...
|
|
78
|
+
} satisfies GalbeConfig
|
|
79
|
+
```
|
|
80
|
+
|
package/docs/context.md
CHANGED
|
@@ -1,41 +1,30 @@
|
|
|
1
1
|
# Context
|
|
2
2
|
|
|
3
|
-
An instance of the context object is created when a new request is initiated and
|
|
4
|
-
See the [Lifecycle](https://galbe.dev/documentation/lifecycle) section to get more details.
|
|
3
|
+
An instance of the context object is created when a new request is initiated and is carried throughout the entire request lifecycle. See the [Lifecycle](https://galbe.dev/documentation/lifecycle) section for more details.
|
|
5
4
|
|
|
6
|
-
Its purpose is to
|
|
5
|
+
Its purpose is to carry all relevant information about the request and facilitate data sharing between different stages of the request lifecycle.
|
|
7
6
|
|
|
8
7
|
## Definition
|
|
9
8
|
|
|
10
|
-
A context has the following properties:
|
|
9
|
+
A context object has the following properties:
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
### request
|
|
13
12
|
|
|
14
13
|
An instance of the [Request](https://developer.mozilla.org/en-US/docs/Web/API/Request) object created by the server.
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
### headers
|
|
17
16
|
|
|
18
|
-
A
|
|
17
|
+
A JavaScript object representing the headers of the current request.
|
|
19
18
|
|
|
20
|
-
- key (string):
|
|
21
|
-
- value
|
|
19
|
+
- **key** (string): Header name
|
|
20
|
+
- **value** (string | [schema defined](schemas.md#headers)): Header value
|
|
22
21
|
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
"accept": "*/*",
|
|
26
|
-
"accept-encoding": "gzip, deflate, br",
|
|
27
|
-
"cookie": "Cookie_1=value; Cookie_2=value",
|
|
28
|
-
"host": "localhost:3000",
|
|
29
|
-
"user-agent": "galbe/1.0.0"
|
|
30
|
-
}
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
**params**
|
|
22
|
+
### params
|
|
34
23
|
|
|
35
|
-
A
|
|
24
|
+
A JavaScript object representing the route parameters of the current request.
|
|
36
25
|
|
|
37
|
-
- key (string):
|
|
38
|
-
- value
|
|
26
|
+
- **key** (string): Parameter name
|
|
27
|
+
- **value** (string | [schema defined](schemas.md#params)): Parameter value
|
|
39
28
|
|
|
40
29
|
```js
|
|
41
30
|
galbe.get('/default/:p1/foo/:p2', ctx => console.log(ctx.params))
|
|
@@ -43,67 +32,64 @@ galbe.get('/default/:p1/foo/:p2', ctx => console.log(ctx.params))
|
|
|
43
32
|
{ p1: "four", p2: "2" }
|
|
44
33
|
```
|
|
45
34
|
|
|
46
|
-
|
|
35
|
+
### query
|
|
47
36
|
|
|
48
|
-
A
|
|
37
|
+
A JavaScript object representing the query parameters of the current request.
|
|
49
38
|
|
|
50
|
-
- key (string):
|
|
51
|
-
- value
|
|
39
|
+
- **key** (string): Query parameter name
|
|
40
|
+
- **value** (string | [schema defined](schemas.md#query)): Query parameter value
|
|
52
41
|
|
|
53
42
|
```js
|
|
54
|
-
galbe.get('/test', ctx => console.log(ctx.
|
|
43
|
+
galbe.get('/test', ctx => console.log(ctx.query))
|
|
55
44
|
// GET /test?one=1&two=2
|
|
56
45
|
{ one: "1", two: "2" }
|
|
57
46
|
```
|
|
58
47
|
|
|
59
|
-
|
|
48
|
+
### body
|
|
60
49
|
|
|
61
|
-
The body payload of the incoming request. The body type is
|
|
50
|
+
The body payload of the incoming request. The body type is determined based on the following rules:
|
|
62
51
|
|
|
63
|
-
If no [Schema](schemas.md) is defined, Galbe will parse the body type according to `content-type`
|
|
52
|
+
If no [Schema](schemas.md) is defined, Galbe will parse the body type according to the `content-type` header:
|
|
64
53
|
|
|
65
54
|
- `text/.*`: string
|
|
66
55
|
- `application/json`: object
|
|
67
|
-
- `application/x-www-form-urlencoded`: { [key: string]: any }
|
|
68
|
-
- `multipart/form-data`: { [key: string]:
|
|
69
|
-
|
|
70
|
-
content: any
|
|
71
|
-
} }
|
|
72
|
-
- `other`: AsyncGenerator\<Uint8Array\>
|
|
56
|
+
- `application/x-www-form-urlencoded`: `{ [key: string]: any }`
|
|
57
|
+
- `multipart/form-data`: `{ [key: string]: { headers: { name: string; type?: string; filename?: string }; content: any } }`
|
|
58
|
+
- `other`: `AsyncGenerator<Uint8Array>`
|
|
73
59
|
|
|
74
|
-
If a [Schema](schemas.md) is defined, Galbe will parse the body
|
|
60
|
+
If a [Schema](schemas.md) is defined, Galbe will parse the body according to the [Schema.body](schemas.md#body) definition for the current route.
|
|
75
61
|
|
|
76
|
-
|
|
62
|
+
### set
|
|
77
63
|
|
|
78
|
-
The set property contains modifiable
|
|
64
|
+
The `set` property contains modifiable attributes intended to provide information to the response parser.
|
|
79
65
|
|
|
80
|
-
-
|
|
81
|
-
-
|
|
66
|
+
- **status**: Sets the response status.
|
|
67
|
+
- **headers**: Sets the response headers.
|
|
82
68
|
|
|
83
69
|
```js
|
|
84
70
|
galbe.get('/example', ctx => {
|
|
85
|
-
ctx.set.status = 418
|
|
86
|
-
return "I don't do coffee"
|
|
71
|
+
ctx.set.status = 418;
|
|
72
|
+
return "I don't do coffee";
|
|
87
73
|
})
|
|
88
74
|
```
|
|
89
75
|
|
|
90
|
-
|
|
76
|
+
### state
|
|
91
77
|
|
|
92
|
-
The state property
|
|
78
|
+
The `state` property allows storing custom user-defined objects throughout the request lifecycle. It is commonly used to share data between [hooks](hooks.md) and the [handler](handler.md).
|
|
93
79
|
|
|
94
|
-
- key (string):
|
|
95
|
-
- value (any):
|
|
80
|
+
- **key** (string): User-defined key
|
|
81
|
+
- **value** (any): User-defined object
|
|
96
82
|
|
|
97
83
|
```js
|
|
98
84
|
galbe.get(
|
|
99
85
|
'/example',
|
|
100
86
|
[
|
|
101
87
|
ctx => {
|
|
102
|
-
ctx.state['foo'] = 'bar'
|
|
88
|
+
ctx.state['foo'] = 'bar';
|
|
103
89
|
}
|
|
104
90
|
],
|
|
105
91
|
ctx => {
|
|
106
|
-
return ctx.state.foo
|
|
92
|
+
return ctx.state.foo;
|
|
107
93
|
}
|
|
108
94
|
)
|
|
109
95
|
```
|
|
@@ -113,6 +99,6 @@ $ curl http://localhost:3000/example
|
|
|
113
99
|
bar
|
|
114
100
|
```
|
|
115
101
|
|
|
116
|
-
|
|
102
|
+
### remoteAddress
|
|
117
103
|
|
|
118
|
-
An instance of
|
|
104
|
+
An instance of [SocketAddress](https://github.com/oven-sh/bun/blob/fe62a614046948ebba260bed87db96287e67921f/packages/bun-types/bun.d.ts#L2600-L2613) representing the remote address of the client.
|