bun-crumb 0.3.1 → 0.7.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  <div align='center'>
4
4
 
5
- [![CI](https://github.com/a-marigold/crumb/actions/workflows/ci.yaml/badge.svg)](https://github.com/a-marigold/crumb/actions) [![Status](https://img.shields.io/badge/BETA-darkgreen?style=for-the-badge)](https://npmjs.com/package/bun-crumb)
5
+ [![CI](https://github.com/a-marigold/crumb/actions/workflows/ci.yaml/badge.svg)](https://github.com/a-marigold/bun-crumb/actions) [![Status](https://img.shields.io/badge/BETA-darkgreen?style=for-the-badge)](https://npmjs.com/package/bun-crumb)
6
6
  [![bun](https://img.shields.io/badge/Bun-000?logo=bun&logoColor=fff)](https://bun.com) [![npm](https://img.shields.io/npm/v/bun-crumb)](https://npmjs.com/package/bun-crumb)
7
7
 
8
8
  </div>
package/dist/index.d.ts CHANGED
@@ -49,6 +49,7 @@ type Validate = (data: unknown, schema: SchemaData) => boolean;
49
49
  * ```
50
50
  */
51
51
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
52
+ type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 307 | 308;
52
53
  /**
53
54
  * HTTP Header struct.
54
55
  */
@@ -87,6 +88,7 @@ interface RouteResponse<T extends {
87
88
  }> {
88
89
  setHeader: (name: Header['name'], value: Header['value']) => void;
89
90
  send: (data: T['body'], options?: ResponseOptions) => void;
91
+ redirect: (url: string, status: RedirectStatusCode | (number & {})) => void;
90
92
  }
91
93
  type Route = Partial<Record<HttpMethod, RouteOptions>>;
92
94
  type RouteHandler = (request: RouteRequest, response: RouteResponse) => Promise<void> | void;
@@ -94,11 +96,14 @@ type RouteOptions = {
94
96
  url: string;
95
97
  method: HttpMethod;
96
98
  schema?: SchemaData;
97
- onRequest?: RouteHandler;
98
- preHandler?: RouteHandler;
99
99
  handler: RouteHandler;
100
100
  };
101
101
 
102
+ /**
103
+ *
104
+ *
105
+ * Type of `options` parameter in `listen` function
106
+ */
102
107
  interface ListenOptions {
103
108
  /**
104
109
  * Server port to listen
@@ -106,7 +111,6 @@ interface ListenOptions {
106
111
  port?: number | string;
107
112
  /**
108
113
  * Server hostname to listen
109
- *
110
114
  * @example `localhost`, `0.0.0.0`
111
115
  *
112
116
  */
@@ -123,90 +127,6 @@ interface ListenOptions {
123
127
  schemaValidator?: Validate;
124
128
  }
125
129
 
126
- type PreparedRoute = Partial<Record<HttpMethod, WrappedRouteCallback>>;
127
- type WrappedRouteCallback = (request: BunRequest) => Promise<Response>;
128
- /**
129
- * Used straight as Bun.serve `routes` object.
130
- */
131
- type PreparedRoutes = Record<RouteOptions['url'], PreparedRoute>;
132
- type Routes = Map<RouteOptions['url'], Route>;
133
- /**
134
- * An internal Map with routes of app. Do not use it in user code to prevent undefined errors
135
- */
136
- declare const _routes: Routes;
137
- /**
138
- * Runtime function that used in request.
139
- * Parses body to supported content type (json, plain text) and validates it with route schema.
140
- *
141
- * @param {BunRequest} request incoming bun request.
142
- * @param {string} contentType request `Content-Type` header value.
143
- * @param {Schema} schema json or any schema with declared `Schema` type.
144
- * @param {Validate} schemaValidator schema validator function that receives `data` and `schema` arguments.
145
- *
146
- * @returns {Promise<unknown>} Promise with body
147
- */
148
- declare const handleBody: (request: BunRequest, contentType: string, schema?: SchemaData, schemaValidator?: Validate) => Promise<unknown>;
149
- /**
150
- * Internal `server` function.
151
- * Creates a function with handler and all route hooks.
152
- *
153
- * The created function can be used as a callback for route in Bun.serve `routes` object.
154
- *
155
- *
156
- *
157
- *
158
- *
159
- * @param routeOptions options of route
160
- * @returns {WrappedRouteCallback} Function that is ready to be used in Bun.serve `routes`
161
- */
162
- declare const wrapRouteCallback: (routeOptions: RouteOptions, schemaValidator?: Validate) => WrappedRouteCallback;
163
- /**
164
- * Internal `server` function.
165
- * Prepares a route to be used in Bun.serve `routes` object.
166
- *
167
- * @param {Route} route
168
- *
169
- * @returns {PreparedRoute} Route object with `GET` or other http method keys with wrapped route callbacks.
170
- *
171
- * @example
172
- *
173
- * ```typescript
174
- * prepareRoute({
175
- * GET: {
176
- * url: '/products',
177
- * method: 'GET',
178
- * handler: (request, response) => {},
179
- * },
180
- * POST: {
181
- * url: '/products/:id',
182
- * method: 'POST',
183
- * handler: (request, response) => {},
184
- * },
185
- * });
186
- * // Output will be:
187
- * ({
188
- * GET: (request: BunRequest) => {
189
- * // ...code
190
- * return new Response();
191
- * },
192
- * POST: (request: BunRequest) => {
193
- * // ...code
194
- * return new Response();
195
- * },
196
- * })
197
- * ```
198
- *
199
- */
200
- declare const prepareRoute: (route: Route, schemaValidator?: Validate) => PreparedRoute;
201
- /**
202
- * Internal server function.
203
- * Calls `prepareRoute` for every route of `routes` Map and returns prepared routes to use in Bun.serve `routes`.
204
- *
205
- * @param {Routes} routes Map with routes to prepare.
206
- *
207
- * @returns {PreparedRoutes} An object that is used straight in Bun.serve `routes` object.
208
- */
209
- declare const prepareRoutes: (routes: Routes, schemaValidator?: Validate) => PreparedRoutes;
210
130
  /**
211
131
  * Starts serving http server.
212
132
  *
@@ -225,7 +145,7 @@ declare const prepareRoutes: (routes: Routes, schemaValidator?: Validate) => Pre
225
145
  * listen(PORT, 'localhost');
226
146
  * ```
227
147
  */
228
- declare const listen: (options: ListenOptions) => void;
148
+ declare const listen: (options?: ListenOptions) => void;
229
149
 
230
150
  /**
231
151
  * Creates a route with `url`, `method` and `handler`.
@@ -282,5 +202,5 @@ declare const listen: (options: ListenOptions) => void;
282
202
  */
283
203
  declare const createRoute: (routeOptions: RouteOptions) => void;
284
204
 
285
- export { _routes, createRoute, handleBody, listen, prepareRoute, prepareRoutes, wrapRouteCallback };
286
- export type { Header, Headers, HttpMethod, ListenOptions, ResponseOptions, Route, RouteHandler, RouteOptions, RouteRequest, RouteResponse, Routes, Schema, SchemaData, Validate, WrappedRouteCallback };
205
+ export { createRoute, listen };
206
+ export type { Header, Headers, HttpMethod, ListenOptions, RedirectStatusCode, ResponseOptions, Route, RouteHandler, RouteOptions, RouteRequest, RouteResponse, Schema, SchemaData, Validate };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{serve as t}from"bun";class e extends Error{status;constructor(t,e){super(e),this.status=t,this.name="HttpError"}}const n=new Map,s=(t,n,s,o)=>{const r={"application/json":t=>t.json().catch(t=>{throw new e(400,t)}).then(t=>{if(s&&o&&!o(t,s))throw new e(400,"Request does not match schema");return t}),"text/plain":t=>t.text().catch(t=>{throw new e(400,t)}).then(t=>{if(s&&o&&!o(t,s))throw new e(400,"Request does not match schema");return t})};return n in r?r[n](t):Promise.reject(new e(415,"Unsupported media type"))},o=(t,n)=>o=>{const r=o.headers.get("Content-Type")??"text/plain",a=o;return a.handleBody=()=>s(o,r,t.schema,n).then(t=>t),Promise.resolve(((t,e)=>{let n,s,o=null;const r={},a={setHeader:(t,e)=>{r[t]=e},send:(t,e)=>{"object"==typeof t?r["Content-Type"]="application/json":"string"==typeof t&&(r["Content-Type"]="text/plain"),o=t,n=e?.status,s=e?.statusText}};return Promise.all([e.onRequest?.(t,a),e.preHandler?.(t,a),e.handler(t,a)]).then(()=>new Response(null==o?null:JSON.stringify(o),{headers:r,status:n,statusText:s}))})(a,t)).then(t=>t).catch(t=>t instanceof e?new Response(t.message,{status:t.status}):new Response("Internal server error",{status:500}))},r=(t,e)=>{const n={};for(const s in t)Object.hasOwn(t,s)&&(n[s]=o(t[s],e));return n},a=(t,e)=>{const n={};for(const s of t)n[s[0]]=r(s[1],e);return t.clear(),n},c=e=>{t({port:e.port,hostname:e.hostname,development:e.development??!1,routes:a(n,e?.schemaValidator)})},h=t=>{const e=n.get(t.url);e?e[t.method]=t:n.set(t.url,{[t.method]:t})};export{n as _routes,h as createRoute,s as handleBody,c as listen,r as prepareRoute,a as prepareRoutes,o as wrapRouteCallback};
1
+ import{serve as t}from"bun";class e extends Error{status;constructor(t,e){super(e),this.status=t,this.name="HttpError"}}const s=new Map,n=(t,s)=>n=>{const o=n.headers.get("Content-Type")??"text/plain",r=n;return r.handleBody=()=>((t,s,n,o)=>{const r={"application/json":t=>t.json().catch(()=>{throw new e(400,"Bad Request")}).then(t=>{if(n&&o&&!o(t,n))throw new e(400,"Request does not match schema");return t}),"text/plain":t=>t.text().catch(t=>{throw new e(400,t)}).then(t=>{if(n&&o&&!o(t,n))throw new e(400,"Request does not match schema");return t})};return s in r?r[s](t):Promise.reject(new e(415,"Unsupported media type"))})(n,o,t.schema,s),Promise.resolve(((t,e)=>{let s=200,n="",o="";const r={},a={setHeader:(t,e)=>{r[t]=e},send:(t,e)=>{"object"==typeof t?(r["Content-Type"]="application/json",o=JSON.stringify(t)):"string"==typeof t&&(r["Content-Type"]="text/plain",o=t),e&&(s=e.status,n=e.statusText)},redirect:(t,e)=>{o="",s=e,r.Location=t}};return Promise.resolve(e.handler(t,a)).then(()=>new Response(o,{headers:r,status:s,statusText:n}))})(r,t)).then(t=>t).catch(t=>t instanceof e?new Response(t.message,{status:t.status}):new Response("Internal server error",{status:500}))},o=(t,e)=>{const s={};for(const o in t)Object.hasOwn(t,o)&&(s[o]=n(t[o],e));return s},r=(t,e)=>{const s={};for(const n of t)s[n[0]]=o(n[1],e);return t.clear(),s},a=e=>{t({port:e?.port,hostname:e?.hostname,development:e?.development??!1,routes:r(s,e?.schemaValidator)})},c=t=>{const e=s.get(t.url);e?e[t.method]=t:s.set(t.url,{[t.method]:t})};export{c as createRoute,a as listen};
package/dist/server.d.ts CHANGED
@@ -104,5 +104,5 @@ export declare const prepareRoutes: (routes: Routes, schemaValidator?: Validate)
104
104
  * listen(PORT, 'localhost');
105
105
  * ```
106
106
  */
107
- export declare const listen: (options: ListenOptions) => void;
107
+ export declare const listen: (options?: ListenOptions) => void;
108
108
  export {};
@@ -9,6 +9,7 @@ import type { SchemaData } from './schema';
9
9
  * ```
10
10
  */
11
11
  export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
12
+ export type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 307 | 308;
12
13
  /**
13
14
  * HTTP Header struct.
14
15
  */
@@ -47,6 +48,7 @@ export interface RouteResponse<T extends {
47
48
  }> {
48
49
  setHeader: (name: Header['name'], value: Header['value']) => void;
49
50
  send: (data: T['body'], options?: ResponseOptions) => void;
51
+ redirect: (url: string, status: RedirectStatusCode | (number & {})) => void;
50
52
  }
51
53
  export type Route = Partial<Record<HttpMethod, RouteOptions>>;
52
54
  export type RouteHandler = (request: RouteRequest, response: RouteResponse) => Promise<void> | void;
@@ -54,7 +56,5 @@ export type RouteOptions = {
54
56
  url: string;
55
57
  method: HttpMethod;
56
58
  schema?: SchemaData;
57
- onRequest?: RouteHandler;
58
- preHandler?: RouteHandler;
59
59
  handler: RouteHandler;
60
60
  };
@@ -1,4 +1,9 @@
1
1
  import type { Validate } from './schema';
2
+ /**
3
+ *
4
+ *
5
+ * Type of `options` parameter in `listen` function
6
+ */
2
7
  export interface ListenOptions {
3
8
  /**
4
9
  * Server port to listen
@@ -6,7 +11,6 @@ export interface ListenOptions {
6
11
  port?: number | string;
7
12
  /**
8
13
  * Server hostname to listen
9
- *
10
14
  * @example `localhost`, `0.0.0.0`
11
15
  *
12
16
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-crumb",
3
- "version": "0.3.1",
3
+ "version": "0.7.0",
4
4
  "author": "marigold",
5
5
  "module": "dist/index.js",
6
6
  "license": "UNLICENSED",