bun-router 0.7.3 → 0.7.4-experimental.11
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/.eslintrc.json +35 -0
- package/README.md +28 -0
- package/bun.lockb +0 -0
- package/examples/basic.ts +4 -4
- package/examples/dynamic.ts +8 -8
- package/examples/logger.ts +2 -2
- package/examples/ssr/index.ts +8 -0
- package/examples/ssr/pages/foo.tsx +7 -0
- package/examples/ssr/pages/home.tsx +7 -0
- package/examples/static.ts +1 -0
- package/examples/todo.ts +22 -22
- package/examples/tsx/components/user.tsx +7 -0
- package/examples/tsx/index.ts +20 -0
- package/lib/fs/filetree.ts +75 -0
- package/lib/fs/fsys.ts +25 -11
- package/lib/http/http.ts +81 -67
- package/lib/http/status.ts +64 -64
- package/lib/logger/color.ts +25 -25
- package/lib/logger/logger.d.ts +1 -1
- package/lib/logger/logger.ts +86 -70
- package/lib/router/context.ts +50 -37
- package/lib/router/routeTree.ts +86 -0
- package/lib/router/router.d.ts +6 -4
- package/lib/router/router.ts +122 -89
- package/package.json +13 -2
- package/tests/router.test.ts +21 -20
- package/tsconfig.json +5 -3
- package/examples/db.ts +0 -10
- package/lib/router/tree.ts +0 -63
- package/lib/util/strings.ts +0 -3
    
        package/.eslintrc.json
    ADDED
    
    | @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            {
         | 
| 2 | 
            +
                "env": {
         | 
| 3 | 
            +
                    "browser": true,
         | 
| 4 | 
            +
                    "es2021": true
         | 
| 5 | 
            +
                },
         | 
| 6 | 
            +
                "extends": [
         | 
| 7 | 
            +
                    "eslint:recommended",
         | 
| 8 | 
            +
                    "plugin:@typescript-eslint/recommended",
         | 
| 9 | 
            +
                    "plugin:react/recommended"
         | 
| 10 | 
            +
                ],
         | 
| 11 | 
            +
                "parser": "@typescript-eslint/parser",
         | 
| 12 | 
            +
                "parserOptions": {
         | 
| 13 | 
            +
                    "ecmaVersion": "latest",
         | 
| 14 | 
            +
                    "sourceType": "module"
         | 
| 15 | 
            +
                },
         | 
| 16 | 
            +
                "plugins": [
         | 
| 17 | 
            +
                    "@typescript-eslint",
         | 
| 18 | 
            +
                    "react"
         | 
| 19 | 
            +
                ],
         | 
| 20 | 
            +
                "rules": {
         | 
| 21 | 
            +
                    "indent": [
         | 
| 22 | 
            +
                        "warn",
         | 
| 23 | 
            +
                        "tab"
         | 
| 24 | 
            +
                    ],
         | 
| 25 | 
            +
                    "quotes": [
         | 
| 26 | 
            +
                        "error",
         | 
| 27 | 
            +
                        "single"
         | 
| 28 | 
            +
                    ],
         | 
| 29 | 
            +
                    "semi": [
         | 
| 30 | 
            +
                        "error",
         | 
| 31 | 
            +
                        "always"
         | 
| 32 | 
            +
                    ],
         | 
| 33 | 
            +
                    "no-control-regex": "off"
         | 
| 34 | 
            +
                }
         | 
| 35 | 
            +
            }
         | 
    
        package/README.md
    CHANGED
    
    | @@ -29,6 +29,10 @@ router.serve(); | |
| 29 29 | 
             
            ```
         | 
| 30 30 |  | 
| 31 31 | 
             
            ##### Static
         | 
| 32 | 
            +
            Read a directory and serve it's contents. `.tsx` and `.html` files are rendered by default, everything else is served, including the extension. 
         | 
| 33 | 
            +
             | 
| 34 | 
            +
            Ex: `/assets/gopher.png` would serve a `.png` image. `/home` would be `.tsx` or `.html` depending on extension.
         | 
| 35 | 
            +
             | 
| 32 36 | 
             
            ```ts
         | 
| 33 37 | 
             
            import { Router } from 'bun-router';
         | 
