galbe 0.12.0 → 0.12.2
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 +2 -2
- package/package.json +6 -1
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -35
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -23
- package/.github/workflows/build_test.yml +0 -17
- package/.github/workflows/deploy_website.yml +0 -20
- package/.github/workflows/release.yml +0 -39
- package/.prettierrc +0 -10
- package/bun.lock +0 -904
- package/bunfig.toml +0 -2
- package/docs/CONTRIBUTING.md +0 -105
- package/docs/cli.md +0 -343
- package/docs/configuration.md +0 -80
- package/docs/context.md +0 -104
- package/docs/error-handler.md +0 -54
- package/docs/getting-started.md +0 -164
- package/docs/handler.md +0 -119
- package/docs/hooks.md +0 -90
- package/docs/plugins.md +0 -146
- package/docs/router.md +0 -10
- package/docs/routes.md +0 -133
- package/docs/schemas.md +0 -327
- package/test/hooks.test.ts +0 -200
- package/test/parser.test.ts +0 -1358
- package/test/plugins.test.ts +0 -239
- package/test/requests.test.ts +0 -917
- package/test/resources/image.png +0 -0
- package/test/resources/object.badSyntax.json +0 -8
- package/test/resources/object.json +0 -8
- package/test/resources/object.missing.json +0 -6
- package/test/resources/static/chameleon.png +0 -0
- package/test/resources/static/index.html +0 -13
- package/test/resources/static/sub/index.html +0 -13
- package/test/resources/static/sub/other.html +0 -13
- package/test/resources/test.route.comment.ts +0 -58
- package/test/resources/test.route.empty.ts +0 -9
- package/test/responses.test.ts +0 -483
- package/test/routeFiles.test.ts +0 -239
- package/test/router.test.ts +0 -231
- package/test/test.utils.ts +0 -109
- package/test/types.test.ts +0 -909
- package/tsconfig.json +0 -23
package/docs/schemas.md
DELETED
|
@@ -1,327 +0,0 @@
|
|
|
1
|
-
# Schemas
|
|
2
|
-
|
|
3
|
-
Galbe provides a custom Schema Type processor that offers type safety, data parsing, and validation. The use of Schemas highly simplifies request input validation and automatic error handling. Additionally, it enhances the developer experience by inferring static TypeScript types from schema definitions.
|
|
4
|
-
|
|
5
|
-
## Schema Types
|
|
6
|
-
|
|
7
|
-
To use Schema definitions, import `$T` from the `galbe` library:
|
|
8
|
-
|
|
9
|
-
```js
|
|
10
|
-
import { $T } from 'galbe'
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
Here is the list of available Schema types in Galbe:
|
|
14
|
-
|
|
15
|
-
#### boolean
|
|
16
|
-
|
|
17
|
-
Schema Type matching `boolean` values.
|
|
18
|
-
|
|
19
|
-
```ts
|
|
20
|
-
const boolSchema = $T.boolean()
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
#### string
|
|
24
|
-
|
|
25
|
-
Schema Type matching `string` values.
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
const strSchema = $T.string(options)
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
#### number
|
|
32
|
-
|
|
33
|
-
Schema Type matching `number` values.
|
|
34
|
-
|
|
35
|
-
```ts
|
|
36
|
-
const numSchema = $T.number(options)
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
#### integer
|
|
40
|
-
|
|
41
|
-
Schema Type matching integer `number` values.
|
|
42
|
-
|
|
43
|
-
```ts
|
|
44
|
-
const intSchema = $T.integer(options)
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
#### null
|
|
48
|
-
|
|
49
|
-
Schema Type matching `null` values.
|
|
50
|
-
|
|
51
|
-
```ts
|
|
52
|
-
const nullSchema = $T.null(options)
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
#### any
|
|
56
|
-
|
|
57
|
-
Schema Type matching `any` of the previous Schema Types.
|
|
58
|
-
|
|
59
|
-
```ts
|
|
60
|
-
const anySchema = $T.any()
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
#### array
|
|
64
|
-
|
|
65
|
-
Schema Type matching `array` values.
|
|
66
|
-
|
|
67
|
-
```ts
|
|
68
|
-
const arraySchema = $T.array($T.any(), options)
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
#### optional
|
|
72
|
-
|
|
73
|
-
Makes any type optional, allowing `undefined` values.
|
|
74
|
-
|
|
75
|
-
```ts
|
|
76
|
-
const optionalSchema = $T.optional($T.string())
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
#### nullable
|
|
80
|
-
|
|
81
|
-
Makes any type nullable, allowing `null` values.
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
const nullableSchema = $T.nullable($T.string())
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
#### nullish
|
|
88
|
-
|
|
89
|
-
Makes any type nullish, allowing both `undefined` and `null` values.
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
const nullishSchema = $T.nullish($T.string())
|
|
93
|
-
```
|
|
94
|
-
|
|
95
|
-
#### union
|
|
96
|
-
|
|
97
|
-
Creates a union of Schema Types.
|
|
98
|
-
|
|
99
|
-
```ts
|
|
100
|
-
const unionSchema = $T.union([$T.string(), $T.number()])
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
## Request Schema Definition
|
|
104
|
-
|
|
105
|
-
The Request Schema definition allows you to define a schema for your request in your [Route Definition](routes.md#route-definition). It must be defined right after the route path.
|
|
106
|
-
|
|
107
|
-
```js
|
|
108
|
-
const schema = {}
|
|
109
|
-
galbe.get('/foo/:bar', schema, ctx => {})
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
The Request Schema has four optional properties:
|
|
113
|
-
|
|
114
|
-
### headers
|
|
115
|
-
|
|
116
|
-
```ts
|
|
117
|
-
headers: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
Defines request headers with their respective Schema types.
|
|
121
|
-
|
|
122
|
-
**Example:**
|
|
123
|
-
|
|
124
|
-
```ts
|
|
125
|
-
const schema = {
|
|
126
|
-
headers: {
|
|
127
|
-
'User-Agent': $T.optional($T.string({ pattern: '^Bun' }))
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
### params
|
|
133
|
-
|
|
134
|
-
```ts
|
|
135
|
-
params: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
Defines route parameters with their respective Schema types.
|
|
139
|
-
|
|
140
|
-
**Example:**
|
|
141
|
-
|
|
142
|
-
```ts
|
|
143
|
-
const schema = {
|
|
144
|
-
params: {
|
|
145
|
-
name: $T.string(),
|
|
146
|
-
age: $T.integer({ min: 0 })
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
```
|
|
150
|
-
|
|
151
|
-
> [!WARNING]
|
|
152
|
-
> Every key should match an existing [route path](routes.md#route-definition) parameter. Otherwise, TypeScript will show an error. If no schema is defined for a given parameter, Galbe assumes it is of type `string`.
|
|
153
|
-
|
|
154
|
-
### query
|
|
155
|
-
|
|
156
|
-
```ts
|
|
157
|
-
query: { [key: string]: STString | STBoolean | STNumber | STInteger | STLiteral }
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
Defines query parameters with their respective Schema types.
|
|
161
|
-
|
|
162
|
-
**Example:**
|
|
163
|
-
|
|
164
|
-
```ts
|
|
165
|
-
const schema = {
|
|
166
|
-
query: {
|
|
167
|
-
name: $T.literal('Galbe'),
|
|
168
|
-
list: $T.array($T.number())
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
```
|
|
172
|
-
|
|
173
|
-
### body
|
|
174
|
-
|
|
175
|
-
<!-- prettier-ignore -->
|
|
176
|
-
```ts
|
|
177
|
-
body: {
|
|
178
|
-
byteArray?: STByteArray | STStream
|
|
179
|
-
text?: STString | STLiteral | STBoolean | STNumber | STInteger | STUnion | STStream
|
|
180
|
-
json?: STJson | STObject | STBoolean | STInteger | STNumber | STString | STArray | STUnion
|
|
181
|
-
urlForm?: STObject | STStream | STUnion
|
|
182
|
-
multipart?: STMultipartForm | STStream | STUnion
|
|
183
|
-
default?: STString | STByteArray | STStream | STAny
|
|
184
|
-
}
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
Defines the request body Schema type based on content type.
|
|
188
|
-
|
|
189
|
-
#### Byte Array
|
|
190
|
-
|
|
191
|
-
Defines an `application/octet-stream` request body.
|
|
192
|
-
|
|
193
|
-
```ts
|
|
194
|
-
const body = {
|
|
195
|
-
byteArray: $T.byteArray()
|
|
196
|
-
}
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
#### Text
|
|
200
|
-
|
|
201
|
-
Defines an `text/*` request body.
|
|
202
|
-
|
|
203
|
-
```ts
|
|
204
|
-
const body = {
|
|
205
|
-
text: $T.string()
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
#### JSON
|
|
210
|
-
|
|
211
|
-
Defines an `application/json` request body.
|
|
212
|
-
|
|
213
|
-
```ts
|
|
214
|
-
const body = {
|
|
215
|
-
json: $T.object({
|
|
216
|
-
name: $T.string(),
|
|
217
|
-
age: $T.integer({ min: 0 })
|
|
218
|
-
})
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
#### URL Form
|
|
223
|
-
|
|
224
|
-
Defines an `application/x-www-form-urlencoded` request body.
|
|
225
|
-
|
|
226
|
-
```ts
|
|
227
|
-
const body = {
|
|
228
|
-
urlForm: {$T.object({
|
|
229
|
-
name: $T.string(),
|
|
230
|
-
age: $T.integer({ min: 0 })
|
|
231
|
-
})
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
#### Multipart Form
|
|
236
|
-
|
|
237
|
-
Defines a `multipart/form-data` request body.
|
|
238
|
-
|
|
239
|
-
```ts
|
|
240
|
-
const body = {
|
|
241
|
-
multipart: $T.multipartForm({
|
|
242
|
-
name: $T.string(),
|
|
243
|
-
age: $T.integer({ min: 0 })
|
|
244
|
-
})
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
#### stream
|
|
249
|
-
|
|
250
|
-
Certain request body types can be streamed using `STStream` wrapper, improving performance by validating data incrementally.
|
|
251
|
-
This can be usefull to imporve performances in case you have heavy body payloads by leveraging early validation and fail fast behaviors.
|
|
252
|
-
|
|
253
|
-
**Example**
|
|
254
|
-
|
|
255
|
-
Let's consider a `multipart/form-data` body request that has two properties: `username` and `heavyImageFile`:
|
|
256
|
-
|
|
257
|
-
```ts
|
|
258
|
-
galbe.post(
|
|
259
|
-
'user/create',
|
|
260
|
-
{
|
|
261
|
-
body: {
|
|
262
|
-
multipart: $T.multipartForm({
|
|
263
|
-
username: $T.string(),
|
|
264
|
-
heavyImageFile: $T.byteArray()
|
|
265
|
-
})
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
ctx => {
|
|
269
|
-
// At that point, the full body request has been processed
|
|
270
|
-
if(!isValid(ctx.body.username))
|
|
271
|
-
throw new RequestError({ status: 400 })
|
|
272
|
-
else ctx.set.status = 201
|
|
273
|
-
}
|
|
274
|
-
})
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
In that case, even if the `username` doesn't pass the validation, the full request body, including the `heavyImageFile` is processed before sending the response. This induces unnecessary time and resource consumption because the `heavyImageFile` is processed despite never been used.
|
|
278
|
-
|
|
279
|
-
A better approach would consist in leveraging `STStream` wrapper to implement early validation and fail fast behavior. By defining the request body as a stream. Instead of receiving a plain js object as `ctx.body`, you will receive an [AsyncGenerator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator).
|
|
280
|
-
|
|
281
|
-
```ts
|
|
282
|
-
galbe.post(
|
|
283
|
-
'user/create',
|
|
284
|
-
{
|
|
285
|
-
body: {
|
|
286
|
-
multipart: $T.stream($T.multipartForm({
|
|
287
|
-
username: $T.string(),
|
|
288
|
-
heavyImageFile: $T.byteArray()
|
|
289
|
-
}))
|
|
290
|
-
}
|
|
291
|
-
},
|
|
292
|
-
async ctx => {
|
|
293
|
-
// At that point, the body has not been processed yet.
|
|
294
|
-
for await (const [key, value] of ctx.body) {
|
|
295
|
-
if (key === "username" && !isValid(value)) {
|
|
296
|
-
// Returns an early response before heavyImageFile is processed
|
|
297
|
-
throw new RequestError({ status: 400 })
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
ctx.set.status = 201
|
|
301
|
-
}
|
|
302
|
-
})
|
|
303
|
-
```
|
|
304
|
-
|
|
305
|
-
### response
|
|
306
|
-
|
|
307
|
-
<!-- prettier-ignore -->
|
|
308
|
-
```ts
|
|
309
|
-
response: Record<number | 'default', STByteArray | STString | STBoolean | STNumber | STInteger | STLiteral | STObject | STArray | STStream>
|
|
310
|
-
```
|
|
311
|
-
|
|
312
|
-
Defines response validation by associating schema types with specific HTTP status codes. The special key `default` can also be used to define the Response Schema for the remaining status codes.
|
|
313
|
-
|
|
314
|
-
**Example:**
|
|
315
|
-
|
|
316
|
-
```ts
|
|
317
|
-
const response = {
|
|
318
|
-
200: $T.object({ data: $T.array($T.number()) }),
|
|
319
|
-
404: $T.literal("Not found"),
|
|
320
|
-
default: $T.string(),
|
|
321
|
-
}
|
|
322
|
-
```
|
|
323
|
-
|
|
324
|
-
This ensures all responses adhere to the defined schema.
|
|
325
|
-
|
|
326
|
-
> [!NOTE]
|
|
327
|
-
> The response validation is enabled by default, meaning that every enpoint response that has a schema defined will be validated at runtime. To disable runtime validation, you can set the `responseValidator?.enabled` option to `false` in the [configuration](getting-started.md#configuration).
|
package/test/hooks.test.ts
DELETED
|
@@ -1,200 +0,0 @@
|
|
|
1
|
-
import { expect, test, describe } from 'bun:test'
|
|
2
|
-
import { Galbe } from '../src'
|
|
3
|
-
|
|
4
|
-
const port = 7360
|
|
5
|
-
|
|
6
|
-
describe('hooks', async () => {
|
|
7
|
-
const galbe = new Galbe()
|
|
8
|
-
await galbe.listen(port)
|
|
9
|
-
|
|
10
|
-
test('hooks, empty', async () => {
|
|
11
|
-
galbe.get('/hooks/empty', [], () => 'handled')
|
|
12
|
-
|
|
13
|
-
let resp = await fetch(`http://localhost:${port}/hooks/empty`, {
|
|
14
|
-
method: 'GET'
|
|
15
|
-
})
|
|
16
|
-
expect(resp.status).toBe(200)
|
|
17
|
-
expect(await resp?.text()).toBe('handled')
|
|
18
|
-
})
|
|
19
|
-
|
|
20
|
-
test('hooks, void', async () => {
|
|
21
|
-
galbe.get('/hooks/void', [(_ctx, _next) => {}], () => 'handled')
|
|
22
|
-
|
|
23
|
-
let resp = await fetch(`http://localhost:${port}/hooks/void`, {
|
|
24
|
-
method: 'GET'
|
|
25
|
-
})
|
|
26
|
-
expect(resp.status).toBe(200)
|
|
27
|
-
expect(await resp?.text()).toBe('handled')
|
|
28
|
-
})
|
|
29
|
-
|
|
30
|
-
test('hooks, called without next', async () => {
|
|
31
|
-
let hookCalled = 0
|
|
32
|
-
galbe.get(
|
|
33
|
-
'/hooks/called',
|
|
34
|
-
[
|
|
35
|
-
_ => {
|
|
36
|
-
hookCalled++
|
|
37
|
-
}
|
|
38
|
-
],
|
|
39
|
-
() => 'handled'
|
|
40
|
-
)
|
|
41
|
-
expect(hookCalled).toBe(0)
|
|
42
|
-
let resp = await fetch(`http://localhost:${port}/hooks/called`, {
|
|
43
|
-
method: 'GET'
|
|
44
|
-
})
|
|
45
|
-
expect(hookCalled).toBe(1)
|
|
46
|
-
expect(resp.status).toBe(200)
|
|
47
|
-
expect(await resp?.text()).toBe('handled')
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
test('hooks, called with next', async () => {
|
|
51
|
-
let hookCalled = 0
|
|
52
|
-
galbe.get(
|
|
53
|
-
'/hooks/called',
|
|
54
|
-
[
|
|
55
|
-
async (_, next) => {
|
|
56
|
-
hookCalled++
|
|
57
|
-
await Bun.sleep(10)
|
|
58
|
-
await next()
|
|
59
|
-
}
|
|
60
|
-
],
|
|
61
|
-
() => 'handled'
|
|
62
|
-
)
|
|
63
|
-
expect(hookCalled).toBe(0)
|
|
64
|
-
let resp = await fetch(`http://localhost:${port}/hooks/called`, {
|
|
65
|
-
method: 'GET'
|
|
66
|
-
})
|
|
67
|
-
expect(hookCalled).toBe(1)
|
|
68
|
-
expect(resp.status).toBe(200)
|
|
69
|
-
expect(await resp?.text()).toBe('handled')
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test('hooks, wrapper hook', async () => {
|
|
73
|
-
let before = 0
|
|
74
|
-
let after = 0
|
|
75
|
-
galbe.get(
|
|
76
|
-
'/hooks/called',
|
|
77
|
-
[
|
|
78
|
-
async (_, next) => {
|
|
79
|
-
before++
|
|
80
|
-
await Bun.sleep(10)
|
|
81
|
-
await next()
|
|
82
|
-
after++
|
|
83
|
-
}
|
|
84
|
-
],
|
|
85
|
-
() => {
|
|
86
|
-
expect(before).toBe(1)
|
|
87
|
-
expect(after).toBe(0)
|
|
88
|
-
return 'handled'
|
|
89
|
-
}
|
|
90
|
-
)
|
|
91
|
-
expect(before).toBe(0)
|
|
92
|
-
expect(after).toBe(0)
|
|
93
|
-
let resp = await fetch(`http://localhost:${port}/hooks/called`, {
|
|
94
|
-
method: 'GET'
|
|
95
|
-
})
|
|
96
|
-
expect(before).toBe(1)
|
|
97
|
-
expect(after).toBe(1)
|
|
98
|
-
expect(resp.status).toBe(200)
|
|
99
|
-
expect(await resp?.text()).toBe('handled')
|
|
100
|
-
})
|
|
101
|
-
|
|
102
|
-
test('hooks, nested wrappers', async () => {
|
|
103
|
-
let before1 = 0
|
|
104
|
-
let after1 = 0
|
|
105
|
-
let before2 = 0
|
|
106
|
-
let after2 = 0
|
|
107
|
-
galbe.get(
|
|
108
|
-
'/hooks/called',
|
|
109
|
-
[
|
|
110
|
-
async (_, next) => {
|
|
111
|
-
before1++
|
|
112
|
-
await Bun.sleep(10)
|
|
113
|
-
await next()
|
|
114
|
-
after1++
|
|
115
|
-
},
|
|
116
|
-
async (_, next) => {
|
|
117
|
-
before2++
|
|
118
|
-
await Bun.sleep(10)
|
|
119
|
-
await next()
|
|
120
|
-
after2++
|
|
121
|
-
}
|
|
122
|
-
],
|
|
123
|
-
() => {
|
|
124
|
-
expect(before1).toBe(1)
|
|
125
|
-
expect(before2).toBe(1)
|
|
126
|
-
expect(after1).toBe(0)
|
|
127
|
-
expect(after2).toBe(0)
|
|
128
|
-
return 'handled'
|
|
129
|
-
}
|
|
130
|
-
)
|
|
131
|
-
expect(before1).toBe(0)
|
|
132
|
-
expect(before2).toBe(0)
|
|
133
|
-
expect(after1).toBe(0)
|
|
134
|
-
expect(after2).toBe(0)
|
|
135
|
-
let resp = await fetch(`http://localhost:${port}/hooks/called`, {
|
|
136
|
-
method: 'GET'
|
|
137
|
-
})
|
|
138
|
-
expect(before1).toBe(1)
|
|
139
|
-
expect(before2).toBe(1)
|
|
140
|
-
expect(after1).toBe(1)
|
|
141
|
-
expect(after2).toBe(1)
|
|
142
|
-
expect(resp.status).toBe(200)
|
|
143
|
-
expect(await resp?.text()).toBe('handled')
|
|
144
|
-
})
|
|
145
|
-
|
|
146
|
-
test('hooks, linear chaining', async () => {
|
|
147
|
-
let hook1 = 0
|
|
148
|
-
let hook2 = 0
|
|
149
|
-
galbe.get(
|
|
150
|
-
'/hooks/called',
|
|
151
|
-
[
|
|
152
|
-
async _ => {
|
|
153
|
-
hook1++
|
|
154
|
-
await Bun.sleep(10)
|
|
155
|
-
},
|
|
156
|
-
async _ => {
|
|
157
|
-
hook2++
|
|
158
|
-
await Bun.sleep(10)
|
|
159
|
-
}
|
|
160
|
-
],
|
|
161
|
-
() => {
|
|
162
|
-
expect(hook1).toBe(1)
|
|
163
|
-
expect(hook2).toBe(1)
|
|
164
|
-
return 'handled'
|
|
165
|
-
}
|
|
166
|
-
)
|
|
167
|
-
expect(hook1).toBe(0)
|
|
168
|
-
expect(hook2).toBe(0)
|
|
169
|
-
let resp = await fetch(`http://localhost:${port}/hooks/called`, {
|
|
170
|
-
method: 'GET'
|
|
171
|
-
})
|
|
172
|
-
expect(hook1).toBe(1)
|
|
173
|
-
expect(hook2).toBe(1)
|
|
174
|
-
expect(resp.status).toBe(200)
|
|
175
|
-
expect(await resp?.text()).toBe('handled')
|
|
176
|
-
})
|
|
177
|
-
|
|
178
|
-
test('hooks, early response', async () => {
|
|
179
|
-
let hook = 0
|
|
180
|
-
galbe.get(
|
|
181
|
-
'/hooks',
|
|
182
|
-
[
|
|
183
|
-
async _ => {
|
|
184
|
-
hook++
|
|
185
|
-
return 'hook'
|
|
186
|
-
}
|
|
187
|
-
],
|
|
188
|
-
() => {
|
|
189
|
-
expect.unreachable()
|
|
190
|
-
}
|
|
191
|
-
)
|
|
192
|
-
expect(hook).toBe(0)
|
|
193
|
-
let resp = await fetch(`http://localhost:${port}/hooks`, {
|
|
194
|
-
method: 'GET'
|
|
195
|
-
})
|
|
196
|
-
expect(hook).toBe(1)
|
|
197
|
-
expect(resp.status).toBe(200)
|
|
198
|
-
expect(await resp?.text()).toBe('hook')
|
|
199
|
-
})
|
|
200
|
-
})
|