bun-crumb 0.8.0 → 0.10.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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { BunRequest } from 'bun';
1
+ import { BunRequest, CookieInit } from 'bun';
2
2
 
3
3
  /**
4
4
  * The global Schema type that reflects schema type in `Validate` function
@@ -43,13 +43,14 @@ type Validate = (data: unknown, schema: SchemaData) => boolean;
43
43
  /**
44
44
  * HTTP Method primitive.
45
45
  *
46
+ *
46
47
  * @example
47
48
  * ```typescript
48
49
  * const method: HttpMethod = 'GET';
49
50
  * ```
50
51
  */
51
52
  type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
52
- type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 307 | 308;
53
+ type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
53
54
  /**
54
55
  * HTTP Header struct.
55
56
  */
@@ -57,7 +58,11 @@ type Header = {
57
58
  name: string;
58
59
  value: string;
59
60
  };
60
- type Headers = ResponseInit['headers'];
61
+
62
+ /**
63
+ *
64
+ * Type of Request search parameters
65
+ */
61
66
  type RouteRequestParams<T extends string | undefined = undefined> = [
62
67
  T
63
68
  ] extends [string] ? {
@@ -66,21 +71,16 @@ type RouteRequestParams<T extends string | undefined = undefined> = [
66
71
  [key: string]: string | undefined;
67
72
  };
68
73
  /**
69
- *
70
- *
71
74
  * Type of RouteRequest generic
72
- *
73
- *
74
- *
75
- *
76
- *
77
75
  */
78
76
  interface RouteRequestGeneric {
79
77
  body?: unknown;
80
78
  params?: string;
81
79
  }
82
80
  /**
83
- * Type of route handler `request`
81
+ *
82
+ *
83
+ * Type of route handler `request` parameter
84
84
  */
85
85
  interface RouteRequest<T extends RouteRequestGeneric = RouteRequestGeneric> extends Omit<BunRequest, 'params'> {
86
86
  params: RouteRequestParams<T['params']>;
@@ -117,9 +117,53 @@ interface RouteResponse<T extends {
117
117
  } = {
118
118
  body: unknown;
119
119
  }> {
120
- setHeader: (name: Header['name'], value: Header['value']) => void;
121
120
  send: (data: T['body'], options?: ResponseOptions) => void;
122
- redirect: (url: string, status: RedirectStatusCode | (number & {})) => void;
121
+ /**
122
+ * Sets `Location` header to provided `url` and `response.status` to provided `status`
123
+ *
124
+ *
125
+ *
126
+ * @param url `Location` to redirect
127
+ *
128
+ * @param status redirect http code (`3xx`)
129
+ *
130
+ * @example
131
+ *
132
+ *
133
+ * ```typescript
134
+ * return response.redirect('https://bun-crumb.vercel.app', 302);
135
+ * ```
136
+ * The same behaviour is
137
+ * ```typescript
138
+ * response.setHeader('Location', 'https://bun-crumb.vercel.app');
139
+ * return response.send('', {status: 302});
140
+ * ```
141
+ */
142
+ redirect: (url: string, status?: RedirectStatusCode | (number & {})) => void;
143
+ /**
144
+ * Sets the response header.
145
+ *
146
+ *
147
+ *
148
+ *
149
+ * Overrides header if it already exists.
150
+ *
151
+ * Case of `name` is not important. Names 'content-type', 'Content-Type', 'CoNteNt=TYPE' are the same.
152
+ *
153
+ *
154
+ * @param name header name
155
+ * @param value header value
156
+ *
157
+ *
158
+ * @example
159
+ * ```typescript
160
+ * response.setHeader('Access-Control-Allow-Origin', '*');
161
+ * // The code below will override the header above
162
+ * response.setHeader('access-control-allow-origin', 'https://bun-crumb.vercel.app');
163
+ * ```
164
+ */
165
+ setHeader: (name: Header['name'], value: Header['value']) => void;
166
+ setCookie: (options: CookieInit) => void;
123
167
  }
124
168
  type Route = Partial<Record<HttpMethod, RouteOptions>>;
125
169
  type RouteHandler = (request: RouteRequest, response: RouteResponse) => Promise<void> | void;
@@ -166,6 +210,7 @@ interface ListenOptions {
166
210
  *
167
211
  *
168
212
  *
213
+ *
169
214
  * @example
170
215
  *
171
216
  * ```typescript
@@ -234,4 +279,4 @@ declare const listen: (options?: ListenOptions) => void;
234
279
  declare const createRoute: (routeOptions: RouteOptions) => void;
235
280
 
236
281
  export { createRoute, listen };
237
- export type { Header, Headers, HttpMethod, ListenOptions, RedirectStatusCode, ResponseOptions, Route, RouteHandler, RouteOptions, RouteRequest, RouteRequestGeneric, RouteRequestParams, RouteResponse, Schema, SchemaData, Validate };
282
+ export type { Header, HttpMethod, ListenOptions, RedirectStatusCode, ResponseOptions, Route, RouteHandler, RouteOptions, RouteRequest, RouteRequestGeneric, RouteRequestParams, 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 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};
1
+ import{serve as e,Cookie as t}from"bun";class s extends Error{status;constructor(e,t){super(t),this.status=e,this.name="HttpError"}}const n=new Map,o=(e,n)=>o=>{const r=o.headers.get("Content-Type")??"text/plain",a=o;return a.handleBody=()=>((e,t,n,o)=>{const r={"application/json":e=>e.json().catch(()=>{throw new s(400,"Bad Request")}).then(e=>{if(n&&o&&!o(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&&o&&!o(e,n))throw new s(400,"Request does not match schema");return e})};return t in r?r[t](e):Promise.reject(new s(415,"Unsupported media type"))})(o,r,e.schema,n),a.query=new URLSearchParams(o.url.split("?")[1]||""),Promise.resolve(((e,s)=>{let n=200,o="",r="";const a=new Headers,c={send:(e,t)=>{"object"==typeof e?(a.has("Content-Type")||a.set("Content-Type","application/json"),r=JSON.stringify(e)):"string"==typeof e?(a.has("Content-Type")||a.set("Content-Type","text/plain"),r=e):r=e,t&&(n=t.status,o=t.statusText)},redirect:(e,t)=>{r="",n=t||302,a.set("Location",e)},setHeader:(e,t)=>{a.set(e,t)},setCookie:e=>{a.append("Set-Cookie",new t(e).toString())}};return Promise.resolve(s.handler(e,c)).then(()=>new Response(r,{headers:a,status:n,statusText:o}))})(a,e)).then(e=>e).catch(e=>e instanceof s?new Response(e.message,{status:e.status}):new Response("Internal server error",{status:500}))},r=(e,t)=>{const s={};for(const n in e)Object.hasOwn(e,n)&&(s[n]=o(e[n],t));return s},a=(e,t)=>{const s={};for(const n of e)s[n[0]]=r(n[1],t);return e.clear(),s},c=t=>{e({port:t?.port,hostname:t?.hostname,development:t?.development??!1,routes:a(n,t?.schemaValidator)})},p=e=>{const t=n.get(e.url);t?t[e.method]=e:n.set(e.url,{[e.method]:e})};export{p as createRoute,c as listen};
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { BunRequest } from 'bun';
2
- import type { Route, RouteOptions, HttpMethod } from './types/route';
2
+ import type { Route, RouteOptions, HttpMethod } from './types';
3
3
  import type { SchemaData, Validate } from './types';
4
4
  import type { ListenOptions } from './types';
5
5
  type PreparedRoute = Partial<Record<HttpMethod, WrappedRouteCallback>>;
@@ -14,6 +14,7 @@ export type Routes = Map<RouteOptions['url'], Route>;
14
14
  */
15
15
  export declare const _routes: Routes;
16
16
  /**
17
+ *
17
18
  * Runtime function that used in request.
18
19
  * Parses body to supported content type (json, plain text) and validates it with route schema.
19
20
  *
@@ -36,6 +37,7 @@ export declare const handleBody: (request: BunRequest, contentType: string, sche
36
37
  *
37
38
  *
38
39
  * @param routeOptions options of route
40
+ *
39
41
  * @returns {WrappedRouteCallback} Function that is ready to be used in Bun.serve `routes`
40
42
  */
41
43
  export declare const wrapRouteCallback: (routeOptions: RouteOptions, schemaValidator?: Validate) => WrappedRouteCallback;
@@ -94,6 +96,7 @@ export declare const prepareRoutes: (routes: Routes, schemaValidator?: Validate)
94
96
  *
95
97
  *
96
98
  *
99
+ *
97
100
  * @example
98
101
  *
99
102
  * ```typescript
@@ -1,3 +1,4 @@
1
1
  export * from './route';
2
2
  export * from './server';
3
3
  export * from './schema';
4
+ export * from './utils';
@@ -1,23 +1,10 @@
1
- import type { BunRequest } from 'bun';
1
+ import type { BunRequest, CookieInit } from 'bun';
2
2
  import type { SchemaData } from './schema';
3
+ import type { Header, HttpMethod, RedirectStatusCode } from './utils';
3
4
  /**
4
- * HTTP Method primitive.
5
5
  *
6
- * @example
7
- * ```typescript
8
- * const method: HttpMethod = 'GET';
9
- * ```
6
+ * Type of Request search parameters
10
7
  */
11
- export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
12
- export type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 307 | 308;
13
- /**
14
- * HTTP Header struct.
15
- */
16
- export type Header = {
17
- name: string;
18
- value: string;
19
- };
20
- export type Headers = ResponseInit['headers'];
21
8
  export type RouteRequestParams<T extends string | undefined = undefined> = [
22
9
  T
23
10
  ] extends [string] ? {
@@ -26,21 +13,16 @@ export type RouteRequestParams<T extends string | undefined = undefined> = [
26
13
  [key: string]: string | undefined;
27
14
  };
28
15
  /**
29
- *
30
- *
31
16
  * Type of RouteRequest generic
32
- *
33
- *
34
- *
35
- *
36
- *
37
17
  */
38
18
  export interface RouteRequestGeneric {
39
19
  body?: unknown;
40
20
  params?: string;
41
21
  }
42
22
  /**
43
- * Type of route handler `request`
23
+ *
24
+ *
25
+ * Type of route handler `request` parameter
44
26
  */
45
27
  export interface RouteRequest<T extends RouteRequestGeneric = RouteRequestGeneric> extends Omit<BunRequest, 'params'> {
46
28
  params: RouteRequestParams<T['params']>;
@@ -77,9 +59,53 @@ export interface RouteResponse<T extends {
77
59
  } = {
78
60
  body: unknown;
79
61
  }> {
80
- setHeader: (name: Header['name'], value: Header['value']) => void;
81
62
  send: (data: T['body'], options?: ResponseOptions) => void;
82
- redirect: (url: string, status: RedirectStatusCode | (number & {})) => void;
63
+ /**
64
+ * Sets `Location` header to provided `url` and `response.status` to provided `status`
65
+ *
66
+ *
67
+ *
68
+ * @param url `Location` to redirect
69
+ *
70
+ * @param status redirect http code (`3xx`)
71
+ *
72
+ * @example
73
+ *
74
+ *
75
+ * ```typescript
76
+ * return response.redirect('https://bun-crumb.vercel.app', 302);
77
+ * ```
78
+ * The same behaviour is
79
+ * ```typescript
80
+ * response.setHeader('Location', 'https://bun-crumb.vercel.app');
81
+ * return response.send('', {status: 302});
82
+ * ```
83
+ */
84
+ redirect: (url: string, status?: RedirectStatusCode | (number & {})) => void;
85
+ /**
86
+ * Sets the response header.
87
+ *
88
+ *
89
+ *
90
+ *
91
+ * Overrides header if it already exists.
92
+ *
93
+ * Case of `name` is not important. Names 'content-type', 'Content-Type', 'CoNteNt=TYPE' are the same.
94
+ *
95
+ *
96
+ * @param name header name
97
+ * @param value header value
98
+ *
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * response.setHeader('Access-Control-Allow-Origin', '*');
103
+ * // The code below will override the header above
104
+ * response.setHeader('access-control-allow-origin', 'https://bun-crumb.vercel.app');
105
+ * ```
106
+ */
107
+ setHeader: (name: Header['name'], value: Header['value']) => void;
108
+ setCookie: (options: CookieInit) => void;
83
109
  }
84
110
  export type Route = Partial<Record<HttpMethod, RouteOptions>>;
85
111
  export type RouteHandler = (request: RouteRequest, response: RouteResponse) => Promise<void> | void;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * HTTP Method primitive.
3
+ *
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const method: HttpMethod = 'GET';
8
+ * ```
9
+ */
10
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
11
+ export type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
12
+ /**
13
+ * HTTP Header struct.
14
+ */
15
+ export type Header = {
16
+ name: string;
17
+ value: string;
18
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-crumb",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "author": "marigold",
5
5
  "module": "dist/index.js",
6
6
  "license": "UNLICENSED",