bun-router 0.5.5 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
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();
@@ -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();
@@ -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({status: 500, text: 'Foo is undefined'});
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();
@@ -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();
@@ -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: 200,
8
- statusText: httpStatusCodes[200],
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: 200,
16
- statusText: httpStatusCodes[200],
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: 200,
36
- statusText: httpStatusCodes[200],
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 }
@@ -4,12 +4,18 @@ import { Database } from 'bun:sqlite';
4
4
 
5
5
 
6
6
  type Context = {
7
- request: Request,
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 }
@@ -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 (url.pathname === '/favicon.ico') {
135
- return http.noContent();
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
- if (route.method !== req.method) {
139
- statusCode = 405;
140
- continue;
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
- if (match(route, ctx)) {
144
- const res = await route.callback(ctx);
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
- statusCode = 500;
151
- break;
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
@@ -8,5 +8,5 @@
8
8
  "peerDependencies": {
9
9
  "typescript": "^5.0.0"
10
10
  },
11
- "version": "0.5.5"
11
+ "version": "0.6.0"
12
12
  }