move to #, move things to const, move types
All checks were successful
Code quality checks / biome (push) Successful in 23s

This commit is contained in:
creations 2025-06-15 17:59:18 -04:00
parent 908005dad5
commit c35b767b87
Signed by: creations
GPG key ID: 8F553AA4320FC711
9 changed files with 89 additions and 36 deletions

View file

@ -0,0 +1,3 @@
export * from "./server";
export const requiredVariables = ["HOST", "PORT"];

View file

@ -0,0 +1,6 @@
const reqLoggerIgnores = {
ignoredStartsWith: ["/public"],
ignoredPaths: ["/favicon.ico"],
};
export { reqLoggerIgnores };

View file

@ -1,4 +1,5 @@
import { echo } from "@atums/echo"; import { echo } from "@atums/echo";
import { requiredVariables } from "#environment/constants";
const environment: Environment = { const environment: Environment = {
port: Number.parseInt(process.env.PORT || "8080", 10), port: Number.parseInt(process.env.PORT || "8080", 10),
@ -8,8 +9,6 @@ const environment: Environment = {
}; };
function verifyRequiredVariables(): void { function verifyRequiredVariables(): void {
const requiredVariables = ["HOST", "PORT"];
let hasError = false; let hasError = false;
for (const key of requiredVariables) { for (const key of requiredVariables) {

View file

@ -1,7 +1,7 @@
import { echo } from "@atums/echo"; import { echo } from "@atums/echo";
import { verifyRequiredVariables } from "@config"; import { verifyRequiredVariables } from "#environment";
import { serverHandler } from "@server"; import { serverHandler } from "#server";
async function main(): Promise<void> { async function main(): Promise<void> {
verifyRequiredVariables(); verifyRequiredVariables();

View file

@ -1,13 +1,14 @@
import { resolve } from "node:path"; import { resolve } from "node:path";
import { Echo, echo } from "@atums/echo"; import { Echo, echo } from "@atums/echo";
import { environment } from "@config";
import { webSocketHandler } from "@websocket";
import { import {
type BunFile, type BunFile,
FileSystemRouter, FileSystemRouter,
type MatchedRoute, type MatchedRoute,
type Server, type Server,
} from "bun"; } from "bun";
import { environment } from "#environment";
import { reqLoggerIgnores } from "#environment/constants";
import { webSocketHandler } from "#websocket";
class ServerHandler { class ServerHandler {
private router: FileSystemRouter; private router: FileSystemRouter;
@ -105,12 +106,11 @@ class ServerHandler {
): void { ): void {
const pathname = new URL(request.url).pathname; const pathname = new URL(request.url).pathname;
const ignoredStartsWith: string[] = ["/public"];
const ignoredPaths: string[] = ["/favicon.ico"];
if ( if (
ignoredStartsWith.some((prefix) => pathname.startsWith(prefix)) || reqLoggerIgnores.ignoredStartsWith.some((prefix) =>
ignoredPaths.includes(pathname) pathname.startsWith(prefix),
) ||
reqLoggerIgnores.ignoredPaths.includes(pathname)
) { ) {
return; return;
} }
@ -168,7 +168,7 @@ class ServerHandler {
} }
const match: MatchedRoute | null = this.router.match(request); const match: MatchedRoute | null = this.router.match(request);
let requestBody: unknown = {}; let requestBody: unknown = null;
if (match) { if (match) {
const { filePath, params, query } = match; const { filePath, params, query } = match;
@ -185,7 +185,7 @@ class ServerHandler {
actualContentType === "application/json" actualContentType === "application/json"
) { ) {
try { try {
requestBody = await request.json(); requestBody = (await request.json()) as Record<string, unknown>;
} catch { } catch {
requestBody = {}; requestBody = {};
} }
@ -194,10 +194,49 @@ class ServerHandler {
actualContentType === "multipart/form-data" actualContentType === "multipart/form-data"
) { ) {
try { 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 { } catch {
requestBody = {}; 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 ( if (
@ -250,7 +289,7 @@ class ServerHandler {
} else { } else {
extendedRequest.params = params; extendedRequest.params = params;
extendedRequest.query = query; extendedRequest.query = query;
extendedRequest.body = requestBody; extendedRequest.requestBody = requestBody;
response = await routeModule.handler(extendedRequest, server); response = await routeModule.handler(extendedRequest, server);

View file

@ -2,8 +2,7 @@
"compilerOptions": { "compilerOptions": {
"baseUrl": "./", "baseUrl": "./",
"paths": { "paths": {
"@*": ["src/*"], "#*": ["src/*"]
"@config": ["config/index.ts"]
}, },
"typeRoots": ["./types", "./node_modules/@types"], "typeRoots": ["./types", "./node_modules/@types"],
"lib": ["ESNext", "DOM"], "lib": ["ESNext", "DOM"],

16
types/routes.d.ts vendored
View file

@ -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> | Response;
type RouteModule = {
handler: handler;
routeDef: RouteDef;
};

29
types/server.d.ts vendored
View file

@ -1,9 +1,32 @@
type Query = Record<string, string>; type QueryParams = Record<string, string>;
type Params = Record<string, string>;
interface ExtendedRequest extends Request { interface ExtendedRequest extends Request {
startPerf: number; startPerf: number;
query: Query; query: Query;
params: Params; 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> | Response;
type RouteModule = {
handler: Handler;
routeDef: RouteDef;
};