From 1833002f3cb61d8d3c8d24fec462947fe587574c Mon Sep 17 00:00:00 2001 From: Jacco Flenter Date: Wed, 5 Jun 2024 14:19:06 +0200 Subject: [PATCH] Use biome for formatting for the javascript projects (#12) * Add zod for types or whatever * wip * Remove type defs from queries/decoders * Further cleanup * Remove commented out code + minor code tweak * Add basic biome setup * wip * Simplify * lint & format using biome * Refine config * Tray once more to ignore test-content * Use original scripts/test-content * Add format command * Add format command to frontend * Remove author/license from package.json * update pacakge-lock.json * Use biome 1.8.0 * enable css linter * Add linting to api Plus fix some biome complaints * Simplify biome.jsonc files And format code * Add github workflow * Fix build * Add build step for ci * Oh npm, you do like the word `run` * Skip build if no build present --------- Co-authored-by: Brett Beutell --- .github/workflows/build_frontends.yml | 35 + .gitignore | 2 + api/biome.jsonc | 18 + api/drizzle.config.ts | 16 +- api/migrate.ts | 22 +- api/package.json | 7 +- api/src/app.ts | 14 +- api/src/db/schema.ts | 12 +- api/src/index.node.ts | 6 +- api/src/lib/find-source-function.ts | 134 +- api/src/lib/types.ts | 6 +- api/src/routes/dependencies.ts | 2 +- api/src/routes/issues.ts | 16 +- api/src/routes/logs.ts | 8 +- api/src/routes/openai.ts | 2 +- api/src/routes/source.ts | 38 +- .../find-error-loc.js | 0 .../find-function-loc.ts | 0 .../function-loc-index.js | 0 .../function-loc-index.js.map | 0 .../github_issues.json | 0 api/{scripts => test-content}/index.js | 0 api/{scripts => test-content}/index.js.map | 0 api/{scripts => test-content}/mizu_logs.json | 0 api/{scripts => test-content}/seed.ts | 0 api/tsconfig.json | 50 +- biome.jsonc | 42 + client-library/biome.json | 22 - client-library/biome.jsonc | 18 + client-library/package.json | 6 +- client-library/tsconfig.json | 20 +- frontend/.eslintrc.cjs | 18 +- frontend/package.json | 2 + frontend/postcss.config.js | 2 +- frontend/src/App.tsx | 8 +- frontend/src/Layout.tsx | 126 +- frontend/src/components/ui/DataTable.tsx | 70 +- frontend/src/components/ui/accordion.tsx | 26 +- frontend/src/components/ui/badge/Badge.tsx | 11 +- frontend/src/components/ui/badge/variants.ts | 7 +- frontend/src/components/ui/breadcrumb.tsx | 48 +- frontend/src/components/ui/button/Button.tsx | 22 +- frontend/src/components/ui/button/index.tsx | 2 +- .../src/components/ui/button/varviants.ts | 6 +- frontend/src/components/ui/card.tsx | 39 +- frontend/src/components/ui/collapsible.tsx | 10 +- frontend/src/components/ui/dropdown-menu.tsx | 80 +- frontend/src/components/ui/input.tsx | 16 +- frontend/src/components/ui/separator.tsx | 18 +- frontend/src/components/ui/sheet.tsx | 54 +- frontend/src/components/ui/table.tsx | 46 +- frontend/src/components/ui/tabs.tsx | 28 +- frontend/src/components/ui/tooltip.tsx | 20 +- frontend/src/main.tsx | 14 +- .../pages/RequestDetailsPage/DataTable.tsx | 62 +- .../RequestDetailsPage/RequestDetails.tsx | 463 +- .../RequestDetailsPage/RequestDetailsPage.tsx | 39 +- .../src/pages/RequestDetailsPage/columns.tsx | 50 +- .../src/pages/RequestsPage/RequestSheet.tsx | 16 +- frontend/src/pages/RequestsPage/Requests.tsx | 119 +- frontend/src/pages/RequestsPage/columns.tsx | 48 +- frontend/src/queries/index.ts | 32 +- frontend/src/queries/queries.ts | 66 +- frontend/src/queries/types.ts | 72 +- frontend/src/queries/vscodeLinks.ts | 40 +- frontend/src/utils/index.ts | 21 +- frontend/tailwind.config.js | 10 +- frontend/tsconfig.json | 52 +- frontend/vite.config.ts | 12 +- .../package-lock.json => package-lock.json | 14166 +++++++++++----- package.json | 21 + 71 files changed, 10973 insertions(+), 5485 deletions(-) create mode 100644 .github/workflows/build_frontends.yml create mode 100644 api/biome.jsonc rename api/{scripts => test-content}/find-error-loc.js (100%) rename api/{scripts => test-content}/find-function-loc.ts (100%) rename api/{scripts => test-content}/function-loc-index.js (100%) rename api/{scripts => test-content}/function-loc-index.js.map (100%) rename api/{scripts => test-content}/github_issues.json (100%) rename api/{scripts => test-content}/index.js (100%) rename api/{scripts => test-content}/index.js.map (100%) rename api/{scripts => test-content}/mizu_logs.json (100%) rename api/{scripts => test-content}/seed.ts (100%) create mode 100644 biome.jsonc delete mode 100644 client-library/biome.json create mode 100644 client-library/biome.jsonc rename frontend/package-lock.json => package-lock.json (54%) create mode 100644 package.json diff --git a/.github/workflows/build_frontends.yml b/.github/workflows/build_frontends.yml new file mode 100644 index 000000000..315530252 --- /dev/null +++ b/.github/workflows/build_frontends.yml @@ -0,0 +1,35 @@ +# This workflow will be triggered by a GitHub pull-request. +--- + name: Build node packages + + on: + pull_request: + branches: ["*"] + push: + branches: ["main", "release-*"] + + env: + CARGO_TERM_COLOR: always + FORCE_COLOR: true + + jobs: + build_packages: + name: Build packages + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v2 + + - name: Setup Node.js + uses: actions/setup-node@v2 + with: + node-version: lts/* + cache: npm + - name: Install dependencies + run: npm ci --workspaces + + - name: Lint all workspaces + run: npm run lint:ci --workspaces + + - name: Build all workspaces + run: npm run build --workspaces --if-present diff --git a/.gitignore b/.gitignore index 5539d6351..485244153 100644 --- a/.gitignore +++ b/.gitignore @@ -33,4 +33,6 @@ yarn-error.log # Personal files mizu.boots.code-workspace +.vscode +node_modules start-dev.sh diff --git a/api/biome.jsonc b/api/biome.jsonc new file mode 100644 index 000000000..f79860f92 --- /dev/null +++ b/api/biome.jsonc @@ -0,0 +1,18 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", + "extends": ["../biome.jsonc"], + "css": { + "linter": { + "enabled": true + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "files": { + "ignore": ["meta/*.json", "test-content/*.*"] + } +} diff --git a/api/drizzle.config.ts b/api/drizzle.config.ts index bc8b8ee32..e58d43130 100644 --- a/api/drizzle.config.ts +++ b/api/drizzle.config.ts @@ -1,14 +1,14 @@ -import type { Config } from 'drizzle-kit'; -import { config } from 'dotenv'; +import { config } from "dotenv"; +import type { Config } from "drizzle-kit"; -config({ path: '.dev.vars' }); +config({ path: ".dev.vars" }); export default { - schema: './src/db/schema.ts', - out: 'drizzle', - dialect: 'sqlite', + schema: "./src/db/schema.ts", + out: "drizzle", + dialect: "sqlite", dbCredentials: { // biome-ignore lint/style/noNonNullAssertion: we want this to fail if not defined url: process.env.DATABASE_URL!, - } -} satisfies Config; \ No newline at end of file + }, +} satisfies Config; diff --git a/api/migrate.ts b/api/migrate.ts index 95bba12f8..457aef70e 100644 --- a/api/migrate.ts +++ b/api/migrate.ts @@ -1,29 +1,29 @@ -import { createClient } from '@libsql/client'; -import { config } from 'dotenv'; -import { migrate } from 'drizzle-orm/libsql/migrator'; -import { drizzle } from 'drizzle-orm/libsql'; +import { createClient } from "@libsql/client"; +import { config } from "dotenv"; +import { drizzle } from "drizzle-orm/libsql"; +import { migrate } from "drizzle-orm/libsql/migrator"; -config({ path: '.dev.vars' }); +config({ path: ".dev.vars" }); if (!process.env.DATABASE_URL) { - console.error('DATABASE_URL not defined'); + console.error("DATABASE_URL not defined"); process.exit(1); } const databaseUrl = process.env.DATABASE_URL; const sql = createClient({ - url: databaseUrl -}) + url: databaseUrl, +}); const db = drizzle(sql); const main = async () => { try { - await migrate(db, { migrationsFolder: 'drizzle' }); - console.log('Migration complete'); + await migrate(db, { migrationsFolder: "drizzle" }); + console.log("Migration complete"); } catch (error) { console.log(error); } process.exit(0); }; -main(); \ No newline at end of file +main(); diff --git a/api/package.json b/api/package.json index 293f97232..8af856559 100644 --- a/api/package.json +++ b/api/package.json @@ -1,10 +1,15 @@ { "type": "module", + "name": "api", + "version": "0.0.1", "scripts": { "dev": "tsx watch src/index.node.ts", "db:generate": "drizzle-kit generate", "db:migrate": "tsx migrate.ts", - "db:seed": "tsx scripts/seed.ts" + "db:seed": "tsx scripts/seed.ts", + "format": "biome check . --write", + "lint": "biome lint . && tsc --noEmit", + "lint:ci": "biome ci . #api" }, "dependencies": { "@hono/node-server": "^1.11.1", diff --git a/api/src/app.ts b/api/src/app.ts index d6b6c4576..0783c0537 100644 --- a/api/src/app.ts +++ b/api/src/app.ts @@ -1,24 +1,24 @@ -import { Hono } from "hono"; -import { env } from "hono/adapter"; import { createClient } from "@libsql/client"; import { drizzle } from "drizzle-orm/libsql"; +import { Hono } from "hono"; +import { env } from "hono/adapter"; +import { logger } from "hono/logger"; import type { WebSocket } from "ws"; import * as schema from "./db/schema"; -import { Bindings, Variables } from "./lib/types"; +import type { Bindings, Variables } from "./lib/types"; +import dependencies from "./routes/dependencies"; +import issues from "./routes/issues"; import logs from "./routes/logs"; import openai from "./routes/openai"; import source from "./routes/source"; -import { logger } from "hono/logger"; -import dependencies from "./routes/dependencies"; -import issues from "./routes/issues"; export function createApp(wsConnections?: Set) { const app = new Hono<{ Bindings: Bindings; Variables: Variables }>(); - // biome-ignore lint/suspicious/noExplicitAny: // this is a bucket of any kind of errors that we just want to log // and make available on a route + // biome-ignore lint/suspicious/noExplicitAny: const DB_ERRORS: Array = []; // NOTE - This middleware adds `db` on the context so we don't have to initiate it every time diff --git a/api/src/db/schema.ts b/api/src/db/schema.ts index 0784b840b..00bd969bb 100644 --- a/api/src/db/schema.ts +++ b/api/src/db/schema.ts @@ -1,6 +1,6 @@ -import { sql } from "drizzle-orm"; -import { text, integer, sqliteTable } from "drizzle-orm/sqlite-core"; import type { Endpoints } from "@octokit/types"; +import { sql } from "drizzle-orm"; +import { integer, sqliteTable, text } from "drizzle-orm/sqlite-core"; import { createInsertSchema, createSelectSchema } from "drizzle-zod"; import { z } from "zod"; @@ -22,12 +22,8 @@ export const mizuLogs = sqliteTable("mizu_logs", { ignored: integer("ignored", { mode: "boolean" }).default(false), args: text("args", { mode: "json" }), // NOTE - Should only be present iff message is a string callerLocation: text("caller_location", { mode: "json" }), - createdAt: text("created_at") - .notNull() - .default(sql`(CURRENT_TIMESTAMP)`), - updatedAt: text("updated_at") - .notNull() - .default(sql`(CURRENT_TIMESTAMP)`), + createdAt: text("created_at").notNull().default(sql`(CURRENT_TIMESTAMP)`), + updatedAt: text("updated_at").notNull().default(sql`(CURRENT_TIMESTAMP)`), matchingIssues: text("matching_issues", { mode: "json" }).$type< number[] | null >(), diff --git a/api/src/index.node.ts b/api/src/index.node.ts index fce27469d..0c400f0db 100644 --- a/api/src/index.node.ts +++ b/api/src/index.node.ts @@ -1,8 +1,9 @@ +import { createServer } from "node:http"; import { serve } from "@hono/node-server"; import { config } from "dotenv"; -import { createApp } from "./app"; import { type WebSocket, WebSocketServer } from "ws"; +import { createApp } from "./app"; config({ path: ".dev.vars" }); @@ -18,7 +19,8 @@ const port = 8788; const server = serve({ fetch: app.fetch, port, -}); + createServer, +}) as ReturnType; console.log(`Server is running: http://localhost:${port}`); const wss = new WebSocketServer({ server }); diff --git a/api/src/lib/find-source-function.ts b/api/src/lib/find-source-function.ts index 289100c72..ce9626e5b 100644 --- a/api/src/lib/find-source-function.ts +++ b/api/src/lib/find-source-function.ts @@ -1,8 +1,8 @@ -import fs from 'fs'; -import { promisify } from 'util'; -import { parse } from 'acorn'; -import { simple as walkSimple } from 'acorn-walk'; -import { SourceMapConsumer } from 'source-map'; +import fs from "node:fs"; +import { promisify } from "node:util"; +import { parse } from "acorn"; +import { simple as walkSimple } from "acorn-walk"; +import { SourceMapConsumer } from "source-map"; const readFileAsync = promisify(fs.readFile); @@ -13,10 +13,17 @@ interface FunctionLocation { endColumn: number; } -async function findFunctionByDefinition(jsFilePath: string, functionDefinition: string): Promise { +async function findFunctionByDefinition( + jsFilePath: string, + functionDefinition: string, +): Promise { try { - const fileContent = await readFileAsync(jsFilePath, { encoding: 'utf-8' }); - const ast = parse(fileContent, { ecmaVersion: 'latest', locations: true, sourceType: 'module' }); + const fileContent = await readFileAsync(jsFilePath, { encoding: "utf-8" }); + const ast = parse(fileContent, { + ecmaVersion: "latest", + locations: true, + sourceType: "module", + }); let foundLocation: FunctionLocation | null = null; @@ -24,88 +31,129 @@ async function findFunctionByDefinition(jsFilePath: string, functionDefinition: FunctionDeclaration(node) { // Extract function source from original content based on node's location const funcSource = fileContent.substring(node.start, node.end); - if (funcSource.replace(/\s+/g, ' ').trim() === functionDefinition.replace(/\s+/g, ' ').trim()) { + if ( + funcSource.replace(/\s+/g, " ").trim() === + functionDefinition.replace(/\s+/g, " ").trim() && + node.loc + ) { foundLocation = { startLine: node.loc.start.line, startColumn: node.loc.start.column + 1, endLine: node.loc.end.line, - endColumn: node.loc.end.column + 1 + endColumn: node.loc.end.column + 1, }; } }, FunctionExpression(node) { // Repeat as FunctionDeclaration const funcSource = fileContent.substring(node.start, node.end); - if (funcSource.replace(/\s+/g, ' ').trim() === functionDefinition.replace(/\s+/g, ' ').trim()) { + if ( + funcSource.replace(/\s+/g, " ").trim() === + functionDefinition.replace(/\s+/g, " ").trim() && + node.loc + ) { foundLocation = { startLine: node.loc.start.line, startColumn: node.loc.start.column + 1, endLine: node.loc.end.line, - endColumn: node.loc.end.column + 1 + endColumn: node.loc.end.column + 1, }; } }, ArrowFunctionExpression(node) { // Repeat as FunctionDeclaration const funcSource = fileContent.substring(node.start, node.end); - if (funcSource.replace(/\s+/g, ' ').trim() === functionDefinition.replace(/\s+/g, ' ').trim()) { + if ( + funcSource.replace(/\s+/g, " ").trim() === + functionDefinition.replace(/\s+/g, " ").trim() && + node.loc + ) { foundLocation = { startLine: node.loc.start.line, startColumn: node.loc.start.column + 1, endLine: node.loc.end.line, - endColumn: node.loc.end.column + 1 + endColumn: node.loc.end.column + 1, }; } - } + }, }); return foundLocation; } catch (error) { - console.error('Error reading or parsing file:', jsFilePath); + console.error("Error reading or parsing file:", jsFilePath); return null; } } -async function findOriginalSource(jsFile: string, line: number, column: number) { - const mapFile = jsFile + '.map'; // Adjust if your source map is located elsewhere - const sourceMapContent = JSON.parse(fs.readFileSync(mapFile, 'utf8')); - - return await SourceMapConsumer.with(sourceMapContent, null, consumer => { +/** + * This function will throw an error if the + */ +async function findOriginalSource( + jsFile: string, + line: number, + column: number, +) { + const mapFile = `${jsFile}.map`; // Adjust if your source map is located elsewhere + const sourceMapContent = JSON.parse(fs.readFileSync(mapFile, "utf8")); + + return await SourceMapConsumer.with(sourceMapContent, null, (consumer) => { const pos = consumer.originalPositionFor({ - line: line, // Line number from JS file - column: column // Column number from JS file + line: line, // Line number from JS file + column: column, // Column number from JS file }); consumer.destroy(); // console.log('Original Source:', pos); // Optional: Display the source code snippet if needed - const sourceContent = consumer.sourceContentFor(pos.source); + const returnNullOnMissing = true; + const sourceContent = consumer.sourceContentFor( + pos.source ?? "", + returnNullOnMissing, + ); // console.log('Source Content:\n', sourceContent); return { ...pos, sourceContent }; }); } -export async function findSourceFunction(jsFilePath: string, functionText: string) { - return findFunctionByDefinition(jsFilePath, functionText).then(async loc => { - const functionStartLine = loc?.startLine ?? 0; - const functionStartColumn = loc?.startColumn ?? 0; - const functionEndLine = loc?.endLine ?? 0; - const functionEndColumn = loc?.endColumn ?? 0; - - const [sourceFunctionStart, sourceFunctionEnd] = await Promise.all([ - findOriginalSource(jsFilePath, functionStartLine, functionStartColumn), - findOriginalSource(jsFilePath, functionEndLine, functionEndColumn), - ]); - - // console.log('Function start:', sourceFunctionStart); - // console.log('Function end:', sourceFunctionEnd); +export async function findSourceFunction( + jsFilePath: string, + functionText: string, +) { + return findFunctionByDefinition(jsFilePath, functionText).then( + async (loc) => { + const functionStartLine = loc?.startLine ?? 0; + const functionStartColumn = loc?.startColumn ?? 0; + const functionEndLine = loc?.endLine ?? 0; + const functionEndColumn = loc?.endColumn ?? 0; + + const [sourceFunctionStart, sourceFunctionEnd] = await Promise.all([ + findOriginalSource(jsFilePath, functionStartLine, functionStartColumn), + findOriginalSource(jsFilePath, functionEndLine, functionEndColumn), + ]); + + // console.log('Function start:', sourceFunctionStart); + // console.log('Function end:', sourceFunctionEnd); + + const sourceContent = sourceFunctionStart.sourceContent ?? ""; + const startLine = sourceFunctionStart.line; + const endLine = sourceFunctionEnd.line; + // Check if the start/end line are null and otherwise just return sourceContent as is + if (startLine === null || endLine === null) { + // TODO decide what the proper behavior should be when + // we can't find the correct source content. + // For now: return the source content as is + return sourceContent; + } - const sourceContent = sourceFunctionStart?.sourceContent ?? ''; - const sourceFunction = sourceContent.split('\n').filter((_, i) => i >= sourceFunctionStart.line - 1 && i <= sourceFunctionEnd.line - 1).join("\n"); + const sourceFunction = sourceContent + .split("\n") + .filter((_, i) => i >= startLine - 1 && i <= endLine - 1) + .join("\n"); - // console.log('Function source:', sourceFunction) - return sourceFunction; - }); + // console.log('Function source:', sourceFunction) + return sourceFunction; + }, + ); } /** * Example Usage diff --git a/api/src/lib/types.ts b/api/src/lib/types.ts index 2c8aded4b..62970a5e7 100644 --- a/api/src/lib/types.ts +++ b/api/src/lib/types.ts @@ -1,7 +1,7 @@ // Only using for global types -import { LibSQLDatabase } from "drizzle-orm/libsql"; -import * as schema from "@/db/schema"; +import type * as schema from "@/db/schema"; +import type { LibSQLDatabase } from "drizzle-orm/libsql"; import type { WebSocket } from "ws"; export type Bindings = { @@ -13,6 +13,6 @@ export type Bindings = { export type Variables = { db: LibSQLDatabase; wsConnections: Set; + // biome-ignore lint/suspicious/noExplicitAny: dbErrors: Array; }; - diff --git a/api/src/routes/dependencies.ts b/api/src/routes/dependencies.ts index e67972ccf..5ee2c84dd 100644 --- a/api/src/routes/dependencies.ts +++ b/api/src/routes/dependencies.ts @@ -1,5 +1,5 @@ +import type { Bindings, Variables } from "@/lib/types"; import { Hono } from "hono"; -import { Bindings, Variables } from "@/lib/types"; const app = new Hono<{ Bindings: Bindings; Variables: Variables }>(); diff --git a/api/src/routes/issues.ts b/api/src/routes/issues.ts index bef0f1d0d..174f9c18c 100644 --- a/api/src/routes/issues.ts +++ b/api/src/routes/issues.ts @@ -1,14 +1,14 @@ -import { Hono } from "hono"; -import { z } from "zod"; -import { and, eq, inArray } from "drizzle-orm"; +import fs from "node:fs"; +import { githubIssues, newGithubIssueSchema } from "@/db/schema"; +import * as schema from "@/db/schema"; +import type { Bindings, Variables } from "@/lib/types"; import { zValidator } from "@hono/zod-validator"; -import { Bindings, Variables } from "@/lib/types"; +import { and, eq, inArray } from "drizzle-orm"; +import type { LibSQLDatabase } from "drizzle-orm/libsql"; +import { Hono } from "hono"; import { cors } from "hono/cors"; -import { githubIssues, newGithubIssueSchema } from "@/db/schema"; import { Octokit } from "octokit"; -import { LibSQLDatabase } from "drizzle-orm/libsql"; -import * as schema from "@/db/schema"; -import fs from "node:fs"; +import { z } from "zod"; const app = new Hono<{ Bindings: Bindings; Variables: Variables }>(); diff --git a/api/src/routes/logs.ts b/api/src/routes/logs.ts index 91b07d11c..c46295eed 100644 --- a/api/src/routes/logs.ts +++ b/api/src/routes/logs.ts @@ -1,11 +1,11 @@ -import { Hono } from "hono"; -import { Bindings, Variables } from "@/lib/types"; import * as schema from "@/db/schema"; -import { inArray, ne, desc } from "drizzle-orm"; +import type { Bindings, Variables } from "@/lib/types"; import { tryParseJsonObjectMessage } from "@/lib/utils"; +import { zValidator } from "@hono/zod-validator"; +import { desc, inArray, ne } from "drizzle-orm"; +import { Hono } from "hono"; import { cors } from "hono/cors"; import { z } from "zod"; -import { zValidator } from "@hono/zod-validator"; const { mizuLogs } = schema; diff --git a/api/src/routes/openai.ts b/api/src/routes/openai.ts index d3fb598a5..fb19a7e69 100644 --- a/api/src/routes/openai.ts +++ b/api/src/routes/openai.ts @@ -1,4 +1,4 @@ -import { Bindings, Variables } from "@/lib/types"; +import type { Bindings, Variables } from "@/lib/types"; import { zValidator } from "@hono/zod-validator"; import { Hono } from "hono"; import { cors } from "hono/cors"; diff --git a/api/src/routes/source.ts b/api/src/routes/source.ts index da95544ef..55d956f9d 100644 --- a/api/src/routes/source.ts +++ b/api/src/routes/source.ts @@ -1,9 +1,9 @@ +import { readFileSync } from "node:fs"; import { findSourceFunction } from "@/lib/find-source-function"; -import { Bindings, Variables } from "@/lib/types"; +import type { Bindings, Variables } from "@/lib/types"; import { zValidator } from "@hono/zod-validator"; import { Hono } from "hono"; import { cors } from "hono/cors"; -import { readFileSync } from "node:fs"; import { SourceMapConsumer } from "source-map"; import { z } from "zod"; @@ -30,9 +30,16 @@ app.get( return ctx.json(pos); } catch (err) { - console.error("Could not read source file", err?.message); + const message = getValueFromObject(err, "message", "Unknown error"); + const name = getValueFromObject(err, "name", ""); + + console.error("Could not read source file", message); return ctx.json( - { error: "Error reading file", name: err?.name, message: err?.message }, + { + error: "Error reading file", + name, + message, + }, 500, ); } @@ -47,15 +54,34 @@ app.post("/v0/source-function", cors(), async (ctx) => { return ctx.json({ functionText }); } catch (err) { console.error("Could not find function in source", source); + const message = getValueFromObject(err, "message", "Unknown error"); + const name = getValueFromObject(err, "name", ""); + return ctx.json( { error: "Error finding function", - name: err?.name, - message: err?.message, + name, + message, }, 500, ); } }); +function getValueFromObject( + element: unknown, + key: string, + defaultValue: T, +): T { + if (typeof element === "object" && element !== null && key in element) { + const value = (element as Record)[key]; + // Rough check to see if the type of the value is the same as the default value + if (typeof value === typeof defaultValue || value === defaultValue) { + return (element as Record)[key] as T; + } + } + + return defaultValue; +} + export default app; diff --git a/api/scripts/find-error-loc.js b/api/test-content/find-error-loc.js similarity index 100% rename from api/scripts/find-error-loc.js rename to api/test-content/find-error-loc.js diff --git a/api/scripts/find-function-loc.ts b/api/test-content/find-function-loc.ts similarity index 100% rename from api/scripts/find-function-loc.ts rename to api/test-content/find-function-loc.ts diff --git a/api/scripts/function-loc-index.js b/api/test-content/function-loc-index.js similarity index 100% rename from api/scripts/function-loc-index.js rename to api/test-content/function-loc-index.js diff --git a/api/scripts/function-loc-index.js.map b/api/test-content/function-loc-index.js.map similarity index 100% rename from api/scripts/function-loc-index.js.map rename to api/test-content/function-loc-index.js.map diff --git a/api/scripts/github_issues.json b/api/test-content/github_issues.json similarity index 100% rename from api/scripts/github_issues.json rename to api/test-content/github_issues.json diff --git a/api/scripts/index.js b/api/test-content/index.js similarity index 100% rename from api/scripts/index.js rename to api/test-content/index.js diff --git a/api/scripts/index.js.map b/api/test-content/index.js.map similarity index 100% rename from api/scripts/index.js.map rename to api/test-content/index.js.map diff --git a/api/scripts/mizu_logs.json b/api/test-content/mizu_logs.json similarity index 100% rename from api/scripts/mizu_logs.json rename to api/test-content/mizu_logs.json diff --git a/api/scripts/seed.ts b/api/test-content/seed.ts similarity index 100% rename from api/scripts/seed.ts rename to api/test-content/seed.ts diff --git a/api/tsconfig.json b/api/tsconfig.json index 59fcbb896..b0cd9a7ca 100644 --- a/api/tsconfig.json +++ b/api/tsconfig.json @@ -1,33 +1,21 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "declaration": true, - "lib": [ - "ESNext" - ], - "types": [ - "@cloudflare/workers-types" - ], - "jsx": "react-jsx", - "jsxImportSource": "hono/jsx", - "baseUrl": ".", - "outDir": "dist", - "paths": { - "@/*": [ - "src/*" - ] - } - }, - "include": [ - "src" - ], - "exclude": [ - "node_modules", - "dist", - "scripts" - ] + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "declaration": true, + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types"], + "jsx": "react-jsx", + "jsxImportSource": "hono/jsx", + "baseUrl": ".", + "outDir": "dist", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "scripts"] } diff --git a/biome.jsonc b/biome.jsonc new file mode 100644 index 000000000..e0a17eddb --- /dev/null +++ b/biome.jsonc @@ -0,0 +1,42 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", + "css": { + "linter": { + "enabled": true + } + }, + "vcs": { + "enabled": true, + "clientKind": "git" + }, + "formatter": { + "enabled": true, + "indentStyle": "space" + }, + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": false + }, + "overrides": [ + { + "include": ["client-library", "api"], + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + } + }, + { + "include": ["test-content"], + "formatter": { + "enabled": false + } + } + ], + "files": { + "ignore": ["meta/*.json", "dist/*.js", "test-content/*.*", "fpx/*.*"] + } +} diff --git a/client-library/biome.json b/client-library/biome.json deleted file mode 100644 index c2dd159cd..000000000 --- a/client-library/biome.json +++ /dev/null @@ -1,22 +0,0 @@ -{ - "$schema": "node_modules/@biomejs/biome/configuration_schema.json", - "formatter": { - "enabled": true, - "indentStyle": "space" - }, - "linter": { - "enabled": true - }, - "organizeImports": { - "enabled": true - }, - "files": { - "ignore": [ - "*.d.ts", - ".nx", - ".yarn", - "dist", - "node_modules" - ] - } -} diff --git a/client-library/biome.jsonc b/client-library/biome.jsonc new file mode 100644 index 000000000..878a016fd --- /dev/null +++ b/client-library/biome.jsonc @@ -0,0 +1,18 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.0/schema.json", + "extends": ["../biome.jsonc"], + "css": { + "linter": { + "enabled": true + } + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true + } + }, + "files": { + "ignore": ["dist/*.js"] + } +} diff --git a/client-library/package.json b/client-library/package.json index b77479b30..d78ae4392 100644 --- a/client-library/package.json +++ b/client-library/package.json @@ -20,9 +20,9 @@ }, "scripts": { "build": "cd src && swc . -d ../dist", - "lint": "biome lint ./src/**.ts", - "format": "biome check ./src/**.ts --apply", - "lint:ci": "biome ci ./src/**.ts", + "format": "biome check . --write", + "lint": "biome lint .", + "lint:ci": "biome ci .", "postinstall": "cd src && swc . -d ../dist" } } diff --git a/client-library/tsconfig.json b/client-library/tsconfig.json index 28c195be8..0e796b36a 100644 --- a/client-library/tsconfig.json +++ b/client-library/tsconfig.json @@ -1,12 +1,12 @@ { - "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "Bundler", - "strict": true, - "skipLibCheck": true, - "lib": ["ESNext"], - "types": ["@cloudflare/workers-types"], - "noEmit": true - } + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "Bundler", + "strict": true, + "skipLibCheck": true, + "lib": ["ESNext"], + "types": ["@cloudflare/workers-types"], + "noEmit": true + } } diff --git a/frontend/.eslintrc.cjs b/frontend/.eslintrc.cjs index d6c953795..6e8698b72 100644 --- a/frontend/.eslintrc.cjs +++ b/frontend/.eslintrc.cjs @@ -2,17 +2,17 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:@typescript-eslint/recommended', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:@typescript-eslint/recommended", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parser: '@typescript-eslint/parser', - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parser: "@typescript-eslint/parser", + plugins: ["react-refresh"], rules: { - 'react-refresh/only-export-components': [ - 'warn', + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, -} +}; diff --git a/frontend/package.json b/frontend/package.json index b19215c05..6ed5f26d6 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -7,6 +7,8 @@ "dev": "vite", "build": "tsc && vite build", "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "lint:ci": "biome ci . && eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0", + "format": "biome check . --write", "preview": "vite preview", "deploy": "npm run build && wrangler pages deploy dist" }, diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js index 2e7af2b7f..2aa7205d4 100644 --- a/frontend/postcss.config.js +++ b/frontend/postcss.config.js @@ -3,4 +3,4 @@ export default { tailwindcss: {}, autoprefixer: {}, }, -} +}; diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index aff9c2072..7745b88bf 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,8 +1,8 @@ -import { BrowserRouter as Router, Route, Routes } from "react-router-dom"; -import Layout from "./Layout"; -import { RequestsPage } from "./pages/RequestsPage/Requests"; import { QueryClientProvider, queryClient } from "@/queries"; +import { Route, BrowserRouter as Router, Routes } from "react-router-dom"; +import Layout from "./Layout"; import { RequestDetailsPage } from "./pages/RequestDetailsPage/RequestDetailsPage"; +import { RequestsPage } from "./pages/RequestsPage/Requests"; export function App() { return ( @@ -17,7 +17,7 @@ export function App() { - ) + ); } export default App; diff --git a/frontend/src/Layout.tsx b/frontend/src/Layout.tsx index 28a303419..5c16ed670 100644 --- a/frontend/src/Layout.tsx +++ b/frontend/src/Layout.tsx @@ -1,15 +1,20 @@ -import type React from 'react'; -import { NavLink, Link as RouterLink, matchPath, useLocation } from 'react-router-dom'; import { ActivityLogIcon, DashboardIcon, BarChartIcon as LineChart, // FIXME + MixerHorizontalIcon, CubeIcon as Package, - MagnifyingGlassIcon as Search, LayoutIcon as PanelLeft, + MagnifyingGlassIcon as Search, AvatarIcon as UserIcon, - MixerHorizontalIcon, -} from "@radix-ui/react-icons" +} from "@radix-ui/react-icons"; +import type React from "react"; +import { + NavLink, + Link as RouterLink, + matchPath, + useLocation, +} from "react-router-dom"; import { Breadcrumb, @@ -18,8 +23,8 @@ import { BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator, -} from "@/components/ui/breadcrumb" -import { Button } from "@/components/ui/button" +} from "@/components/ui/breadcrumb"; +import { Button } from "@/components/ui/button"; import { DropdownMenu, DropdownMenuContent, @@ -27,18 +32,25 @@ import { DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, -} from "@/components/ui/dropdown-menu" -import { Input } from "@/components/ui/input" -import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet" +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"; import { Tooltip, TooltipContent, TooltipTrigger, -} from "@/components/ui/tooltip" -import { Fragment, useMemo, type SVGProps } from 'react'; +} from "@/components/ui/tooltip"; +import { Fragment, type SVGProps, useMemo } from "react"; const WaveIcon: React.FC> = (props) => ( - + Wave Icon > = (props) => ( d="M0 7.5 Q 3.75 0, 7.5 7.5 T 15 7.5 M0 22.5 Q 3.75 15, 7.5 22.5 T 15 22.5" /> -) +); const getPathSegments = (pathname: string) => { - if (pathname === '/') return ['/']; - const segments = pathname.split('/').filter(Boolean); - const paths = segments.map((_, index) => `/${segments.slice(0, index + 1).join('/')}`); + if (pathname === "/") return ["/"]; + const segments = pathname.split("/").filter(Boolean); + const paths = segments.map( + (_, index) => `/${segments.slice(0, index + 1).join("/")}`, + ); return paths; }; const breadcrumbMap: Record = { - '/': 'Dashboard', - '/requests': 'Requests', - '/requests/:traceId': 'Request Details', + "/": "Dashboard", + "/requests": "Requests", + "/requests/:traceId": "Request Details", }; // TODO - Check out React Router useMatches @@ -74,38 +88,47 @@ const MizuBreadcrumbs: React.FC = () => { const paths = getPathSegments(location.pathname); const breadcrumbs = useMemo(() => { - return paths.map((path) => { - const matchingPathPattern = Object.keys(breadcrumbMap).find((pathPattern) => matchPath(pathPattern, path)) - if (matchingPathPattern) { - return { path, label: breadcrumbMap[matchingPathPattern] }; - } - return null; - }).filter(Boolean); + return paths + .map((path) => { + const matchingPathPattern = Object.keys(breadcrumbMap).find( + (pathPattern) => matchPath(pathPattern, path), + ); + if (matchingPathPattern) { + return { path, label: breadcrumbMap[matchingPathPattern] }; + } + return null; + }) + .filter(Boolean); }, [paths]); return ( - {breadcrumbs.map((crumb, index) => crumb && ( - - - {index === breadcrumbs.length - 1 ? ( - {crumb.label} - ) : ( - - {crumb.label} - - )} - - {index < breadcrumbs.length - 1 && } - - ))} + {breadcrumbs.map( + (crumb, index) => + crumb && ( + + + {index === breadcrumbs.length - 1 ? ( + {crumb.label} + ) : ( + + {crumb.label} + + )} + + {index < breadcrumbs.length - 1 && } + + ), + )} - ) -} + ); +}; -export const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) => { +export const Layout: React.FC<{ children?: React.ReactNode }> = ({ + children, +}) => { return (
@@ -276,6 +304,6 @@ export const Layout: React.FC<{ children?: React.ReactNode }> = ({ children }) =
); -} +}; export default Layout; diff --git a/frontend/src/components/ui/DataTable.tsx b/frontend/src/components/ui/DataTable.tsx index 7a2e6ec6f..9a569a2c0 100644 --- a/frontend/src/components/ui/DataTable.tsx +++ b/frontend/src/components/ui/DataTable.tsx @@ -1,4 +1,11 @@ -import { ColumnDef, Row, RowData, flexRender, getCoreRowModel, useReactTable } from "@tanstack/react-table" +import { + type ColumnDef, + type Row, + type RowData, + flexRender, + getCoreRowModel, + useReactTable, +} from "@tanstack/react-table"; import { clsx } from "clsx"; import { @@ -8,14 +15,14 @@ import { TableHead, TableHeader, TableRow, -} from "@/components/ui/table" +} from "@/components/ui/table"; // Extend the ColumnMeta type to include headerClassName and cellClassName // // https://github.com/TanStack/table/discussions/4100 // https://tanstack.com/table/v8/docs/api/core/column-def#meta // -declare module '@tanstack/react-table' { +declare module "@tanstack/react-table" { // eslint-disable-next-line @typescript-eslint/no-unused-vars interface ColumnMeta { headerClassName?: string; @@ -24,20 +31,22 @@ declare module '@tanstack/react-table' { } export interface DataTableProps { - columns: ColumnDef[] - data: TData[] + columns: ColumnDef[]; + data: TData[]; /** Custom prop for optionally handling row clicks */ - handleRowClick?: (row: Row) => void + handleRowClick?: (row: Row) => void; } const rowHasId = (row: TData): row is TData & { id: string } => { - return row && typeof row === "object" && "id" in row && typeof row.id === "string"; -} + return ( + row && typeof row === "object" && "id" in row && typeof row.id === "string" + ); +}; /** - * Custom component (not part of shadcn/ui) to handle more complex data interactions than the + * Custom component (not part of shadcn/ui) to handle more complex data interactions than the * out of the box table component - * + * * Features: * - Add an optional prop for a click handler when the entire row is clicked * - Allow setting header and cell className prop through column definition metadata @@ -53,10 +62,10 @@ export function DataTable({ columns, getCoreRowModel: getCoreRowModel(), // If all the data have an `id` property of type `string`, set that to the row id - ...data.every(rowHasId) && ({ - getRowId: (row: TData, index) => rowHasId(row) ? row.id : `${index}`, - }) - }) + ...(data.every(rowHasId) && { + getRowId: (row: TData, index) => (rowHasId(row) ? row.id : `${index}`), + }), + }); return (
@@ -66,15 +75,20 @@ export function DataTable({ {headerGroup.headers.map((header) => { return ( - + {header.isPlaceholder ? null : flexRender( - header.column.columnDef.header, - header.getContext() - )} + header.column.columnDef.header, + header.getContext(), + )} - ) + ); })} ))} @@ -88,10 +102,13 @@ export function DataTable({ onClick={() => handleRowClick?.(row)} > {row.getVisibleCells().map((cell) => ( - + {flexRender(cell.column.columnDef.cell, cell.getContext())} ))} @@ -99,7 +116,10 @@ export function DataTable({ )) ) : ( - + No Results @@ -107,5 +127,5 @@ export function DataTable({
- ) + ); } diff --git a/frontend/src/components/ui/accordion.tsx b/frontend/src/components/ui/accordion.tsx index 8e9987d0a..5a2395bae 100644 --- a/frontend/src/components/ui/accordion.tsx +++ b/frontend/src/components/ui/accordion.tsx @@ -1,10 +1,10 @@ -import * as React from "react" -import * as AccordionPrimitive from "@radix-ui/react-accordion" -import { ChevronDownIcon } from "@radix-ui/react-icons" +import * as AccordionPrimitive from "@radix-ui/react-accordion"; +import { ChevronDownIcon } from "@radix-ui/react-icons"; +import * as React from "react"; -import { cn } from "@/utils" +import { cn } from "@/utils"; -const Accordion = AccordionPrimitive.Root +const Accordion = AccordionPrimitive.Root; const AccordionItem = React.forwardRef< React.ElementRef, @@ -15,8 +15,8 @@ const AccordionItem = React.forwardRef< className={cn("border-b", className)} {...props} /> -)) -AccordionItem.displayName = "AccordionItem" +)); +AccordionItem.displayName = "AccordionItem"; const AccordionTrigger = React.forwardRef< React.ElementRef, @@ -27,7 +27,7 @@ const AccordionTrigger = React.forwardRef< ref={ref} className={cn( "flex flex-1 items-center justify-between py-4 text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180", - className + className, )} {...props} > @@ -35,8 +35,8 @@ const AccordionTrigger = React.forwardRef< -)) -AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName +)); +AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName; const AccordionContent = React.forwardRef< React.ElementRef, @@ -49,7 +49,7 @@ const AccordionContent = React.forwardRef< >
{children}
-)) -AccordionContent.displayName = AccordionPrimitive.Content.displayName +)); +AccordionContent.displayName = AccordionPrimitive.Content.displayName; -export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } +export { Accordion, AccordionItem, AccordionTrigger, AccordionContent }; diff --git a/frontend/src/components/ui/badge/Badge.tsx b/frontend/src/components/ui/badge/Badge.tsx index 289ff2e53..c9683c01b 100644 --- a/frontend/src/components/ui/badge/Badge.tsx +++ b/frontend/src/components/ui/badge/Badge.tsx @@ -1,9 +1,8 @@ -import * as React from "react" -import {type VariantProps } from "class-variance-authority" - -import { cn } from "@/utils" -import { badgeVariants } from "./variants" +import type { VariantProps } from "class-variance-authority"; +import type * as React from "react"; +import { cn } from "@/utils"; +import { badgeVariants } from "./variants"; export interface BadgeProps extends React.HTMLAttributes, @@ -12,5 +11,5 @@ export interface BadgeProps export function Badge({ className, variant, ...props }: BadgeProps) { return (
- ) + ); } diff --git a/frontend/src/components/ui/badge/variants.ts b/frontend/src/components/ui/badge/variants.ts index 04e54a016..ac5e40003 100644 --- a/frontend/src/components/ui/badge/variants.ts +++ b/frontend/src/components/ui/badge/variants.ts @@ -1,5 +1,4 @@ -import { cva } from "class-variance-authority" - +import { cva } from "class-variance-authority"; export const badgeVariants = cva( "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", @@ -18,5 +17,5 @@ export const badgeVariants = cva( defaultVariants: { variant: "default", }, - } -) + }, +); diff --git a/frontend/src/components/ui/breadcrumb.tsx b/frontend/src/components/ui/breadcrumb.tsx index ea283f4a5..568edd062 100644 --- a/frontend/src/components/ui/breadcrumb.tsx +++ b/frontend/src/components/ui/breadcrumb.tsx @@ -1,16 +1,16 @@ -import * as React from "react" -import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons" -import { Slot } from "@radix-ui/react-slot" +import { ChevronRightIcon, DotsHorizontalIcon } from "@radix-ui/react-icons"; +import { Slot } from "@radix-ui/react-slot"; +import * as React from "react"; -import { cn } from "@/utils" +import { cn } from "@/utils"; const Breadcrumb = React.forwardRef< HTMLElement, React.ComponentPropsWithoutRef<"nav"> & { - separator?: React.ReactNode + separator?: React.ReactNode; } ->(({ ...props }, ref) =>