bun-crumb 0.7.0 → 0.9.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
- type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS';
52
- type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 307 | 308;
52
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'OPTIONS' | 'HEAD';
53
+ type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308;
53
54
  /**
54
55
  * HTTP Header struct.
55
56
  */
@@ -58,16 +59,47 @@ type Header = {
58
59
  value: string;
59
60
  };
60
61
  type Headers = ResponseInit['headers'];
62
+
61
63
  /**
62
- * Type of route handler `request`
64
+ *
65
+ * Type of Request search parameters
63
66
  */
64
- interface RouteRequest<T extends {
65
- body: unknown;
66
- } = {
67
- body: unknown;
68
- }> extends Omit<BunRequest, 'body'> {
67
+ type RouteRequestParams<T extends string | undefined = undefined> = [
68
+ T
69
+ ] extends [string] ? {
70
+ [K in T]: string;
71
+ } : {
72
+ [key: string]: string | undefined;
73
+ };
74
+ /**
75
+ * Type of RouteRequest generic
76
+ */
77
+ interface RouteRequestGeneric {
78
+ body?: unknown;
79
+ params?: string;
80
+ }
81
+ /**
82
+ *
83
+ *
84
+ * Type of route handler `request` parameter
85
+ */
86
+ interface RouteRequest<T extends RouteRequestGeneric = RouteRequestGeneric> extends Omit<BunRequest, 'params'> {
87
+ params: RouteRequestParams<T['params']>;
69
88
  /**
70
- * Parsed, validated from schema body of reqeust
89
+ * #### Parses and validates body of request.
90
+ *
91
+ * - Parses body to JSON or text
92
+ * - Validates body with `schemaValidator` if it is provided.
93
+ *
94
+ * @returns Promise with handled body
95
+ *
96
+ * @example
97
+ *
98
+ * ```typescript
99
+ * request.handleBody().then((bodyData) => {
100
+ * console.log(bodyData);
101
+ * })
102
+ * ```
71
103
  */
72
104
  handleBody: () => Promise<T extends {
73
105
  body: unknown;
@@ -86,9 +118,53 @@ interface RouteResponse<T extends {
86
118
  } = {
87
119
  body: unknown;
88
120
  }> {
89
- setHeader: (name: Header['name'], value: Header['value']) => void;
90
121
  send: (data: T['body'], options?: ResponseOptions) => void;
91
- redirect: (url: string, status: RedirectStatusCode | (number & {})) => void;
122
+ /**
123
+ * Sets `Location` header to provided `url` and `response.status` to provided `status`
124
+ *
125
+ *
126
+ *
127
+ * @param url `Location` to redirect
128
+ *
129
+ * @param status redirect http code (`3xx`)
130
+ *
131
+ * @example
132
+ *
133
+ *
134
+ * ```typescript
135
+ * return response.redirect('https://bun-crumb.vercel.app', 302);
136
+ * ```
137
+ * The same behaviour is
138
+ * ```typescript
139
+ * response.setHeader('Location', 'https://bun-crumb.vercel.app');
140
+ * return response.send('', {status: 302});
141
+ * ```
142
+ */
143
+ redirect: (url: string, status?: RedirectStatusCode | (number & {})) => void;
144
+ /**
145
+ * Sets the response header.
146
+ *
147
+ *
148
+ *
149
+ *
150
+ * Overrides header if it already exists.
151
+ *
152
+ * Case of `name` is not important. Names 'content-type', 'Content-Type', 'CoNteNt=TYPE' are the same.
153
+ *
154
+ *
155
+ * @param name header name
156
+ * @param value header value
157
+ *
158
+ *
159
+ * @example
160
+ * ```typescript
161
+ * response.setHeader('Access-Control-Allow-Origin', '*');
162
+ * // The code below will override the header above
163
+ * response.setHeader('access-control-allow-origin', 'https://bun-crumb.vercel.app');
164
+ * ```
165
+ */
166
+ setHeader: (name: Header['name'], value: Header['value']) => void;
167
+ setCookie: (options: CookieInit) => void;
92
168
  }
93
169
  type Route = Partial<Record<HttpMethod, RouteOptions>>;
94
170
  type RouteHandler = (request: RouteRequest, response: RouteResponse) => Promise<void> | void;
@@ -135,6 +211,7 @@ interface ListenOptions {
135
211
  *
136
212
  *
137
213
  *
214
+ *
138
215
  * @example
139
216
  *
140
217
  * ```typescript
@@ -203,4 +280,4 @@ declare const listen: (options?: ListenOptions) => void;
203
280
  declare const createRoute: (routeOptions: RouteOptions) => void;
204
281
 
205
282
  export { createRoute, listen };
206
- export type { Header, Headers, HttpMethod, ListenOptions, RedirectStatusCode, ResponseOptions, Route, RouteHandler, RouteOptions, RouteRequest, RouteResponse, Schema, SchemaData, Validate };
283
+ export type { Header, Headers, 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),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,33 +1,46 @@
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';
12
- export type RedirectStatusCode = 300 | 301 | 302 | 303 | 304 | 307 | 308;
8
+ export type RouteRequestParams<T extends string | undefined = undefined> = [
9
+ T
10
+ ] extends [string] ? {
11
+ [K in T]: string;
12
+ } : {
13
+ [key: string]: string | undefined;
14
+ };
13
15
  /**
14
- * HTTP Header struct.
16
+ * Type of RouteRequest generic
15
17
  */
16
- export type Header = {
17
- name: string;
18
- value: string;
19
- };
20
- export type Headers = ResponseInit['headers'];
18
+ export interface RouteRequestGeneric {
19
+ body?: unknown;
20
+ params?: string;
21
+ }
21
22
  /**
22
- * Type of route handler `request`
23
+ *
24
+ *
25
+ * Type of route handler `request` parameter
23
26
  */
24
- export interface RouteRequest<T extends {
25
- body: unknown;
26
- } = {
27
- body: unknown;
28
- }> extends Omit<BunRequest, 'body'> {
27
+ export interface RouteRequest<T extends RouteRequestGeneric = RouteRequestGeneric> extends Omit<BunRequest, 'params'> {
28
+ params: RouteRequestParams<T['params']>;
29
29
  /**
30
- * Parsed, validated from schema body of reqeust
30
+ * #### Parses and validates body of request.
31
+ *
32
+ * - Parses body to JSON or text
33
+ * - Validates body with `schemaValidator` if it is provided.
34
+ *
35
+ * @returns Promise with handled body
36
+ *
37
+ * @example
38
+ *
39
+ * ```typescript
40
+ * request.handleBody().then((bodyData) => {
41
+ * console.log(bodyData);
42
+ * })
43
+ * ```
31
44
  */
32
45
  handleBody: () => Promise<T extends {
33
46
  body: unknown;
@@ -46,9 +59,53 @@ export interface RouteResponse<T extends {
46
59
  } = {
47
60
  body: unknown;
48
61
  }> {
49
- setHeader: (name: Header['name'], value: Header['value']) => void;
50
62
  send: (data: T['body'], options?: ResponseOptions) => void;
51
- 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;
52
109
  }
53
110
  export type Route = Partial<Record<HttpMethod, RouteOptions>>;
54
111
  export type RouteHandler = (request: RouteRequest, response: RouteResponse) => Promise<void> | void;
@@ -0,0 +1,19 @@
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
+ };
19
+ export type Headers = ResponseInit['headers'];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-crumb",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "author": "marigold",
5
5
  "module": "dist/index.js",
6
6
  "license": "UNLICENSED",