bun-crumb 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/README.md +128 -29
- package/dist/bench/src/k6/bunCrumb.d.ts +1 -0
- package/dist/bench/src/k6/fastify.d.ts +1 -0
- package/dist/bench/src/k6/pureBun.d.ts +1 -0
- package/dist/bench/src/k6/script.d.ts +10 -2
- package/dist/index.d.ts +49 -8
- package/dist/route.d.ts +4 -3
- package/dist/runtime.d.ts +1 -0
- package/dist/server.d.ts +6 -1
- package/dist/types/route.d.ts +43 -4
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
|
|
10
10
|
### Features
|
|
11
11
|
|
|
12
|
-
-
|
|
12
|
+
- Only Bun is supported
|
|
13
13
|
|
|
14
|
-
-
|
|
14
|
+
- No classes
|
|
15
15
|
|
|
16
|
-
-
|
|
16
|
+
- Uses `Bun.serve` routes
|
|
17
17
|
|
|
18
18
|
### Installation
|
|
19
19
|
|
|
@@ -34,59 +34,158 @@ const products: Product[] = [];
|
|
|
34
34
|
|
|
35
35
|
createRoute({
|
|
36
36
|
url: '/products',
|
|
37
|
+
|
|
37
38
|
method: 'GET',
|
|
39
|
+
|
|
38
40
|
handler: (request, response: RouteResponse<{ body: Product[] }>) => {
|
|
39
41
|
return response.send(
|
|
40
42
|
[
|
|
41
|
-
{ title: '
|
|
42
|
-
|
|
43
|
+
{ title: 'bananas', price: 0.57, id: 1 },
|
|
44
|
+
|
|
45
|
+
{ title: 'bread', price: 0.89, id: 2 },
|
|
46
|
+
|
|
47
|
+
{ title: 'milk', price: 0.83, id: 3 },
|
|
43
48
|
],
|
|
44
|
-
|
|
49
|
+
|
|
50
|
+
{ status: 200 },
|
|
45
51
|
);
|
|
46
52
|
},
|
|
47
53
|
});
|
|
48
54
|
```
|
|
49
55
|
|
|
50
|
-
|
|
56
|
+
### Benchmarks
|
|
57
|
+
|
|
58
|
+
- Machine:
|
|
59
|
+
- windows 11
|
|
60
|
+
- intel core i5 10300H
|
|
61
|
+
- 16gb ram
|
|
62
|
+
|
|
63
|
+
- Load testing tool: grafana/k6
|
|
64
|
+
|
|
65
|
+
- Bun version: 1.3.6
|
|
66
|
+
|
|
67
|
+
<details>
|
|
68
|
+
<summary>k6 script</summary>
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
sleep 1 | k6 run script.js
|
|
72
|
+
```
|
|
51
73
|
|
|
52
|
-
|
|
74
|
+
```javascript
|
|
75
|
+
import { PORT } from './constants.js';
|
|
76
|
+
|
|
77
|
+
import http from 'k6/http';
|
|
78
|
+
|
|
79
|
+
export const options = {
|
|
80
|
+
scenarios: {
|
|
81
|
+
load: {
|
|
82
|
+
executor: 'constant-arrival-rate',
|
|
83
|
+
|
|
84
|
+
rate: 16000,
|
|
85
|
+
timeUnit: '1s',
|
|
86
|
+
|
|
87
|
+
duration: '30s',
|
|
88
|
+
preAllocatedVUs: 1000,
|
|
89
|
+
|
|
90
|
+
maxVUs: 3000,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
export default () => {
|
|
96
|
+
http.post(
|
|
97
|
+
'http://localhost:' + PORT + '/',
|
|
98
|
+
JSON.stringify({ key: 'value' }),
|
|
99
|
+
{ headers: { 'Content-Type': 'application/json' } },
|
|
100
|
+
);
|
|
101
|
+
};
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
</details>
|
|
105
|
+
|
|
106
|
+
<details>
|
|
107
|
+
<summary>Pure Bun server code</summary>
|
|
53
108
|
|
|
54
109
|
```typescript
|
|
55
|
-
import {
|
|
110
|
+
import { serve, stdout } from 'bun';
|
|
111
|
+
|
|
112
|
+
import { PORT } from './constants';
|
|
113
|
+
|
|
114
|
+
serve({
|
|
115
|
+
routes: {
|
|
116
|
+
'/': {
|
|
117
|
+
POST: (request) => {
|
|
118
|
+
return request.json().then(
|
|
119
|
+
(body) =>
|
|
120
|
+
new Response(JSON.stringify(body), {
|
|
121
|
+
headers: { 'Content-Type': 'application/json' },
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
port: PORT,
|
|
128
|
+
});
|
|
129
|
+
stdout.write('Bun is running\n');
|
|
130
|
+
```
|
|
56
131
|
|
|
57
|
-
|
|
132
|
+
</details>
|
|
58
133
|
|
|
59
|
-
|
|
134
|
+
<details>
|
|
135
|
+
<summary>Fastify server code</summary>
|
|
60
136
|
|
|
61
|
-
|
|
62
|
-
|
|
137
|
+
```typescript
|
|
138
|
+
import Fastify from 'fastify';
|
|
139
|
+
|
|
140
|
+
import { PORT } from './constants';
|
|
141
|
+
|
|
142
|
+
const fastify = Fastify({ logger: false });
|
|
143
|
+
|
|
144
|
+
fastify.route({
|
|
145
|
+
url: '/',
|
|
63
146
|
method: 'POST',
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
return response.send(
|
|
67
|
-
{ message: 'Product with this id already exists' },
|
|
68
|
-
{ status: 409 }
|
|
69
|
-
);
|
|
70
|
-
}
|
|
147
|
+
handler: (request, response) => {
|
|
148
|
+
return response.send(request.body);
|
|
71
149
|
},
|
|
72
|
-
|
|
73
|
-
products.push(body);
|
|
150
|
+
});
|
|
74
151
|
|
|
75
|
-
|
|
76
|
-
|
|
152
|
+
fastify.listen({ port: PORT }, () => {
|
|
153
|
+
Bun.stdout.write('Fastify is running\n');
|
|
77
154
|
});
|
|
78
155
|
```
|
|
79
156
|
|
|
80
|
-
|
|
157
|
+
</details>
|
|
158
|
+
|
|
159
|
+
<details>
|
|
160
|
+
<summary>Bun Crumb server code</summary>
|
|
81
161
|
|
|
82
|
-
|
|
162
|
+
```bash
|
|
163
|
+
sleep 1 | k6 run script.js
|
|
164
|
+
```
|
|
83
165
|
|
|
84
166
|
```typescript
|
|
85
|
-
import {
|
|
167
|
+
import { PORT } from './constants';
|
|
168
|
+
|
|
169
|
+
import { listen, createRoute } from 'bun-crumb';
|
|
86
170
|
|
|
171
|
+
//
|
|
87
172
|
createRoute({
|
|
88
|
-
url: '/
|
|
173
|
+
url: '/',
|
|
174
|
+
|
|
89
175
|
method: 'POST',
|
|
90
|
-
|
|
176
|
+
|
|
177
|
+
handler: (request, response) => {
|
|
178
|
+
return request.handleBody().then(response.send);
|
|
179
|
+
},
|
|
91
180
|
});
|
|
181
|
+
|
|
182
|
+
listen({ port: PORT });
|
|
92
183
|
```
|
|
184
|
+
|
|
185
|
+
</details>
|
|
186
|
+
|
|
187
|
+
| Library | Total requests | RPS | Requests failed | Avg. request duration |
|
|
188
|
+
| --------- | -------------- | -------------- | --------------- | --------------------- |
|
|
189
|
+
| Pure Bun | 482681 | 15958.069946/s | 0.00% (0) | 12.48ms |
|
|
190
|
+
| Bun Crumb | 474870 | 15789.504029/s | 0.02% (101) | 23.08ms |
|
|
191
|
+
| Fastify | 364525 | 12060.63486/s | 0.27% (1002) | 237.68ms |
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
export namespace options {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
namespace scenarios {
|
|
3
|
+
namespace load {
|
|
4
|
+
let executor: string;
|
|
5
|
+
let rate: number;
|
|
6
|
+
let timeUnit: string;
|
|
7
|
+
let duration: string;
|
|
8
|
+
let preAllocatedVUs: number;
|
|
9
|
+
let maxVUs: number;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
4
12
|
}
|
|
5
13
|
declare function _default(): void;
|
|
6
14
|
export default _default;
|
package/dist/index.d.ts
CHANGED
|
@@ -121,7 +121,46 @@ interface RouteResponse<T extends {
|
|
|
121
121
|
} = {
|
|
122
122
|
body: unknown;
|
|
123
123
|
}> {
|
|
124
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Sets Response body to provided `data`.
|
|
126
|
+
* Automatically sets `Content-Type` to `application/json` if an object was provided and transforms `data` to JSON.
|
|
127
|
+
*
|
|
128
|
+
* ### **IMPORTANT NOTES**:
|
|
129
|
+
* - #### If `Content-Type` header is already set, this function will not change this header.
|
|
130
|
+
*
|
|
131
|
+
*
|
|
132
|
+
*
|
|
133
|
+
*
|
|
134
|
+
* - #### If you want to complete request and send response, you must necessarily `return` the call of this function. Otherwise, code of your handler will continue executing and `Response` body could be changed.
|
|
135
|
+
*
|
|
136
|
+
* @param data value that will be sent to client
|
|
137
|
+
* @param options standard `Response` options from web API
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* response.setHeader('content-type', 'x-www-form-urlencoded'); // Set `Content-Type` header
|
|
142
|
+
*
|
|
143
|
+
* return response.send('Hello'); // This does not change `Content-Type` header, because this header is set above
|
|
144
|
+
* ```
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* return response.send('Hello'); // This sets `Content-Type` header to `text/plain`, because this header is not set above.
|
|
149
|
+
* ```
|
|
150
|
+
*
|
|
151
|
+
* @example
|
|
152
|
+
* ```typescript
|
|
153
|
+
* return response.send({ key: 'value' }); // This will set `Content-Type` header to `application/json`, because this header is not set above. Object is transformed to JSON automatically
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```typescript
|
|
158
|
+
* response.send({key: 'value'}); // ❌ Without return, the code below changes the body of response
|
|
159
|
+
* response.send('Hello'); // ❌ This changes the body of response, but `Content-Type` header will not change
|
|
160
|
+
* ```
|
|
161
|
+
*
|
|
162
|
+
*/
|
|
163
|
+
send: (data?: T['body'], options?: ResponseOptions) => void;
|
|
125
164
|
/**
|
|
126
165
|
* Sets `Location` header to provided `url` and `response.status` to provided `status`
|
|
127
166
|
*
|
|
@@ -129,7 +168,7 @@ interface RouteResponse<T extends {
|
|
|
129
168
|
*
|
|
130
169
|
* @param url `Location` to redirect
|
|
131
170
|
*
|
|
132
|
-
* @param status redirect http code (`3xx`)
|
|
171
|
+
* @param status redirect http code (`3xx`). Equals to 302 by default
|
|
133
172
|
*
|
|
134
173
|
* @example
|
|
135
174
|
*
|
|
@@ -140,7 +179,7 @@ interface RouteResponse<T extends {
|
|
|
140
179
|
* The same behaviour is
|
|
141
180
|
* ```typescript
|
|
142
181
|
* response.setHeader('Location', 'https://bun-crumb.vercel.app');
|
|
143
|
-
* return response.send('', {status: 302});
|
|
182
|
+
* return response.send('', { status: 302 });
|
|
144
183
|
* ```
|
|
145
184
|
*/
|
|
146
185
|
redirect: (url: string, status?: RedirectStatusCode | (number & {})) => void;
|
|
@@ -169,7 +208,7 @@ interface RouteResponse<T extends {
|
|
|
169
208
|
setHeader: (name: Header['name'], value: Header['value']) => void;
|
|
170
209
|
/**
|
|
171
210
|
*
|
|
172
|
-
* @param options `Bun.Cookie` options
|
|
211
|
+
* @param options `Bun.Cookie` options parameter
|
|
173
212
|
*
|
|
174
213
|
*
|
|
175
214
|
*
|
|
@@ -215,7 +254,8 @@ interface ListenOptions {
|
|
|
215
254
|
}
|
|
216
255
|
|
|
217
256
|
/**
|
|
218
|
-
*
|
|
257
|
+
*
|
|
258
|
+
* Starts to serve http server.
|
|
219
259
|
*
|
|
220
260
|
*
|
|
221
261
|
* @param {ListenOption} options - options
|
|
@@ -236,7 +276,8 @@ interface ListenOptions {
|
|
|
236
276
|
declare const listen: (options?: ListenOptions) => void;
|
|
237
277
|
|
|
238
278
|
/**
|
|
239
|
-
* Creates a route with `url`, `method` and `handler`.
|
|
279
|
+
* #### Creates a route with `url`, `method` and `handler`.
|
|
280
|
+
*
|
|
240
281
|
* Should be called before `listen` function is called.
|
|
241
282
|
*
|
|
242
283
|
*
|
|
@@ -260,8 +301,8 @@ declare const listen: (options?: ListenOptions) => void;
|
|
|
260
301
|
*
|
|
261
302
|
* @example
|
|
262
303
|
* ```typescript
|
|
263
|
-
* const deleteProduct = (
|
|
264
|
-
* request
|
|
304
|
+
* const deleteProduct: RouteHandler = (
|
|
305
|
+
* request,
|
|
265
306
|
* response: RouteResponse<{ body: { error: string } | { product: Product } }>
|
|
266
307
|
* ) => {
|
|
267
308
|
* const id = request.params.id;
|
package/dist/route.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { RouteOptions } from './types';
|
|
2
2
|
/**
|
|
3
|
-
* Creates a route with `url`, `method` and `handler`.
|
|
3
|
+
* #### Creates a route with `url`, `method` and `handler`.
|
|
4
|
+
*
|
|
4
5
|
* Should be called before `listen` function is called.
|
|
5
6
|
*
|
|
6
7
|
*
|
|
@@ -24,8 +25,8 @@ import type { RouteOptions } from './types';
|
|
|
24
25
|
*
|
|
25
26
|
* @example
|
|
26
27
|
* ```typescript
|
|
27
|
-
* const deleteProduct = (
|
|
28
|
-
* request
|
|
28
|
+
* const deleteProduct: RouteHandler = (
|
|
29
|
+
* request,
|
|
29
30
|
* response: RouteResponse<{ body: { error: string } | { product: Product } }>
|
|
30
31
|
* ) => {
|
|
31
32
|
* const id = request.params.id;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/server.d.ts
CHANGED
|
@@ -25,6 +25,10 @@ export declare const _routes: Routes;
|
|
|
25
25
|
* @param {Validate} schemaValidator schema validator function that receives `data` and `schema` arguments.
|
|
26
26
|
*
|
|
27
27
|
* @returns {Promise<unknown>} Promise with body
|
|
28
|
+
*
|
|
29
|
+
*
|
|
30
|
+
*
|
|
31
|
+
*
|
|
28
32
|
*/
|
|
29
33
|
export declare const handleBody: (request: BunRequest, contentType: string, schema?: SchemaData, schemaValidator?: Validate) => Promise<unknown>;
|
|
30
34
|
/**
|
|
@@ -90,7 +94,8 @@ export declare const prepareRoute: (route: Route, schemaValidator?: Validate) =>
|
|
|
90
94
|
*/
|
|
91
95
|
export declare const prepareRoutes: (routes: Routes, schemaValidator?: Validate) => PreparedRoutes;
|
|
92
96
|
/**
|
|
93
|
-
*
|
|
97
|
+
*
|
|
98
|
+
* Starts to serve http server.
|
|
94
99
|
*
|
|
95
100
|
*
|
|
96
101
|
* @param {ListenOption} options - options
|
package/dist/types/route.d.ts
CHANGED
|
@@ -60,7 +60,46 @@ export interface RouteResponse<T extends {
|
|
|
60
60
|
} = {
|
|
61
61
|
body: unknown;
|
|
62
62
|
}> {
|
|
63
|
-
|
|
63
|
+
/**
|
|
64
|
+
* Sets Response body to provided `data`.
|
|
65
|
+
* Automatically sets `Content-Type` to `application/json` if an object was provided and transforms `data` to JSON.
|
|
66
|
+
*
|
|
67
|
+
* ### **IMPORTANT NOTES**:
|
|
68
|
+
* - #### If `Content-Type` header is already set, this function will not change this header.
|
|
69
|
+
*
|
|
70
|
+
*
|
|
71
|
+
*
|
|
72
|
+
*
|
|
73
|
+
* - #### If you want to complete request and send response, you must necessarily `return` the call of this function. Otherwise, code of your handler will continue executing and `Response` body could be changed.
|
|
74
|
+
*
|
|
75
|
+
* @param data value that will be sent to client
|
|
76
|
+
* @param options standard `Response` options from web API
|
|
77
|
+
*
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* response.setHeader('content-type', 'x-www-form-urlencoded'); // Set `Content-Type` header
|
|
81
|
+
*
|
|
82
|
+
* return response.send('Hello'); // This does not change `Content-Type` header, because this header is set above
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* ```typescript
|
|
87
|
+
* return response.send('Hello'); // This sets `Content-Type` header to `text/plain`, because this header is not set above.
|
|
88
|
+
* ```
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* return response.send({ key: 'value' }); // This will set `Content-Type` header to `application/json`, because this header is not set above. Object is transformed to JSON automatically
|
|
93
|
+
* ```
|
|
94
|
+
*
|
|
95
|
+
* @example
|
|
96
|
+
* ```typescript
|
|
97
|
+
* response.send({key: 'value'}); // ❌ Without return, the code below changes the body of response
|
|
98
|
+
* response.send('Hello'); // ❌ This changes the body of response, but `Content-Type` header will not change
|
|
99
|
+
* ```
|
|
100
|
+
*
|
|
101
|
+
*/
|
|
102
|
+
send: (data?: T['body'], options?: ResponseOptions) => void;
|
|
64
103
|
/**
|
|
65
104
|
* Sets `Location` header to provided `url` and `response.status` to provided `status`
|
|
66
105
|
*
|
|
@@ -68,7 +107,7 @@ export interface RouteResponse<T extends {
|
|
|
68
107
|
*
|
|
69
108
|
* @param url `Location` to redirect
|
|
70
109
|
*
|
|
71
|
-
* @param status redirect http code (`3xx`)
|
|
110
|
+
* @param status redirect http code (`3xx`). Equals to 302 by default
|
|
72
111
|
*
|
|
73
112
|
* @example
|
|
74
113
|
*
|
|
@@ -79,7 +118,7 @@ export interface RouteResponse<T extends {
|
|
|
79
118
|
* The same behaviour is
|
|
80
119
|
* ```typescript
|
|
81
120
|
* response.setHeader('Location', 'https://bun-crumb.vercel.app');
|
|
82
|
-
* return response.send('', {status: 302});
|
|
121
|
+
* return response.send('', { status: 302 });
|
|
83
122
|
* ```
|
|
84
123
|
*/
|
|
85
124
|
redirect: (url: string, status?: RedirectStatusCode | (number & {})) => void;
|
|
@@ -108,7 +147,7 @@ export interface RouteResponse<T extends {
|
|
|
108
147
|
setHeader: (name: Header['name'], value: Header['value']) => void;
|
|
109
148
|
/**
|
|
110
149
|
*
|
|
111
|
-
* @param options `Bun.Cookie` options
|
|
150
|
+
* @param options `Bun.Cookie` options parameter
|
|
112
151
|
*
|
|
113
152
|
*
|
|
114
153
|
*
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bun-crumb",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.2",
|
|
4
4
|
"author": "marigold",
|
|
5
5
|
"module": "dist/index.js",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"rollup-plugin-dts": "^6.3.0",
|
|
18
18
|
"tslib": "^2.8.1"
|
|
19
19
|
},
|
|
20
|
+
"homepage": "https://bun-crumb.vercel.app",
|
|
20
21
|
"files": [
|
|
21
22
|
"dist"
|
|
22
23
|
],
|