nextjs-hackathon-stack 0.1.24 → 0.1.26

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nextjs-hackathon-stack",
3
- "version": "0.1.24",
3
+ "version": "0.1.26",
4
4
  "description": "Scaffold a full-stack Next.js hackathon starter",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,83 @@
1
+ ---
2
+ name: backend
3
+ description: Backend specialist for Node.js, Next.js server-side, Supabase RLS policies, and Drizzle ORM schema/migrations.
4
+ model: inherit
5
+ readonly: false
6
+ ---
7
+
8
+ # Backend Agent
9
+
10
+ ## Responsibilities
11
+ - Build Next.js Server Actions, API routes, and middleware
12
+ - Define Drizzle ORM schemas and manage migrations via CLI
13
+ - Configure Supabase RLS policies and auth flows
14
+ - Select appropriate runtime (Edge vs Node.js)
15
+
16
+ ## Rules
17
+
18
+ ### Drizzle ORM — Schema Definition Only
19
+ ```typescript
20
+ // ✅ Use Drizzle for schema definition
21
+ export const users = pgTable("users", {
22
+ id: uuid("id").primaryKey().defaultRandom(),
23
+ email: text("email").notNull().unique(),
24
+ createdAt: timestamp("created_at").defaultNow().notNull(),
25
+ });
26
+
27
+ // ✅ Auto-generate Zod from Drizzle — never write Zod for DB types manually
28
+ export const insertUserSchema = createInsertSchema(users);
29
+ export const selectUserSchema = createSelectSchema(users);
30
+
31
+ // ❌ NEVER use Drizzle for runtime queries — bypasses RLS
32
+ const users = await db.select().from(usersTable); // WRONG
33
+ ```
34
+
35
+ ### Runtime Queries — supabase-js (RLS always active)
36
+ ```typescript
37
+ // ✅ All runtime queries via supabase-js
38
+ const { data, error } = await supabase.from("users").select("*").eq("id", userId);
39
+
40
+ // ❌ NEVER use Drizzle for runtime queries
41
+ ```
42
+
43
+ ### Migration Workflow
44
+ **NEVER create or edit files in `src/shared/db/migrations/` directly.**
45
+
46
+ Always follow this workflow:
47
+ 1. Edit schema: `src/shared/db/schema.ts`
48
+ 2. Generate migration: `pnpm db:generate`
49
+ 3. Review the generated SQL in `src/shared/db/migrations/`
50
+ 4. Apply migration: `pnpm db:migrate`
51
+ 5. Dev shortcut (push without migration file): `pnpm db:push`
52
+
53
+ ### Supabase RLS
54
+ - Enable RLS on ALL tables — no exceptions
55
+ - Every table must have explicit `SELECT`, `INSERT`, `UPDATE`, `DELETE` policies
56
+ - Use `auth.uid()` in policies to scope to the authenticated user
57
+
58
+ ### Server Actions
59
+ ```typescript
60
+ // ✅ Mutations via Server Actions with Zod validation
61
+ "use server";
62
+ export async function createUser(formData: FormData) {
63
+ const parsed = insertUserSchema.safeParse(Object.fromEntries(formData));
64
+ if (!parsed.success) return { error: parsed.error.flatten() };
65
+ // ... supabase-js mutation
66
+ }
67
+
68
+ // ❌ Never expose DATABASE_URL to the client
69
+ ```
70
+
71
+ ### Environment Variables
72
+ - `NEXT_PUBLIC_SUPABASE_URL` + `NEXT_PUBLIC_SUPABASE_ANON_KEY` — client-safe
73
+ - `DATABASE_URL` — server-only (Drizzle migrations only), NEVER prefix with `NEXT_PUBLIC_`
74
+
75
+ ### Runtime Selection
76
+ - Default to **Node.js runtime** for Server Actions and API routes
77
+ - Use **Edge runtime** only when latency is critical and no Node.js APIs are needed
78
+
79
+ ## Guardrails
80
+ - Never write migration SQL directly — always use `pnpm db:generate`
81
+ - Never expose `DATABASE_URL` to the browser
82
+ - RLS policies are required before any table goes to production
83
+ - Validate all mutation inputs with Zod on the server side
@@ -12,7 +12,7 @@ readonly: true
12
12
  ### Authentication & Authorization
13
13
  - [ ] Supabase RLS enabled on all tables
