diff --git a/src/environment/constants/index.ts b/src/environment/constants/index.ts new file mode 100644 index 0000000..76a630d --- /dev/null +++ b/src/environment/constants/index.ts @@ -0,0 +1,3 @@ +export * from "./server"; + +export const requiredVariables = ["HOST", "PORT"]; diff --git a/src/environment/constants/server.ts b/src/environment/constants/server.ts new file mode 100644 index 0000000..aedf92e --- /dev/null +++ b/src/environment/constants/server.ts @@ -0,0 +1,6 @@ +const reqLoggerIgnores = { + ignoredStartsWith: ["/public"], + ignoredPaths: ["/favicon.ico"], +}; + +export { reqLoggerIgnores }; diff --git a/config/index.ts b/src/environment/index.ts similarity index 91% rename from config/index.ts rename to src/environment/index.ts index 543cd38..f2dc805 100644 --- a/config/index.ts +++ b/src/environment/index.ts @@ -1,4 +1,5 @@ import { echo } from "@atums/echo"; +import { requiredVariables } from "#environment/constants"; const environment: Environment = { port: Number.parseInt(process.env.PORT || "8080", 10), @@ -8,8 +9,6 @@ const environment: Environment = { }; function verifyRequiredVariables(): void { - const requiredVariables = ["HOST", "PORT"]; - let hasError = false; for (const key of requiredVariables) { diff --git a/src/index.ts b/src/index.ts index f095bcd..835ac45 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,7 +1,7 @@ import { echo } from "@atums/echo"; -import { verifyRequiredVariables } from "@config"; -import { serverHandler } from "@server"; +import { verifyRequiredVariables } from "#environment"; +import { serverHandler } from "#server"; async function main(): Promise { verifyRequiredVariables(); diff --git a/src/server.ts b/src/server.ts index 192af15..3ef678f 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,13 +1,14 @@ import { resolve } from "node:path"; import { Echo, echo } from "@atums/echo"; -import { environment } from "@config"; -import { webSocketHandler } from "@websocket"; import { type BunFile, FileSystemRouter, type MatchedRoute, type Server, } from "bun"; +import { environment } from "#environment"; +import { reqLoggerIgnores } from "#environment/constants"; +import { webSocketHandler } from "#websocket"; class ServerHandler { private router: FileSystemRouter; @@ -105,12 +106,11 @@ class ServerHandler { ): void { const pathname = new URL(request.url).pathname; - const ignoredStartsWith: string[] = ["/public"]; - const ignoredPaths: string[] = ["/favicon.ico"]; - if ( - ignoredStartsWith.some((prefix) => pathname.startsWith(prefix)) || - ignoredPaths.includes(pathname) + reqLoggerIgnores.ignoredStartsWith.some((prefix) => + pathname.startsWith(prefix), + ) || + reqLoggerIgnores.ignoredPaths.includes(pathname) ) { return; } @@ -168,7 +168,7 @@ class ServerHandler { } const match: MatchedRoute | null = this.router.match(request); - let requestBody: unknown = {}; + let requestBody: unknown = null; if (match) { const { filePath, params, query } = match; @@ -185,7 +185,7 @@ class ServerHandler { actualContentType === "application/json" ) { try { - requestBody = await request.json(); + requestBody = (await request.json()) as Record; } catch { requestBody = {}; } @@ -194,10 +194,49 @@ class ServerHandler { actualContentType === "multipart/form-data" ) { try { - requestBody = await request.formData(); + requestBody = (await request.formData()) as FormData; + } catch { + requestBody = new FormData(); + } + } else if ( + routeModule.routeDef.needsBody === "urlencoded" && + actualContentType === "application/x-www-form-urlencoded" + ) { + try { + const formData = await request.formData(); + requestBody = Object.fromEntries(formData.entries()) as Record< + string, + string + >; } catch { requestBody = {}; } + } else if ( + routeModule.routeDef.needsBody === "text" && + actualContentType?.startsWith("text/") + ) { + try { + requestBody = (await request.text()) as string; + } catch { + requestBody = ""; + } + } else if ( + routeModule.routeDef.needsBody === "raw" || + routeModule.routeDef.needsBody === "buffer" + ) { + try { + requestBody = (await request.arrayBuffer()) as ArrayBuffer; + } catch { + requestBody = new ArrayBuffer(0); + } + } else if (routeModule.routeDef.needsBody === "blob") { + try { + requestBody = (await request.blob()) as Blob; + } catch { + requestBody = new Blob(); + } + } else if (routeModule.routeDef.needsBody) { + requestBody = null; } if ( @@ -250,7 +289,7 @@ class ServerHandler { } else { extendedRequest.params = params; extendedRequest.query = query; - extendedRequest.body = requestBody; + extendedRequest.requestBody = requestBody; response = await routeModule.handler(extendedRequest, server); diff --git a/tsconfig.json b/tsconfig.json index cf343ea..32b9468 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,8 +2,7 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@*": ["src/*"], - "@config": ["config/index.ts"] + "#*": ["src/*"] }, "typeRoots": ["./types", "./node_modules/@types"], "lib": ["ESNext", "DOM"], diff --git a/types/config.d.ts b/types/environment.d.ts similarity index 100% rename from types/config.d.ts rename to types/environment.d.ts diff --git a/types/routes.d.ts b/types/routes.d.ts deleted file mode 100644 index d2a8633..0000000 --- a/types/routes.d.ts +++ /dev/null @@ -1,16 +0,0 @@ -type RouteDef = { - method: string | string[]; - accepts: string | null | string[]; - returns: string; - needsBody?: "multipart" | "json"; -}; - -type handler = ( - request: Request | ExtendedRequest, - server: Server, -) => Promise | Response; - -type RouteModule = { - handler: handler; - routeDef: RouteDef; -}; diff --git a/types/server.d.ts b/types/server.d.ts index 85a4839..796cd74 100644 --- a/types/server.d.ts +++ b/types/server.d.ts @@ -1,9 +1,32 @@ -type Query = Record; -type Params = Record; +type QueryParams = Record; interface ExtendedRequest extends Request { startPerf: number; query: Query; params: Params; - body: unknown; + requestBody: unknown; } + +type RouteDef = { + method: string | string[]; + accepts: string | null | string[]; + returns: string; + needsBody?: + | "multipart" + | "json" + | "urlencoded" + | "text" + | "raw" + | "buffer" + | "blob"; +}; + +type Handler = ( + request: ExtendedRequest, + server: Server, +) => Promise | Response; + +type RouteModule = { + handler: Handler; + routeDef: RouteDef; +};