import { Middleware, MiddlewareHandler } from './middleware.ts';import { Router } from './router.ts';import { Data } from './data/data.ts';
import { getContentType } from './utils/getContentType.ts';
export class Server { #middleware = new Array<Middleware>(); #listeners = new Array<Middleware>(); #server: Deno.Listener | null = null
async listen(port = 8080): Promise<void> { this.#server = Deno.listen({ port });
try { for await (const conn of this.#server!) (async () => { for await (const { request, respondWith } of Deno.serveHttp(conn)) { let responded = false;
this.#run(request, (response: Response) => { if (responded) return null; respondWith(response); responded = true; }); } })(); } catch { throw new Error('Something went wrong'); } }
#run(request: Request, respond: (resonse: Response) => void) { const data = new Data(request, respond); const handlers = [...this.#middleware, ...this.#listeners]; const used = new Set<number>();
function next(index = -1): () => void { if (used.has(index) || !handlers[index + 1]) return () => undefined
return async () => { if (!used.has(index)) await handlers[index + 1](data, next(index + 1)); used.add(index); next(index + 1)(); } }
next()(); }
on(route = '/*', method = 'GET'): (...handlers: MiddlewareHandler[]) => Server { return (...handlers: MiddlewareHandler[]) => { handlers.forEach(handler => this.#listeners.push(Middleware(method, route, handler)));
return this; }; } use(route = '/*', method = 'ANY') { return (...handlers: MiddlewareHandler[] | Router[]): Server => { handlers[0] instanceof Router ? (handlers as Router[]).forEach(router => this.#middleware.push(...router.handlers())) : (handlers as MiddlewareHandler[]).forEach(handler => this.#middleware.push(Middleware(method, route, handler))); return this; } }
static(route = '/*', root = '/') { route = `/${route}/*`.replace(/\/+/g, '/'); this.use(route, 'GET')(async ({ request, respond }) => { const { path: upath } = request.url;
const base = route.split('/').filter((item) => item != '*' && item != ''); const rest = upath.replace(/^\//, '').split('/').filter((_, index) => index > base.length); const path = `${Deno.cwd()}/${root}/${rest.join('/')}`.replace(/(\/+|\\)/g, '/');
await fetch(`file://${path}`) .then(async (file) => { respond({ body: await file.text(), headers: { 'content-type': getContentType(path) } }); }) .catch(() => respond({ status: 404 })); });
return this; }
close() { this.#server?.close(); }}