14
14
  - [ ] Auth check in every protected API route
15
- - [ ] Session validation in middleware
15
+ - [ ] Session validation in proxy
16
16
  - [ ] No hardcoded credentials or API keys
17
17
 
18
18
  ### Input Validation
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: technical-lead
3
- description: Architecture decisions, code review, and PR review. Use when planning features, reviewing PRs, or making architectural decisions about the codebase.
3
+ description: Orchestrator and architecture owner. Receives all requests, delegates to specialist agents, and reviews architectural decisions.
4
4
  model: inherit
5
5
  readonly: false
6
6
  ---
@@ -8,29 +8,55 @@ readonly: false
8
8
  # Technical Lead Agent
9
9
 
10
10
  ## Responsibilities
11
+ - Act as the default entry point for all requests
12
+ - Classify requests and delegate to the appropriate specialist agent(s)
13
+ - Launch multiple agents in parallel for cross-domain tasks
11
14
  - Validate architectural decisions against project conventions
12
- - Review code for SOLID, KISS, DRY principles
13
15
  - Ensure proper layer separation and no circular dependencies
14
16
  - Verify TDD compliance (tests exist before implementation)
15
17
 
16
- ## Review Checklist
18
+ ## Orchestration
19
+
20
+ The technical-lead is the orchestrator. Every request passes through here first. Delegate to the appropriate specialist based on domain:
21
+
22
+ | Agent | Use when |
23
+ |---|---|
24
+ | `frontend` | UI components, pages, layouts, Tailwind, shadcn/ui, accessibility |
25
+ | `backend` | Server Actions, API routes, Supabase RLS, Drizzle schema, migrations |
26
+ | `business-intelligence` | Requirements definition, user stories, acceptance criteria |
27
+ | `test-qa` | Writing tests, TDD workflow, coverage enforcement |
28
+ | `code-reviewer` | Branch/PR review (readonly) |
29
+ | `security-researcher` | Security audits, vulnerability scanning (readonly) |
30
+
31
+ ## Delegation Rules
32
+
33
+ - **Single-domain** → delegate to the matching agent
34
+ - **Cross-domain** → launch multiple agents in parallel (e.g., a new feature needs `business-intelligence` for requirements + `backend` for API + `frontend` for UI)
35
+ - Always delegate code review to `code-reviewer` — do not duplicate its checklist here
36
+ - Always delegate security audits to `security-researcher`
37
+ - The technical-lead retains final say on all architectural decisions
38
+
39
+ ## Workflow Sequences
40
+
41
+ ### New feature
42
+ `business-intelligence` → `test-qa` (RED) → `backend`/`frontend` (GREEN) → `code-reviewer` → `security-researcher`
43
+
44
+ ### Bug fix
45
+ `test-qa` (reproduce with failing test) → `backend`/`frontend` (fix) → `code-reviewer`
46
+
47
+ ### Refactor
48
+ `technical-lead` (plan) → `backend`/`frontend` (implement) → `test-qa` (verify) → `code-reviewer`
49
+
50
+ ## Architectural Review Checklist
51
+
17
52
  - [ ] Zero `any` types
18
- - [ ] Zero comments (code is self-documenting)
19
- - [ ] Functions 20 lines, files ≤ 200 lines
20
- - [ ] Tests exist and were written BEFORE implementation
21
- - [ ] Coverage is 100% on all new/changed files
22
- - [ ] No circular imports (features → shared, not reverse)
23
- - [ ] Proper error handling (no silent failures)
24
- - [ ] No magic numbers or strings
25
- - [ ] Tests were NOT weakened or modified to make code pass (check git diff for test changes alongside impl)
26
- - [ ] All branches covered (if/else, ternary, ??, catch)
27
- - [ ] Edge cases tested (null, empty, boundary, error, auth expired)
28
- - [ ] Every test follows AAA pattern with labeled comments
53
+ - [ ] No circular imports (`features shared`, not reverse)
54
+ - [ ] Proper layer separation
55
+ - [ ] Edge runtime on all AI routes (Risk 3)
56
+ - [ ] TanStack Query is NOT used for mutations (Risk 1)
29
57
 
30
58
  ## Guardrails
31
59
  - Reject any PR without test coverage
32
60
  - Reject any `any` type usage
33
61
  - Reject implementations without prior test (TDD violation)
