bun-router 0.5.5 → 0.6.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/examples/basic.ts +3 -3
- package/examples/cookies.ts +15 -0
- package/examples/dynamic.ts +6 -6
- package/examples/logger.ts +2 -2
- package/examples/sqlite.ts +2 -2
- package/examples/todo.ts +49 -0
- package/lib/http/generic-methods.ts +10 -11
- package/lib/router/router.d.ts +11 -4
- package/lib/router/router.ts +34 -29
- package/package.json +1 -1
package/examples/basic.ts
CHANGED
@@ -2,17 +2,17 @@ import { router, http } from '..';
|
|
2
2
|
|
3
3
|
const r = router();
|
4
4
|
|
5
|
-
r.add('/', 'GET', () => http.json('ok'));
|
5
|
+
r.add('/', 'GET', () => http.json(200, 'ok'));
|
6
6
|
|
7
7
|
r.add('/user/:name', 'GET', (ctx) => {
|
8
8
|
const name = ctx.params.get('name');
|
9
|
-
return http.json(name);
|
9
|
+
return http.json(200, 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 http.json({name: name, id: id});
|
15
|
+
return http.json(200, {name: name, id: id});
|
16
16
|
});
|
17
17
|
|
18
18
|
r.serve();
|
@@ -0,0 +1,15 @@
|
|
1
|
+
import { router } from '..';
|
2
|
+
|
3
|
+
const r = router();
|
4
|
+
|
5
|
+
r.add('/set-cookie', 'GET', ctx => {
|
6
|
+
ctx.cookies.set('domain', 'localhost');
|
7
|
+
ctx.cookies.set('path', '/set-cookie');
|
8
|
+
|
9
|
+
ctx.logger.message(ctx.token ?? 'no token provided');
|
10
|
+
|
11
|
+
return ctx.json( 200, {message: 'cookie stored'});
|
12
|
+
});
|
13
|
+
|
14
|
+
|
15
|
+
r.serve();
|
package/examples/dynamic.ts
CHANGED
@@ -3,20 +3,20 @@ 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 http.html('<h6 style="color: red">User Undefined</h6>');
|
7
|
-
return http.html(`<h4>Hello, ${name}!</h4>`);
|
6
|
+
if (typeof name === 'undefined' || name === '') return http.html(500, '<h6 style="color: red">User Undefined</h6>');
|
7
|
+
return http.html(200, `<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 http.html(`<h6 style="color: red">Space [${name}] Not Found</h6>`);
|
13
|
-
return http.html(`<h4>Welcome to ${name}!`)
|
12
|
+
if (typeof name === 'undefined' || name === '') return http.html(404, `<h6 style="color: red">Space [${name}] Not Found</h6>`);
|
13
|
+
return http.html(200, `<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 http.html(`<h6 style="color: red">User Not Found</h6>`);
|
19
|
-
return http.html(`<h4>Settings for ${name}</h4>`)
|
18
|
+
if (typeof name === 'undefined' || name === '') return http.html(404, `<h6 style="color: red">User Not Found</h6>`);
|
19
|
+
return http.html(200, `<h4>Settings for ${name}</h4>`)
|
20
20
|
}
|
21
21
|
|
22
22
|
const r = router();
|
package/examples/logger.ts
CHANGED
@@ -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 http.json(
|
11
|
+
return http.json(500,{text: 'Foo is undefined'});
|
12
12
|
}
|
13
|
-
return http.html(`<h4 style='font-family: sans-serif;'>Oh hello, ${foo}</h4>`)
|
13
|
+
return http.html(200, `<h4 style='font-family: sans-serif;'>Oh hello, ${foo}</h4>`)
|
14
14
|
});
|
15
15
|
|
16
16
|
r.serve();
|
package/examples/sqlite.ts
CHANGED
@@ -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 http.json({message: 'ok'});
|
11
|
+
return http.json(200, {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 ? http.json(d) : new Response('not found', {status: 404});
|
19
|
+
return d ? http.json(200, d) : new Response('not found', {status: 404});
|
20
20
|
});
|
21
21
|
|
22
22
|
r.serve();
|
package/examples/todo.ts
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
import { router, http } from '..';
|
2
|
+
import { Context } from '../lib/router/router.d';
|
3
|
+
|
4
|
+
const Todo = () => {
|
5
|
+
const list: Record<string, string> = {};
|
6
|
+
|
7
|
+
return {
|
8
|
+
add: (key: string, value: string) => { list[key] = value },
|
9
|
+
get: (key: string) => list[key],
|
10
|
+
remove: (key: string) => { delete list[key] },
|
11
|
+
size: () => Object.entries(list).length,
|
12
|
+
export: () => list,
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
const todo = Todo();
|
17
|
+
|
18
|
+
const r = router();
|
19
|
+
|
20
|
+
r.add('/api/new', 'POST', ctx => {
|
21
|
+
const query = new URL(ctx.request.url).searchParams;
|
22
|
+
const key = query.get('key');
|
23
|
+
const content = query.get('content');
|
24
|
+
|
25
|
+
if (!key || !content) return http.message(400, 'invalid query params');
|
26
|
+
ctx.logger.message(`Adding ${key} with ${content}`);
|
27
|
+
todo.add(key, content);
|
28
|
+
|
29
|
+
return ctx.json(200, { message: 'ok' });
|
30
|
+
});
|
31
|
+
|
32
|
+
r.add('/api/todo/:key', 'GET', ctx => {
|
33
|
+
const key = ctx.params.get('key');
|
34
|
+
if (!key) return http.message(400, 'invalid params');
|
35
|
+
|
36
|
+
const content = todo.get(key);
|
37
|
+
if (!content) return http.notFound();
|
38
|
+
|
39
|
+
return ctx.json(200, {key: key, content: content});
|
40
|
+
});
|
41
|
+
|
42
|
+
r.add('/api/get/all', 'GET', ctx => {
|
43
|
+
return ctx.json(200, todo.export());
|
44
|
+
});
|
45
|
+
|
46
|
+
r.serve();
|
47
|
+
|
48
|
+
|
49
|
+
|
@@ -1,23 +1,23 @@
|
|
1
1
|
import { httpStatusCodes } from "./status";
|
2
2
|
|
3
3
|
const http = {
|
4
|
-
json: async (data: any): Promise<Response> => {
|
4
|
+
json: async (statusCode: number, data: any): Promise<Response> => {
|
5
5
|
const jsonString = JSON.stringify(data);
|
6
6
|
return Promise.resolve(new Response(jsonString, {
|
7
|
-
status:
|
8
|
-
statusText: httpStatusCodes[
|
7
|
+
status: statusCode,
|
8
|
+
statusText: httpStatusCodes[statusCode],
|
9
9
|
headers: {'Content-Type': 'application/json'},
|
10
10
|
}));
|
11
11
|
},
|
12
|
-
html: async (content: string): Promise<Response> => {
|
12
|
+
html: async (statusCode: number, content: string): Promise<Response> => {
|
13
13
|
content = Bun.escapeHTML(content);
|
14
14
|
return Promise.resolve(new Response(Bun.escapeHTML(content), {
|
15
|
-
status:
|
16
|
-
statusText: httpStatusCodes[
|
15
|
+
status: statusCode,
|
16
|
+
statusText: httpStatusCodes[statusCode],
|
17
17
|
headers: {'Content-Type': 'text/html; charset=utf-8'}
|
18
18
|
}));
|
19
19
|
},
|
20
|
-
file: async (fp: string): Promise<Response> => {
|
20
|
+
file: async (statusCode: number, fp: string): Promise<Response> => {
|
21
21
|
const file = Bun.file(fp);
|
22
22
|
const exists = await file.exists();
|
23
23
|
|
@@ -32,8 +32,8 @@ const http = {
|
|
32
32
|
contentType = file.type + '; charset=utf-8';
|
33
33
|
|
34
34
|
return Promise.resolve(new Response(content, {
|
35
|
-
status:
|
36
|
-
statusText: httpStatusCodes[
|
35
|
+
status: statusCode,
|
36
|
+
statusText: httpStatusCodes[statusCode],
|
37
37
|
headers: { 'Content-Type': contentType}
|
38
38
|
}));
|
39
39
|
},
|
@@ -57,8 +57,7 @@ const http = {
|
|
57
57
|
headers: {'Content-Type': 'text/html; charset-utf-8'},
|
58
58
|
});
|
59
59
|
return Promise.resolve(response)
|
60
|
-
}
|
61
|
-
|
60
|
+
},
|
62
61
|
}
|
63
62
|
|
64
63
|
export { http }
|
package/lib/router/router.d.ts
CHANGED
@@ -4,12 +4,18 @@ import { Database } from 'bun:sqlite';
|
|
4
4
|
|
5
5
|
|
6
6
|
type Context = {
|
7
|
-
|
8
|
-
params: Map<string, string>,
|
9
|
-
token?: string,
|
7
|
+
cookies: Map<string, string>,
|
10
8
|
db: Database,
|
9
|
+
formData: FormData | Promise<FormData> | undefined,
|
10
|
+
json: (statusCode: number, data: any) => Response | Promise<Response>,
|
11
11
|
logger: Logger,
|
12
|
-
|
12
|
+
params: Map<string, string>,
|
13
|
+
query: URLSearchParams,
|
14
|
+
request: Request,
|
15
|
+
route: Route,
|
16
|
+
token?: string,
|
17
|
+
};
|
18
|
+
|
13
19
|
|
14
20
|
type Route = {
|
15
21
|
pattern: string,
|
@@ -37,4 +43,5 @@ type Router = (port?: number | string, options?: RouterOptions) => {
|
|
37
43
|
}
|
38
44
|
|
39
45
|
|
46
|
+
|
40
47
|
export { Context , Route, Router, RouterOptions, Options }
|
package/lib/router/router.ts
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
+
import path from 'path';
|
1
2
|
import { Database } from 'bun:sqlite';
|
2
3
|
import { Route, Router, Context, RouterOptions, Options } from './router.d';
|
3
4
|
import { httpStatusCodes } from '../http/status';
|
4
5
|
import { readDir } from '../fs/fsys';
|
5
6
|
import { logger } from '../logger/logger';
|
6
|
-
import path from 'path';
|
7
7
|
import { Logger } from '../logger/logger.d';
|
8
8
|
import { http } from '../http/generic-methods';
|
9
9
|
|
@@ -47,21 +47,25 @@ const match = (route: Route, ctx: Context): boolean => {
|
|
47
47
|
}
|
48
48
|
|
49
49
|
// set the context for the reuest
|
50
|
-
const setContext = (req: Request, lgr: Logger, opts: Options): Context => {
|
50
|
+
const setContext = (req: Request, lgr: Logger, opts: Options, route: Route): Context => {
|
51
|
+
const token = req.headers.get('Authorization');
|
51
52
|
return {
|
53
|
+
token: token ?? '',
|
54
|
+
cookies: new Map(),
|
55
|
+
formData: req.formData(),
|
52
56
|
request: req,
|
53
57
|
params: new Map(),
|
58
|
+
query: new URL(req.url).searchParams,
|
54
59
|
db: new Database(opts.db ?? ':memory:'),
|
55
60
|
logger: lgr,
|
61
|
+
route: route,
|
62
|
+
json: (statusCode: number, data: any) => http.json(statusCode, data),
|
56
63
|
}
|
57
64
|
}
|
58
65
|
|
59
|
-
|
60
|
-
|
61
66
|
const router: Router = (port?: number | string, options?: RouterOptions<Options>) => {
|
62
67
|
const routes: Array<Route> = new Array();
|
63
68
|
const lgr = logger();
|
64
|
-
let dbConn = '';
|
65
69
|
|
66
70
|
return {
|
67
71
|
// add a new route
|
@@ -105,8 +109,9 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
105
109
|
const route: Route = {
|
106
110
|
pattern: patternPath,
|
107
111
|
method: 'GET',
|
108
|
-
callback: async () => await http.file(pure),
|
112
|
+
callback: async () => await http.file(200, pure),
|
109
113
|
};
|
114
|
+
|
110
115
|
routes.push(route);
|
111
116
|
});
|
112
117
|
},
|
@@ -129,38 +134,38 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
129
134
|
let statusCode = 404;
|
130
135
|
|
131
136
|
for (const route of routes) {
|
132
|
-
const ctx = setContext(req, lgr, opts);
|
137
|
+
const ctx = setContext(req, lgr, opts, route);
|
133
138
|
|
134
|
-
if (
|
135
|
-
|
136
|
-
|
139
|
+
if (match(route, ctx) || route.pattern === url.pathname) {
|
140
|
+
if (route.method === ctx.request.method) {
|
141
|
+
const res = await route.callback(ctx);
|
137
142
|
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
143
|
+
let cookieValue: string[] = [];
|
144
|
+
if (ctx.cookies.size !== 0) {
|
145
|
+
for (const [key, value] of ctx.cookies) {
|
146
|
+
cookieValue.push(`${key}=${value}`);
|
147
|
+
}
|
148
|
+
}
|
149
|
+
|
150
|
+
res.headers.set('Set-Cookie', cookieValue.join('; '));
|
151
|
+
|
152
|
+
statusCode = res.status;
|
142
153
|
|
143
|
-
|
144
|
-
|
145
|
-
if (res) {
|
146
|
-
statusCode = 200;
|
147
|
-
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
148
|
-
return res
|
154
|
+
lgr.info(res.status, route.pattern, req.method, httpStatusCodes[res.status]);
|
155
|
+
return Promise.resolve(res);
|
149
156
|
} else {
|
150
|
-
|
151
|
-
|
157
|
+
const res = new Response(httpStatusCodes[405], {
|
158
|
+
status: 405,
|
159
|
+
statusText: httpStatusCodes[405]
|
160
|
+
});
|
161
|
+
lgr.info(405, route.pattern, req.method, httpStatusCodes[405])
|
162
|
+
return Promise.resolve(res);
|
152
163
|
}
|
153
164
|
}
|
154
165
|
}
|
155
166
|
|
156
|
-
if (statusCode === 405) {
|
157
|
-
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
158
|
-
return http.message(statusCode, httpStatusCodes[statusCode]);
|
159
|
-
}
|
160
|
-
|
161
167
|
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
162
|
-
return http.message(statusCode, httpStatusCodes[statusCode]);
|
163
|
-
|
168
|
+
return Promise.resolve(http.message(statusCode, httpStatusCodes[statusCode]));
|
164
169
|
}
|
165
170
|
});
|
166
171
|
},
|
package/package.json
CHANGED