bun-router 0.3.9 → 0.5.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/README.md +28 -0
- package/examples/dbs/test.db +0 -0
- package/examples/sqlite.ts +22 -0
- package/index.ts +0 -2
- package/lib/fs/fsys.ts +7 -5
- package/lib/logger/logger.ts +2 -0
- package/lib/router/router.d.ts +12 -7
- package/lib/router/router.ts +59 -23
- package/package.json +1 -1
- package/tests/router.test.ts +3 -7
    
        package/README.md
    CHANGED
    
    | @@ -52,4 +52,32 @@ r.add('/user/:id', 'GET', (ctx) => { | |
| 52 52 | 
             
            });
         | 
| 53 53 |  | 
| 54 54 | 
             
            r.serve();
         | 
| 55 | 
            +
            ```
         | 
| 56 | 
            +
             | 
| 57 | 
            +
            **SQLite**
         | 
| 58 | 
            +
            ```ts
         | 
| 59 | 
            +
            import { router, json } from '..';
         | 
| 60 | 
            +
            import { Database } from 'bun:sqlite';
         | 
| 61 | 
            +
             | 
| 62 | 
            +
            const r = router(3000, {db: './examples/dbs/test.db'});
         | 
| 63 | 
            +
             | 
| 64 | 
            +
            r.add('/u/new/:name', 'GET', (ctx) => {
         | 
| 65 | 
            +
                const name = ctx.params.get('name');
         | 
| 66 | 
            +
                const rando = Math.floor(Math.random()*1000);
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                ctx.db.run(`INSERT INTO test VALUES(${rando}, "${name}")`);
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                return json({message: 'ok'});
         | 
| 71 | 
            +
            });
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            r.add('/u/:name', 'GET', (ctx) => {
         | 
| 74 | 
            +
                const name = ctx.params.get('name');
         | 
| 75 | 
            +
                const data = ctx.db.query(`SELECT * FROM test WHERE name = "${name}";`).get();
         | 
| 76 | 
            +
                const d = data as {id: number, name: string};
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                return d ? json(d) : new Response('not found', {status: 404});
         | 
| 79 | 
            +
            });
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            r.serve();
         | 
| 82 | 
            +
             | 
