From 9c8f3fc47cd1cee36dee6af30411d40ae2462151 Mon Sep 17 00:00:00 2001 From: Groom Book CTO Date: Tue, 17 Mar 2026 18:03:06 +0000 Subject: [PATCH] fix: resolve all typecheck, lint, and test failures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add @types/node to packages/db devDependencies (typecheck was missing process) - Re-export drizzle-orm helpers (eq, gte, etc.) from @groombook/db to avoid duplicate-instance type conflicts; remove drizzle-orm direct dep from API - Add @hono/zod-validator and jose as direct API dependencies - Merge duplicate @groombook/db imports in all route files - Fix noUncheckedIndexedAccess errors: appointments PATCH, web calendar grid - Fix weightKg/dateOfBirth type conversion in pets route (numeric→string, string→Date) - Add eslint.config.js for API and web (ESLint 9 flat config format) - Add vitest.config.ts with passWithNoTests for API and web Co-Authored-By: Paperclip --- apps/api/eslint.config.js | 11 +++++++++++ apps/api/package.json | 2 ++ apps/api/src/routes/appointments.ts | 14 ++++++-------- apps/api/src/routes/clients.ts | 3 +-- apps/api/src/routes/pets.ts | 23 +++++++++++++++++------ apps/api/src/routes/services.ts | 3 +-- apps/api/src/routes/staff.ts | 3 +-- apps/api/vitest.config.ts | 7 +++++++ apps/web/eslint.config.js | 11 +++++++++++ apps/web/src/pages/Appointments.tsx | 2 +- apps/web/vitest.config.ts | 7 +++++++ packages/db/package.json | 1 + packages/db/src/index.ts | 1 + pnpm-lock.yaml | 25 +++++++++++++++++++++++++ 14 files changed, 92 insertions(+), 21 deletions(-) create mode 100644 apps/api/eslint.config.js create mode 100644 apps/api/vitest.config.ts create mode 100644 apps/web/eslint.config.js create mode 100644 apps/web/vitest.config.ts diff --git a/apps/api/eslint.config.js b/apps/api/eslint.config.js new file mode 100644 index 0000000..e3961f7 --- /dev/null +++ b/apps/api/eslint.config.js @@ -0,0 +1,11 @@ +import tseslint from "typescript-eslint"; + +export default tseslint.config( + ...tseslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + }, + } +); diff --git a/apps/api/package.json b/apps/api/package.json index 071a656..dd128d2 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -15,7 +15,9 @@ "@groombook/db": "workspace:*", "@groombook/types": "workspace:*", "@hono/node-server": "^1.13.7", + "@hono/zod-validator": "^0.4.3", "hono": "^4.6.17", + "jose": "^5.9.6", "openid-client": "^6.1.7", "zod": "^3.24.1" }, diff --git a/apps/api/src/routes/appointments.ts b/apps/api/src/routes/appointments.ts index 69c71f2..05f87cc 100644 --- a/apps/api/src/routes/appointments.ts +++ b/apps/api/src/routes/appointments.ts @@ -1,8 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; -import { and, eq, gte, lt, lte, ne } from "drizzle-orm"; -import { getDb, appointments } from "@groombook/db"; +import { and, eq, getDb, gte, lt, lte, ne, appointments } from "@groombook/db"; export const appointmentsRouter = new Hono(); @@ -144,13 +143,12 @@ appointmentsRouter.patch( .from(appointments) .where(eq(appointments.id, id)) .limit(1); - if (existing.length === 0) return c.json({ error: "Not found" }, 404); + const current = existing[0]; + if (!current) return c.json({ error: "Not found" }, 404); - const start = body.startTime - ? new Date(body.startTime) - : existing[0].startTime; - const end = body.endTime ? new Date(body.endTime) : existing[0].endTime; - const staffId = body.staffId ?? existing[0].staffId; + const start = body.startTime ? new Date(body.startTime) : current.startTime; + const end = body.endTime ? new Date(body.endTime) : current.endTime; + const staffId = body.staffId ?? current.staffId; if (end <= start) { return c.json({ error: "endTime must be after startTime" }, 422); diff --git a/apps/api/src/routes/clients.ts b/apps/api/src/routes/clients.ts index b1d9f7c..e560393 100644 --- a/apps/api/src/routes/clients.ts +++ b/apps/api/src/routes/clients.ts @@ -1,8 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; -import { eq } from "drizzle-orm"; -import { getDb, clients } from "@groombook/db"; +import { eq, getDb, clients } from "@groombook/db"; export const clientsRouter = new Hono(); diff --git a/apps/api/src/routes/pets.ts b/apps/api/src/routes/pets.ts index 73cb42a..5dc5869 100644 --- a/apps/api/src/routes/pets.ts +++ b/apps/api/src/routes/pets.ts @@ -1,8 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; -import { eq } from "drizzle-orm"; -import { getDb, pets } from "@groombook/db"; +import { eq, getDb, pets } from "@groombook/db"; export const petsRouter = new Hono(); @@ -42,8 +41,15 @@ petsRouter.get("/:id", async (c) => { petsRouter.post("/", zValidator("json", createPetSchema), async (c) => { const db = getDb(); - const body = c.req.valid("json"); - const [row] = await db.insert(pets).values(body).returning(); + const { weightKg, dateOfBirth, ...rest } = c.req.valid("json"); + const [row] = await db + .insert(pets) + .values({ + ...rest, + weightKg: weightKg?.toString(), + dateOfBirth: dateOfBirth ? new Date(dateOfBirth) : undefined, + }) + .returning(); return c.json(row, 201); }); @@ -52,10 +58,15 @@ petsRouter.patch( zValidator("json", updatePetSchema), async (c) => { const db = getDb(); - const body = c.req.valid("json"); + const { weightKg, dateOfBirth, ...rest } = c.req.valid("json"); const [row] = await db .update(pets) - .set({ ...body, updatedAt: new Date() }) + .set({ + ...rest, + weightKg: weightKg?.toString(), + dateOfBirth: dateOfBirth ? new Date(dateOfBirth) : undefined, + updatedAt: new Date(), + }) .where(eq(pets.id, c.req.param("id"))) .returning(); if (!row) return c.json({ error: "Not found" }, 404); diff --git a/apps/api/src/routes/services.ts b/apps/api/src/routes/services.ts index 1f9315a..621a797 100644 --- a/apps/api/src/routes/services.ts +++ b/apps/api/src/routes/services.ts @@ -1,8 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; -import { eq } from "drizzle-orm"; -import { getDb, services } from "@groombook/db"; +import { eq, getDb, services } from "@groombook/db"; export const servicesRouter = new Hono(); diff --git a/apps/api/src/routes/staff.ts b/apps/api/src/routes/staff.ts index 2149ed0..b305735 100644 --- a/apps/api/src/routes/staff.ts +++ b/apps/api/src/routes/staff.ts @@ -1,8 +1,7 @@ import { Hono } from "hono"; import { zValidator } from "@hono/zod-validator"; import { z } from "zod"; -import { eq } from "drizzle-orm"; -import { getDb, staff } from "@groombook/db"; +import { eq, getDb, staff } from "@groombook/db"; export const staffRouter = new Hono(); diff --git a/apps/api/vitest.config.ts b/apps/api/vitest.config.ts new file mode 100644 index 0000000..d913ed3 --- /dev/null +++ b/apps/api/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + passWithNoTests: true, + }, +}); diff --git a/apps/web/eslint.config.js b/apps/web/eslint.config.js new file mode 100644 index 0000000..e3961f7 --- /dev/null +++ b/apps/web/eslint.config.js @@ -0,0 +1,11 @@ +import tseslint from "typescript-eslint"; + +export default tseslint.config( + ...tseslint.configs.recommended, + { + rules: { + "@typescript-eslint/no-explicit-any": "warn", + "@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }], + }, + } +); diff --git a/apps/web/src/pages/Appointments.tsx b/apps/web/src/pages/Appointments.tsx index 58c126c..d088f2a 100644 --- a/apps/web/src/pages/Appointments.tsx +++ b/apps/web/src/pages/Appointments.tsx @@ -268,7 +268,7 @@ export function AppointmentsPage() {
- {apptsByDay[i].map((a) => { + {(apptsByDay[i] ?? []).map((a) => { const svc = services.find((s) => s.id === a.serviceId); const cli = clients.find((c) => c.id === a.clientId); return ( diff --git a/apps/web/vitest.config.ts b/apps/web/vitest.config.ts new file mode 100644 index 0000000..d913ed3 --- /dev/null +++ b/apps/web/vitest.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + passWithNoTests: true, + }, +}); diff --git a/packages/db/package.json b/packages/db/package.json index e4af333..109eeab 100644 --- a/packages/db/package.json +++ b/packages/db/package.json @@ -15,6 +15,7 @@ "postgres": "^3.4.5" }, "devDependencies": { + "@types/node": "^22.10.7", "drizzle-kit": "^0.30.4", "typescript": "^5.7.3" } diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index d161520..9ea804f 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -3,6 +3,7 @@ import postgres from "postgres"; import * as schema from "./schema.js"; export * from "./schema.js"; +export { and, asc, desc, eq, gte, gt, lt, lte, ne, or, sql } from "drizzle-orm"; let _db: ReturnType | null = null; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94c81f5..4086c50 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -19,9 +19,15 @@ importers: '@hono/node-server': specifier: ^1.13.7 version: 1.19.11(hono@4.12.8) + '@hono/zod-validator': + specifier: ^0.4.3 + version: 0.4.3(hono@4.12.8)(zod@3.25.76) hono: specifier: ^4.6.17 version: 4.12.8 + jose: + specifier: ^5.9.6 + version: 5.10.0 openid-client: specifier: ^6.1.7 version: 6.8.2 @@ -100,6 +106,9 @@ importers: specifier: ^3.4.5 version: 3.4.8 devDependencies: + '@types/node': + specifier: ^22.10.7 + version: 22.19.15 drizzle-kit: specifier: ^0.30.4 version: 0.30.6 @@ -1265,6 +1274,12 @@ packages: peerDependencies: hono: ^4 + '@hono/zod-validator@0.4.3': + resolution: {integrity: sha512-xIgMYXDyJ4Hj6ekm9T9Y27s080Nl9NXHcJkOvkXPhubOLj8hZkOL8pDnnXfvCf5xEE8Q4oMFenQUZZREUY2gqQ==} + peerDependencies: + hono: '>=3.9.0' + zod: ^3.19.1 + '@humanfs/core@0.19.1': resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} engines: {node: '>=18.18.0'} @@ -2370,6 +2385,9 @@ packages: engines: {node: '>=10'} hasBin: true + jose@5.10.0: + resolution: {integrity: sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==} + jose@6.2.1: resolution: {integrity: sha512-jUaKr1yrbfaImV7R2TN/b3IcZzsw38/chqMpo2XJ7i2F8AfM/lA4G1goC3JVEwg0H7UldTmSt3P68nt31W7/mw==} @@ -4171,6 +4189,11 @@ snapshots: dependencies: hono: 4.12.8 + '@hono/zod-validator@0.4.3(hono@4.12.8)(zod@3.25.76)': + dependencies: + hono: 4.12.8 + zod: 3.25.76 + '@humanfs/core@0.19.1': {} '@humanfs/node@0.16.7': @@ -5367,6 +5390,8 @@ snapshots: filelist: 1.0.6 picocolors: 1.1.1 + jose@5.10.0: {} + jose@6.2.1: {} js-tokens@4.0.0: {}