bun-router 0.5.0 → 0.5.4
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/dynamic.ts +2 -1
- package/lib/http/status.ts +66 -0
- package/lib/logger/logger.d.ts +1 -0
- package/lib/logger/logger.ts +11 -1
- package/lib/router/router.d.ts +4 -0
- package/lib/router/router.ts +58 -15
- package/package.json +1 -1
package/examples/dynamic.ts
CHANGED
@@ -0,0 +1,66 @@
|
|
1
|
+
const httpStatusCodes: { [key: number]: string } = {
|
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
|
+
|
66
|
+
export { httpStatusCodes }
|
package/lib/logger/logger.d.ts
CHANGED
@@ -3,6 +3,7 @@ type Logger = {
|
|
3
3
|
info: (statusCode: number, routePath: string, method: string, message?: string) => void,
|
4
4
|
error: (statusCode: number, routePath: string, method: string, error: Error) => void,
|
5
5
|
warn: (msg: string) => void,
|
6
|
+
message: (msg: string) => void,
|
6
7
|
}
|
7
8
|
|
8
9
|
export { Logger }
|
package/lib/logger/logger.ts
CHANGED
@@ -26,6 +26,7 @@ const colorCode = (n: number, text?:string): string => {
|
|
26
26
|
return color('white', 'bgBlack', `[${s}]`).trim();
|
27
27
|
}
|
28
28
|
|
29
|
+
|
29
30
|
const clean = (s: string) => s.replace(/\x1B\[\d{1,2}(;\d{1,2}){0,2}m/g, '');
|
30
31
|
|
31
32
|
const format = (statusCode: number, routePath: string, method: string, message?: string): string => {
|
@@ -53,7 +54,7 @@ const logger = (): Logger => {
|
|
53
54
|
const { stamp } = timestamp((new Date(Date.now())));
|
54
55
|
const source = color('green', 'bgBlack', `[bun-router ${stamp}]`);
|
55
56
|
const rp = color('white', 'bgBlack', routePath);
|
56
|
-
const msg = `${source}: ${colorCode(statusCode)}: ${rp} ${(method === 'GET') ? '->' : '<-'} ${method}\n`
|
57
|
+
const msg = `${source}: ${colorCode(statusCode)}: ${rp} ${(method === 'GET') ? '->' : '<-'} ${method}${' | ' +message ?? ''}\n`
|
57
58
|
|
58
59
|
await Bun.write(Bun.stdout, msg);
|
59
60
|
|
@@ -75,6 +76,15 @@ const logger = (): Logger => {
|
|
75
76
|
const msgColor = color('yellow', 'bgBlack', msg);
|
76
77
|
msg = `${source} : ${msgColor}\n`;
|
77
78
|
await Bun.write(Bun.stdout, msg);
|
79
|
+
},
|
80
|
+
message: async (msg: string) => {
|
81
|
+
const { stamp } = timestamp((new Date(Date.now())));
|
82
|
+
const source = color('black', 'bgCyan', `[message ${stamp}]`);
|
83
|
+
const msgColor = color('yellow', 'bgBlack', msg);
|
84
|
+
msg = `${source}: ${msgColor}\n`;
|
85
|
+
await Bun.write(Bun.stdout, msg);
|
86
|
+
|
87
|
+
messages.push(clean(msg));
|
78
88
|
}
|
79
89
|
}
|
80
90
|
}
|
package/lib/router/router.d.ts
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
import { TLSOptions, TLSWebSocketServeOptions, WebSocketServeOptions, ServeOptions, TLSServeOptions } from 'bun';
|
2
|
+
import { Logger } from '../logger/logger';
|
2
3
|
import { Database } from 'bun:sqlite';
|
3
4
|
|
4
5
|
|
@@ -7,6 +8,7 @@ type Context = {
|
|
7
8
|
params: Map<string, string>,
|
8
9
|
token?: string,
|
9
10
|
db: Database,
|
11
|
+
logger: Logger,
|
10
12
|
}
|
11
13
|
|
12
14
|
type Route = {
|
@@ -28,6 +30,8 @@ type RouterOptions<Options> = ServeOptions
|
|
28
30
|
|
29
31
|
type Router = (port?: number | string, options?: RouterOptions) => {
|
30
32
|
add: (pattern: string, method: string, callback: (req: Context) => Response | Promise<Response>) => void,
|
33
|
+
GET: (pattern: string, callback: (ctx: Context) => Response | Promise<Response>) => void,
|
34
|
+
POST: (pattern: string, callback: (ctx: Context) => Response | Promise<Response>) => void,
|
31
35
|
static: (pattern: string, root: string) => void,
|
32
36
|
serve: () => void,
|
33
37
|
}
|
package/lib/router/router.ts
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
import { Database } from 'bun:sqlite';
|
2
2
|
import { Route, Router, Context, RouterOptions, Options } from './router.d';
|
3
|
+
import { httpStatusCodes } from '../http/status';
|
3
4
|
import { readDir } from '../fs/fsys';
|
4
5
|
import { logger } from '../logger/logger';
|
5
6
|
import path from 'path';
|
7
|
+
import { Logger } from '../logger/logger.d';
|
6
8
|
|
7
9
|
// create a generic HTTP response
|
8
10
|
const httpMessage = async (status: number, msg?: string): Promise<Response> => {
|
9
11
|
const response = new Response(msg ?? '?', {
|
10
12
|
status: status,
|
11
13
|
statusText: msg ?? '?',
|
12
|
-
headers: {'Content-Type': 'text/html; charset-uft-8'}
|
14
|
+
headers: { 'Content-Type': 'text/html; charset-uft-8' }
|
13
15
|
});
|
14
16
|
return new Promise((resolve) => {
|
15
17
|
resolve(response);
|
@@ -127,7 +129,7 @@ const match = (route: Route, ctx: Context): boolean => {
|
|
127
129
|
const patternRegex = new RegExp('^' + route.pattern.replace(/:[^/]+/g, '([^/]+)') + '$');
|
128
130
|
const matches = url.pathname.match(patternRegex);
|
129
131
|
|
130
|
-
if (matches) {
|
132
|
+
if (matches && route.method === ctx.request.method) {
|
131
133
|
const extractor = extract(route, ctx);
|
132
134
|
extractor?.params();
|
133
135
|
|
@@ -137,6 +139,15 @@ const match = (route: Route, ctx: Context): boolean => {
|
|
137
139
|
return false;
|
138
140
|
}
|
139
141
|
|
142
|
+
const setContext = (req: Request, lgr: Logger, opts: Options): Context => {
|
143
|
+
return {
|
144
|
+
request: req,
|
145
|
+
params: new Map(),
|
146
|
+
db: new Database(opts.db ?? ':memory:'),
|
147
|
+
logger: lgr,
|
148
|
+
}
|
149
|
+
}
|
150
|
+
|
140
151
|
|
141
152
|
|
142
153
|
const router: Router = (port?: number | string, options?: RouterOptions<Options>) => {
|
@@ -153,6 +164,20 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
153
164
|
callback: callback,
|
154
165
|
})
|
155
166
|
},
|
167
|
+
GET: (pattern: string, callback: (ctx: Context) => Response | Promise<Response>) => {
|
168
|
+
routes.push({
|
169
|
+
pattern: pattern,
|
170
|
+
method: 'GET',
|
171
|
+
callback: callback,
|
172
|
+
});
|
173
|
+
},
|
174
|
+
POST: (pattern: string, callback: (ctx: Context) => Response | Promise<Response>) => {
|
175
|
+
routes.push({
|
176
|
+
pattern: pattern,
|
177
|
+
method: 'POST',
|
178
|
+
callback: callback,
|
179
|
+
});
|
180
|
+
},
|
156
181
|
// add a route for static files
|
157
182
|
static: async (pattern: string, root: string) => {
|
158
183
|
await readDir(root, async (fp, _) => {
|
@@ -180,36 +205,54 @@ const router: Router = (port?: number | string, options?: RouterOptions<Options>
|
|
180
205
|
// start the server
|
181
206
|
serve: () => {
|
182
207
|
lgr.start(port ?? 3000);
|
183
|
-
let opts: Options = {db: ':memory:'};
|
208
|
+
let opts: Options = { db: ':memory:' };
|
184
209
|
|
185
210
|
Bun.serve({
|
186
211
|
port: port ?? 3000,
|
187
212
|
...options,
|
188
213
|
async fetch(req) {
|
189
214
|
const url = new URL(req.url);
|
190
|
-
|
191
|
-
//? ????
|
215
|
+
|
192
216
|
if (options) {
|
193
217
|
let o = options as Options;
|
194
218
|
opts.db = o.db;
|
195
219
|
}
|
220
|
+
|
221
|
+
let statusCode = 404; // Default status code for route not found
|
222
|
+
|
196
223
|
for (const route of routes) {
|
197
|
-
const ctx
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
}
|
224
|
+
const ctx = setContext(req, lgr, opts);
|
225
|
+
|
226
|
+
if (url.pathname === '/favicon.ico') {
|
227
|
+
return noContent();
|
228
|
+
}
|
202
229
|
|
203
|
-
if (
|
230
|
+
if (route.method !== req.method) {
|
231
|
+
statusCode = 405;
|
232
|
+
continue;
|
233
|
+
}
|
204
234
|
|
205
235
|
if (match(route, ctx)) {
|
206
236
|
const res = await route.callback(ctx);
|
207
|
-
|
208
|
-
|
237
|
+
if (res) {
|
238
|
+
statusCode = 200;
|
239
|
+
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
240
|
+
return res
|
241
|
+
} else {
|
242
|
+
statusCode = 500;
|
243
|
+
break;
|
244
|
+
}
|
209
245
|
}
|
210
246
|
}
|
211
|
-
|
212
|
-
|
247
|
+
|
248
|
+
if (statusCode === 405) {
|
249
|
+
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
250
|
+
return httpMessage(statusCode, httpStatusCodes[statusCode]);
|
251
|
+
}
|
252
|
+
|
253
|
+
lgr.info(statusCode, url.pathname, req.method, httpStatusCodes[statusCode]);
|
254
|
+
return httpMessage(statusCode, httpStatusCodes[statusCode]);
|
255
|
+
|
213
256
|
}
|
214
257
|
});
|
215
258
|
},
|
package/package.json
CHANGED