galbe 0.1.13 → 0.3.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/bun.lockb +0 -0
- package/docs/context.md +1 -1
- package/docs/getting-started.md +8 -0
- package/docs/handler.md +1 -1
- package/docs/hooks.md +5 -5
- package/docs/plugins.md +117 -0
- package/docs/schemas.md +19 -2
- package/package.json +1 -1
- package/src/index.ts +61 -49
- package/src/parser.ts +2 -3
- package/src/router.ts +1 -0
- package/src/routes.ts +13 -10
- package/src/schema.ts +10 -0
- package/src/server.ts +69 -57
- package/src/types.ts +51 -26
- package/src/util.ts +18 -2
- package/src/validator.ts +25 -5
- package/test/plugins.test.ts +3 -3
- package/test/responses.test.ts +288 -13
package/test/responses.test.ts
CHANGED
|
@@ -21,12 +21,14 @@ const rsTxt = (text: string) => {
|
|
|
21
21
|
})
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
+
const handleResp = ctx => ctx.body
|
|
25
|
+
|
|
24
26
|
describe('responses', () => {
|
|
25
27
|
beforeAll(async () => {
|
|
26
28
|
const galbe = new Galbe()
|
|
27
29
|
|
|
28
30
|
galbe.post(
|
|
29
|
-
'/
|
|
31
|
+
'/none',
|
|
30
32
|
{
|
|
31
33
|
body: $T.optional($T.object($T.any())),
|
|
32
34
|
query: { text: $T.optional($T.string()), stream: $T.optional($T.string()) }
|
|
@@ -42,20 +44,25 @@ describe('responses', () => {
|
|
|
42
44
|
}
|
|
43
45
|
)
|
|
44
46
|
|
|
45
|
-
galbe.
|
|
46
|
-
return { next: () => {} }
|
|
47
|
-
})
|
|
47
|
+
galbe.post('', { response: { test: '' } }, () => {})
|
|
48
48
|
|
|
49
|
-
galbe.
|
|
50
|
-
|
|
51
|
-
})
|
|
49
|
+
galbe.post('/ba', { response: { 200: $T.byteArray() } }, handleResp)
|
|
50
|
+
galbe.post('/bool', { response: { 200: $T.boolean() } }, handleResp)
|
|
51
|
+
galbe.post('/num', { response: { 200: $T.number() } }, handleResp)
|
|
52
|
+
galbe.post('/str', { response: { 200: $T.string() } }, handleResp)
|
|
53
|
+
galbe.post('/arr', { response: { 200: $T.array() } }, handleResp)
|
|
54
|
+
galbe.post('/obj', { response: { 200: $T.object($T.any()) } }, handleResp)
|
|
55
|
+
galbe.post('/stream/ba', { response: { 200: $T.stream($T.byteArray()) } }, ctx =>
|
|
56
|
+
ctx.body ? genTxt(ctx.body) : ''
|
|
57
|
+
)
|
|
58
|
+
galbe.post('/stream/str', { response: { 200: $T.stream($T.string()) } }, ctx => (ctx.body ? genTxt(ctx.body) : 42))
|
|
52
59
|
|
|
53
60
|
await galbe.listen(port)
|
|
54
61
|
})
|
|
55
62
|
|
|
56
|
-
test('response, string', async () => {
|
|
63
|
+
test('response, no schema, string', async () => {
|
|
57
64
|
const reqTxt = 'Hello Mom!'
|
|
58
|
-
let resp = await fetch(`http://localhost:${port}/
|
|
65
|
+
let resp = await fetch(`http://localhost:${port}/none?text=${reqTxt}`, {
|
|
59
66
|
method: 'POST'
|
|
60
67
|
})
|
|
61
68
|
|
|
@@ -66,9 +73,9 @@ describe('responses', () => {
|
|
|
66
73
|
expect(body).toBe(reqTxt)
|
|
67
74
|
})
|
|
68
75
|
|
|
69
|
-
test('response, object', async () => {
|
|
76
|
+
test('response, no schema, object', async () => {
|
|
70
77
|
const reqBody = { foo: 'bar' }
|
|
71
|
-
let resp = await fetch(`http://localhost:${port}/
|
|
78
|
+
let resp = await fetch(`http://localhost:${port}/none`, {
|
|
72
79
|
method: 'POST',
|
|
73
80
|
body: JSON.stringify(reqBody),
|
|
74
81
|
headers: {
|
|
@@ -83,12 +90,12 @@ describe('responses', () => {
|
|
|
83
90
|
expect(body).toEqual(reqBody)
|
|
84
91
|
})
|
|
85
92
|
|
|
86
|
-
test('response, stream', async () => {
|
|
93
|
+
test('response, no schema, stream', async () => {
|
|
87
94
|
const reqTxt = 'Hello Mom!'
|
|
88
95
|
const cases = [{ stream: 'generatorFunction' }, { stream: 'readableStream' }]
|
|
89
96
|
|
|
90
97
|
for (const c of cases) {
|
|
91
|
-
let resp = await fetch(`http://localhost:${port}/
|
|
98
|
+
let resp = await fetch(`http://localhost:${port}/none?text=${reqTxt}&stream=${c.stream}`, {
|
|
92
99
|
method: 'POST',
|
|
93
100
|
headers: {
|
|
94
101
|
'content-type': 'application/json'
|
|
@@ -106,4 +113,272 @@ describe('responses', () => {
|
|
|
106
113
|
expect(body).toMatch(new RegExp(`id:${UUID_RGX}\ndata:Hello\n\nid:${UUID_RGX}\ndata:Mom!\n\n`))
|
|
107
114
|
}
|
|
108
115
|
})
|
|
116
|
+
|
|
117
|
+
test('response, ba, validation OK', async () => {
|
|
118
|
+
let bodyStr = 'Hello mom!'
|
|
119
|
+
let reqBody = new TextEncoder().encode(bodyStr)
|
|
120
|
+
|
|
121
|
+
let resp = await fetch(`http://localhost:${port}/ba`, {
|
|
122
|
+
method: 'POST',
|
|
123
|
+
body: bodyStr,
|
|
124
|
+
headers: {
|
|
125
|
+
'content-type': 'application/octet-stream'
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
const body = await resp.json()
|
|
130
|
+
|
|
131
|
+
expect(resp.status).toBe(200)
|
|
132
|
+
expect(resp.headers.get('content-type')).toBe('application/json')
|
|
133
|
+
expect(body).toEqual(reqBody)
|
|
134
|
+
})
|
|
135
|
+
|
|
136
|
+
test('response, ba, validation failed', async () => {
|
|
137
|
+
let bodyStr = 'Hello mom!'
|
|
138
|
+
|
|
139
|
+
let resp = await fetch(`http://localhost:${port}/ba`, {
|
|
140
|
+
method: 'POST',
|
|
141
|
+
body: bodyStr,
|
|
142
|
+
headers: {
|
|
143
|
+
'content-type': 'text/plain'
|
|
144
|
+
}
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
expect(resp.status).toBe(500)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('response, bool, validation OK', async () => {
|
|
151
|
+
let bodyStr = 'true'
|
|
152
|
+
let reqBody = true
|
|
153
|
+
|
|
154
|
+
let resp = await fetch(`http://localhost:${port}/bool`, {
|
|
155
|
+
method: 'POST',
|
|
156
|
+
body: bodyStr,
|
|
157
|
+
headers: {
|
|
158
|
+
'content-type': 'application/json'
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
const body = await resp.json()
|
|
163
|
+
|
|
164
|
+
expect(resp.status).toBe(200)
|
|
165
|
+
expect(resp.headers.get('content-type')).toBe('application/json')
|
|
166
|
+
expect(body).toEqual(reqBody)
|
|
167
|
+
})
|
|
168
|
+
|
|
169
|
+
test('response, bool, validation failed', async () => {
|
|
170
|
+
let bodyStr = 'true'
|
|
171
|
+
|
|
172
|
+
let resp = await fetch(`http://localhost:${port}/bool`, {
|
|
173
|
+
method: 'POST',
|
|
174
|
+
body: bodyStr,
|
|
175
|
+
headers: {
|
|
176
|
+
'content-type': 'text/plain'
|
|
177
|
+
}
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
expect(resp.status).toBe(500)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('response, num, validation OK', async () => {
|
|
184
|
+
let bodyStr = '42'
|
|
185
|
+
let reqBody = 42
|
|
186
|
+
|
|
187
|
+
let resp = await fetch(`http://localhost:${port}/num`, {
|
|
188
|
+
method: 'POST',
|
|
189
|
+
body: bodyStr,
|
|
190
|
+
headers: {
|
|
191
|
+
'content-type': 'application/json'
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
|
|
195
|
+
const body = await resp.json()
|
|
196
|
+
|
|
197
|
+
expect(resp.status).toBe(200)
|
|
198
|
+
expect(resp.headers.get('content-type')).toBe('application/json')
|
|
199
|
+
expect(body).toEqual(reqBody)
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
test('response, num, validation failed', async () => {
|
|
203
|
+
let bodyStr = '"test"'
|
|
204
|
+
|
|
205
|
+
let resp = await fetch(`http://localhost:${port}/num`, {
|
|
206
|
+
method: 'POST',
|
|
207
|
+
body: bodyStr,
|
|
208
|
+
headers: {
|
|
209
|
+
'content-type': 'application/json'
|
|
210
|
+
}
|
|
211
|
+
})
|
|
212
|
+
|
|
213
|
+
expect(resp.status).toBe(500)
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
test('response, str, validation OK', async () => {
|
|
217
|
+
let bodyStr = 'Hello Mom!'
|
|
218
|
+
|
|
219
|
+
let resp = await fetch(`http://localhost:${port}/str`, {
|
|
220
|
+
method: 'POST',
|
|
221
|
+
body: bodyStr,
|
|
222
|
+
headers: {
|
|
223
|
+
'content-type': 'text/plain'
|
|
224
|
+
}
|
|
225
|
+
})
|
|
226
|
+
|
|
227
|
+
const body = await resp.text()
|
|
228
|
+
|
|
229
|
+
expect(resp.status).toBe(200)
|
|
230
|
+
expect(resp.headers.get('content-type')).toBe('text/plain')
|
|
231
|
+
expect(body).toEqual(bodyStr)
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
test('response, str, validation failed', async () => {
|
|
235
|
+
let bodyStr = '3.14'
|
|
236
|
+
|
|
237
|
+
let resp = await fetch(`http://localhost:${port}/str`, {
|
|
238
|
+
method: 'POST',
|
|
239
|
+
body: bodyStr,
|
|
240
|
+
headers: {
|
|
241
|
+
'content-type': 'application/json'
|
|
242
|
+
}
|
|
243
|
+
})
|
|
244
|
+
|
|
245
|
+
expect(resp.status).toBe(500)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('response, array, validation OK', async () => {
|
|
249
|
+
let bodyStr = '[false, "one", 2]'
|
|
250
|
+
let reqBody = [false, 'one', 2]
|
|
251
|
+
|
|
252
|
+
let resp = await fetch(`http://localhost:${port}/arr`, {
|
|
253
|
+
method: 'POST',
|
|
254
|
+
body: bodyStr,
|
|
255
|
+
headers: {
|
|
256
|
+
'content-type': 'application/json'
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
|
|
260
|
+
const body = await resp.json()
|
|
261
|
+
|
|
262
|
+
expect(resp.status).toBe(200)
|
|
263
|
+
expect(resp.headers.get('content-type')).toBe('application/json')
|
|
264
|
+
expect(body).toEqual(reqBody)
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
test('response, array, validation failed', async () => {
|
|
268
|
+
let bodyStr = '0'
|
|
269
|
+
|
|
270
|
+
let resp = await fetch(`http://localhost:${port}/arr`, {
|
|
271
|
+
method: 'POST',
|
|
272
|
+
body: bodyStr,
|
|
273
|
+
headers: {
|
|
274
|
+
'content-type': 'application/json'
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
|
|
278
|
+
expect(resp.status).toBe(500)
|
|
279
|
+
})
|
|
280
|
+
|
|
281
|
+
test('response, object, validation OK', async () => {
|
|
282
|
+
let bodyStr = '{"str":"str", "num":0, "arr":[1,2,3], "nested": {"foo":"bar"}}'
|
|
283
|
+
let reqBody = { str: 'str', num: 0, arr: [1, 2, 3], nested: { foo: 'bar' } }
|
|
284
|
+
|
|
285
|
+
let resp = await fetch(`http://localhost:${port}/obj`, {
|
|
286
|
+
method: 'POST',
|
|
287
|
+
body: bodyStr,
|
|
288
|
+
headers: {
|
|
289
|
+
'content-type': 'application/json'
|
|
290
|
+
}
|
|
291
|
+
})
|
|
292
|
+
|
|
293
|
+
const body = await resp.json()
|
|
294
|
+
|
|
295
|
+
expect(resp.status).toBe(200)
|
|
296
|
+
expect(resp.headers.get('content-type')).toBe('application/json')
|
|
297
|
+
expect(body).toEqual(reqBody)
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
test('response, object, validation failed', async () => {
|
|
301
|
+
let bodyStr = '"This"'
|
|
302
|
+
|
|
303
|
+
let resp = await fetch(`http://localhost:${port}/obj`, {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
body: bodyStr,
|
|
306
|
+
headers: {
|
|
307
|
+
'content-type': 'application/json'
|
|
308
|
+
}
|
|
309
|
+
})
|
|
310
|
+
|
|
311
|
+
expect(resp.status).toBe(500)
|
|
312
|
+
})
|
|
313
|
+
|
|
314
|
+
test('response, stream ba, validation OK', async () => {
|
|
315
|
+
let bodyStr = 'Hello Mom!'
|
|
316
|
+
|
|
317
|
+
let resp = await fetch(`http://localhost:${port}/stream/ba`, {
|
|
318
|
+
method: 'POST',
|
|
319
|
+
body: bodyStr,
|
|
320
|
+
headers: {
|
|
321
|
+
'content-type': 'text/plain'
|
|
322
|
+
}
|
|
323
|
+
})
|
|
324
|
+
|
|
325
|
+
const reader = resp.body?.getReader()
|
|
326
|
+
let body = ''
|
|
327
|
+
while (reader) {
|
|
328
|
+
const { value, done } = await reader.read()
|
|
329
|
+
if (done) break
|
|
330
|
+
body += decoder.decode(value)
|
|
331
|
+
}
|
|
332
|
+
expect(resp.status).toBe(200)
|
|
333
|
+
expect(resp.headers.get('content-type')).toBe('text/event-stream')
|
|
334
|
+
expect(body).toMatch(new RegExp(`id:${UUID_RGX}\ndata:Hello\n\nid:${UUID_RGX}\ndata:Mom!\n\n`))
|
|
335
|
+
})
|
|
336
|
+
|
|
337
|
+
test('response, stream ba, validation failed', async () => {
|
|
338
|
+
let bodyStr = ''
|
|
339
|
+
|
|
340
|
+
let resp = await fetch(`http://localhost:${port}/stream/ba`, {
|
|
341
|
+
method: 'POST',
|
|
342
|
+
body: bodyStr
|
|
343
|
+
})
|
|
344
|
+
|
|
345
|
+
expect(resp.status).toBe(500)
|
|
346
|
+
})
|
|
347
|
+
|
|
348
|
+
test('response, stream str, validation OK', async () => {
|
|
349
|
+
let bodyStr = 'Hello Mom!'
|
|
350
|
+
|
|
351
|
+
let resp = await fetch(`http://localhost:${port}/stream/str`, {
|
|
352
|
+
method: 'POST',
|
|
353
|
+
body: bodyStr,
|
|
354
|
+
headers: {
|
|
355
|
+
'content-type': 'text/plain'
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
|
|
359
|
+
const reader = resp.body?.getReader()
|
|
360
|
+
let body = ''
|
|
361
|
+
while (reader) {
|
|
362
|
+
const { value, done } = await reader.read()
|
|
363
|
+
if (done) break
|
|
364
|
+
body += decoder.decode(value)
|
|
365
|
+
}
|
|
366
|
+
expect(resp.status).toBe(200)
|
|
367
|
+
expect(resp.headers.get('content-type')).toBe('text/event-stream')
|
|
368
|
+
expect(body).toMatch(new RegExp(`id:${UUID_RGX}\ndata:Hello\n\nid:${UUID_RGX}\ndata:Mom!\n\n`))
|
|
369
|
+
})
|
|
370
|
+
|
|
371
|
+
test('response, stream str, validation failed', async () => {
|
|
372
|
+
let bodyStr = '0'
|
|
373
|
+
|
|
374
|
+
let resp = await fetch(`http://localhost:${port}/stream/str`, {
|
|
375
|
+
method: 'POST',
|
|
376
|
+
body: bodyStr,
|
|
377
|
+
headers: {
|
|
378
|
+
'content-type': 'application/json'
|
|
379
|
+
}
|
|
380
|
+
})
|
|
381
|
+
|
|
382
|
+
expect(resp.status).toBe(500)
|
|
383
|
+
})
|
|
109
384
|
})
|