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 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
  }