bun-crumb 0.11.0 → 0.12.1
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/constants/MB_MULTIPLIER.d.ts +10 -0
- package/dist/bench/src/fastifyRoutes.d.ts +1 -0
- package/dist/bench/src/handleBody.d.ts +1 -0
- package/dist/bench/src/headersMicroBench.d.ts +1 -0
- package/dist/bench/src/k6/bunCrumb.d.ts +1 -0
- package/dist/bench/src/k6/constants.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 +14 -0
- package/dist/bench/src/k6/server.d.ts +1 -0
- package/dist/bench/src/prepareRoutes.d.ts +1 -0
- package/dist/index.d.ts +7 -3
- package/dist/index.js +1 -1
- package/dist/server.d.ts +6 -1
- package/dist/types/route.d.ts +2 -2
- package/dist/types/schema.d.ts +3 -0
- package/package.json +1 -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 {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const PORT: 59957;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export namespace options {
|
|
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
|
+
}
|
|
12
|
+
}
|
|
13
|
+
declare function _default(): void;
|
|
14
|
+
export default _default;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/index.d.ts
CHANGED
|
@@ -13,12 +13,15 @@ import { BunRequest, CookieInit } from 'bun';
|
|
|
13
13
|
*
|
|
14
14
|
*
|
|
15
15
|
*
|
|
16
|
+
*
|
|
17
|
+
*
|
|
16
18
|
* declare module 'crumb-bun' {
|
|
17
19
|
* interface Schema {
|
|
18
20
|
* zod: ZodType;
|
|
19
21
|
* }
|
|
20
22
|
* }
|
|
21
23
|
* ```
|
|
24
|
+
*
|
|
22
25
|
*
|
|
23
26
|
* ```json
|
|
24
27
|
* // tsconfig.json
|
|
@@ -118,7 +121,7 @@ interface RouteResponse<T extends {
|
|
|
118
121
|
} = {
|
|
119
122
|
body: unknown;
|
|
120
123
|
}> {
|
|
121
|
-
send: (data
|
|
124
|
+
send: (data?: T['body'], options?: ResponseOptions) => void;
|
|
122
125
|
/**
|
|
123
126
|
* Sets `Location` header to provided `url` and `response.status` to provided `status`
|
|
124
127
|
*
|
|
@@ -166,7 +169,7 @@ interface RouteResponse<T extends {
|
|
|
166
169
|
setHeader: (name: Header['name'], value: Header['value']) => void;
|
|
167
170
|
/**
|
|
168
171
|
*
|
|
169
|
-
* @param options `Bun.Cookie` options
|
|
172
|
+
* @param options `Bun.Cookie` options parameter
|
|
170
173
|
*
|
|
171
174
|
*
|
|
172
175
|
*
|
|
@@ -212,7 +215,8 @@ interface ListenOptions {
|
|
|
212
215
|
}
|
|
213
216
|
|
|
214
217
|
/**
|
|
215
|
-
*
|
|
218
|
+
*
|
|
219
|
+
* Starts to serve http server.
|
|
216
220
|
*
|
|
217
221
|
*
|
|
218
222
|
* @param {ListenOption} options - options
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{serve as e,Cookie as
|
|
1
|
+
import{serve as e,stdout as t,Cookie as n}from"bun";class s extends Error{status;constructor(e,t){super(t),this.status=e,this.name="HttpError"}}const r=new Map,o=(e,t)=>r=>{const o=r.headers.get("Content-Type")??"text/plain",a=r;return a.handleBody=()=>((e,t,n,r)=>{const o={"application/json":e=>e.json().catch(()=>{throw new s(400,"Bad Request")}).then(e=>{if(n&&r&&!r(e,n))throw new s(400,"Request does not match schema");return e}),"text/plain":e=>e.text().catch(e=>{throw new s(400,e)}).then(e=>{if(n&&r&&!r(e,n))throw new s(400,"Request does not match schema");return e})};return t in o?o[t](e):Promise.reject(new s(415,"Unsupported media type"))})(r,o,e.schema,t),a.query=new URLSearchParams(r.url.split("?")[1]||""),Promise.resolve(((e,t)=>{let s=200,r="",o="";const a=new Headers,c={send:(e,t)=>{"object"==typeof e?(a.has("Content-Type")||a.set("Content-Type","application/json"),o=JSON.stringify(e)):"string"==typeof e?(a.has("Content-Type")||a.set("Content-Type","text/plain"),o=e):o=e,t&&(s=t.status,r=t.statusText)},redirect:(e,t)=>{o="",s=t||302,a.set("Location",e)},setHeader:(e,t)=>{a.set(e,t)},setCookie:e=>{a.append("Set-Cookie",new n(e).toString())}};return Promise.resolve(t.handler(e,c)).then(()=>new Response(o,{headers:a,status:s,statusText:r}))})(a,e)).then(e=>e).catch(e=>e instanceof s?new Response(e.message,{status:e.status}):new Response("Internal server error",{status:500}))},a=(e,t)=>{const n={};for(const s in e)Object.hasOwn(e,s)&&(n[s]=o(e[s],t));return n},c=(e,t)=>{const n={};for(const s of e)n[s[0]]=a(s[1],t);return e.clear(),n},h=n=>{const s=e({port:n?.port,hostname:n?.hostname,development:n?.development??!1,routes:c(r,n?.schemaValidator)});t.write("Server is running on "+s.url.href+"\n")},i=e=>{const t=r.get(e.url);t?t[e.method]=e:r.set(e.url,{[e.method]:e})};export{i as createRoute,h as listen};
|
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,7 @@ export interface RouteResponse<T extends {
|
|
|
60
60
|
} = {
|
|
61
61
|
body: unknown;
|
|
62
62
|
}> {
|
|
63
|
-
send: (data
|
|
63
|
+
send: (data?: T['body'], options?: ResponseOptions) => void;
|
|
64
64
|
/**
|
|
65
65
|
* Sets `Location` header to provided `url` and `response.status` to provided `status`
|
|
66
66
|
*
|
|
@@ -108,7 +108,7 @@ export interface RouteResponse<T extends {
|
|
|
108
108
|
setHeader: (name: Header['name'], value: Header['value']) => void;
|
|
109
109
|
/**
|
|
110
110
|
*
|
|
111
|
-
* @param options `Bun.Cookie` options
|
|
111
|
+
* @param options `Bun.Cookie` options parameter
|
|
112
112
|
*
|
|
113
113
|
*
|
|
114
114
|
*
|
package/dist/types/schema.d.ts
CHANGED