| 34 38 |  | 
| @@ -53,4 +57,28 @@ router.post('/register', ctx => { | |
| 53 57 |  | 
| 54 58 | 
             
            ```
         | 
| 55 59 |  | 
| 60 | 
            +
            ##### JSX
         | 
| 61 | 
            +
            ```tsx
         | 
| 62 | 
            +
            // ./pages/home.tsx
         | 
| 63 | 
            +
            export default const Home = (title: string) => {
         | 
| 64 | 
            +
                return (
         | 
| 65 | 
            +
                    <main>
         | 
| 66 | 
            +
                        <h1>{ title }</h1>
         | 
| 67 | 
            +
                    </main>
         | 
| 68 | 
            +
                );
         | 
| 69 | 
            +
            };
         | 
| 70 | 
            +
            ```
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            ```ts
         | 
| 73 | 
            +
            // ./index.ts
         | 
| 74 | 
            +
            import { Router } from 'bun-router';
         | 
| 75 | 
            +
            import Home from './pages/home';
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            const router = Router();
         | 
| 78 | 
            +
             | 
| 79 | 
            +
            router.get('/', ctx => ctx.render(Home('Hello World')))
         | 
| 80 | 
            +
             | 
| 81 | 
            +
            router.serve();
         | 
| 82 | 
            +
            ```
         | 
| 83 | 
            +
             | 
| 56 84 |  | 
    
        package/bun.lockb
    CHANGED
    
    | Binary file | 
    
        package/examples/basic.ts
    CHANGED
    
    | @@ -1,13 +1,13 @@ | |
| 1 1 | 
             
            import { Router, http } from '..';
         | 
| 2 2 |  | 
| 3 | 
            -
            const router = Router();
         | 
| 3 | 
            +
            const router = Router(3000, {enableFileLogging: false});
         | 
| 4 4 |  | 
| 5 5 | 
             
            router.add('/', 'GET', () => http.json(200, 'ok'));
         | 
| 6 6 |  | 
| 7 7 | 
             
            router.add('/user/:name', 'GET', (ctx) => {
         | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 8 | 
            +
            	const name = ctx.params.get('name');
         | 
| 9 | 
            +
            	if (!name) return http.json(500, 'no name');
         | 
| 10 | 
            +
            	return http.json(200, name);
         | 
| 11 11 | 
             
            });
         | 
| 12 12 |  | 
| 13 13 | 
             
            router.serve();
         | 
    
        package/examples/dynamic.ts
    CHANGED
    
    | @@ -4,16 +4,16 @@ import { Context } from '../lib/router/router.d'; | |
| 4 4 | 
             
            const home = () => new Response('Welcome Home', { status: 200 });
         | 
| 5 5 |  | 
| 6 6 | 
             
            const subreddit = (ctx: Context) => {
         | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
            }
         | 
| 7 | 
            +
            	const sub = ctx.params.get('subreddit');
         | 
| 8 | 
            +
            	if (!sub) return http.json(400, { error: 'no subreddit provided' });
         | 
| 9 | 
            +
            	return http.json(200, { subreddit: sub });
         | 
| 10 | 
            +
            };
         | 
| 11 11 |  | 
| 12 12 | 
             
            const user = (ctx: Context) => {
         | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
            }
         | 
| 13 | 
            +
            	const user = ctx.params.get('user');
         | 
| 14 | 
            +
            	if (!user) return http.json(400, { error: 'no user provided' });
         | 
| 15 | 
            +
            	return http.json(200, { user: user });
         | 
| 16 | 
            +
            };
         | 
| 17 17 |  | 
| 18 18 | 
             
            const r = Router();
         | 
| 19 19 |  | 
    
        package/examples/logger.ts
    CHANGED
    
    
    
        package/examples/static.ts
    CHANGED
    
    
    
        package/examples/todo.ts
    CHANGED
    
    | @@ -1,45 +1,45 @@ | |
| 1 1 | 
             
            import { Router, http } from '..';
         | 
| 2 2 |  | 
| 3 3 | 
             
            const Todo = () => {
         | 
| 4 | 
            -
             | 
| 4 | 
            +
            	const list: Record<string, string> = {};
         | 
| 5 5 |  | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
            }
         | 
| 6 | 
            +
            	return {
         | 
| 7 | 
            +
            		add: (key: string, value: string) => { list[key] = value; },
         | 
| 8 | 
            +
            		get: (key: string) => list[key],
         | 
| 9 | 
            +
            		remove: (key: string) => { delete list[key]; },
         | 
| 10 | 
            +
            		size: () => Object.entries(list).length,
         | 
| 11 | 
            +
            		export: () => list,
         | 
| 12 | 
            +
            	};
         | 
| 13 | 
            +
            };
         | 
| 14 14 |  | 
| 15 15 | 
             
            const todo = Todo();
         | 
| 16 16 |  | 
| 17 17 | 
             
            const r = Router();
         | 
| 18 18 |  | 
| 19 19 | 
             
            r.add('/api/new', 'POST', ctx => {
         | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 20 | 
            +
            	const query = new URL(ctx.request.url).searchParams;
         | 
| 21 | 
            +
            	const key = query.get('key');
         | 
| 22 | 
            +
            	const content = query.get('content');
         | 
| 23 23 |  | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 24 | 
            +
            	if (!key || !content) return http.message(400, 'invalid query params');
         | 
| 25 | 
            +
            	ctx.logger.message(`Adding ${key} with ${content}`);
         | 
| 26 | 
            +
            	todo.add(key, content);
         | 
| 27 27 |  | 
| 28 | 
            -
             | 
| 28 | 
            +
            	return ctx.json(200, { message: 'ok' });
         | 
| 29 29 | 
             
            });
         | 
| 30 30 |  | 
| 31 31 | 
             
            r.add('/api/todo/:key', 'GET', ctx => {
         | 
| 32 | 
            -
             | 
| 33 | 
            -
             | 
| 32 | 
            +
            	const key = ctx.params.get('key');
         | 
| 33 | 
            +
            	if (!key) return http.message(400, 'invalid params');
         | 
| 34 34 |  | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 35 | 
            +
            	const content = todo.get(key);
         | 
| 36 | 
            +
            	if (!content) return http.notFound();
         | 
| 37 37 |  | 
| 38 | 
            -
             | 
| 38 | 
            +
            	return ctx.json(200, {key: key, content: content});
         | 
| 39 39 | 
             
            });
         | 
| 40 40 |  | 
| 41 41 | 
             
            r.add('/api/get/all', 'GET', ctx => {
         | 
| 42 | 
            -
             | 
| 42 | 
            +
            	return ctx.json(200, todo.export());
         | 
| 43 43 | 
             
            });
         | 
| 44 44 |  | 
| 45 45 | 
             
            r.serve();
         | 
| @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            import { Router, http } from '../../';
         | 
| 2 | 
            +
            import { User } from './components/user';
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            const router = Router();
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            router.get('/', () => {
         | 
| 7 | 
            +
            	return http.ok();
         | 
| 8 | 
            +
            });
         | 
| 9 | 
            +
             | 
| 10 | 
            +
             | 
| 11 | 
            +
            router.get('/u/:username', async ctx => {
         | 
| 12 | 
            +
            	const username = ctx.params.get('username');
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            	if (!username) return http.message(400, 'invalid username');
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            	return http.render(User(username));
         | 
| 17 | 
            +
            });
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            router.serve();
         | 
| 20 | 
            +
             | 
| @@ -0,0 +1,75 @@ | |
| 1 | 
            +
            import path from 'node:path';
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            type File = {
         | 
| 4 | 
            +
                name: string;
         | 
| 5 | 
            +
                path: string;
         | 
| 6 | 
            +
                extension: string;
         | 
| 7 | 
            +
                children: Map<string, File>;
         | 
| 8 | 
            +
                isLast: boolean;
         | 
| 9 | 
            +
            };
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            function createFile(name: string): File {
         | 
| 12 | 
            +
            	return {
         | 
| 13 | 
            +
            		name,
         | 
| 14 | 
            +
            		path: '',
         | 
| 15 | 
            +
            		extension: '',
         | 
| 16 | 
            +
            		children: new Map(),
         | 
| 17 | 
            +
            		isLast: false,
         | 
| 18 | 
            +
            	};
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            const FileTree = (dir: string) => {
         | 
| 22 | 
            +
            	const root = createFile(dir);
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            	const addFile = (filepath: string) => {
         | 
| 25 | 
            +
            		const pathParts = filepath.split(path.sep);
         | 
| 26 | 
            +
            		let current = root;
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            		for (let i = 0; i < pathParts.length; i++) {
         | 
| 29 | 
            +
            			const part = pathParts[i];
         | 
| 30 | 
            +
            			if (!current.children.has(part)) {
         | 
| 31 | 
            +
            				current.children.set(part, createFile(part));
         | 
| 32 | 
            +
            			}
         | 
| 33 | 
            +
            			current = current.children.get(part)!;
         | 
| 34 | 
            +
            		}
         | 
| 35 | 
            +
             | 
| 36 | 
            +
            		current.isLast = true;
         | 
| 37 | 
            +
            		current.path = filepath;
         | 
| 38 | 
            +
            		current.extension = path.extname(filepath);
         | 
| 39 | 
            +
            	};
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            	const getFilesByExtension = (extension: string): string[] => {
         | 
| 42 | 
            +
            		let current = root;
         | 
| 43 | 
            +
            		const files: string[] = [];
         | 
| 44 | 
            +
             | 
| 45 | 
            +
            		for (const [name, file] of current.children) {
         | 
| 46 | 
            +
            			if (file.extension === extension) {
         | 
| 47 | 
            +
            				files.push(file.path);
         | 
| 48 | 
            +
            			}
         | 
| 49 | 
            +
            			current = current.children.get(name)!;
         | 
| 50 | 
            +
            		}
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            		return files;
         | 
| 53 | 
            +
            	};
         | 
| 54 | 
            +
             | 
| 55 | 
            +
            	const getFileByName = (filepath: string): string | undefined => {
         | 
| 56 | 
            +
            		let current = root;
         | 
| 57 | 
            +
            		const pathParts = filepath.split(path.sep);
         | 
| 58 | 
            +
            		for (let i = 0; i < pathParts.length; i++) {
         | 
| 59 | 
            +
            			const part = pathParts[i];
         | 
| 60 | 
            +
            			if (current.children.has(part)) {
         | 
| 61 | 
            +
            				current = current.children.get(part)!;
         | 
| 62 | 
            +
            			} else {
         | 
| 63 | 
            +
            				return;
         | 
| 64 | 
            +
            			}
         | 
| 65 | 
            +
            		}
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            		if (!current.isLast) return;
         | 
| 68 | 
            +
             | 
| 69 | 
            +
            		return current.path;
         | 
| 70 | 
            +
            	};
         | 
| 71 | 
            +
                
         | 
| 72 | 
            +
            	return { addFile, getFilesByExtension, getFileByName };
         | 
| 73 | 
            +
            };
         | 
| 74 | 
            +
             | 
| 75 | 
            +
            export { FileTree };
         | 
    
        package/lib/fs/fsys.ts
    CHANGED
    
    | @@ -1,25 +1,39 @@ | |
| 1 | 
            -
            import { BunFile } from  | 
| 1 | 
            +
            import { BunFile } from 'bun';
         | 
| 2 2 | 
             
            import fs from 'node:fs/promises';
         | 
| 3 3 | 
             
            import path from 'path';
         | 
| 4 4 |  | 
| 5 5 | 
             
            // check if the file path is a directory
         | 
| 6 6 | 
             
            const isDir = async (fp: string): Promise<boolean> => (await fs.lstat(fp)).isDirectory();
         | 
| 7 7 |  | 
| 8 | 
            +
            // recursively read a directory and call a handler function on each file
         | 
| 8 9 | 
             
            async function readDir(dirpath: string, handler: (filepath: string, entry: BunFile) => void) {
         | 
| 9 | 
            -
             | 
| 10 | 
            +
            	const files = await fs.readdir(dirpath);
         | 
| 10 11 |  | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 12 | 
            +
            	for (const file of files) {
         | 
| 13 | 
            +
            		const bunFile = Bun.file(file);
         | 
| 13 14 |  | 
| 14 | 
            -
             | 
| 15 | 
            +
            		if (typeof bunFile.name === 'undefined') return;
         | 
| 15 16 |  | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 17 | 
            +
            		const fp = path.join(dirpath, bunFile.name);
         | 
| 18 | 
            +
            		const isdir = await isDir(fp);
         | 
| 18 19 |  | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 20 | 
            +
            		if (isdir) await readDir(fp, handler);
         | 
| 21 | 
            +
            		else handler(fp, bunFile);
         | 
| 22 | 
            +
            	}
         | 
| 22 23 | 
             
            }
         | 
| 23 24 |  | 
| 25 | 
            +
            // resolve module paths relative to the current working directory
         | 
| 26 | 
            +
            function resolveModulePath(module: string) {
         | 
| 27 | 
            +
            	return path.join(process.cwd(), module);
         | 
| 28 | 
            +
            }
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            function exists(filepath: string): boolean {
         | 
| 31 | 
            +
            	try {
         | 
| 32 | 
            +
            		fs.access(filepath);
         | 
| 33 | 
            +
            		return true;
         | 
| 34 | 
            +
            	} catch (err) {
         | 
| 35 | 
            +
            		return false;
         | 
| 36 | 
            +
            	}
         | 
| 37 | 
            +
            }
         | 
| 24 38 |  | 
| 25 | 
            -
            export { readDir }
         | 
| 39 | 
            +
            export { readDir, resolveModulePath, exists };
         | 
    
        package/lib/http/http.ts
    CHANGED
    
    | @@ -1,77 +1,91 @@ | |
| 1 | 
            -
             | 
| 1 | 
            +
            /* eslint-disable @typescript-eslint/no-explicit-any */
         | 
| 2 | 
            +
            import { httpStatusCodes } from './status';
         | 
| 3 | 
            +
            import { ReactNode } from 'react';
         | 
| 4 | 
            +
            import { renderToReadableStream } from 'react-dom/server';
         | 
| 2 5 |  | 
| 6 | 
            +
            // http is a collection of functions that return a Response
         | 
| 7 | 
            +
            // object with the appropriate status code and content type
         | 
| 8 | 
            +
            // e.g. http.ok() returns a 200 response
         | 
| 3 9 | 
             
            const http = {
         | 
| 4 | 
            -
             | 
| 5 | 
            -
             | 
| 6 | 
            -
             | 
| 7 | 
            -
             | 
| 8 | 
            -
             | 
| 9 | 
            -
             | 
| 10 | 
            -
             | 
| 11 | 
            -
             | 
| 12 | 
            -
             | 
| 13 | 
            -
             | 
| 14 | 
            -
             | 
| 15 | 
            -
             | 
| 16 | 
            -
             | 
| 17 | 
            -
             | 
| 18 | 
            -
             | 
| 19 | 
            -
             | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
             | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
             | 
| 26 | 
            -
             | 
| 27 | 
            -
             | 
| 10 | 
            +
            	ok: async (msg?: string): Promise<Response> => {
         | 
| 11 | 
            +
            		return Promise.resolve(new Response(msg ?? httpStatusCodes[200], {
         | 
| 12 | 
            +
            			status: 200,
         | 
| 13 | 
            +
            			statusText: httpStatusCodes[200],
         | 
| 14 | 
            +
            		}));
         | 
| 15 | 
            +
            	},
         | 
| 16 | 
            +
            	json: async (statusCode: number, data: any): Promise<Response> => {
         | 
| 17 | 
            +
            		const jsonString = JSON.stringify(data);
         | 
| 18 | 
            +
            		return Promise.resolve(new Response(jsonString, {
         | 
| 19 | 
            +
            			status: statusCode,
         | 
| 20 | 
            +
            			statusText: httpStatusCodes[statusCode],
         | 
| 21 | 
            +
            			headers: {'Content-Type': 'application/json'},
         | 
| 22 | 
            +
            		}));
         | 
| 23 | 
            +
            	},
         | 
| 24 | 
            +
            	html: async (statusCode: number, content: string): Promise<Response> => {
         | 
| 25 | 
            +
            		return Promise.resolve(new Response(content, {
         | 
| 26 | 
            +
            			status: statusCode,
         | 
| 27 | 
            +
            			statusText: httpStatusCodes[statusCode],
         | 
| 28 | 
            +
            			headers: {'Content-Type': 'text/html; charset=utf-8'}
         | 
| 29 | 
            +
            		}));
         | 
| 30 | 
            +
            	},
         | 
| 31 | 
            +
            	file: async (statusCode: number, fp: string): Promise<Response> => {
         | 
| 32 | 
            +
            		const file = Bun.file(fp);
         | 
| 33 | 
            +
            		const exists = await file.exists();
         | 
| 28 34 |  | 
| 29 | 
            -
             | 
| 35 | 
            +
            		if (!exists) return http.notFound(`File not found: ${fp}`);
         | 
| 30 36 |  | 
| 31 | 
            -
             | 
| 32 | 
            -
             | 
| 37 | 
            +
            		const content = await file.arrayBuffer();
         | 
| 38 | 
            +
            		if (!content) return http.noContent();
         | 
| 33 39 |  | 
| 34 | 
            -
             | 
| 40 | 
            +
            		let contentType = 'text/html; charset=utf-9';
         | 
| 35 41 |  | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 42 | 
            +
            		if (file.type.includes('image'))
         | 
| 43 | 
            +
            			contentType = file.type + '; charset=utf-8';
         | 
| 38 44 |  | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 45 | 
            -
             | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
             | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
             | 
| 53 | 
            -
             | 
| 54 | 
            -
             | 
| 45 | 
            +
            		return Promise.resolve(new Response(content, {
         | 
| 46 | 
            +
            			status: statusCode,
         | 
| 47 | 
            +
            			statusText: httpStatusCodes[statusCode],
         | 
| 48 | 
            +
            			headers: { 'Content-Type': contentType}
         | 
| 49 | 
            +
            		}));
         | 
| 50 | 
            +
            	},
         | 
| 51 | 
            +
            	render: async (component: ReactNode): Promise<Response> => {
         | 
| 52 | 
            +
            		const stream = await renderToReadableStream(component);
         | 
| 53 | 
            +
            		return new Response(stream, { 
         | 
| 54 | 
            +
            			status: 200, 
         | 
| 55 | 
            +
            			statusText: httpStatusCodes[200], 
         | 
| 56 | 
            +
            			headers: {'Content-Type': 'text/html; charset=utf-8'}
         | 
| 57 | 
            +
            		});
         | 
| 58 | 
            +
            	},
         | 
| 59 | 
            +
            	noContent: async (): Promise<Response> => Promise.resolve(new Response('no content', {
         | 
| 60 | 
            +
            		status: 204,
         | 
| 61 | 
            +
            		statusText: 'no content',
         | 
| 62 | 
            +
            	})),
         | 
| 63 | 
            +
            	notFound: async(msg?: string): Promise<Response> => {
         | 
| 64 | 
            +
            		const response = new Response(msg ?? 'not found', {
         | 
| 65 | 
            +
            			status: 404,
         | 
| 66 | 
            +
            			statusText: httpStatusCodes[404],
         | 
| 67 | 
            +
            			headers: {'Content-Type': 'text/html'},
         | 
| 68 | 
            +
            		});
         | 
| 55 69 |  | 
| 56 | 
            -
             | 
| 57 | 
            -
             | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
             | 
| 61 | 
            -
             | 
| 62 | 
            -
             | 
| 63 | 
            -
             | 
| 70 | 
            +
            		return Promise.resolve(response);
         | 
| 71 | 
            +
            	},
         | 
| 72 | 
            +
            	methodNotAllowed: async (msg?: string): Promise<Response> => {
         | 
| 73 | 
            +
            		const response = new Response(msg ?? 'method not allowed', {
         | 
| 74 | 
            +
            			status: 405,
         | 
| 75 | 
            +
            			statusText: httpStatusCodes[405],
         | 
| 76 | 
            +
            			headers: {'Content-Type': 'text/html'},
         | 
| 77 | 
            +
            		});
         | 
| 64 78 |  | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 68 | 
            -
             | 
| 69 | 
            -
             | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
             | 
| 73 | 
            -
             | 
| 74 | 
            -
             | 
| 75 | 
            -
            }
         | 
| 79 | 
            +
            		return Promise.resolve(response);
         | 
| 80 | 
            +
            	},
         | 
| 81 | 
            +
            	message:  async (status: number, msg?: string): Promise<Response> => {
         | 
| 82 | 
            +
            		const response = new Response(msg ?? '?', {
         | 
| 83 | 
            +
            			status: status,
         | 
| 84 | 
            +
            			statusText: httpStatusCodes[status],
         | 
| 85 | 
            +
            			headers: {'Content-Type': 'text/html; charset-utf-8'},
         | 
| 86 | 
            +
            		});
         | 
| 87 | 
            +
            		return Promise.resolve(response);
         | 
| 88 | 
            +
            	},
         | 
| 89 | 
            +
            };
         | 
| 76 90 |  | 
| 77 | 
            -
            export { http }
         | 
| 91 | 
            +
            export { http };
         |