galbe 0.9.0 → 0.10.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/build.ts +12 -2
- package/bin/commands/dev.ts +22 -18
- package/bin/commands/generate/code/openapi.parser.ts +1 -1
- package/bin/commands/generate/spec.ts +4 -2
- package/bin/util.ts +3 -13
- package/bun.lock +904 -0
- package/docs/getting-started.md +72 -37
- package/package.json +2 -1
- package/src/extras/spec/openapi.serializer.ts +8 -4
- package/src/index.ts +2 -3
- package/src/parser.ts +1 -1
- package/src/routes.ts +1 -1
- package/src/server.ts +15 -8
- package/src/types.ts +21 -9
- package/src/util.ts +9 -0
- package/src/validator.ts +1 -0
- package/test/plugins.test.ts +1 -1
- package/test/responses.test.ts +3 -2
- package/test/routeFiles.test.ts +6 -6
- package/tsconfig.json +1 -1
- package/bun.lockb +0 -0
package/docs/getting-started.md
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
# Getting started
|
|
2
2
|
|
|
3
|
-
Galbe is a Javascript web framework for building fast and versatile backend
|
|
3
|
+
Galbe is a Javascript web framework for building fast and versatile backend
|
|
4
|
+
servers with Bun.
|
|
4
5
|
|
|
5
|
-
Designed with simplicity in mind, Galbe allows you to quickly create and set up
|
|
6
|
+
Designed with simplicity in mind, Galbe allows you to quickly create and set up
|
|
7
|
+
a project. In addition to its ease of use, Galbe also offers a range of useful
|
|
8
|
+
features that help you focus on the core logic of your application.
|
|
6
9
|
|
|
7
10
|
## Requirements
|
|
8
11
|
|
|
9
|
-
To start developing your Galbe project, you first need to install
|
|
12
|
+
To start developing your Galbe project, you first need to install
|
|
13
|
+
[Bun](https://bun.sh).
|
|
10
14
|
|
|
11
15
|
## Automatic installation
|
|
12
16
|
|
|
@@ -16,7 +20,9 @@ This is the recommended way of setting up a Galbe project.
|
|
|
16
20
|
$ bun create galbe app
|
|
17
21
|
```
|
|
18
22
|
|
|
19
|
-
The Galbe starter CLI will request you to chose a template and a target language
|
|
23
|
+
The Galbe starter CLI will request you to chose a template and a target language
|
|
24
|
+
for your project. Let's select `hello` as template and `ts` as language. This
|
|
25
|
+
will create a new project under `app` directory.
|
|
20
26
|
|
|
21
27
|
Now you can navigate to your newly created project and install it:
|
|
22
28
|
|
|
@@ -46,8 +52,9 @@ $ curl localhost:3000/hello/John?age=32
|
|
|
46
52
|
Hello John! You're 32 y.o.
|
|
47
53
|
```
|
|
48
54
|
|
|
49
|
-
> [!TIP]
|
|
50
|
-
> If you want to have a more complete view of Galbe capabilities, feel free to
|
|
55
|
+
> [!TIP]
|
|
56
|
+
> If you want to have a more complete view of Galbe capabilities, feel free to
|
|
57
|
+
> take a look at the `demo` template from the Galbe starter CLI.
|
|
51
58
|
|
|
52
59
|
## Manual installation
|
|
53
60
|
|
|
@@ -70,30 +77,37 @@ Open `package.json` file and add the following scripts:
|
|
|
70
77
|
}
|
|
71
78
|
```
|
|
72
79
|
|
|
73
|
-
As you can see, those scripts rely on Galbe CLI to run and build the
|
|
80
|
+
As you can see, those scripts rely on Galbe CLI to run and build the
|
|
81
|
+
application. You will find more info about it on the [CLI](cli.md) page.
|
|
74
82
|
|
|
75
|
-
This require your `index.ts` to export a default Galbe instance in order to
|
|
83
|
+
This require your `index.ts` to export a default Galbe instance in order to
|
|
84
|
+
work. As in the following example:
|
|
76
85
|
|
|
77
86
|
```ts
|
|
78
|
-
import { Galbe } from
|
|
87
|
+
import { Galbe } from "galbe";
|
|
79
88
|
|
|
80
|
-
const galbe = new Galbe({ port: 3000 })
|
|
81
|
-
galbe.get(
|
|
89
|
+
const galbe = new Galbe({ port: 3000 });
|
|
90
|
+
galbe.get("/hello", () => "Hello Mom!");
|
|
82
91
|
|
|
83
|
-
export default galbe
|
|
92
|
+
export default galbe;
|
|
84
93
|
```
|
|
85
94
|
|
|
86
|
-
This is the recommended way to proceed but it is not mandatory. Galbe instances
|
|
95
|
+
This is the recommended way to proceed but it is not mandatory. Galbe instances
|
|
96
|
+
also provide a `listen` method that will allow you to manually start your server
|
|
97
|
+
instance from the code.
|
|
87
98
|
|
|
88
99
|
> [!WARNING]
|
|
89
|
-
> In the case you decide to not rely on Galbe CLI to run/build your app, you
|
|
100
|
+
> In the case you decide to not rely on Galbe CLI to run/build your app, you
|
|
101
|
+
> will not have access to
|
|
102
|
+
> [Automatic Route Analyzer](routes.md#automatic-route-analyzer) feature.
|
|
90
103
|
|
|
91
104
|
## Configuration
|
|
92
105
|
|
|
93
|
-
To configure your Galbe server, you should pass your configuration to the Galbe
|
|
106
|
+
To configure your Galbe server, you should pass your configuration to the Galbe
|
|
107
|
+
constructor when you instanciate it.
|
|
94
108
|
|
|
95
109
|
```ts
|
|
96
|
-
const galbe = new Galbe(configuration)
|
|
110
|
+
const galbe = new Galbe(configuration);
|
|
97
111
|
```
|
|
98
112
|
|
|
99
113
|
### Properties
|
|
@@ -112,11 +126,14 @@ The base path is added as a prefix to all the routes created.
|
|
|
112
126
|
|
|
113
127
|
**routes**
|
|
114
128
|
|
|
115
|
-
A Glob Pattern or a list of Glob patterns defining the route files to be
|
|
129
|
+
A Glob Pattern or a list of Glob patterns defining the route files to be
|
|
130
|
+
analyzed by the [Automatic Route Analyzer](routes.md#automatic-route-analyzer).
|
|
131
|
+
Default is `src/**/*.route.{js,ts}`.
|
|
116
132
|
|
|
117
133
|
**plugin**
|
|
118
134
|
|
|
119
|
-
A property that can be used by plugins to add plugin's specific configuration.
|
|
135
|
+
A property that can be used by plugins to add plugin's specific configuration.
|
|
136
|
+
Every key should correspond to a [Unique Plugin Identifier](plugins.md).
|
|
120
137
|
|
|
121
138
|
**tls**
|
|
122
139
|
|
|
@@ -130,15 +147,21 @@ Enable or disable TLS support. Default value is `false`.
|
|
|
130
147
|
|
|
131
148
|
**requestValidator.enabled**
|
|
132
149
|
|
|
133
|
-
Enable or disable the _request_ schema validation (See
|
|
150
|
+
Enable or disable the _request_ schema validation (See
|
|
151
|
+
[Request Schema definition](schemas.md#request-schema-definition)). Default
|
|
152
|
+
value is `true`.
|
|
134
153
|
|
|
135
154
|
**responseValidator.enabled**
|
|
136
155
|
|
|
137
|
-
Enable or disable the _response_ schema validation (See
|
|
156
|
+
Enable or disable the _response_ schema validation (See
|
|
157
|
+
[Request Schema definition](schemas.md#request-schema-definition)). Default
|
|
158
|
+
value is `true`.
|
|
138
159
|
|
|
139
160
|
### Examples
|
|
140
161
|
|
|
141
|
-
A common way to handle server configuration is to create new file
|
|
162
|
+
A common way to handle server configuration is to create new file
|
|
163
|
+
`galbe.config.(js|ts|json)` at the root of your project directory and import it
|
|
164
|
+
in your code. Here is an example:
|
|
142
165
|
|
|
143
166
|
galbe.config.js
|
|
144
167
|
|
|
@@ -152,27 +175,31 @@ export default {
|
|
|
152
175
|
index.js
|
|
153
176
|
|
|
154
177
|
```js
|
|
155
|
-
import { Galbe } from
|
|
156
|
-
import config from
|
|
178
|
+
import { Galbe } from "galbe";
|
|
179
|
+
import config from "./galbe.config";
|
|
157
180
|
|
|
158
|
-
export default new Galbe(config)
|
|
181
|
+
export default new Galbe(config);
|
|
159
182
|
```
|
|
160
183
|
|
|
161
|
-
> [!TIP]
|
|
162
|
-
> If you are using Typescript, you can import `GalbeConfig` type from galbe
|
|
184
|
+
> [!TIP]
|
|
185
|
+
> If you are using Typescript, you can import `GalbeConfig` type from galbe
|
|
186
|
+
> package to ensure type consistency for your configuration. Here is an example:
|
|
163
187
|
>
|
|
164
188
|
> ```ts
|
|
165
|
-
> import type { GalbeConfig } from
|
|
189
|
+
> import type { GalbeConfig } from "galbe";
|
|
166
190
|
> const config: GalbeConfig = {
|
|
167
191
|
> port: Number(Bun.env.GALBE_PORT),
|
|
168
|
-
> routes:
|
|
169
|
-
> }
|
|
170
|
-
> export default config
|
|
192
|
+
> routes: "routes/*.route.ts",
|
|
193
|
+
> };
|
|
194
|
+
> export default config;
|
|
171
195
|
> ```
|
|
172
196
|
|
|
173
197
|
## Project structure
|
|
174
198
|
|
|
175
|
-
One key aspect of Galbe, is its versatility in terms of project structure. This
|
|
199
|
+
One key aspect of Galbe, is its versatility in terms of project structure. This
|
|
200
|
+
is partly allowed by the
|
|
201
|
+
[Automatic Route Analyzer](routes.md#automatic-route-analyzer) and the `routes`
|
|
202
|
+
config property which defaults to `src/**/*.route.{js,ts}`.
|
|
176
203
|
|
|
177
204
|
Here are two examples of valid project structures by default:
|
|
178
205
|
|
|
@@ -214,18 +241,26 @@ Here are two examples of valid project structures by default:
|
|
|
214
241
|
└── tsconfig.json
|
|
215
242
|
```
|
|
216
243
|
|
|
217
|
-
In both cases, the
|
|
244
|
+
In both cases, the
|
|
245
|
+
[Automatic Route Analyzer](routes.md#automatic-route-analyzer) will analyze
|
|
246
|
+
`foo.route.ts` and `bar.route.ts` Route Files to find route definitions.
|
|
218
247
|
|
|
219
|
-
You can find more info about Route Files definition in the
|
|
248
|
+
You can find more info about Route Files definition in the
|
|
249
|
+
[Routes Files](routes.md#route-files) section.
|
|
220
250
|
|
|
221
251
|
> [!NOTE]
|
|
222
|
-
> The examples provided above will work with the default configuration, but you
|
|
252
|
+
> The examples provided above will work with the default configuration, but you
|
|
253
|
+
> can easily customize the routes property to fit your own project structure.
|
|
254
|
+
> Simply redefine the `routes` property with your own pattern(s) to to fit your
|
|
255
|
+
> own project structure.
|
|
223
256
|
|
|
224
257
|
## How to debug
|
|
225
258
|
|
|
226
|
-
The easiest way to debug your app is by installing the
|
|
259
|
+
The easiest way to debug your app is by installing the
|
|
260
|
+
[VSCode Bun extension](https://marketplace.visualstudio.com/items?itemName=oven.bun-vscode).
|
|
227
261
|
|
|
228
|
-
You can then create a `.vscode/launch.json` config file in your project root
|
|
262
|
+
You can then create a `.vscode/launch.json` config file in your project root
|
|
263
|
+
directory. Here is an example of configuration:
|
|
229
264
|
|
|
230
265
|
```json
|
|
231
266
|
{
|
|
@@ -239,7 +274,7 @@ You can then create a `.vscode/launch.json` config file in your project root dir
|
|
|
239
274
|
"env": { "TERM": "xterm" },
|
|
240
275
|
"cwd": "${workspaceFolder}",
|
|
241
276
|
"runtime": "bun",
|
|
242
|
-
"runtimeArgs": ["dev", "index.ts", "
|
|
277
|
+
"runtimeArgs": ["dev", "index.ts", "-w", "."]
|
|
243
278
|
}
|
|
244
279
|
]
|
|
245
280
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "galbe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Fast, lightweight and highly customizable JavaScript web framework based on Bun",
|
|
5
5
|
"author": "Pierre Caillaud M (https://github.com/pierre-cm)",
|
|
6
6
|
"type": "module",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"license": "MIT",
|
|
30
30
|
"scripts": {
|
|
31
31
|
"test": "bun test",
|
|
32
|
+
"typecheck": "tsc --noEmit --emitDeclarationOnly false",
|
|
32
33
|
"postinstall": "bun run ./scripts/postinstall.ts",
|
|
33
34
|
"release": "release-it"
|
|
34
35
|
},
|
|
@@ -239,16 +239,17 @@ export const OpenAPISerializer = async (g: Galbe, version = '3.0.3'): Promise<Op
|
|
|
239
239
|
if (r.schema.response && Object.keys(r.schema.response).length) {
|
|
240
240
|
responses = Object.fromEntries(
|
|
241
241
|
Object.entries(r.schema.response).map(([status, v]) => {
|
|
242
|
-
|
|
242
|
+
if(!v) return []
|
|
243
|
+
let s = status as keyof typeof HttpStatus | 'default'
|
|
243
244
|
let { schema, isJson } = schemaToOpenapi(v)
|
|
244
245
|
let { type, format } = resolveRef(schema)
|
|
245
246
|
let media = schemaToMedia({ type, format, isJson } as SchemaType)
|
|
246
247
|
let response: OpenAPIV3.ResponseObject = {
|
|
247
|
-
description: v.description || HttpStatus[
|
|
248
|
+
description: v.description || HttpStatus[s as keyof typeof HttpStatus] || 'Response',
|
|
248
249
|
content: { [media]: { schema: schema } }
|
|
249
250
|
}
|
|
250
251
|
if (components.responses && r.schema.response?.[s]?.id) {
|
|
251
|
-
components.responses[r.schema.response?.[s]
|
|
252
|
+
components.responses[r.schema.response?.[s]?.id as string] = response
|
|
252
253
|
//@ts-ignore
|
|
253
254
|
response = { $ref: `#/components/responses/${r.schema.response?.[s].id}` }
|
|
254
255
|
}
|
|
@@ -260,9 +261,12 @@ export const OpenAPISerializer = async (g: Galbe, version = '3.0.3'): Promise<Op
|
|
|
260
261
|
default: { description: HttpStatus[200] }
|
|
261
262
|
}
|
|
262
263
|
}
|
|
264
|
+
let summary = meta?.head.match(/^([^\n]+)/)?.[1]
|
|
265
|
+
console.log('#', r.method, r.path)
|
|
266
|
+
console.log(r.schema.body)
|
|
263
267
|
paths[path][r.method] = {
|
|
264
268
|
tags: tags.length ? tags : undefined,
|
|
265
|
-
summary:
|
|
269
|
+
summary: summary,
|
|
266
270
|
operationId: meta?.operationId,
|
|
267
271
|
parameters: parameters.length ? parameters : undefined,
|
|
268
272
|
requestBody,
|
package/src/index.ts
CHANGED
|
@@ -111,6 +111,8 @@ export const $T = new SchemaType()
|
|
|
111
111
|
|
|
112
112
|
export { RequestError } from './types'
|
|
113
113
|
|
|
114
|
+
export const config = (config: GalbeConfig) => config
|
|
115
|
+
|
|
114
116
|
/**
|
|
115
117
|
* #### Galbe Server
|
|
116
118
|
* Instanciate a Galbe web server
|
|
@@ -136,13 +138,10 @@ export class Galbe {
|
|
|
136
138
|
plugins: GalbePlugin[] = []
|
|
137
139
|
constructor(config?: GalbeConfig) {
|
|
138
140
|
this.config = config ?? {}
|
|
139
|
-
this.config.routes = this.config.routes ?? true
|
|
140
141
|
this.router = new GalbeRouter({
|
|
141
142
|
prefix: this.config?.basePath || '',
|
|
142
143
|
cacheEnabled: this.config?.router?.cacheEnabled
|
|
143
144
|
})
|
|
144
|
-
this.config.requestValidator = config?.requestValidator ?? { enabled: true }
|
|
145
|
-
this.config.responseValidator = config?.responseValidator ?? { enabled: true }
|
|
146
145
|
}
|
|
147
146
|
private add(route: any) {
|
|
148
147
|
this.router.add(route)
|
package/src/parser.ts
CHANGED
|
@@ -655,7 +655,7 @@ export const responseParser = (response: any, ctx: Context, schema?: STResponse)
|
|
|
655
655
|
if (response instanceof Response) return response
|
|
656
656
|
else if (typeof response === 'string') {
|
|
657
657
|
if (!details?.headers?.has('content-type')) {
|
|
658
|
-
if (schema?.[details.status][Kind] === 'json') {
|
|
658
|
+
if (schema?.[details.status]?.[Kind] === 'json') {
|
|
659
659
|
details?.headers?.set('content-type', 'application/json')
|
|
660
660
|
response = `"${response}"`
|
|
661
661
|
} else details?.headers?.set('content-type', 'text/plain')
|
package/src/routes.ts
CHANGED
|
@@ -234,7 +234,7 @@ export const defineRoutes = async (
|
|
|
234
234
|
options: Pick<GalbeConfig, 'routes'>,
|
|
235
235
|
proxy: GalbeProxy,
|
|
236
236
|
) => {
|
|
237
|
-
const routes = options?.routes === true ? DEFAULT_ROUTE_PATTERN : options?.routes
|
|
237
|
+
const routes = options?.routes === undefined || options?.routes === true ? DEFAULT_ROUTE_PATTERN : options?.routes
|
|
238
238
|
if (!routes) return
|
|
239
239
|
const root = process.cwd()
|
|
240
240
|
if (typeof routes === 'string') {
|
package/src/server.ts
CHANGED
|
@@ -30,6 +30,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
30
30
|
|
|
31
31
|
const server = Bun.serve({
|
|
32
32
|
port: port || galbe.config?.port || 3000,
|
|
33
|
+
reusePort: galbe?.config?.reusePort,
|
|
33
34
|
hostname: hostname || galbe.config?.hostname || 'localhost',
|
|
34
35
|
tls: galbe.config?.tls,
|
|
35
36
|
|
|
@@ -81,7 +82,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
81
82
|
context.params = inParams
|
|
82
83
|
|
|
83
84
|
// request validation
|
|
84
|
-
if (galbe.config?.requestValidator?.enabled) {
|
|
85
|
+
if (galbe.config?.requestValidator?.enabled !== false) {
|
|
85
86
|
let errors: RequestError[] = []
|
|
86
87
|
try {
|
|
87
88
|
if (schema?.headers)
|
|
@@ -150,7 +151,7 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
150
151
|
|
|
151
152
|
const parsedResponse = responseParser(response, context as Context, schema.response)
|
|
152
153
|
|
|
153
|
-
if (galbe.config?.responseValidator?.enabled && schema.response)
|
|
154
|
+
if (galbe.config?.responseValidator?.enabled !== false && schema.response)
|
|
154
155
|
validateResponse(response, schema.response, parsedResponse.status || 200)
|
|
155
156
|
|
|
156
157
|
for (const p of pluginsCb.afterHandle) {
|
|
@@ -172,14 +173,20 @@ export default async (galbe: Galbe, port?: number, hostname?: string) => {
|
|
|
172
173
|
headers: { 'Content-Type': 'application/json' }
|
|
173
174
|
})
|
|
174
175
|
} else if (error instanceof RequestError) {
|
|
175
|
-
let payload =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
let payload = error.payload
|
|
177
|
+
let headers = new Headers(error?.headers || {})
|
|
178
|
+
if (!headers.has('content-type')) {
|
|
179
|
+
if (typeof error.payload === 'string') headers.set('content-type', 'text/plain')
|
|
180
|
+
else {
|
|
181
|
+
headers.set('content-type', 'application/json')
|
|
182
|
+
try {
|
|
183
|
+
payload = JSON.stringify(error.payload)
|
|
184
|
+
} catch (err) { }
|
|
185
|
+
}
|
|
186
|
+
}
|
|
180
187
|
return new Response(payload, {
|
|
181
188
|
status: error.status,
|
|
182
|
-
headers
|
|
189
|
+
headers
|
|
183
190
|
})
|
|
184
191
|
} else console.log(error)
|
|
185
192
|
return new Response('"Internal Server Error"', {
|
package/src/types.ts
CHANGED
|
@@ -52,7 +52,7 @@ export type STResponseValue =
|
|
|
52
52
|
| STStream
|
|
53
53
|
| STAny
|
|
54
54
|
| STNull
|
|
55
|
-
export type STResponse = Record<number | 'default', STResponseValue
|
|
55
|
+
export type STResponse = Partial<Record<number | 'default', STResponseValue>>
|
|
56
56
|
|
|
57
57
|
export type MaybeArray<T> = T | T[]
|
|
58
58
|
export type MaybeSTArray<T extends STSchema> = T | STArray<T>
|
|
@@ -99,15 +99,25 @@ export type STQuery = Record<string, STQueryValue>
|
|
|
99
99
|
* ```
|
|
100
100
|
*/
|
|
101
101
|
export type GalbeConfig = {
|
|
102
|
+
/** The port number that the server will be listening on. */
|
|
102
103
|
port?: number
|
|
104
|
+
/** Allow to share the same port across processes (Linux only). */
|
|
105
|
+
reusePort?: boolean
|
|
106
|
+
/** The hostname of the server. */
|
|
103
107
|
hostname?: string
|
|
108
|
+
/** The base path is added as a prefix to all the routes created. */
|
|
104
109
|
basePath?: string
|
|
110
|
+
/** Enable or disable TLS support. */
|
|
105
111
|
tls?: TLSOptions
|
|
106
112
|
server?: Exclude<ServeOptions, 'port'> | TLSServeOptions
|
|
113
|
+
/** A Glob Pattern or a list of Glob patterns defining the route files to be analyzed by the Automatic Route Analyzer. */
|
|
107
114
|
routes?: boolean | string | string[]
|
|
108
115
|
router?: { cacheEnabled: boolean }
|
|
116
|
+
/** A property that can be used by plugins to add plugin's specific configuration. */
|
|
109
117
|
plugin?: Record<string, any>
|
|
118
|
+
/** Enable or disable the request schema validation.*/
|
|
110
119
|
requestValidator?: { enabled: boolean }
|
|
120
|
+
/** Enable or disable the response schema validation.*/
|
|
111
121
|
responseValidator?: { enabled: boolean }
|
|
112
122
|
}
|
|
113
123
|
/**
|
|
@@ -136,7 +146,7 @@ export type RequestSchema<
|
|
|
136
146
|
P extends Partial<STParams<Path>> = Partial<STParams<Path>>,
|
|
137
147
|
Q extends STQuery = STQuery,
|
|
138
148
|
B extends STBody = STBody,
|
|
139
|
-
R extends Partial<STResponse> =
|
|
149
|
+
R extends Partial<STResponse> = STResponse
|
|
140
150
|
> = {
|
|
141
151
|
headers?: H
|
|
142
152
|
params?: P
|
|
@@ -194,7 +204,7 @@ export type Endpoint<M extends Method> = {
|
|
|
194
204
|
H extends STHeaders = any,
|
|
195
205
|
Q extends STQuery = any,
|
|
196
206
|
B extends STBody = any,
|
|
197
|
-
R extends
|
|
207
|
+
R extends STResponse = STResponse
|
|
198
208
|
>(
|
|
199
209
|
path: Path,
|
|
200
210
|
schema: RequestSchema<M, Path, H, P, Q, B, R>,
|
|
@@ -207,7 +217,7 @@ export type Endpoint<M extends Method> = {
|
|
|
207
217
|
H extends STHeaders = any,
|
|
208
218
|
Q extends STQuery = any,
|
|
209
219
|
B extends STBody = any,
|
|
210
|
-
R extends
|
|
220
|
+
R extends STResponse = STResponse
|
|
211
221
|
>(
|
|
212
222
|
path: Path,
|
|
213
223
|
schema: RequestSchema<M, Path, H, P, Q, B, R>,
|
|
@@ -219,7 +229,7 @@ export type Endpoint<M extends Method> = {
|
|
|
219
229
|
H extends STHeaders = any,
|
|
220
230
|
Q extends STQuery = any,
|
|
221
231
|
B extends STBody = any,
|
|
222
|
-
R extends
|
|
232
|
+
R extends STResponse = STResponse
|
|
223
233
|
>(
|
|
224
234
|
path: Path,
|
|
225
235
|
hooks: Hook<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>[],
|
|
@@ -231,7 +241,7 @@ export type Endpoint<M extends Method> = {
|
|
|
231
241
|
H extends STHeaders = any,
|
|
232
242
|
Q extends STQuery = any,
|
|
233
243
|
B extends STBody = any,
|
|
234
|
-
R extends
|
|
244
|
+
R extends STResponse = STResponse
|
|
235
245
|
>(
|
|
236
246
|
path: Path,
|
|
237
247
|
handler: Handler<M, Path, RequestSchema<M, Path, H, P, Q, B, R>>
|
|
@@ -245,10 +255,12 @@ export type StaticEndpoint<P extends string = string, T extends string = string>
|
|
|
245
255
|
|
|
246
256
|
export class RequestError {
|
|
247
257
|
status: number
|
|
248
|
-
payload
|
|
249
|
-
|
|
258
|
+
payload?: any
|
|
259
|
+
headers?: Record<string, string>
|
|
260
|
+
constructor(options: { status?: number; payload?: any, headers?: Record<string, string> }) {
|
|
250
261
|
this.status = options.status ?? 500
|
|
251
262
|
this.payload = options.payload
|
|
263
|
+
this.headers = options.headers
|
|
252
264
|
}
|
|
253
265
|
}
|
|
254
266
|
|
|
@@ -267,7 +279,7 @@ export type Route<
|
|
|
267
279
|
H extends STHeaders = STHeaders,
|
|
268
280
|
Q extends STQuery = STQuery,
|
|
269
281
|
B extends STBody = STBody,
|
|
270
|
-
R extends
|
|
282
|
+
R extends STResponse = STResponse,
|
|
271
283
|
SP extends string = string,
|
|
272
284
|
SR extends string = string
|
|
273
285
|
> = {
|
package/src/util.ts
CHANGED
|
@@ -12,6 +12,15 @@ const METHOD_COLOR: Record<string, string> = {
|
|
|
12
12
|
}
|
|
13
13
|
const ansiRegex = /[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/g
|
|
14
14
|
|
|
15
|
+
export const softMerge = <T> (base: T, override: T): T => {
|
|
16
|
+
for (const key in override) {
|
|
17
|
+
if (override[key] instanceof Object && !(override[key] instanceof Array)) {
|
|
18
|
+
if (!base[key]) Object.assign(base as any, { [key]: {} })
|
|
19
|
+
softMerge(base[key], override[key])
|
|
20
|
+
} else Object.assign(base as any, { [key]: override[key] })
|
|
21
|
+
}
|
|
22
|
+
return base
|
|
23
|
+
}
|
|
15
24
|
export const logRoute = (
|
|
16
25
|
r: { method: string; path: string, static?: { path: string, root: string } },
|
|
17
26
|
meta?: RouteMeta,
|
package/src/validator.ts
CHANGED
|
@@ -96,6 +96,7 @@ export const validate = (elt: any, schema: STSchema, parse = false): any => {
|
|
|
96
96
|
export const validateResponse = (response: any, schema: STResponse, status: number) => {
|
|
97
97
|
if (!(status in schema)) return
|
|
98
98
|
const s = schema?.[status] || schema?.['default']
|
|
99
|
+
if(!s) return
|
|
99
100
|
if (response instanceof ReadableStream) {
|
|
100
101
|
if (!s[Stream]) throw new InternalError(`Expected ${s[Kind]} response, but got ReadableStream`)
|
|
101
102
|
} else if (isIterator(response)) {
|
package/test/plugins.test.ts
CHANGED
package/test/responses.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test, describe, beforeAll } from 'bun:test'
|
|
2
|
-
import { Galbe, $T } from '../src'
|
|
2
|
+
import { Galbe, $T, type Context } from '../src'
|
|
3
3
|
import { decoder } from './test.utils'
|
|
4
4
|
|
|
5
5
|
const port = 7359
|
|
@@ -21,7 +21,7 @@ const rsTxt = (text: string) => {
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
const handleResp = ctx => ctx.body
|
|
24
|
+
const handleResp = (ctx: Context) => ctx.body
|
|
25
25
|
|
|
26
26
|
describe('responses', () => {
|
|
27
27
|
beforeAll(async () => {
|
|
@@ -143,6 +143,7 @@ describe('responses', () => {
|
|
|
143
143
|
|
|
144
144
|
expect(resp.status).toBe(200)
|
|
145
145
|
expect(resp.headers.get('content-type')).toBe('application/octet-stream')
|
|
146
|
+
//@ts-ignore
|
|
146
147
|
expect(body).toEqual(reqBody)
|
|
147
148
|
})
|
|
148
149
|
|
package/test/routeFiles.test.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { expect, test, describe } from 'bun:test'
|
|
2
|
-
import { defineRoutes, metaAnalysis } from '../src/routes'
|
|
2
|
+
import { defineRoutes, GalbeProxy, metaAnalysis } from '../src/routes'
|
|
3
3
|
import { Galbe } from '../src'
|
|
4
4
|
|
|
5
5
|
describe('routeFiles', () => {
|
|
@@ -68,7 +68,7 @@ describe('routeFiles', () => {
|
|
|
68
68
|
})
|
|
69
69
|
|
|
70
70
|
test('define routes, no route', async () => {
|
|
71
|
-
const k = new Galbe()
|
|
71
|
+
const k = new GalbeProxy(new Galbe())
|
|
72
72
|
await defineRoutes({}, k)
|
|
73
73
|
expect(k.router.routes).toEqual({
|
|
74
74
|
routes: {}
|
|
@@ -76,7 +76,7 @@ describe('routeFiles', () => {
|
|
|
76
76
|
})
|
|
77
77
|
|
|
78
78
|
test('define routes, no route (false)', async () => {
|
|
79
|
-
const k = new Galbe({ routes: false })
|
|
79
|
+
const k = new GalbeProxy(new Galbe({ routes: false }))
|
|
80
80
|
await defineRoutes({}, k)
|
|
81
81
|
expect(k.router.routes).toEqual({
|
|
82
82
|
routes: {}
|
|
@@ -84,7 +84,7 @@ describe('routeFiles', () => {
|
|
|
84
84
|
})
|
|
85
85
|
|
|
86
86
|
test('define routes, no route found', async () => {
|
|
87
|
-
const k = new Galbe()
|
|
87
|
+
const k = new GalbeProxy(new Galbe())
|
|
88
88
|
await defineRoutes({ routes: 'unexisting_route' }, k)
|
|
89
89
|
expect(k.router.routes).toEqual({
|
|
90
90
|
routes: {}
|
|
@@ -92,7 +92,7 @@ describe('routeFiles', () => {
|
|
|
92
92
|
})
|
|
93
93
|
|
|
94
94
|
test('define routes, route.empty', async () => {
|
|
95
|
-
const k = new Galbe()
|
|
95
|
+
const k = new GalbeProxy(new Galbe())
|
|
96
96
|
|
|
97
97
|
await defineRoutes({ routes: 'test/resources/test.route.empty.ts' }, k)
|
|
98
98
|
|
|
@@ -132,7 +132,7 @@ describe('routeFiles', () => {
|
|
|
132
132
|
})
|
|
133
133
|
|
|
134
134
|
test('define routes, all', async () => {
|
|
135
|
-
const k = new Galbe()
|
|
135
|
+
const k = new GalbeProxy(new Galbe())
|
|
136
136
|
|
|
137
137
|
await defineRoutes({ routes: ['test/resources/test.route.*.ts'] }, k)
|
|
138
138
|
|
package/tsconfig.json
CHANGED
package/bun.lockb
DELETED
|
Binary file
|