| 55 83 | 
             
            ```
         | 
| Binary file | 
| @@ -0,0 +1,22 @@ | |
| 1 | 
            +
            import { router, json } from '..';
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            const r = router(3000, {db: './examples/dbs/test.db'});
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            r.add('/u/new/:name', 'GET', (ctx) => {
         | 
| 6 | 
            +
                const name = ctx.params.get('name');
         | 
| 7 | 
            +
                const rando = Math.floor(Math.random()*1000);
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                ctx.db.run(`INSERT INTO test VALUES(${rando}, "${name}")`);
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                return json({message: 'ok'});
         | 
| 12 | 
            +
            });
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            r.add('/u/:name', 'GET', (ctx) => {
         | 
| 15 | 
            +
                const name = ctx.params.get('name');
         | 
| 16 | 
            +
                const data = ctx.db.query(`SELECT * FROM test WHERE name = "${name}";`).get();
         | 
| 17 | 
            +
                const d = data as {id: number, name: string};
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                return d ? json(d) : new Response('not found', {status: 404});
         | 
| 20 | 
            +
            });
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            r.serve();
         | 
    
        package/index.ts
    CHANGED
    
    
    
        package/lib/fs/fsys.ts
    CHANGED
    
    | @@ -2,23 +2,25 @@ import { BunFile } from "bun"; | |
| 2 2 | 
             
            import fs from 'node:fs/promises';
         | 
| 3 3 | 
             
            import path from 'path';
         | 
| 4 4 |  | 
| 5 | 
            +
            // check if the file path is a directory
         | 
| 5 6 | 
             
            const isDir = async (fp: string): Promise<boolean> => (await fs.lstat(fp)).isDirectory();
         | 
| 6 7 |  | 
| 8 | 
            +
            // read a directory recursively and apply the callback to each one
         | 
| 7 9 | 
             
            const readDir = async (dirpath: string, handler: (filepath: string, entry: BunFile) => void) => {
         | 
| 8 | 
            -
                try {
         | 
| 9 10 | 
             
                    const files = await fs.readdir(dirpath);
         | 
| 10 11 |  | 
| 11 12 | 
             
                    for (const file of files) {
         | 
| 12 13 | 
             
                        const bunFile = Bun.file(file);
         | 
| 13 | 
            -
             | 
| 14 | 
            +
             | 
| 15 | 
            +
                        if (typeof bunFile.name === 'undefined') return
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                        const fp = path.join(dirpath, bunFile.name);
         | 
| 14 18 | 
             
                        const isdir = await isDir(fp);
         | 
| 15 19 |  | 
| 16 20 | 
             
                        if (isdir) await readDir(fp, handler);
         | 
| 17 21 | 
             
                        else handler(fp, bunFile);
         | 
| 18 22 | 
             
                    }
         | 
| 19 | 
            -
             | 
| 20 | 
            -
                    console.error(err);
         | 
| 21 | 
            -
                }
         | 
| 23 | 
            +
             | 
| 22 24 | 
             
            }
         | 
| 23 25 |  | 
| 24 26 | 
             
            export { readDir }
         | 
    
        package/lib/logger/logger.ts
    CHANGED
    
    | @@ -14,6 +14,7 @@ const timestamp = (date: Date) => { | |
| 14 14 | 
             
                return {month, day, hour, minute, stamp};
         | 
| 15 15 | 
             
            }
         | 
| 16 16 |  | 
| 17 | 
            +
            // append ANSI color escape sequences to a string based on the given HTTP status code.
         | 
| 17 18 | 
             
            const colorCode = (n: number, text?:string): string => {
         | 
| 18 19 | 
             
                const s = ` [${String(n)}${text ?? ''}] `;
         | 
| 19 20 | 
             
                if (n < 100) return color('black', 'bgYellow', s);
         | 
| @@ -39,6 +40,7 @@ const logger = (): Logger => { | |
| 39 40 | 
             
                const messages: string[] = [];
         | 
| 40 41 | 
             
                const errors: string[] = [];
         | 
| 41 42 | 
             
                return {
         | 
| 43 | 
            +
                    // initial log message
         | 
| 42 44 | 
             
                    start: async (port: number | string) => {
         | 
| 43 45 | 
             
                        const { stamp } = timestamp((new Date(Date.now())));
         | 
| 44 46 | 
             
                        const source = color('green', 'bgBlack', `[bun-router ${stamp}]`)
         | 
    
        package/lib/router/router.d.ts
    CHANGED
    
    | @@ -1,11 +1,12 @@ | |
| 1 1 | 
             
            import { TLSOptions, TLSWebSocketServeOptions, WebSocketServeOptions, ServeOptions, TLSServeOptions } from 'bun';
         | 
| 2 | 
            +
            import { Database } from 'bun:sqlite';
         | 
| 2 3 |  | 
| 3 4 |  | 
| 4 5 | 
             
            type Context = {
         | 
| 5 6 | 
             
                request: Request,
         | 
| 6 7 | 
             
                params: Map<string, string>,
         | 
| 7 | 
            -
                fs: Map<string, string>,
         | 
| 8 8 | 
             
                token?: string,
         | 
| 9 | 
            +
                db: Database,
         | 
| 9 10 | 
             
            }
         | 
| 10 11 |  | 
| 11 12 | 
             
            type Route = {
         | 
| @@ -14,18 +15,22 @@ type Route = { | |
| 14 15 | 
             
                callback: (req: Context) => Response | Promise<Response>
         | 
| 15 16 | 
             
            }
         | 
| 16 17 |  | 
| 17 | 
            -
            type Options =  | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 18 | 
            +
            type Options = {
         | 
| 19 | 
            +
                db: string,
         | 
| 20 | 
            +
            }
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            type RouterOptions<Options> = ServeOptions
         | 
| 23 | 
            +
            | TLSServeOptions<Options>
         | 
| 24 | 
            +
            | WebSocketServeOptions<Options>
         | 
| 25 | 
            +
            | TLSWebSocketServeOptions<Options>
         | 
| 21 26 | 
             
            | undefined
         | 
| 22 27 |  | 
| 23 28 |  | 
| 24 | 
            -
            type Router = (port?: number | string, options?:  | 
| 29 | 
            +
            type Router = (port?: number | string, options?: RouterOptions) => {
         | 
| 25 30 | 
             
                add: (pattern: string, method: string, callback: (req: Context) => Response | Promise<Response>) => void,
         | 
| 26 31 | 
             
                static: (pattern: string, root: string) => void,
         | 
| 27 32 | 
             
                serve: () => void,
         | 
| 28 33 | 
             
            }
         | 
| 29 34 |  | 
| 30 35 |  | 
| 31 | 
            -
            export { Context , Route, Router, Options }
         | 
| 36 | 
            +
            export { Context , Route, Router, RouterOptions, Options }
         | 
    
        package/lib/router/router.ts
    CHANGED
    
    | @@ -1,10 +1,24 @@ | |
| 1 | 
            -
            import {  | 
| 1 | 
            +
            import { Database } from 'bun:sqlite';
         | 
| 2 | 
            +
            import { Route, Router, Context, RouterOptions, Options } from './router.d';
         | 
| 2 3 | 
             
            import { readDir } from '../fs/fsys';
         | 
| 3 4 | 
             
            import { logger } from '../logger/logger';
         | 
| 4 5 | 
             
            import path from 'path';
         | 
| 5 6 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 7 | 
            +
            // create a generic HTTP response
         | 
| 8 | 
            +
            const httpMessage = async (status: number, msg?: string): Promise<Response> => {
         | 
| 9 | 
            +
                const response = new Response(msg ?? '?', {
         | 
| 10 | 
            +
                    status: status,
         | 
| 11 | 
            +
                    statusText: msg ?? '?',
         | 
| 12 | 
            +
                    headers: {'Content-Type': 'text/html; charset-uft-8'}
         | 
| 13 | 
            +
                });
         | 
| 14 | 
            +
                return new Promise((resolve) => {
         | 
| 15 | 
            +
                    resolve(response);
         | 
| 16 | 
            +
                });
         | 
| 17 | 
            +
            };
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            // a generic 'not found' HTTP response
         | 
| 20 | 
            +
            const notFound = async (msg?: string): Promise<Response> => {
         | 
| 21 | 
            +
                const response = new Response(msg ?? 'not found', {
         | 
| 8 22 | 
             
                    status: 404,
         | 
| 9 23 | 
             
                    statusText: 'not found',
         | 
| 10 24 | 
             
                    headers: { 'Content-Type': 'text/html' },
         | 
| @@ -15,6 +29,7 @@ const notFound = async (): Promise<Response> => { | |
| 15 29 | 
             
                });
         | 
| 16 30 | 
             
            }
         | 
| 17 31 |  | 
| 32 | 
            +
            // a generic 'no content' HTTP response
         | 
| 18 33 | 
             
            const noContent = async (): Promise<Response> => {
         | 
| 19 34 | 
             
                const response = new Response('no content', {
         | 
| 20 35 | 
             
                    status: 204,
         | 
| @@ -26,47 +41,54 @@ const noContent = async (): Promise<Response> => { | |
| 26 41 | 
             
                });
         | 
| 27 42 | 
             
            }
         | 
| 28 43 |  | 
| 44 | 
            +
            // IO handling
         | 
| 29 45 | 
             
            const file = async (filepath: string): Promise<Response> => {
         | 
| 30 46 | 
             
                const file = Bun.file(filepath);
         | 
| 31 47 | 
             
                const exists = await file.exists();
         | 
| 32 48 |  | 
| 49 | 
            +
                // check if the file exists, return 'not found' if it doesn't.
         | 
| 33 50 | 
             
                if (!exists)
         | 
| 34 | 
            -
                    return notFound();
         | 
| 51 | 
            +
                    return notFound(`File not found: ${filepath}`);
         | 
| 35 52 |  | 
| 53 | 
            +
                // get the content of the file as an ArrayBuffer
         | 
| 36 54 | 
             
                const content = await file.arrayBuffer();
         | 
| 37 55 | 
             
                if (!content)
         | 
| 38 | 
            -
                    return  | 
| 56 | 
            +
                    return noContent();
         | 
| 39 57 |  | 
| 58 | 
            +
                // default Content-Type + encoding
         | 
| 40 59 | 
             
                let contentType = 'text/html; charset=utf-8';
         | 
| 41 60 |  | 
| 61 | 
            +
                // change the Content-Type if the file type is an image.
         | 
| 62 | 
            +
                // file.type provides the necessary Content-Type
         | 
| 42 63 | 
             
                if (file.type.includes('image')) {
         | 
| 43 64 | 
             
                    contentType = file.type + '; charset=utf-8';
         | 
| 44 65 | 
             
                }
         | 
| 45 66 |  | 
| 67 | 
            +
                // create a new response with the necessary criteria
         | 
| 46 68 | 
             
                const response = new Response(content, {
         | 
| 47 69 | 
             
                    status: 200,
         | 
| 48 70 | 
             
                    statusText: 'ok',
         | 
| 49 71 | 
             
                    headers: { 'Content-Type': contentType },
         | 
| 50 72 | 
             
                });
         | 
| 51 73 |  | 
| 52 | 
            -
                return  | 
| 53 | 
            -
                    resolve(response);
         | 
| 54 | 
            -
                });
         | 
| 74 | 
            +
                return Promise.resolve(response);
         | 
| 55 75 | 
             
            }
         | 
| 56 76 |  | 
| 77 | 
            +
            // handle strings as HTML
         | 
| 57 78 | 
             
            const html = async (content: string): Promise<Response> => {
         | 
| 58 79 | 
             
                const response = new Response(content, {
         | 
| 59 80 | 
             
                    status: 200,
         | 
| 60 81 | 
             
                    statusText: 'ok',
         | 
| 61 82 | 
             
                    headers: { 'Content-Type': 'text/html; charset=utf-8' },
         | 
| 62 83 | 
             
                });
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                // escape the HTML
         | 
| 63 86 | 
             
                content = Bun.escapeHTML(content);
         | 
| 64 87 |  | 
| 65 | 
            -
                return  | 
| 66 | 
            -
                    resolve(response);
         | 
| 67 | 
            -
                });
         | 
| 88 | 
            +
                return Promise.resolve(response);
         | 
| 68 89 | 
             
            }
         | 
| 69 90 |  | 
| 91 | 
            +
            // create a JSON response
         | 
| 70 92 | 
             
            const json = (data: any): Response => {
         | 
| 71 93 | 
             
                const jsonString = JSON.stringify(data);
         | 
| 72 94 |  | 
| @@ -76,6 +98,8 @@ const json = (data: any): Response => { | |
| 76 98 | 
             
                return res
         | 
| 77 99 | 
             
            }
         | 
| 78 100 |  | 
| 101 | 
            +
            // extract dynamic URL parameters
         | 
| 102 | 
            +
            // if the route pattern is /:foo and the request URL is /bar: {foo: 'bar'}
         | 
| 79 103 | 
             
            const extract = (route: Route, ctx: Context) => {
         | 
| 80 104 | 
             
                const url = new URL(ctx.request.url);
         | 
| 81 105 | 
             
                const pathSegments = route.pattern.split('/');
         | 
| @@ -83,7 +107,6 @@ const extract = (route: Route, ctx: Context) => { | |
| 83 107 |  | 
| 84 108 | 
             
                if (pathSegments.length !== urlSegments.length) return
         | 
| 85 109 |  | 
| 86 | 
            -
             | 
| 87 110 | 
             
                return {
         | 
| 88 111 | 
             
                    params: () => {
         | 
| 89 112 | 
             
                        for (let i = 0; i < pathSegments.length; i++) {
         | 
| @@ -98,6 +121,7 @@ const extract = (route: Route, ctx: Context) => { | |
| 98 121 |  | 
| 99 122 | 
             
            }
         | 
| 100 123 |  | 
| 124 | 
            +
            // ensure the route pattern matches the request URL
         | 
| 101 125 | 
             
            const match = (route: Route, ctx: Context): boolean => {
         | 
| 102 126 | 
             
                const url = new URL(ctx.request.url);
         | 
| 103 127 | 
             
                const patternRegex = new RegExp('^' + route.pattern.replace(/:[^/]+/g, '([^/]+)') + '$');
         | 
| @@ -113,12 +137,15 @@ const match = (route: Route, ctx: Context): boolean => { | |
| 113 137 | 
             
                return false;
         | 
| 114 138 | 
             
            }
         | 
| 115 139 |  | 
| 116 | 
            -
             | 
| 140 | 
            +
             | 
| 141 | 
            +
             | 
| 142 | 
            +
            const router: Router = (port?: number | string, options?: RouterOptions<Options>) => {
         | 
| 117 143 | 
             
                const routes: Array<Route> = new Array();
         | 
| 118 | 
            -
                const paths: { [key: string]: string } = {};
         | 
| 119 144 | 
             
                const lgr = logger();
         | 
| 145 | 
            +
                let dbConn = '';
         | 
| 120 146 |  | 
| 121 147 | 
             
                return {
         | 
| 148 | 
            +
                    // add a new route
         | 
| 122 149 | 
             
                    add: (pattern: string, method: string, callback: (ctx: Context) => Response | Promise<Response>) => {
         | 
| 123 150 | 
             
                        routes.push({
         | 
| 124 151 | 
             
                            pattern: pattern,
         | 
| @@ -126,6 +153,7 @@ const router: Router = (port?: number | string, options?: Options) => { | |
| 126 153 | 
             
                            callback: callback,
         | 
| 127 154 | 
             
                        })
         | 
| 128 155 | 
             
                    },
         | 
| 156 | 
            +
                    // add a route for static files
         | 
| 129 157 | 
             
                    static: async (pattern: string, root: string) => {
         | 
| 130 158 | 
             
                        await readDir(root, async (fp, _) => {
         | 
| 131 159 | 
             
                            const pure = path.join('.', fp);
         | 
| @@ -133,10 +161,7 @@ const router: Router = (port?: number | string, options?: Options) => { | |
| 133 161 |  | 
| 134 162 | 
             
                            let base = path.basename(pure);
         | 
| 135 163 |  | 
| 136 | 
            -
                            if (ext === '.html')  | 
| 137 | 
            -
                                base = base.replace(ext, '');
         | 
| 138 | 
            -
             | 
| 139 | 
            -
                            }
         | 
| 164 | 
            +
                            if (ext === '.html') base = base.replace(ext, '');
         | 
| 140 165 |  | 
| 141 166 | 
             
                            if (pattern[0] !== '/') pattern = '/' + pattern;
         | 
| 142 167 |  | 
| @@ -152,33 +177,44 @@ const router: Router = (port?: number | string, options?: Options) => { | |
| 152 177 | 
             
                            routes.push(route);
         | 
| 153 178 | 
             
                        });
         | 
| 154 179 | 
             
                    },
         | 
| 180 | 
            +
                    // start the server
         | 
| 155 181 | 
             
                    serve: () => {
         | 
| 156 182 | 
             
                        lgr.start(port ?? 3000);
         | 
| 183 | 
            +
                        let opts: Options = {db: ':memory:'};
         | 
| 184 | 
            +
             | 
| 157 185 | 
             
                        Bun.serve({
         | 
| 158 186 | 
             
                            port: port ?? 3000,
         | 
| 159 187 | 
             
                            ...options,
         | 
| 160 | 
            -
                            fetch(req) {
         | 
| 188 | 
            +
                            async fetch(req) {
         | 
| 161 189 | 
             
                                const url = new URL(req.url);
         | 
| 190 | 
            +
                                
         | 
| 191 | 
            +
                                //? ????
         | 
| 192 | 
            +
                                if (options) {
         | 
| 193 | 
            +
                                    let o = options as Options;
         | 
| 194 | 
            +
                                    opts.db = o.db;
         | 
| 195 | 
            +
                                }
         | 
| 162 196 | 
             
                                for (const route of routes) {
         | 
| 163 197 | 
             
                                    const ctx: Context = {
         | 
| 164 198 | 
             
                                        request: req,
         | 
| 165 199 | 
             
                                        params: new Map(),
         | 
| 166 | 
            -
                                         | 
| 200 | 
            +
                                        db: new Database(opts.db ?? ':memory:'),
         | 
| 167 201 | 
             
                                    };
         | 
| 168 202 |  | 
| 169 203 | 
             
                                    if (url.pathname === '/favicon.ico') return noContent();
         | 
| 170 204 |  | 
| 171 205 | 
             
                                    if (match(route, ctx)) {
         | 
| 172 | 
            -
                                         | 
| 173 | 
            -
                                         | 
| 206 | 
            +
                                        const res = await route.callback(ctx);
         | 
| 207 | 
            +
                                        lgr.info(res.status, url.pathname, route.method); 
         | 
| 208 | 
            +
                                        return res;
         | 
| 174 209 | 
             
                                    }
         | 
| 175 210 | 
             
                                }
         | 
| 176 211 | 
             
                                lgr.info(404, url.pathname, req.method, 'not found');
         | 
| 177 | 
            -
                                return  | 
| 212 | 
            +
                                return httpMessage(404, 'not found');
         | 
| 178 213 | 
             
                            }
         | 
| 179 214 | 
             
                        });
         | 
| 180 215 | 
             
                    },
         | 
| 181 216 | 
             
                }
         | 
| 182 217 | 
             
            }
         | 
| 183 218 |  | 
| 219 | 
            +
             | 
| 184 220 | 
             
            export { router, json, file, extract, html }
         | 
    
        package/package.json
    CHANGED
    
    
    
        package/tests/router.test.ts
    CHANGED
    
    | @@ -1,8 +1,6 @@ | |
| 1 1 | 
             
            import { describe, test, expect } from 'bun:test';
         | 
| 2 | 
            -
            import {  | 
| 2 | 
            +
            import { extract } from '..';
         | 
| 3 3 | 
             
            import { Context, Route } from '../lib/router/router.d';
         | 
| 4 | 
            -
            import { logger } from '../lib/logger/logger';
         | 
| 5 | 
            -
            import { color } from '../lib/logger/color';
         | 
| 6 4 |  | 
| 7 5 | 
             
            describe('URL Params', () => {
         | 
| 8 6 | 
             
                test('/user/:name', () => {
         | 
| @@ -15,7 +13,6 @@ describe('URL Params', () => { | |
| 15 13 | 
             
                    const ctx: Context = {
         | 
| 16 14 | 
             
                        request: new Request('http://localhost:3000/user/foo'),
         | 
| 17 15 | 
             
                        params: new Map(),
         | 
| 18 | 
            -
                        fs: new Map(),
         | 
| 19 16 | 
             
                    };
         | 
| 20 17 |  | 
| 21 18 | 
             
                    const extractor = extract(route, ctx);
         | 
| @@ -36,7 +33,6 @@ describe('URL Params', () => { | |
| 36 33 | 
             
                    const ctx: Context = {
         | 
| 37 34 | 
             
                        request: new Request('http://localhost:3000/user/foo/123'),
         | 
| 38 35 | 
             
                        params: new Map(),
         | 
| 39 | 
            -
                        fs: new Map(),
         | 
| 40 36 | 
             
                    };
         | 
| 41 37 |  | 
| 42 38 | 
             
                    const extractor = extract(route, ctx);
         | 
| @@ -60,7 +56,6 @@ describe('URL Params', () => { | |
| 60 56 | 
             
                    const ctx: Context = {
         | 
| 61 57 | 
             
                        request: new Request('http://localhost:3000/foo'),
         | 
| 62 58 | 
             
                        params: new Map(),
         | 
| 63 | 
            -
                        fs: new Map(),
         | 
| 64 59 | 
             
                    }
         | 
| 65 60 |  | 
| 66 61 | 
             
                    const url = new URL(ctx.request.url);
         | 
| @@ -72,7 +67,7 @@ describe('URL Params', () => { | |
| 72 67 | 
             
            describe('Router', () => {
         | 
| 73 68 | 
             
                test('Serve', async () => {
         | 
| 74 69 | 
             
                    const proc = Bun.spawn(['./tests/serve.test.sh'], {
         | 
| 75 | 
            -
                        onExit: ( | 
| 70 | 
            +
                        onExit: (_proc, _exitCode, _signalCode , error) => {
         | 
| 76 71 | 
             
                            if (error) console.error(error);     
         | 
| 77 72 | 
             
                        },
         | 
| 78 73 | 
             
                    });
         | 
| @@ -101,3 +96,4 @@ describe('Router', () => { | |
| 101 96 | 
             
                    proc.kill(0);
         | 
| 102 97 | 
             
                });
         | 
| 103 98 | 
             
            });
         | 
| 99 | 
            +
             |