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 CHANGED
@@ -9,11 +9,11 @@
9
9
 
10
10
  ### Features
11
11
 
12
- - Only Bun is supported
12
+ - Only Bun is supported
13
13
 
14
- - No classes
14
+ - No classes
15
15
 
16
- - Uses Bun’s HTTP API
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: 'cookie', price: 100, id: 1 },
42
- { title: 'bread', price: 1600, id: 2 },
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
- { status: 200 }
49
+
50
+ { status: 200 },
45
51
  );
46
52
  },
47
53
  });
48
54
  ```
49
55
 
50
- &nbsp;
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
- Middleware / Pre-handlers
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 { createRoute, type RouteResponse } from 'bun-crumb';
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
- type Product = { title: string; price: number; id: number };
132
+ </details>
58
133
 
59
- const products: Product[] = [];
134
+ <details>
135
+ <summary>Fastify server code</summary>
60
136
 
61
- createRoute({
62
- url: '/products',
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
- preHandler: (request, response) => {
65
- if (products.find((product) => product.id === request.body.id)) {
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
- handler: (request, response: RouteResponse<{ body: Product }>) => {
73
- products.push(body);
150
+ });
74
151
 
75
- return response.send(body, { status: 201 });
76
- },
152
+ fastify.listen({ port: PORT }, () => {
153
+ Bun.stdout.write('Fastify is running\n');
77
154
  });
78
155
  ```
79
156
 
80
- &nbsp;
157
+ </details>
158
+
159
+ <details>
160
+ <summary>Bun Crumb server code</summary>
81
161
 
82
- Setting Headers and Status
162
+ ```bash
163
+ sleep 1 | k6 run script.js
164
+ ```
83
165
 
84
166
  ```typescript
85
- import { createRoute } from 'bun-crumb';
167
+ import { PORT } from './constants';
168
+
169
+ import { listen, createRoute } from 'bun-crumb';
86
170
 
171
+ //
87
172
  createRoute({
88
- url: '/auth',
173
+ url: '/',
174
+
89
175
  method: 'POST',
90
- handler: (request, response) => {},
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
- let vus: number;
3
- let duration: string;
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
- send: (data: T['body'], options?: ResponseOptions) => void;
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 parametr
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
- * Starts serving http server.
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: RouteRequest,
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: RouteRequest,
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
- * Starts serving http server.
97
+ *
98
+ * Starts to serve http server.
94
99
  *
95
100
  *
96
101
  * @param {ListenOption} options - options
@@ -60,7 +60,46 @@ export interface RouteResponse<T extends {
60
60
  } = {
61
61
  body: unknown;
62
62
  }> {
63
- send: (data: T['body'], options?: ResponseOptions) => void;
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 parametr
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.0",
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
  ],