34
62
  - Enforce `features/* → shared/*` dependency direction
35
- - Verify Edge runtime on all AI routes (Risk 3)
36
- - Verify TanStack Query is NOT used for mutations (Risk 1)
@@ -6,7 +6,7 @@ alwaysApply: true
6
6
  # Stack Overview
7
7
 
8
8
  ## Technology Stack
9
- - **Framework**: Next.js 15 (App Router)
9
+ - **Framework**: Next.js 16 (App Router)
10
10
  - **Database**: Supabase (PostgreSQL) with Drizzle ORM for schema/migrations
11
11
  - **Auth**: Supabase Auth via `@supabase/ssr`
12
12
  - **ORM**: Drizzle + `drizzle-zod` (schema + migrations ONLY — no runtime queries)
@@ -0,0 +1,27 @@
1
+ ---
2
+ description: Migration files are auto-generated by Drizzle CLI. NEVER create, edit, or delete them directly.
3
+ globs: ["src/shared/db/migrations/**"]
4
+ ---
5
+
6
+ # Migration Files — Generated Artifacts
7
+
8
+ Files in `src/shared/db/migrations/` are **auto-generated** by the Drizzle CLI. Do not create, edit, or delete them by hand.
9
+
10
+ ## Correct Workflow
11
+
12
+ 1. **Edit the schema**: `src/shared/db/schema.ts`
13
+ 2. **Generate the migration**: `pnpm db:generate`
14
+ 3. **Review** the generated SQL file in `src/shared/db/migrations/`
15
+ 4. **Apply the migration**: `pnpm db:migrate`
16
+
17
+ ## Available CLI Commands
18
+
19
+ | Command | Purpose |
20
+ |---|---|
21
+ | `pnpm db:generate` | Generate a new migration SQL file from schema changes |
22
+ | `pnpm db:migrate` | Apply pending migrations to the database |
23
+ | `pnpm db:push` | Push schema directly to DB (dev only, no migration file) |
24
+
25
+ ## Why
26
+
27
+ Hand-editing migration files causes schema drift and breaks the migration history. The CLI is the only safe source of truth for migration SQL.
@@ -1,9 +1,9 @@
1
1
  ---
2
- description: Next.js 15 App Router patterns.
2
+ description: Next.js 16 App Router patterns.
3
3
  globs: ["src/app/**"]
4
4
  ---
5
5
 
6
- # Next.js 15 Rules
6
+ # Next.js 16 Rules
7
7
 
8
8
  ## App Router Conventions
9
9
  - `page.tsx` — public route page component
@@ -11,7 +11,7 @@ globs: ["src/app/**"]
11
11
  - `loading.tsx` — Suspense boundary UI
12
12
  - `error.tsx` — Error boundary UI
13
13
  - `route.ts` — API route handler
14
- - `middleware.ts` — Edge middleware (project root)
14
+ - `proxy.ts` — Request proxy (project root, Node.js runtime)
15
15
 
16
16
  ## Route Groups
17
17
  - `(auth)` — unauthenticated routes
@@ -34,3 +34,8 @@ const users = await db.select().from(usersTable); // WRONG — bypasses RLS
34
34
  - Enable RLS on ALL tables in Supabase dashboard
35
35
  - Every table must have explicit policies
36
36
  - Test RLS policies in migrations
37
+
38
+ ## Environment Variables
39
+ - `NEXT_PUBLIC_SUPABASE_URL` + `NEXT_PUBLIC_SUPABASE_ANON_KEY` — client-safe, used in browser + server
40
+ - `DATABASE_URL` — server-only, used by Drizzle for migrations (`drizzle.config.ts`)
41
+ - See `.env.example` for all required variables
@@ -29,7 +29,7 @@ For each table in `src/shared/db/schema.ts`:
29
29
  - Confirm explicit policies exist
30
30
 
31
31
  ### 4. Auth Coverage
32
- - Verify `middleware.ts` protects all non-public routes
32
+ - Verify `proxy.ts` protects all non-public routes
33
33
  - Check every `route.ts` in protected features has auth check
34
34
  - Verify `app/(protected)/layout.tsx` has server-side auth check
35
35
 
