bun-router 0.5.4 → 0.5.5
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/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/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/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