bun-router 0.7.4-experimental.7 → 0.7.4-experimental.9
Sign up to get free protection for your applications and to get access to all the features.
- package/examples/static.ts +1 -1
- package/lib/http/status.ts +64 -64
- package/lib/logger/logger.ts +1 -1
- package/lib/router/routeTree.ts +27 -3
- package/lib/router/router.ts +22 -11
- package/package.json +1 -1
- package/tests/router.test.ts +21 -20
package/examples/static.ts
CHANGED
package/lib/http/status.ts
CHANGED
@@ -1,66 +1,66 @@
|
|
1
1
|
const httpStatusCodes: { [key: number]: string } = {
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
2
|
+
100: 'Continue',
|
3
|
+
101: 'Switching Protocols',
|
4
|
+
102: 'Processing',
|
5
|
+
103: 'Early Hints',
|
6
|
+
200: 'OK',
|
7
|
+
201: 'Created',
|
8
|
+
202: 'Accepted',
|
9
|
+
203: 'Non-Authoritative Information',
|
10
|
+
204: 'No Content',
|
11
|
+
205: 'Reset Content',
|
12
|
+
206: 'Partial Content',
|
13
|
+
207: 'Multi-Status',
|
14
|
+
208: 'Already Reported',
|
15
|
+
226: 'IM Used',
|
16
|
+
300: 'Multiple Choices',
|
17
|
+
301: 'Moved Permanently',
|
18
|
+
302: 'Found',
|
19
|
+
303: 'See Other',
|
20
|
+
304: 'Not Modified',
|
21
|
+
305: 'Use Proxy',
|
22
|
+
307: 'Temporary Redirect',
|
23
|
+
308: 'Permanent Redirect',
|
24
|
+
400: 'Bad Request',
|
25
|
+
401: 'Unauthorized',
|
26
|
+
402: 'Payment Required',
|
27
|
+
403: 'Forbidden',
|
28
|
+
404: 'Not Found',
|
29
|
+
405: 'Method Not Allowed',
|
30
|
+
406: 'Not Acceptable',
|
31
|
+
407: 'Proxy Authentication Required',
|
32
|
+
408: 'Request Timeout',
|
33
|
+
409: 'Conflict',
|
34
|
+
410: 'Gone',
|
35
|
+
411: 'Length Required',
|
36
|
+
412: 'Precondition Failed',
|
37
|
+
413: 'Payload Too Large',
|
38
|
+
414: 'URI Too Long',
|
39
|
+
415: 'Unsupported Media Type',
|
40
|
+
416: 'Range Not Satisfiable',
|
41
|
+
417: 'Expectation Failed',
|
42
|
+
418: 'I\'m a Teapot',
|
43
|
+
421: 'Misdirected Request',
|
44
|
+
422: 'Unprocessable Entity',
|
45
|
+
423: 'Locked',
|
46
|
+
424: 'Failed Dependency',
|
47
|
+
425: 'Too Early',
|
48
|
+
426: 'Upgrade Required',
|
49
|
+
428: 'Precondition Required',
|
50
|
+
429: 'Too Many Requests',
|
51
|
+
431: 'Request Header Fields Too Large',
|
52
|
+
451: 'Unavailable For Legal Reasons',
|
53
|
+
500: 'Internal Server Error',
|
54
|
+
501: 'Not Implemented',
|
55
|
+
502: 'Bad Gateway',
|
56
|
+
503: 'Service Unavailable',
|
57
|
+
504: 'Gateway Timeout',
|
58
|
+
505: 'HTTP Version Not Supported',
|
59
|
+
506: 'Variant Also Negotiates',
|
60
|
+
507: 'Insufficient Storage',
|
61
|
+
508: 'Loop Detected',
|
62
|
+
510: 'Not Extended',
|
63
|
+
511: 'Network Authentication Required',
|
64
|
+
};
|
65
65
|
|
66
|
-
|
66
|
+
export { httpStatusCodes };
|
package/lib/logger/logger.ts
CHANGED
@@ -9,7 +9,7 @@ _ _
|
|
9
9
|
|___|___|_|_| |_| |___|___|_| |___|_|
|
10
10
|
|
11
11
|
`;
|
12
|
-
const VERSION = '0.7.4-experimental.
|
12
|
+
const VERSION = '0.7.4-experimental.9';
|
13
13
|
const Logger = (): BunLogger => {
|
14
14
|
return {
|
15
15
|
info: async (statusCode: number, routePath: string, method: string, message?: string) => {
|
package/lib/router/routeTree.ts
CHANGED
@@ -18,7 +18,7 @@ const createRoute = (path: string, method: string, handler: HttpHandler): Route
|
|
18
18
|
const RouteTree = () => {
|
19
19
|
const root = createRoute('', 'GET', () => http.notFound());
|
20
20
|
|
21
|
-
|
21
|
+
function addRoute (pattern: string, method: string, handler: HttpHandler){
|
22
22
|
const pathParts = pattern.split('/');
|
23
23
|
let current = root;
|
24
24
|
|
@@ -36,7 +36,7 @@ const RouteTree = () => {
|
|
36
36
|
current.handler = handler;
|
37
37
|
current.isLast = true;
|
38
38
|
current.path = pattern;
|
39
|
-
}
|
39
|
+
}
|
40
40
|
|
41
41
|
function findRoute(pathname: string): Route | undefined {
|
42
42
|
const pathParts = pathname.split('/');
|
@@ -54,7 +54,31 @@ const RouteTree = () => {
|
|
54
54
|
return current;
|
55
55
|
}
|
56
56
|
|
57
|
-
|
57
|
+
function size() {
|
58
|
+
let count = 0;
|
59
|
+
function traverse(route: Route) {
|
60
|
+
count++;
|
61
|
+
for (const child of route.children.values()) {
|
62
|
+
traverse(child);
|
63
|
+
}
|
64
|
+
}
|
65
|
+
traverse(root);
|
66
|
+
return count;
|
67
|
+
}
|
68
|
+
|
69
|
+
function list() {
|
70
|
+
const routes: Route[] = [];
|
71
|
+
function traverse(route: Route) {
|
72
|
+
routes.push(route);
|
73
|
+
for (const child of route.children.values()) {
|
74
|
+
traverse(child);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
traverse(root);
|
78
|
+
return routes;
|
79
|
+
}
|
80
|
+
|
81
|
+
return { addRoute, findRoute, size, list };
|
58
82
|
|
59
83
|
};
|
60
84
|
|
package/lib/router/router.ts
CHANGED
@@ -2,14 +2,14 @@ import path from 'path';
|
|
2
2
|
import { Database } from 'bun:sqlite';
|
3
3
|
import { Route, BunRouter, RouterOptions, Options, HttpHandler } from './router.d';
|
4
4
|
import { httpStatusCodes } from '../http/status';
|
5
|
-
import { readDir
|
5
|
+
import { readDir } from '../fs/fsys';
|
6
6
|
import { Logger, startMessage } from '../logger/logger';
|
7
7
|
import { http } from '../http/http';
|
8
8
|
import { RouteTree } from './routeTree';
|
9
9
|
import { createContext } from './context';
|
10
10
|
|
11
11
|
const Router: BunRouter = (port?: number | string, options?: RouterOptions<Options>) => {
|
12
|
-
const { addRoute, findRoute } = RouteTree();
|
12
|
+
const { addRoute, findRoute, list } = RouteTree();
|
13
13
|
const logger = Logger();
|
14
14
|
|
15
15
|
async function loadComponent(root: string, name: string) {
|
@@ -17,9 +17,9 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
|
|
17
17
|
return module.default;
|
18
18
|
}
|
19
19
|
|
20
|
-
function
|
20
|
+
function extractPathExtBase(pattern: string, pathname: string) {
|
21
21
|
const extension = path.extname(pathname);
|
22
|
-
let base = path.basename(pathname);
|
22
|
+
let base = encodeURIComponent(path.basename(pathname));
|
23
23
|
|
24
24
|
if (extension === '.html' || extension === '.tsx') base = base.replace(extension, '');
|
25
25
|
|
@@ -27,9 +27,14 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
|
|
27
27
|
|
28
28
|
if (base === 'index') patternPath = pattern;
|
29
29
|
|
30
|
-
return { patternPath, extension, base }
|
30
|
+
return { patternPath, extension, base };
|
31
31
|
}
|
32
32
|
|
33
|
+
async function exists(fp: string) {
|
34
|
+
const f = Bun.file(fp);
|
35
|
+
return await f.exists();
|
36
|
+
}
|
37
|
+
|
33
38
|
return {
|
34
39
|
// add a route to the router tree
|
35
40
|
add: (pattern, method, callback) => { addRoute(pattern, method, callback); },
|
@@ -43,15 +48,15 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
|
|
43
48
|
// all other file extensions are served as files
|
44
49
|
// the root directory is traversed recursively
|
45
50
|
static: async (pattern: string, root: string) => {
|
46
|
-
if (!exists(root)) console.
|
51
|
+
if (!exists(root)) return console.error(`Directory not found: ${root}`);
|
47
52
|
await readDir(root, async (fp) => {
|
48
|
-
const { patternPath, extension, base } =
|
53
|
+
const { patternPath, extension, base } = extractPathExtBase(pattern, fp);
|
49
54
|
|
50
55
|
const route: Route = {
|
51
56
|
children: new Map(),
|
52
57
|
dynamicPath: '',
|
53
58
|
isLast: true,
|
54
|
-
path: patternPath.slice(1),
|
59
|
+
path: patternPath.startsWith('//') ? patternPath.slice(1) : patternPath, // remove the leading '/' if it exists
|
55
60
|
method: 'GET',
|
56
61
|
handler: async () => {
|
57
62
|
if (extension === '.tsx') {
|
@@ -63,10 +68,11 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
|
|
63
68
|
},
|
64
69
|
};
|
65
70
|
|
66
|
-
console.log(route.path);
|
67
|
-
|
68
71
|
addRoute(route.path, 'GET', route.handler);
|
69
72
|
});
|
73
|
+
|
74
|
+
console.log(list());
|
75
|
+
|
70
76
|
},
|
71
77
|
// start the server
|
72
78
|
serve: () => {
|
@@ -104,11 +110,16 @@ const Router: BunRouter = (port?: number | string, options?: RouterOptions<Optio
|
|
104
110
|
return Promise.resolve(response);
|
105
111
|
}
|
106
112
|
|
107
|
-
// if no route is found, return 404
|
113
|
+
// if no route is found, return 404
|
108
114
|
const response = await http.notFound();
|
109
115
|
|
110
116
|
logger.info(response.status, url.pathname, req.method, httpStatusCodes[response.status]);
|
111
117
|
return Promise.resolve(http.notFound());
|
118
|
+
},
|
119
|
+
error(error) {
|
120
|
+
return new Response(`<pre>${error}\n${error.stack}</pre>`, {
|
121
|
+
headers: { 'Content-Type': 'text/html' },
|
122
|
+
});
|
112
123
|
}
|
113
124
|
});
|
114
125
|
},
|
package/package.json
CHANGED
package/tests/router.test.ts
CHANGED
@@ -1,35 +1,36 @@
|
|
1
1
|
import { describe, test, expect } from 'bun:test';
|
2
2
|
|
3
3
|
describe('Router', () => {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
4
|
+
test('Serve', async () => {
|
5
|
+
const proc = Bun.spawn(['./tests/serve.test.sh'], {
|
6
|
+
onExit: (_proc, _exitCode, _signalCode , error) => {
|
7
|
+
if (error) console.error(error);
|
8
|
+
},
|
9
|
+
});
|
10
10
|
|
11
|
-
|
11
|
+
const text = await new Response(proc.stdout).text();
|
12
12
|
|
13
|
-
|
13
|
+
const hasFailed = text.includes('Failed');
|
14
14
|
|
15
|
-
|
15
|
+
if (hasFailed) console.log(text);
|
16
16
|
|
17
|
-
|
17
|
+
expect(hasFailed).toBe(false);
|
18
18
|
|
19
|
-
|
20
|
-
|
19
|
+
proc.kill(0);
|
20
|
+
});
|
21
21
|
|
22
|
-
|
23
|
-
|
22
|
+
test('Static', async() => {
|
23
|
+
const proc = Bun.spawn(['./tests/static.test.sh']);
|
24
24
|
|
25
|
-
|
25
|
+
const text = await new Response(proc.stdout).text();
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
const hasFailed = text.includes('Failed');
|
28
|
+
if (hasFailed) console.log(text);
|
29
29
|
|
30
|
-
|
30
|
+
expect(hasFailed).toBe(false);
|
31
31
|
|
32
|
-
|
33
|
-
|
32
|
+
proc.kill(0);
|
33
|
+
});
|
34
34
|
});
|
35
35
|
|
36
|
+
|