@@ -0,0 +1,29 @@
1
+ <!-- BEGIN:nextjs-agent-rules -->
2
+
3
+ # Next.js: ALWAYS read docs before coding
4
+
5
+ Before any Next.js work, find and read the relevant doc in `node_modules/next/dist/docs/`. Your training data is outdated — the docs are the source of truth.
6
+
7
+ <!-- END:nextjs-agent-rules -->
8
+
9
+ # Environment variables
10
+
11
+ Read `.env.example` for the full list. Key vars:
12
+
13
+ | Variable | Visibility | Where to get it |
14
+ |---|---|---|
15
+ | `NEXT_PUBLIC_SUPABASE_URL` | Client + Server | Supabase > Project Settings > API |
16
+ | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Client + Server | Supabase > Project Settings > API |
17
+ | `DATABASE_URL` | Server only | Supabase > Project Settings > Database > Connection string (Session mode) |
18
+
19
+ - `NEXT_PUBLIC_*` vars are safe to expose to the browser — they are public by design
20
+ - Never add `NEXT_PUBLIC_` prefix to secret keys (`DATABASE_URL`, `AI_API_KEY`)
21
+ - `.env.local` is gitignored — never commit real credentials
22
+
23
+ # Migrations
24
+
25
+ Migration files in `src/shared/db/migrations/` are auto-generated. NEVER create, edit, or delete them directly.
26
+ - Edit schema: `src/shared/db/schema.ts`
27
+ - Generate migration: `pnpm db:generate`
28
+ - Apply migration: `pnpm db:migrate`
29
+ - Push schema (dev only): `pnpm db:push`
@@ -0,0 +1,16 @@
1
+ @AGENTS.md
2
+
3
+ # Architecture
4
+
5
+ Feature-based structure: `src/features/* → src/shared/*` (never reverse).
6
+
7
+ - **Drizzle** = schema + migrations ONLY. Never use Drizzle for runtime queries.
8
+ - **supabase-js** = all runtime queries. RLS is always active.
9
+ - **Zod schemas** for DB types: auto-generate via `drizzle-zod`, never write manually.
10
+ - **Migrations** are auto-generated — NEVER write/edit SQL files in `src/shared/db/migrations/` directly. Use `pnpm db:generate` + `pnpm db:migrate`.
11
+
12
+ # Testing
13
+
14
+ - 100% coverage required — `pnpm test:coverage` must pass
15
+ - AAA pattern (Arrange / Act / Assert) in every test
16
+ - Mock only external boundaries (Supabase, HTTP, DB) — never mock internal code
@@ -1,12 +1,12 @@
1
1
  # {{projectName}}
2
2
 
3
- Full-stack Next.js 15 hackathon starter.
3
+ Full-stack Next.js 16 hackathon starter.
4
4
 
5
5
  ## Stack
6
6
 
7
7
  | Layer | Tool |
8
8
  |---|---|
9
- | Framework | Next.js 15 + App Router |
9
+ | Framework | Next.js 16 + App Router |
10
10
  | Database | Supabase (PostgreSQL) |
11
11
  | Auth | Supabase Auth |
12
12
  | ORM | Drizzle (schema + migrations) |
@@ -2,6 +2,9 @@ import type { NextConfig } from "next";
2
2
 
