nextjs-hackathon-stack 0.1.24 → 0.1.25
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 +1 -1
- package/template/.cursor/agents/security-researcher.md +1 -1
- package/template/.cursor/rules/general.mdc +1 -1
- package/template/.cursor/rules/nextjs.mdc +3 -3
- package/template/.cursor/rules/supabase.mdc +5 -0
- package/template/.cursor/skills/security-audit/SKILL.md +1 -1
- package/template/AGENTS.md +21 -0
- package/template/CLAUDE.md +15 -0
- package/template/README.md +2 -2
- package/template/next.config.ts +3 -0
- package/template/package.json.tmpl +4 -3
- package/template/{middleware.ts → proxy.ts} +1 -1
- package/template/src/shared/__tests__/{middleware.test.ts → proxy.test.ts} +4 -4
package/package.json
CHANGED
|
@@ -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
|
|
15
|
+
- [ ] Session validation in proxy
|
|
16
16
|
- [ ] No hardcoded credentials or API keys
|
|
17
17
|
|
|
18
18
|
### Input Validation
|
|
@@ -6,7 +6,7 @@ alwaysApply: true
|
|
|
6
6
|
# Stack Overview
|
|
7
7
|
|
|
8
8
|
## Technology Stack
|
|
9
|
-
- **Framework**: Next.js
|
|
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)
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: Next.js
|
|
2
|
+
description: Next.js 16 App Router patterns.
|
|
3
3
|
globs: ["src/app/**"]
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# Next.js
|
|
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
|
-
- `
|
|
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 `
|
|
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,21 @@
|
|
|
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
|
|
@@ -0,0 +1,15 @@
|
|
|
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
|
+
|
|
11
|
+
# Testing
|
|
12
|
+
|
|
13
|
+
- 100% coverage required — `pnpm test:coverage` must pass
|
|
14
|
+
- AAA pattern (Arrange / Act / Assert) in every test
|
|
15
|
+
- Mock only external boundaries (Supabase, HTTP, DB) — never mock internal code
|
package/template/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# {{projectName}}
|
|
2
2
|
|
|
3
|
-
Full-stack Next.js
|
|
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
|
|
9
|
+
| Framework | Next.js 16 + App Router |
|
|
10
10
|
| Database | Supabase (PostgreSQL) |
|
|
11
11
|
| Auth | Supabase Auth |
|
|
12
12
|
| ORM | Drizzle (schema + migrations) |
|
package/template/next.config.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"version": "0.1.0",
|
|
4
4
|
"private": true,
|
|
5
5
|
"scripts": {
|
|
6
|
-
"dev": "next dev
|
|
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": "^
|
|
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": "^
|
|
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
|
|
5
|
+
export async function proxy(request: NextRequest) {
|
|
6
6
|
return updateSession(request);
|
|
7
7
|
}
|
|
8
8
|
|
|
@@ -7,16 +7,16 @@ vi.mock("@/shared/lib/supabase/middleware", () => ({
|
|
|
7
7
|
updateSession: (...args: unknown[]): unknown => mockUpdateSession(...args),
|
|
8
8
|
}));
|
|
9
9
|
|
|
10
|
-
describe("
|
|
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("../../../
|
|
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.
|
|
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("../../../
|
|
28
|
+
const mod = await import("../../../proxy") as { config: { matcher: string[] } };
|
|
29
29
|
|
|
30
30
|
// Assert
|
|
31
31
|
expect(mod.config.matcher).toBeDefined();
|