bun-router 0.5.3 → 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- package/examples/basic.ts +4 -4
- package/examples/dynamic.ts +7 -7
- package/examples/logger.ts +3 -3
- package/examples/sqlite.ts +3 -3
- package/lib/http/generic-methods.ts +64 -0
- package/lib/logger/logger.ts +8 -12
- package/lib/router/router.ts +8 -100
- package/package.json +1 -1
package/examples/basic.ts
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
import { router,
|
1
|
+
import { router, http } from '..';
|
2
2
|
|
3
3
|
const r = router();
|
4
4
|
|
5
|
-
r.add('/', 'GET', () => json('ok'));
|
5
|
+
r.add('/', 'GET', () => http.json('ok'));
|
6
6
|
|
7
7
|
r.add('/user/:name', 'GET', (ctx) => {
|
8
8
|
const name = ctx.params.get('name');
|
9
|
-
return json(name);
|
9
|
+
return http.json(name);
|
10
10
|
});
|
11
11
|
|
12
12
|
r.add('/user/:name/:id', 'GET', (ctx) => {
|
13
13
|
const name = ctx.params.get('name');
|
14
14
|
const id = ctx.params.get('id');
|
15
|
-
return json({name: name, id: id});
|
15
|
+
return http.json({name: name, id: id});
|
16
16
|
});
|
17
17
|
|
18
18
|
r.serve();
|
package/examples/dynamic.ts
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
import { router,
|
1
|
+
import { router, http } from '..';
|
2
2
|
import { Context } from '../lib/router/router.d';
|
3
3
|
|
4
4
|
const handler = (ctx: Context) => {
|
5
5
|
const name = ctx.params.get('name');
|
6
|
-
if (typeof name === 'undefined' || name === '') return html('<h6 style="color: red">User Undefined</h6>');
|
7
|
-
return html(`<h4>Hello, ${name}!</h4>`);
|
6
|
+
if (typeof name === 'undefined' || name === '') return http.html('<h6 style="color: red">User Undefined</h6>');
|
7
|
+
return http.html(`<h4>Hello, ${name}!</h4>`);
|
8
8
|
}
|
9
9
|
|
10
10
|
const space = (ctx: Context) => {
|
11
11
|
const name = ctx.params.get('name');
|
12
|
-
if (typeof name === 'undefined' || name === '') return html(`<h6 style="color: red">Space [${name}] Not Found</h6>`);
|
13
|
-
return html(`<h4>Welcome to ${name}!`)
|
12
|
+
if (typeof name === 'undefined' || name === '') return http.html(`<h6 style="color: red">Space [${name}] Not Found</h6>`);
|
13
|
+
return http.html(`<h4>Welcome to ${name}!`)
|
14
14
|
}
|
15
15
|
|
16
16
|
const handleSettings = (ctx: Context) => {
|
17
17
|
const name = ctx.params.get('name');
|
18
|
-
if (typeof name === 'undefined' || name === '') return html(`<h6 style="color: red">User Not Found</h6>`);
|
19
|
-
return html(`<h4>Settings for ${name}</h4>`)
|
18
|
+
if (typeof name === 'undefined' || name === '') return http.html(`<h6 style="color: red">User Not Found</h6>`);
|
19
|
+
return http.html(`<h4>Settings for ${name}</h4>`)
|
20
20
|
}
|
21
21
|
|
22
22
|
const r = router();
|
package/examples/logger.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { router, logger,
|
1
|
+
import { router, logger, http } from '..';
|
2
2
|
|
3
3
|
const r = router();
|
4
4
|
const log = logger();
|
@@ -8,9 +8,9 @@ r.add('/:foo', 'GET', (ctx) => {
|
|
8
8
|
const foo = ctx.params.get('foo');
|
9
9
|
if (!foo) {
|
10
10
|
log.error(500, url.pathname, ctx.request.method, new Error('undefined'));
|
11
|
-
return json({status: 500, text: 'Foo is undefined'});
|
11
|
+
return http.json({status: 500, text: 'Foo is undefined'});
|
12
12
|
}
|
13
|
-
return html(`<h4 style='font-family: sans-serif;'>Oh hello, ${foo}</h4>`)
|
13
|
+
return http.html(`<h4 style='font-family: sans-serif;'>Oh hello, ${foo}</h4>`)
|
14
14
|
});
|
15
15
|
|
16
16
|
r.serve();
|
package/examples/sqlite.ts
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
import { router,
|
1
|
+
import { router, http } from '..';
|
2
2
|
|
3
3
|
const r = router(3000, {db: './examples/dbs/test.db'});
|
4
4
|
|
@@ -8,7 +8,7 @@ r.add('/u/new/:name', 'GET', (ctx) => {
|
|
8
8
|
|
9
9
|
ctx.db.run(`INSERT INTO test VALUES(${rando}, "${name}")`);
|
10
10
|
|
11
|
-
return json({message: 'ok'});
|
11
|
+
return http.json({message: 'ok'});
|
12
12
|
});
|
13
13
|
|
14
14
|
r.add('/u/:name', 'GET', (ctx) => {
|
@@ -16,7 +16,7 @@ r.add('/u/:name', 'GET', (ctx) => {
|
|
16
16
|
const data = ctx.db.query(`SELECT * FROM test WHERE name = "${name}";`).get();
|
17
17
|
const d = data as {id: number, name: string};
|
18
18
|
|
19
|
-
return d ? json(d) : new Response('not found', {status: 404});
|
19
|
+
return d ? http.json(d) : new Response('not found', {status: 404});
|
20
20
|
});
|
21
21
|
|
22
22
|
r.serve();
|
@@ -0,0 +1,64 @@
|
|
1
|
+
import { httpStatusCodes } from "./status";
|
2
|
+
|
3
|
+
const http = {
|
4
|
+
json: async (data: any): Promise<Response> => {
|
5
|
+
const jsonString = JSON.stringify(data);
|
6
|
+
return Promise.resolve(new Response(jsonString, {
|
7
|
+
status: 200,
|
8
|
+
statusText: httpStatusCodes[200],
|
9
|
+
headers: {'Content-Type': 'application/json'},
|
10
|
+
}));
|
11
|
+
},
|
12
|
+
html: async (content: string): Promise<Response> => {
|
13
|
+
content = Bun.escapeHTML(content);
|
14
|
+
return Promise.resolve(new Response(Bun.escapeHTML(content), {
|
15
|
+
status: 200,
|
16
|
+
statusText: httpStatusCodes[200],
|
17
|
+
headers: {'Content-Type': 'text/html; charset=utf-8'}
|
18
|
+
}));
|
19
|
+
},
|
20
|
+
file: async (fp: string): Promise<Response> => {
|
21
|
+
const file = Bun.file(fp);
|
22
|
+
const exists = await file.exists();
|
23
|
+
|
24
|
+
if (!exists) return http.notFound(`File not found: ${fp}`);
|
25
|
+
|
26
|
+
const content = await file.arrayBuffer();
|
27
|
+
if (!content) return http.noContent();
|
28
|
+
|
29
|
+
let contentType = 'text/html; charset=utf-9';
|
30
|
+
|
31
|
+
if (file.type.includes('image'))
|
32
|
+
contentType = file.type + '; charset=utf-8';
|
33
|
+
|
34
|
+
return Promise.resolve(new Response(content, {
|
35
|
+
status: 200,
|
36
|
+
statusText: httpStatusCodes[200],
|
37
|
+
headers: { 'Content-Type': contentType}
|
38
|
+
}));
|
39
|
+
},
|
40
|
+
noContent: async (): Promise<Response> => Promise.resolve(new Response('no content', {
|
41
|
+
status: 204,
|
42
|
+
statusText: 'no content',
|
43
|
+
})),
|
44
|
+
notFound: async(msg?: string): Promise<Response> => {
|
45
|
+
const response = new Response(msg ?? 'not found', {
|
46
|
+
status: 404,
|
47
|
+
statusText: httpStatusCodes[404],
|
48
|
+
headers: {'Content-Type': 'text/html'},
|
49
|
+
});
|
50
|
+
|
51
|
+
return Promise.resolve(response);
|
52
|
+
},
|
53
|
+
message: async (status: number, msg?: string): Promise<Response> => {
|
54
|
+
const response = new Response(msg ?? '?', {
|
55
|
+
status: status,
|
56
|
+
statusText: httpStatusCodes[status],
|
57
|
+
headers: {'Content-Type': 'text/html; charset-utf-8'},
|
58
|
+
});
|
59
|
+
return Promise.resolve(response)
|
60
|
+
}
|
61
|
+
|
62
|
+
}
|
63
|
+
|
64
|
+
export { http }
|
package/lib/logger/logger.ts
CHANGED
@@ -15,19 +15,15 @@ const timestamp = (date: Date) => {
|
|
15
15
|
}
|
16
16
|
|
17
17
|
// append ANSI color escape sequences to a string based on the given HTTP status code.
|
18
|
-
const colorCode = (n: number, text?:
|
18
|
+
const colorCode = (n: number, text?:string): string => {
|
19
19
|
const s = ` [${String(n)}${text ?? ''}] `;
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if (n <
|
24
|
-
else if (n <
|
25
|
-
else if (n
|
26
|
-
|
27
|
-
else if (n < 500) backgroundColor = 'bgRed';
|
28
|
-
else foregroundColor = 'white';
|
29
|
-
|
30
|
-
return color(foregroundColor, backgroundColor, s).trim();
|
20
|
+
if (n < 100) return color('black', 'bgYellow', s);
|
21
|
+
else if (n >= 100 && n < 200) return color('black', 'bgCyan', s);
|
22
|
+
else if (n >= 200 && n < 300) return color('black', 'bgGreen', s);
|
23
|
+
else if (n >= 300 && n < 400) return color('black', 'bgRed', s);
|
24
|
+
else if (n >= 400 && n < 500) return color('black', 'bgRed', s);
|
25
|
+
else if (n >= 500) return color('white', 'bgRed', s);
|
26
|
+
return color('white', 'bgBlack', `[${s}]`).trim();
|
31
27
|
}
|
32
28
|
|
33
29
|
|
package/lib/router/router.ts
CHANGED
@@ -5,100 +5,7 @@ import { readDir } from '../fs/fsys';
|
|
5
5
|
import { logger } from '../logger/logger';
|
6
6
|
import path from 'path';
|
7
7
|
import { Logger } from '../logger/logger.d';
|
8
|
-
|
9
|
-
// create a generic HTTP response
|
10
|
-
const httpMessage = async (status: number, msg?: string): Promise<Response> => {
|
11
|
-
const response = new Response(msg ?? '?', {
|
12
|
-
status: status,
|
13
|
-
statusText: msg ?? '?',
|
14
|
-
headers: { 'Content-Type': 'text/html; charset-uft-8' }
|
15
|
-
});
|
16
|
-
return new Promise((resolve) => {
|
17
|
-
resolve(response);
|
18
|
-
});
|
19
|
-
};
|
20
|
-
|
21
|
-
// a generic 'not found' HTTP response
|
22
|
-
const notFound = async (msg?: string): Promise<Response> => {
|
23
|
-
const response = new Response(msg ?? 'not found', {
|
24
|
-
status: 404,
|
25
|
-
statusText: 'not found',
|
26
|
-
headers: { 'Content-Type': 'text/html' },
|
27
|
-
});
|
28
|
-
|
29
|
-
return new Promise((resolve) => {
|
30
|
-
resolve(response);
|
31
|
-
});
|
32
|
-
}
|
33
|
-
|
34
|
-
// a generic 'no content' HTTP response
|
35
|
-
const noContent = async (): Promise<Response> => {
|
36
|
-
const response = new Response('no content', {
|
37
|
-
status: 204,
|
38
|
-
statusText: 'no content',
|
39
|
-
});
|
40
|
-
|
41
|
-
return new Promise((resolve) => {
|
42
|
-
resolve(response);
|
43
|
-
});
|
44
|
-
}
|
45
|
-
|
46
|
-
// IO handling
|
47
|
-
const file = async (filepath: string): Promise<Response> => {
|
48
|
-
const file = Bun.file(filepath);
|
49
|
-
const exists = await file.exists();
|
50
|
-
|
51
|
-
// check if the file exists, return 'not found' if it doesn't.
|
52
|
-
if (!exists)
|
53
|
-
return notFound(`File not found: ${filepath}`);
|
54
|
-
|
55
|
-
// get the content of the file as an ArrayBuffer
|
56
|
-
const content = await file.arrayBuffer();
|
57
|
-
if (!content)
|
58
|
-
return noContent();
|
59
|
-
|
60
|
-
// default Content-Type + encoding
|
61
|
-
let contentType = 'text/html; charset=utf-8';
|
62
|
-
|
63
|
-
// change the Content-Type if the file type is an image.
|
64
|
-
// file.type provides the necessary Content-Type
|
65
|
-
if (file.type.includes('image')) {
|
66
|
-
contentType = file.type + '; charset=utf-8';
|
67
|
-
}
|
68
|
-
|
69
|
-
// create a new response with the necessary criteria
|
70
|
-
const response = new Response(content, {
|
71
|
-
status: 200,
|
72
|
-
statusText: 'ok',
|
73
|
-
headers: { 'Content-Type': contentType },
|
74
|
-
});
|
75
|
-
|
76
|
-
return Promise.resolve(response);
|
77
|
-
}
|
78
|
-
|
79
|
-
// handle strings as HTML
|
80
|
-
const html = async (content: string): Promise<Response> => {
|
81
|
-
const response = new Response(content, {
|
82
|
-
status: 200,
|
83
|
-
statusText: 'ok',
|
84
|
-
headers: { 'Content-Type': 'text/html; charset=utf-8' },
|
85
|
-
});
|
86
|
-
|
87
|
-
// escape the HTML
|
88
|
-
content = Bun.escapeHTML(content);
|
89
|
-
|
90
|
-
return Promise.resolve(response);
|
91
|
-
}
|
92
|
-
|
93
|
-
// create a JSON response
|
94
|
-
const json = (data: any): Response => {
|
95
|
-
const jsonString = JSON.stringify(data);
|
96
|
-
|
97
|
-
const res = new Response(jsonString);
|
98
|
-
res.headers.set('Content-Type', 'application/json');
|
99
|
-
|
100
|
-
return res
|
101
|
-
}
|
8
|
+
import { http } from '../http/generic-methods';
|
102
9
|
|
103
10
|
// extract dynamic URL parameters
|
104
11
|
// if the route pattern is /:foo and the request URL is /bar: {foo: 'bar'}
|
@@ -139,6 +46,7 @@ const match = (route: Route, ctx: Context): boolean => {
|
|
139
46
|
return false;
|
140
47
|
}
|
141
48
|
|
49
|
+
// set the context for the reuest
|
142
50
|
const setContext = (req: Request, lgr: Logger, opts: Options): Context => {
|
143
51
|
return {
|
144
52
|
request: req,
|
@@ -197,7 +105,7 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
197
105
|
const route: Route = {
|
198
106
|
pattern: patternPath,
|
199
107
|
method: 'GET',
|
200
|
-
callback: async () => await file(pure),
|
108
|
+
callback: async () => await http.file(pure),
|
201
109
|
};
|
202
110
|
routes.push(route);
|
203
111
|
});
|
@@ -218,13 +126,13 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
218
126
|
opts.db = o.db;
|
219
127
|
}
|
220
128
|
|
221
|
-
let statusCode = 404;
|
129
|
+
let statusCode = 404;
|
222
130
|
|
223
131
|
for (const route of routes) {
|
224
132
|
const ctx = setContext(req, lgr, opts);
|
225
133
|
|
226
134
|
if (url.pathname === '/favicon.ico') {
|
227
|
-
return noContent();
|
135
|
+
return http.noContent();
|
228
136
|
}
|
229
137
|
|
230
138
|
if (route.method !== req.method) {
|
@@ -247,11 +155,11 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
247
155
|
|
248
156
|
if (statusCode === 405) {
|
249
157
|
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
250
|
-
return
|
158
|
+
return http.message(statusCode, httpStatusCodes[statusCode]);
|
251
159
|
}
|
252
160
|
|
253
161
|
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
254
|
-
return
|
162
|
+
return http.message(statusCode, httpStatusCodes[statusCode]);
|
255
163
|
|
256
164
|
}
|
257
165
|
});
|
@@ -260,4 +168,4 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
260
168
|
}
|
261
169
|
|
262
170
|
|
263
|
-
export { router,
|
171
|
+
export { router, extract, http }
|
package/package.json
CHANGED