diff --git a/app/(auth)/callback/route.ts b/app/(auth)/callback/route.ts
deleted file mode 100644
index 937b8ab..0000000
--- a/app/(auth)/callback/route.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-import { createClient } from "@/lib/supabase/server";
-import { NextResponse } from "next/server";
-
-/**
- * Handles OAuth provider callbacks and magic link confirmations.
- * Supabase redirects here after the user authenticates externally.
- */
-export async function GET(request: Request) {
- const { searchParams, origin } = new URL(request.url);
- const code = searchParams.get("code");
-
- if (code) {
- const supabase = await createClient();
- const { error } = await supabase.auth.exchangeCodeForSession(code);
-
- if (!error) {
- return NextResponse.redirect(`${origin}/`);
- }
- }
-
- // Return to an error page if the code exchange fails or no code is present
- return NextResponse.redirect(`${origin}/auth/error`);
-}
diff --git a/app/api/health/route.ts b/app/api/health/route.ts
deleted file mode 100644
index 4f4d29e..0000000
--- a/app/api/health/route.ts
+++ /dev/null
@@ -1,33 +0,0 @@
-import { createClient } from "@/lib/supabase/server";
-import { NextResponse } from "next/server";
-
-/**
- * GET /api/health
- * Returns database connection status.
- * Used by the home page and external health monitoring.
- */
-export async function GET() {
- try {
- const supabase = await createClient();
- const { error } = await supabase.from("demo_items").select("id").limit(1);
-
- if (error) {
- console.error("[health] DB query error:", error.message);
- return NextResponse.json(
- { status: "error", message: "Database connection failed" },
- { status: 503 },
- );
- }
-
- return NextResponse.json({
- status: "ok",
- timestamp: new Date().toISOString(),
- });
- } catch (err) {
- console.error("[health] Unexpected error:", err);
- return NextResponse.json(
- { status: "error", message: "Internal server error" },
- { status: 500 },
- );
- }
-}
diff --git a/bun.lock b/bun.lock
index fd82895..02b6018 100644
--- a/bun.lock
+++ b/bun.lock
@@ -5,8 +5,6 @@
"": {
"name": "hackathon-starter",
"dependencies": {
- "@supabase/ssr": "^0.5.0",
- "@supabase/supabase-js": "^2.49.0",
"next": "^15.2.0",
"openai": "^6.33.0",
"react": "^19.0.0",
@@ -289,20 +287,6 @@
"@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="],
- "@supabase/auth-js": ["@supabase/auth-js@2.97.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-2Og/1lqp+AIavr8qS2X04aSl8RBY06y4LrtIAGxat06XoXYiDxKNQMQzWDAKm1EyZFZVRNH48DO5YvIZ7la5fQ=="],
-
- "@supabase/functions-js": ["@supabase/functions-js@2.97.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-fSaA0ZeBUS9hMgpGZt5shIZvfs3Mvx2ZdajQT4kv/whubqDBAp3GU5W8iIXy21MRvKmO2NpAj8/Q6y+ZkZyF/w=="],
-
- "@supabase/postgrest-js": ["@supabase/postgrest-js@2.97.0", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-g4Ps0eaxZZurvfv/KGoo2XPZNpyNtjth9aW8eho9LZWM0bUuBtxPZw3ZQ6ERSpEGogshR+XNgwlSPIwcuHCNww=="],
-
- "@supabase/realtime-js": ["@supabase/realtime-js@2.97.0", "", { "dependencies": { "@types/phoenix": "^1.6.6", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" } }, "sha512-37Jw0NLaFP0CZd7qCan97D1zWutPrTSpgWxAw6Yok59JZoxp4IIKMrPeftJ3LZHmf+ILQOPy3i0pRDHM9FY36Q=="],
-
- "@supabase/ssr": ["@supabase/ssr@0.5.2", "", { "dependencies": { "@types/cookie": "^0.6.0", "cookie": "^0.7.0" }, "peerDependencies": { "@supabase/supabase-js": "^2.43.4" } }, "sha512-n3plRhr2Bs8Xun1o4S3k1CDv17iH5QY9YcoEvXX3bxV1/5XSasA0mNXYycFmADIdtdE6BG9MRjP5CGIs8qxC8A=="],
-
- "@supabase/storage-js": ["@supabase/storage-js@2.97.0", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-9f6NniSBfuMxOWKwEFb+RjJzkfMdJUwv9oHuFJKfe/5VJR8cd90qw68m6Hn0ImGtwG37TUO+QHtoOechxRJ1Yg=="],
-
- "@supabase/supabase-js": ["@supabase/supabase-js@2.97.0", "", { "dependencies": { "@supabase/auth-js": "2.97.0", "@supabase/functions-js": "2.97.0", "@supabase/postgrest-js": "2.97.0", "@supabase/realtime-js": "2.97.0", "@supabase/storage-js": "2.97.0" } }, "sha512-kTD91rZNO4LvRUHv4x3/4hNmsEd2ofkYhuba2VMUPRVef1RCmnHtm7rIws38Fg0yQnOSZOplQzafn0GSiy6GVg=="],
-
"@swc/helpers": ["@swc/helpers@0.5.15", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g=="],
"@testing-library/dom": ["@testing-library/dom@10.4.1", "", { "dependencies": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", "aria-query": "5.3.0", "dom-accessibility-api": "^0.5.9", "lz-string": "^1.5.0", "picocolors": "1.1.1", "pretty-format": "^27.0.2" } }, "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg=="],
@@ -323,20 +307,14 @@
"@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="],
- "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="],
-
"@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="],
"@types/node": ["@types/node@20.19.33", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-Rs1bVAIdBs5gbTIKza/tgpMuG1k3U/UMJLWecIMxNdJFDMzcM5LOiLVRYh3PilWEYDIeUDv7bpiHPLPsbydGcw=="],
- "@types/phoenix": ["@types/phoenix@1.6.7", "", {}, "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q=="],
-
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
"@types/react-dom": ["@types/react-dom@19.2.3", "", { "peerDependencies": { "@types/react": "^19.2.0" } }, "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ=="],
- "@types/ws": ["@types/ws@8.18.1", "", { "dependencies": { "@types/node": "*" } }, "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg=="],
-
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.7.0", "", { "dependencies": { "@babel/core": "^7.28.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", "react-refresh": "^0.17.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA=="],
"@vitest/coverage-v8": ["@vitest/coverage-v8@2.1.9", "", { "dependencies": { "@ampproject/remapping": "^2.3.0", "@bcoe/v8-coverage": "^0.2.3", "debug": "^4.3.7", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-lib-source-maps": "^5.0.6", "istanbul-reports": "^3.1.7", "magic-string": "^0.30.12", "magicast": "^0.3.5", "std-env": "^3.8.0", "test-exclude": "^7.0.1", "tinyrainbow": "^1.2.0" }, "peerDependencies": { "@vitest/browser": "2.1.9", "vitest": "2.1.9" }, "optionalPeers": ["@vitest/browser"] }, "sha512-Z2cOr0ksM00MpEfyVE8KXIYPEcBFxdbLSs56L8PO0QQMxt/6bDj45uQfxoc96v05KW3clk7vvgP0qfDit9DmfQ=="],
@@ -395,8 +373,6 @@
"convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="],
- "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="],
-
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
"css.escape": ["css.escape@1.5.1", "", {}, "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg=="],
@@ -483,8 +459,6 @@
"https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
- "iceberg-js": ["iceberg-js@0.8.1", "", {}, "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA=="],
-
"iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="],
"indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="],
diff --git a/components/features/DatabaseStatus.tsx b/components/features/DatabaseStatus.tsx
deleted file mode 100644
index a1230d1..0000000
--- a/components/features/DatabaseStatus.tsx
+++ /dev/null
@@ -1,71 +0,0 @@
-"use client";
-
-import type { DemoItem } from "@/types";
-
-interface DatabaseStatusProps {
- items: DemoItem[] | null;
- error: string | null;
-}
-
-/**
- * Renders the Supabase connection status and demo items on the home page.
- * Shows three states: success (items list), error, or empty.
- */
-export function DatabaseStatus({ items, error }: DatabaseStatusProps) {
- if (error) {
- return (
-
-
⚠ Connection error
-
{error}
-
- Check that your Supabase credentials in .env.local are correct and that your
- Supabase project is not paused.
-
-
- );
- }
-
- if (!items || items.length === 0) {
- return (
-
-
No demo items found
-
- Run supabase/seed.sql in your Supabase dashboard to add demo rows.
-
-
- );
- }
-
- return (
-
-
✓ Connected to Supabase
-
- {items.map((item) => (
- -
- {item.title}
-
- ))}
-
-
- );
-}
diff --git a/lib/supabase/client.ts b/lib/supabase/client.ts
deleted file mode 100644
index 1bdda32..0000000
--- a/lib/supabase/client.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-"use client";
-
-import { createBrowserClient } from "@supabase/ssr";
-
-/**
- * Creates a Supabase client for use in Client Components.
- * Uses the public anon key — Row Level Security controls data access.
- */
-export function createClient() {
- return createBrowserClient(
- process.env.NEXT_PUBLIC_SUPABASE_URL ?? "",
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "",
- );
-}
diff --git a/lib/supabase/middleware.ts b/lib/supabase/middleware.ts
deleted file mode 100644
index b1f7b83..0000000
--- a/lib/supabase/middleware.ts
+++ /dev/null
@@ -1,37 +0,0 @@
-import { createServerClient } from "@supabase/ssr";
-import { type NextRequest, NextResponse } from "next/server";
-
-/**
- * Refreshes the Supabase session on every request so the auth token stays
- * current. Called from middleware.ts which runs on the Edge Runtime.
- */
-export async function updateSession(request: NextRequest) {
- let supabaseResponse = NextResponse.next({ request });
-
- const supabase = createServerClient(
- process.env.NEXT_PUBLIC_SUPABASE_URL ?? "",
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "",
- {
- cookies: {
- getAll() {
- return request.cookies.getAll();
- },
- setAll(cookiesToSet: { name: string; value: string; options?: Record }[]) {
- for (const { name, value } of cookiesToSet) {
- request.cookies.set(name, value);
- }
- supabaseResponse = NextResponse.next({ request });
- for (const { name, value, options } of cookiesToSet) {
- supabaseResponse.cookies.set(name, value, options);
- }
- },
- },
- },
- );
-
- // Refresh the session — do NOT remove this call.
- // It keeps the auth token alive and syncs server/client state.
- await supabase.auth.getUser();
-
- return supabaseResponse;
-}
diff --git a/lib/supabase/server.ts b/lib/supabase/server.ts
deleted file mode 100644
index dc1d8dc..0000000
--- a/lib/supabase/server.ts
+++ /dev/null
@@ -1,32 +0,0 @@
-import { createServerClient } from "@supabase/ssr";
-import { cookies } from "next/headers";
-
-/**
- * Creates a Supabase client for use in Server Components and Route Handlers.
- * Reads auth session from cookies so the server can access user-specific data.
- */
-export async function createClient() {
- const cookieStore = await cookies();
-
- return createServerClient(
- process.env.NEXT_PUBLIC_SUPABASE_URL ?? "",
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "",
- {
- cookies: {
- getAll() {
- return cookieStore.getAll();
- },
- setAll(cookiesToSet: { name: string; value: string; options?: Record }[]) {
- try {
- for (const { name, value, options } of cookiesToSet) {
- cookieStore.set(name, value, options);
- }
- } catch {
- // setAll is called from a Server Component — cookies can only be
- // set from middleware or Route Handlers. This catch is expected.
- }
- },
- },
- },
- );
-}
diff --git a/package.json b/package.json
index a7725f5..896198e 100644
--- a/package.json
+++ b/package.json
@@ -14,8 +14,6 @@
"test:coverage": "vitest --coverage"
},
"dependencies": {
- "@supabase/ssr": "^0.5.0",
- "@supabase/supabase-js": "^2.49.0",
"next": "^15.2.0",
"openai": "^6.33.0",
"react": "^19.0.0",
diff --git a/supabase/seed.sql b/supabase/seed.sql
deleted file mode 100644
index 72dafde..0000000
--- a/supabase/seed.sql
+++ /dev/null
@@ -1,45 +0,0 @@
--- =============================================================
--- Hackathon Starter — Demo Seed Data
--- =============================================================
--- Run this once to set up the demo_items table used by the
--- home page to demonstrate the Supabase connection.
---
--- Option A: Supabase Dashboard → SQL Editor → paste & run
--- Option B: supabase db seed (if using Supabase CLI)
---
--- Once you have your own schema, delete this file or replace
--- it with your own migration/seed files.
--- =============================================================
-
--- Create demo table
-create table if not exists public.demo_items (
- id bigserial primary key,
- title text not null,
- created_at timestamptz not null default now()
-);
-
--- Enable Row Level Security (always enable RLS on every table)
-alter table public.demo_items enable row level security;
-
--- Allow anyone to read demo items (public read-only)
-do $$
-begin
- if not exists (
- select 1 from pg_policies
- where tablename = 'demo_items'
- and policyname = 'Anyone can read demo items'
- ) then
- create policy "Anyone can read demo items"
- on public.demo_items
- for select
- using (true);
- end if;
-end $$;
-
--- Seed data — replace with your own content
-insert into public.demo_items (title)
-values
- ('Welcome to your hackathon starter!'),
- ('Connected to Supabase ✓'),
- ('Replace this table with your own schema.')
-on conflict do nothing;
diff --git a/tests/api/health.test.ts b/tests/api/health.test.ts
deleted file mode 100644
index 1a4924a..0000000
--- a/tests/api/health.test.ts
+++ /dev/null
@@ -1,69 +0,0 @@
-import { describe, expect, it, vi } from "vitest";
-
-// Mock the Supabase server client before importing the route
-vi.mock("@/lib/supabase/server", () => ({
- createClient: vi.fn(),
-}));
-
-// Mock next/headers (required by server.ts)
-vi.mock("next/headers", () => ({
- cookies: vi.fn(() => ({
- getAll: vi.fn(() => []),
- set: vi.fn(),
- })),
-}));
-
-import { createClient } from "@/lib/supabase/server";
-
-describe("GET /api/health", () => {
- it("returns 200 with status:ok and timestamp when DB is reachable", async () => {
- const mockSelect = vi.fn().mockResolvedValue({ data: [{ id: 1 }], error: null });
- const mockFrom = vi.fn(() => ({ select: vi.fn(() => ({ limit: mockSelect })) }));
- vi.mocked(createClient).mockResolvedValue({ from: mockFrom } as never);
-
- const { GET } = await import("@/app/api/health/route");
- const response = await GET();
- const body = await response.json();
-
- expect(response.status).toBe(200);
- expect(body.status).toBe("ok");
- expect(typeof body.timestamp).toBe("string");
- expect(() => new Date(body.timestamp)).not.toThrow();
- });
-
- it("returns 503 with status:error when DB throws", async () => {
- const mockSelect = vi
- .fn()
- .mockResolvedValue({ data: null, error: new Error("Connection refused") });
- const mockFrom = vi.fn(() => ({ select: vi.fn(() => ({ limit: mockSelect })) }));
- vi.mocked(createClient).mockResolvedValue({ from: mockFrom } as never);
-
- vi.resetModules();
- vi.mock("@/lib/supabase/server", () => ({ createClient: vi.fn() }));
- const { GET } = await import("@/app/api/health/route");
- const response = await GET();
- const body = await response.json();
-
- expect(response.status).toBe(503);
- expect(body.status).toBe("error");
- expect(typeof body.message).toBe("string");
- // Must not expose raw error details
- expect(body.message).not.toContain("Connection refused");
- });
-
- it("response body matches api-health.md contract shape on success", async () => {
- const mockSelect = vi.fn().mockResolvedValue({ data: [{ id: 1 }], error: null });
- const mockFrom = vi.fn(() => ({ select: vi.fn(() => ({ limit: mockSelect })) }));
- vi.mocked(createClient).mockResolvedValue({ from: mockFrom } as never);
-
- vi.resetModules();
- vi.mock("@/lib/supabase/server", () => ({ createClient: vi.fn() }));
- const { GET } = await import("@/app/api/health/route");
- const response = await GET();
- const body = await response.json();
-
- expect(body).toHaveProperty("status");
- expect(body).toHaveProperty("timestamp");
- expect(Object.keys(body)).toHaveLength(2);
- });
-});
diff --git a/tests/components/DatabaseStatus.test.tsx b/tests/components/DatabaseStatus.test.tsx
deleted file mode 100644
index 652a3ec..0000000
--- a/tests/components/DatabaseStatus.test.tsx
+++ /dev/null
@@ -1,40 +0,0 @@
-import { DatabaseStatus } from "@/components/features/DatabaseStatus";
-import type { DemoItem } from "@/types";
-import { render, screen } from "@testing-library/react";
-import { describe, expect, it } from "vitest";
-
-const mockItems: DemoItem[] = [
- { id: 1, title: "Welcome to your hackathon starter!", created_at: "2026-02-23T00:00:00Z" },
- { id: 2, title: "Connected to Supabase ✓", created_at: "2026-02-23T00:00:00Z" },
-];
-
-describe("DatabaseStatus", () => {
- it("renders success state with items list when items are provided", () => {
- render();
-
- expect(screen.getByRole("heading", { name: /connected/i })).toBeInTheDocument();
- expect(screen.getByText("Welcome to your hackathon starter!")).toBeInTheDocument();
- expect(screen.getByText("Connected to Supabase ✓")).toBeInTheDocument();
- });
-
- it("renders error state with message when error prop is set", () => {
- render();
-
- expect(screen.getByText(/database connection failed/i)).toBeInTheDocument();
- // Should not show items list in error state
- expect(screen.queryByText("Welcome to your hackathon starter!")).not.toBeInTheDocument();
- });
-
- it("renders empty state message when items array is empty", () => {
- render();
-
- expect(screen.getByText(/no demo items found/i)).toBeInTheDocument();
- });
-
- it("does not show error indicator in success state", () => {
- render();
-
- // No error indicators in success state
- expect(screen.queryByRole("alert")).not.toBeInTheDocument();
- });
-});
diff --git a/vitest.config.ts b/vitest.config.ts
index 78bb40a..67e4904 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -12,8 +12,6 @@ export default defineConfig({
provider: "v8",
reporter: ["text", "json", "html"],
include: ["lib/**/*.ts", "lib/**/*.tsx"],
- // Supabase wrappers are thin SDK adapters tested via integration — exclude from thresholds
- exclude: ["lib/supabase/**"],
thresholds: {
lines: 80,
},