3
3
  const nextConfig: NextConfig = {
4
4
  typedRoutes: true,
5
+ logging: {
6
+ browserToTerminal: true,
7
+ },
5
8
  turbopack: {
6
9
  root: import.meta.dirname,
7
10
  },
@@ -3,7 +3,7 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "scripts": {
6
- "dev": "next dev --turbopack",
6
+ "dev": "next dev",
7
7
  "build": "next build",
8
8
  "start": "next start",
9
9
  "lint": "eslint . --max-warnings 0",
@@ -21,7 +21,7 @@
21
21
  "prepare": "husky"
22
22
  },
23
23
  "dependencies": {
24
- "next": "^15",
24
+ "next": "^16.2",
25
25
  "@supabase/supabase-js": "^2",
26
26
  "@supabase/ssr": "^0.6",
27
27
  "drizzle-orm": "^0.44",
@@ -45,6 +45,7 @@
45
45
  "@types/node": "^22",
46
46
  "@types/react": "^19",
47
47
  "@types/react-dom": "^19",
48
+ "@next/env": "^16",
48
49
  "drizzle-kit": "^0.30",
49
50
  "vitest": "^3",
50
51
  "@vitejs/plugin-react": "^4",
@@ -60,7 +61,7 @@
60
61
  "eslint": "^9",
61
62
  "@eslint/js": "^9",
62
63
  "typescript-eslint": "^8",
63
- "@next/eslint-plugin-next": "^15",
64
+ "@next/eslint-plugin-next": "^16",
64
65
  "eslint-plugin-react": "^7",
65
66
  "eslint-plugin-react-hooks": "^5",
66
67
  "eslint-plugin-import-x": "^4",
@@ -2,7 +2,7 @@ import type { NextRequest } from "next/server";
2
2
 
3
3
  import { updateSession } from "@/shared/lib/supabase/middleware";
4
4
 
5
- export async function middleware(request: NextRequest) {
5
+ export async function proxy(request: NextRequest) {
6
6
  return updateSession(request);
7
7
  }
8
8
 
@@ -5,9 +5,9 @@ export default function LoginPage() {
5
5
  <main className="flex min-h-screen items-center justify-center bg-muted/40 px-4">
6
6
  <div className="w-full max-w-sm space-y-6">
7
7
  <div className="text-center">
8
- <h1 className="text-3xl font-bold tracking-tight">Welcome back</h1>
8
+ <h1 className="text-3xl font-bold tracking-tight">Bienvenido de nuevo</h1>
9
9
  <p className="mt-2 text-sm text-muted-foreground">
10
- Sign in to your account to continue
10
+ Inicia sesión en tu cuenta para continuar
11
11
  </p>
12
12
  </div>
13
13
  <LoginForm />
@@ -8,12 +8,12 @@ vi.mock("@/features/auth/components/login-form", () => ({
8
8
  }));
9
9
 
10
10
  describe("LoginPage", () => {
11
- it("renders the welcome heading", () => {
11
+ it("renders the welcome heading in Spanish", () => {
12
12
  // Arrange + Act
13
13
  render(<LoginPage />);
14
14
 
15
15
  // Assert
16
- expect(screen.getByText(/welcome back/i)).toBeInTheDocument();
16
+ expect(screen.getByText(/bienvenido de nuevo/i)).toBeInTheDocument();
17
17
  });
18
18
 
19
19
  it("renders the LoginForm component", () => {
@@ -24,11 +24,11 @@ describe("LoginPage", () => {
24
24
  expect(screen.getByTestId("login-form")).toBeInTheDocument();
25
25
  });
26
26
 
27
- it("renders the sign-in description", () => {
27
+ it("renders the sign-in description in Spanish", () => {
28
28
  // Arrange + Act
29
29
  render(<LoginPage />);
30
30
 
31
31
  // Assert
32
- expect(screen.getByText(/sign in to your account/i)).toBeInTheDocument();
32
+ expect(screen.getByText(/inicia sesión en tu cuenta/i)).toBeInTheDocument();
33
33
  });
34
34
  });
@@ -7,16 +7,16 @@ vi.mock("@/shared/lib/supabase/middleware", () => ({
7
7
  updateSession: (...args: unknown[]): unknown => mockUpdateSession(...args),
8
8
  }));
9
9
 
10
- describe("middleware", () => {
10
+ describe("proxy", () => {
11
11
  it("delegates to updateSession", async () => {
12
12
  // Arrange
13
13
  const mockResponse = new Response(null, { status: 200 });
14
14
  mockUpdateSession.mockResolvedValue(mockResponse);
15
- const mod = await import("../../../middleware") as { middleware: (req: NextRequest) => Promise<Response> };
15
+ const mod = await import("../../../proxy") as { proxy: (req: NextRequest) => Promise<Response> };
16
16
  const req = new NextRequest("http://localhost/dashboard");
17
17
 
18
18
  // Act
19
- const response = await mod.middleware(req);
19
+ const response = await mod.proxy(req);
20
20
 
21
21
  // Assert
22
22
  expect(mockUpdateSession).toHaveBeenCalledWith(req);
@@ -25,7 +25,7 @@ describe("middleware", () => {
25
25
 
26
26
  it("exports a matcher config", async () => {
27
27
  // Arrange + Act
28
- const mod = await import("../../../middleware") as { config: { matcher: string[] } };
28
+ const mod = await import("../../../proxy") as { config: { matcher: string[] } };
29
29
 
30
30
  // Assert
31
31
  expect(mod.config.matcher).toBeDefined();