diff --git a/.vscode/settings.json b/.vscode/settings.json index 25117cc..4a841f7 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "github-enterprise.uri": "https://git.creations.works" + "github-enterprise.uri": "https://git.creations.works" } diff --git a/biome.json b/biome.json new file mode 100644 index 0000000..3c4d4f1 --- /dev/null +++ b/biome.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.9.4/schema.json", + "vcs": { + "enabled": true, + "clientKind": "git", + "useIgnoreFile": false + }, + "files": { + "ignoreUnknown": false, + "ignore": [] + }, + "formatter": { + "enabled": true, + "indentStyle": "tab", + "lineEnding": "lf" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "javascript": { + "formatter": { + "quoteStyle": "double", + "indentStyle": "tab", + "lineEnding": "lf", + "jsxQuoteStyle": "double", + "semicolons": "always" + } + } +} diff --git a/config/environment.ts b/config/environment.ts index c584b45..239d2c3 100644 --- a/config/environment.ts +++ b/config/environment.ts @@ -1,9 +1,8 @@ export const environment: Environment = { - port: parseInt(process.env.PORT || "8080", 10), + port: Number.parseInt(process.env.PORT || "8080", 10), host: process.env.HOST || "0.0.0.0", development: - process.env.NODE_ENV === "development" || - process.argv.includes("--dev"), + process.env.NODE_ENV === "development" || process.argv.includes("--dev"), }; export const lanyardConfig: LanyardConfig = { diff --git a/eslint.config.js b/eslint.config.js deleted file mode 100644 index d43df76..0000000 --- a/eslint.config.js +++ /dev/null @@ -1,132 +0,0 @@ -import pluginJs from "@eslint/js"; -import tseslintPlugin from "@typescript-eslint/eslint-plugin"; -import tsParser from "@typescript-eslint/parser"; -import prettier from "eslint-plugin-prettier"; -import promisePlugin from "eslint-plugin-promise"; -import simpleImportSort from "eslint-plugin-simple-import-sort"; -import unicorn from "eslint-plugin-unicorn"; -import unusedImports from "eslint-plugin-unused-imports"; -import globals from "globals"; - -/** @type {import('eslint').Linter.FlatConfig[]} */ -export default [ - { - files: ["**/*.{js,mjs,cjs}"], - languageOptions: { - globals: globals.node, - }, - ...pluginJs.configs.recommended, - plugins: { - "simple-import-sort": simpleImportSort, - "unused-imports": unusedImports, - promise: promisePlugin, - prettier: prettier, - unicorn: unicorn, - }, - rules: { - "eol-last": ["error", "always"], - "no-multiple-empty-lines": ["error", { max: 1, maxEOF: 1 }], - "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { - vars: "all", - varsIgnorePattern: "^_", - args: "after-used", - argsIgnorePattern: "^_", - }, - ], - "promise/always-return": "error", - "promise/no-return-wrap": "error", - "promise/param-names": "error", - "promise/catch-or-return": "error", - "promise/no-nesting": "warn", - "promise/no-promise-in-callback": "warn", - "promise/no-callback-in-promise": "warn", - "prettier/prettier": [ - "error", - { - useTabs: true, - tabWidth: 4, - }, - ], - indent: ["error", "tab", { SwitchCase: 1 }], - "unicorn/filename-case": [ - "error", - { - case: "camelCase", - }, - ], - }, - }, - { - files: ["**/*.{ts,tsx}"], - languageOptions: { - parser: tsParser, - globals: globals.node, - }, - plugins: { - "@typescript-eslint": tseslintPlugin, - "simple-import-sort": simpleImportSort, - "unused-imports": unusedImports, - promise: promisePlugin, - prettier: prettier, - unicorn: unicorn, - }, - rules: { - ...tseslintPlugin.configs.recommended.rules, - quotes: ["error", "double"], - "eol-last": ["error", "always"], - "no-multiple-empty-lines": ["error", { max: 1, maxEOF: 1 }], - "no-mixed-spaces-and-tabs": ["error", "smart-tabs"], - "simple-import-sort/imports": "error", - "simple-import-sort/exports": "error", - "unused-imports/no-unused-imports": "error", - "unused-imports/no-unused-vars": [ - "warn", - { - vars: "all", - varsIgnorePattern: "^_", - args: "after-used", - argsIgnorePattern: "^_", - }, - ], - "promise/always-return": "error", - "promise/no-return-wrap": "error", - "promise/param-names": "error", - "promise/catch-or-return": "error", - "promise/no-nesting": "warn", - "promise/no-promise-in-callback": "warn", - "promise/no-callback-in-promise": "warn", - "prettier/prettier": [ - "error", - { - useTabs: true, - tabWidth: 4, - }, - ], - indent: ["error", "tab", { SwitchCase: 1 }], - "unicorn/filename-case": [ - "error", - { - case: "camelCase", - }, - ], - "@typescript-eslint/explicit-function-return-type": ["error"], - "@typescript-eslint/explicit-module-boundary-types": ["error"], - "@typescript-eslint/typedef": [ - "error", - { - arrowParameter: true, - variableDeclaration: true, - propertyDeclaration: true, - memberVariableDeclaration: true, - parameter: true, - }, - ], - }, - }, -]; diff --git a/package.json b/package.json index 8d6e543..793fe71 100644 --- a/package.json +++ b/package.json @@ -5,24 +5,15 @@ "scripts": { "start": "bun run src/index.ts", "dev": "bun run --hot src/index.ts --dev", - "lint": "eslint", - "lint:fix": "bun lint --fix", + "lint": "bunx biome check", + "lint:fix": "bunx biome check --fix", "cleanup": "rm -rf logs node_modules bun.lockdb" }, "devDependencies": { - "@eslint/js": "^9.24.0", + "@biomejs/biome": "^1.9.4", "@types/bun": "^1.2.8", "@types/ejs": "^3.1.5", - "@typescript-eslint/eslint-plugin": "^8.29.0", - "@typescript-eslint/parser": "^8.29.0", - "eslint": "^9.24.0", - "eslint-plugin-prettier": "^5.2.6", - "eslint-plugin-promise": "^7.2.1", - "eslint-plugin-simple-import-sort": "^12.1.1", - "eslint-plugin-unicorn": "^58.0.0", - "eslint-plugin-unused-imports": "^4.1.4", - "globals": "^16.0.0", - "prettier": "^3.5.3" + "globals": "^16.0.0" }, "peerDependencies": { "typescript": "^5.8.3" diff --git a/public/css/error.css b/public/css/error.css index a8e591d..65dce6b 100644 --- a/public/css/error.css +++ b/public/css/error.css @@ -12,7 +12,7 @@ body { padding: 2rem; background: #1a1a1d; border-radius: 12px; - box-shadow: 0 0 20px rgba(0,0,0,0.3); + box-shadow: 0 0 20px rgba(0, 0, 0, 0.3); } .error-title { font-size: 2rem; diff --git a/public/css/index.css b/public/css/index.css index e4acc03..59e3a37 100644 --- a/public/css/index.css +++ b/public/css/index.css @@ -107,7 +107,6 @@ h1 { flex-wrap: wrap; } - .custom-status .custom-emoji { width: 20px; height: 20px; @@ -379,7 +378,7 @@ ul { align-items: center; text-align: center; padding: 1rem; - border-radius:0; + border-radius: 0; } .activity-art { diff --git a/public/js/index.js b/public/js/index.js index 35dbe32..913773e 100644 --- a/public/js/index.js +++ b/public/js/index.js @@ -1,5 +1,3 @@ -/* eslint-disable indent */ - const activityProgressMap = new Map(); function formatTime(ms) { @@ -23,19 +21,19 @@ function formatVerbose(ms) { function updateElapsedAndProgress() { const now = Date.now(); - document.querySelectorAll(".activity-timestamp").forEach((el) => { + for (const el of document.querySelectorAll(".activity-timestamp")) { const start = Number(el.dataset.start); - if (!start) return; + if (!start) continue; const elapsed = now - start; const display = el.querySelector(".elapsed"); if (display) display.textContent = `(${formatVerbose(elapsed)} ago)`; - }); + } - document.querySelectorAll(".progress-bar").forEach((bar) => { + for (const bar of document.querySelectorAll(".progress-bar")) { const start = Number(bar.dataset.start); const end = Number(bar.dataset.end); - if (!start || !end || end <= start) return; + if (!start || !end || end <= start) continue; const duration = end - start; const elapsed = Math.min(now - start, duration); @@ -46,12 +44,12 @@ function updateElapsedAndProgress() { const fill = bar.querySelector(".progress-fill"); if (fill) fill.style.width = `${progress}%`; - }); + } - document.querySelectorAll(".progress-time-labels").forEach((label) => { + for (const label of document.querySelectorAll(".progress-time-labels")) { const start = Number(label.dataset.start); const end = Number(label.dataset.end); - if (!start || !end || end <= start) return; + if (!start || !end || end <= start) continue; const isPaused = now > end; const current = isPaused ? end - start : Math.max(0, now - start); @@ -77,14 +75,14 @@ function updateElapsedAndProgress() { : formatTime(current); } if (totalEl) totalEl.textContent = formatTime(total); - }); + } } updateElapsedAndProgress(); setInterval(updateElapsedAndProgress, 1000); const head = document.querySelector("head"); -let userId = head?.dataset.userId; +const userId = head?.dataset.userId; let instanceUri = head?.dataset.instanceUri; if (userId && instanceUri) { @@ -189,10 +187,7 @@ function buildActivityHTML(activity) { ? `
${activity.buttons .map((button, index) => { - const label = - typeof button === "string" - ? button - : button.label; + const label = typeof button === "string" ? button : button.label; let url = null; if (typeof button === "object" && button.url) { url = button.url; @@ -258,9 +253,7 @@ function buildActivityHTML(activity) { function updatePresence(data) { const avatarWrapper = document.querySelector(".avatar-wrapper"); const statusIndicator = avatarWrapper?.querySelector(".status-indicator"); - const mobileIcon = avatarWrapper?.querySelector( - ".platform-icon.mobile-only", - ); + const mobileIcon = avatarWrapper?.querySelector(".platform-icon.mobile-only"); const userInfo = document.querySelector(".user-info"); const customStatus = userInfo?.querySelector(".custom-status"); diff --git a/src/helpers/char.ts b/src/helpers/char.ts index 6ecab40..17657b3 100644 --- a/src/helpers/char.ts +++ b/src/helpers/char.ts @@ -1,6 +1,6 @@ export function timestampToReadable(timestamp?: number): string { const date: Date = - timestamp && !isNaN(timestamp) ? new Date(timestamp) : new Date(); - if (isNaN(date.getTime())) return "Invalid Date"; + timestamp && !Number.isNaN(timestamp) ? new Date(timestamp) : new Date(); + if (Number.isNaN(date.getTime())) return "Invalid Date"; return date.toISOString().replace("T", " ").replace("Z", ""); } diff --git a/src/helpers/ejs.ts b/src/helpers/ejs.ts index 6b03dd0..6544009 100644 --- a/src/helpers/ejs.ts +++ b/src/helpers/ejs.ts @@ -1,5 +1,5 @@ +import { resolve } from "node:path"; import { renderFile } from "ejs"; -import { resolve } from "path"; export async function renderEjsTemplate( viewName: string | string[], diff --git a/src/helpers/lanyard.ts b/src/helpers/lanyard.ts index 258b980..de78955 100644 --- a/src/helpers/lanyard.ts +++ b/src/helpers/lanyard.ts @@ -76,7 +76,7 @@ export async function handleReadMe(data: LanyardData): Promise { return null; if (res.headers.has("content-length")) { - const size: number = parseInt( + const size: number = Number.parseInt( res.headers.get("content-length") || "0", 10, ); diff --git a/src/helpers/logger.ts b/src/helpers/logger.ts index 331be1d..4cbb12b 100644 --- a/src/helpers/logger.ts +++ b/src/helpers/logger.ts @@ -1,15 +1,15 @@ -import { environment } from "@config/environment"; -import { timestampToReadable } from "@helpers/char"; -import type { Stats } from "fs"; +import type { Stats } from "node:fs"; import { + type WriteStream, createWriteStream, existsSync, mkdirSync, statSync, - WriteStream, -} from "fs"; -import { EOL } from "os"; -import { basename, join } from "path"; +} from "node:fs"; +import { EOL } from "node:os"; +import { basename, join } from "node:path"; +import { environment } from "@config/environment"; +import { timestampToReadable } from "@helpers/char"; class Logger { private static instance: Logger; @@ -37,7 +37,7 @@ class Logger { mkdirSync(logDir, { recursive: true }); } - let addSeparator: boolean = false; + let addSeparator = false; if (existsSync(logFile)) { const fileStats: Stats = statSync(logFile); @@ -66,9 +66,9 @@ class Logger { private extractFileName(stack: string): string { const stackLines: string[] = stack.split("\n"); - let callerFile: string = ""; + let callerFile = ""; - for (let i: number = 2; i < stackLines.length; i++) { + for (let i = 2; i < stackLines.length; i++) { const line: string = stackLines[i].trim(); if (line && !line.includes("Logger.") && line.includes("(")) { callerFile = line.split("(")[1]?.split(")")[0] || ""; @@ -91,7 +91,7 @@ class Logger { return { filename, timestamp: readableTimestamp }; } - public info(message: string | string[], breakLine: boolean = false): void { + public info(message: string | string[], breakLine = false): void { const stack: string = new Error().stack || ""; const { filename, timestamp } = this.getCallerInfo(stack); @@ -110,7 +110,7 @@ class Logger { this.writeConsoleMessageColored(logMessageParts, breakLine); } - public warn(message: string | string[], breakLine: boolean = false): void { + public warn(message: string | string[], breakLine = false): void { const stack: string = new Error().stack || ""; const { filename, timestamp } = this.getCallerInfo(stack); @@ -131,7 +131,7 @@ class Logger { public error( message: string | Error | (string | Error)[], - breakLine: boolean = false, + breakLine = false, ): void { const stack: string = new Error().stack || ""; const { filename, timestamp } = this.getCallerInfo(stack); @@ -161,7 +161,7 @@ class Logger { bracketMessage2: string, message: string | string[], color: string, - breakLine: boolean = false, + breakLine = false, ): void { const stack: string = new Error().stack || ""; const { timestamp } = this.getCallerInfo(stack); @@ -189,7 +189,7 @@ class Logger { private writeConsoleMessageColored( logMessageParts: ILogMessageParts, - breakLine: boolean = false, + breakLine = false, ): void { const logMessage: string = Object.keys(logMessageParts) .map((key: string) => { diff --git a/src/index.ts b/src/index.ts index 6d2801d..60606d4 100644 --- a/src/index.ts +++ b/src/index.ts @@ -3,11 +3,7 @@ import { logger } from "@helpers/logger"; import { serverHandler } from "@/server"; async function main(): Promise { - try { - serverHandler.initialize(); - } catch (error) { - throw error; - } + serverHandler.initialize(); } main().catch((error: Error) => { diff --git a/src/routes/[id].ts b/src/routes/[id].ts index 25d8f30..b970485 100644 --- a/src/routes/[id].ts +++ b/src/routes/[id].ts @@ -29,8 +29,7 @@ async function handler(request: ExtendedRequest): Promise { } const presence: LanyardData = data.data; - const readme: string | Promise | null = - await handleReadMe(presence); + const readme: string | Promise | null = await handleReadMe(presence); const ejsTemplateData: EjsTemplateData = { title: `${presence.discord_user.username || "Unknown"}`, diff --git a/src/routes/api/colors.ts b/src/routes/api/colors.ts index d8817ca..9e05bd3 100644 --- a/src/routes/api/colors.ts +++ b/src/routes/api/colors.ts @@ -24,10 +24,7 @@ async function handler(request: ExtendedRequest): Promise { try { res = await fetch(url); } catch { - return Response.json( - { error: "Failed to fetch image" }, - { status: 500 }, - ); + return Response.json({ error: "Failed to fetch image" }, { status: 500 }); } if (!res.ok) { diff --git a/src/routes/index.ts b/src/routes/index.ts index 692fc59..faa9e61 100644 --- a/src/routes/index.ts +++ b/src/routes/index.ts @@ -28,12 +28,10 @@ async function handler(): Promise { } const presence: LanyardData = data.data; - const readme: string | Promise | null = - await handleReadMe(presence); + const readme: string | Promise | null = await handleReadMe(presence); const ejsTemplateData: EjsTemplateData = { - title: - presence.discord_user.global_name || presence.discord_user.username, + title: presence.discord_user.global_name || presence.discord_user.username, username: presence.discord_user.global_name || presence.discord_user.username, status: presence.discord_status, diff --git a/src/server.ts b/src/server.ts index 2298034..fc8d4b5 100644 --- a/src/server.ts +++ b/src/server.ts @@ -1,3 +1,4 @@ +import { resolve } from "node:path"; import { environment } from "@config/environment"; import { logger } from "@helpers/logger"; import { @@ -6,7 +7,6 @@ import { type MatchedRoute, type Serve, } from "bun"; -import { resolve } from "path"; import { webSocketHandler } from "@/websocket"; @@ -77,21 +77,16 @@ class ServerHandler { if (await file.exists()) { const fileContent: ArrayBuffer = await file.arrayBuffer(); - const contentType: string = - file.type || "application/octet-stream"; + const contentType: string = file.type || "application/octet-stream"; return new Response(fileContent, { headers: { "Content-Type": contentType }, }); - } else { - logger.warn(`File not found: ${filePath}`); - return new Response("Not Found", { status: 404 }); } + logger.warn(`File not found: ${filePath}`); + return new Response("Not Found", { status: 404 }); } catch (error) { - logger.error([ - `Error serving static file: ${pathname}`, - error as Error, - ]); + logger.error([`Error serving static file: ${pathname}`, error as Error]); return new Response("Internal Server Error", { status: 500 }); } } @@ -117,8 +112,7 @@ class ServerHandler { try { const routeModule: RouteModule = await import(filePath); - const contentType: string | null = - request.headers.get("Content-Type"); + const contentType: string | null = request.headers.get("Content-Type"); const actualContentType: string | null = contentType ? contentType.split(";")[0].trim() : null; @@ -145,9 +139,7 @@ class ServerHandler { if ( (Array.isArray(routeModule.routeDef.method) && - !routeModule.routeDef.method.includes( - request.method, - )) || + !routeModule.routeDef.method.includes(request.method)) || (!Array.isArray(routeModule.routeDef.method) && routeModule.routeDef.method !== request.method) ) { @@ -172,9 +164,7 @@ class ServerHandler { if (Array.isArray(expectedContentType)) { matchesAccepts = expectedContentType.includes("*/*") || - expectedContentType.includes( - actualContentType || "", - ); + expectedContentType.includes(actualContentType || ""); } else { matchesAccepts = expectedContentType === "*/*" || @@ -213,10 +203,7 @@ class ServerHandler { } } } catch (error: unknown) { - logger.error([ - `Error handling route ${request.url}:`, - error as Error, - ]); + logger.error([`Error handling route ${request.url}:`, error as Error]); response = Response.json( { diff --git a/src/websocket.ts b/src/websocket.ts index ce87fe8..99686e8 100644 --- a/src/websocket.ts +++ b/src/websocket.ts @@ -1,5 +1,5 @@ import { logger } from "@helpers/logger"; -import { type ServerWebSocket } from "bun"; +import type { ServerWebSocket } from "bun"; class WebSocketHandler { public handleMessage(ws: ServerWebSocket, message: string): void { @@ -20,11 +20,7 @@ class WebSocketHandler { } } - public handleClose( - ws: ServerWebSocket, - code: number, - reason: string, - ): void { + public handleClose(ws: ServerWebSocket, code: number, reason: string): void { logger.warn(`WebSocket closed with code ${code}, reason: ${reason}`); } } diff --git a/tsconfig.json b/tsconfig.json index ac5f2c7..68a5a97 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -2,28 +2,14 @@ "compilerOptions": { "baseUrl": "./", "paths": { - "@/*": [ - "src/*" - ], - "@config/*": [ - "config/*" - ], - "@types/*": [ - "types/*" - ], - "@helpers/*": [ - "src/helpers/*" - ] + "@/*": ["src/*"], + "@config/*": ["config/*"], + "@types/*": ["types/*"], + "@helpers/*": ["src/helpers/*"] }, - "typeRoots": [ - "./src/types", - "./node_modules/@types" - ], + "typeRoots": ["./src/types", "./node_modules/@types"], // Enable latest features - "lib": [ - "ESNext", - "DOM" - ], + "lib": ["ESNext", "DOM"], "target": "ESNext", "module": "ESNext", "moduleDetection": "force", @@ -41,11 +27,7 @@ // Some stricter flags (disabled by default) "noUnusedLocals": false, "noUnusedParameters": false, - "noPropertyAccessFromIndexSignature": false, + "noPropertyAccessFromIndexSignature": false }, - "include": [ - "src", - "types", - "config" - ], + "include": ["src", "types", "config"] }