nextjs-hackathon-stack 0.1.0
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/LICENSE +21 -0
- package/README.md +83 -0
- package/dist/index.js +152 -0
- package/package.json +54 -0
- package/template/.cursor/agents/business-intelligence.md +38 -0
- package/template/.cursor/agents/code-reviewer.md +74 -0
- package/template/.cursor/agents/frontend.md +45 -0
- package/template/.cursor/agents/security-researcher.md +49 -0
- package/template/.cursor/agents/technical-lead.md +36 -0
- package/template/.cursor/agents/test-qa.md +85 -0
- package/template/.cursor/rules/ai.mdc +53 -0
- package/template/.cursor/rules/architecture.mdc +42 -0
- package/template/.cursor/rules/coding-standards.mdc +53 -0
- package/template/.cursor/rules/components.mdc +33 -0
- package/template/.cursor/rules/data-fetching.mdc +63 -0
- package/template/.cursor/rules/forms.mdc +59 -0
- package/template/.cursor/rules/general.mdc +52 -0
- package/template/.cursor/rules/nextjs.mdc +46 -0
- package/template/.cursor/rules/security.mdc +54 -0
- package/template/.cursor/rules/supabase.mdc +36 -0
- package/template/.cursor/rules/testing.mdc +116 -0
- package/template/.cursor/skills/create-api-route/SKILL.md +62 -0
- package/template/.cursor/skills/create-feature/SKILL.md +52 -0
- package/template/.cursor/skills/review-branch/SKILL.md +61 -0
- package/template/.cursor/skills/security-audit/SKILL.md +69 -0
- package/template/.env.example +24 -0
- package/template/.requirements/README.md +15 -0
- package/template/.requirements/auth.md +50 -0
- package/template/.requirements/template.md +25 -0
- package/template/README.md +98 -0
- package/template/components.json +19 -0
- package/template/drizzle.config.ts +10 -0
- package/template/eslint.config.ts +66 -0
- package/template/middleware.ts +10 -0
- package/template/next.config.ts +31 -0
- package/template/package.json.tmpl +62 -0
- package/template/playwright.config.ts +22 -0
- package/template/src/app/(auth)/login/page.tsx +12 -0
- package/template/src/app/(protected)/layout.tsx +19 -0
- package/template/src/app/(protected)/page.tsx +16 -0
- package/template/src/app/api/auth/callback/route.ts +21 -0
- package/template/src/app/globals.css +1 -0
- package/template/src/app/layout.tsx +21 -0
- package/template/src/e2e/chat.spec.ts +8 -0
- package/template/src/e2e/home.spec.ts +8 -0
- package/template/src/e2e/login.spec.ts +21 -0
- package/template/src/features/auth/__tests__/login-form.test.tsx +43 -0
- package/template/src/features/auth/__tests__/use-auth.test.ts +46 -0
- package/template/src/features/auth/actions/login.action.ts +38 -0
- package/template/src/features/auth/actions/logout.action.ts +10 -0
- package/template/src/features/auth/components/login-form.tsx +80 -0
- package/template/src/features/auth/hooks/use-auth.ts +17 -0
- package/template/src/features/chat/__tests__/chat-ui.test.tsx +30 -0
- package/template/src/features/chat/__tests__/route.test.ts +19 -0
- package/template/src/features/chat/api/route.ts +16 -0
- package/template/src/features/chat/components/chat-ui.tsx +47 -0
- package/template/src/features/chat/hooks/use-chat.ts +1 -0
- package/template/src/features/tts/__tests__/route.test.ts +13 -0
- package/template/src/features/tts/__tests__/tts-player.test.tsx +20 -0
- package/template/src/features/tts/api/route.ts +14 -0
- package/template/src/features/tts/components/tts-player.tsx +59 -0
- package/template/src/features/video/__tests__/route.test.ts +13 -0
- package/template/src/features/video/__tests__/video-generator.test.tsx +20 -0
- package/template/src/features/video/api/route.ts +14 -0
- package/template/src/features/video/components/video-generator.tsx +56 -0
- package/template/src/shared/__tests__/ai.test.ts +8 -0
- package/template/src/shared/__tests__/minimax-media.test.ts +57 -0
- package/template/src/shared/__tests__/providers.test.tsx +14 -0
- package/template/src/shared/__tests__/schema.test.ts +18 -0
- package/template/src/shared/__tests__/supabase-client.test.ts +13 -0
- package/template/src/shared/__tests__/supabase-server.test.ts +22 -0
- package/template/src/shared/components/providers.tsx +19 -0
- package/template/src/shared/db/index.ts +7 -0
- package/template/src/shared/db/schema.ts +15 -0
- package/template/src/shared/lib/ai.ts +8 -0
- package/template/src/shared/lib/minimax-media.ts +63 -0
- package/template/src/shared/lib/supabase/client.ts +8 -0
- package/template/src/shared/lib/supabase/middleware.ts +40 -0
- package/template/src/shared/lib/supabase/server.ts +26 -0
- package/template/src/test-setup.ts +1 -0
- package/template/tailwind.css +20 -0
- package/template/tsconfig.json +31 -0
- package/template/vitest.config.ts +33 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: create-feature
|
|
3
|
+
description: Scaffold a new feature following TDD and project conventions. Use when starting a new feature or user story.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Create Feature Skill
|
|
7
|
+
|
|
8
|
+
## Process
|
|
9
|
+
|
|
10
|
+
### 1. Requirements First
|
|
11
|
+
- Check `.requirements/` for existing spec
|
|
12
|
+
- If none exists, ask the user to describe the feature
|
|
13
|
+
- Document in `.requirements/<feature-name>.md` using the Given/When/Then template
|
|
14
|
+
|
|
15
|
+
### 2. Create Feature Structure
|
|
16
|
+
```
|
|
17
|
+
src/features/<feature-name>/
|
|
18
|
+
├── components/
|
|
19
|
+
├── actions/
|
|
20
|
+
├── hooks/
|
|
21
|
+
├── api/
|
|
22
|
+
└── __tests__/
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
### 3. TDD: RED Phase
|
|
26
|
+
Write ALL test files first:
|
|
27
|
+
- `__tests__/<component>.test.tsx` — component tests
|
|
28
|
+
- `__tests__/use-<feature>.test.ts` — hook tests
|
|
29
|
+
- `__tests__/<action>.test.ts` — action tests
|
|
30
|
+
|
|
31
|
+
Run `npm run test:unit` — all tests must FAIL (RED).
|
|
32
|
+
|
|
33
|
+
### 4. TDD: GREEN Phase
|
|
34
|
+
Implement minimum code to pass each test:
|
|
35
|
+
- Components → actions → hooks → API routes
|
|
36
|
+
|
|
37
|
+
Run `npm run test:unit` — all tests must PASS (GREEN).
|
|
38
|
+
|
|
39
|
+
### 5. Refactor
|
|
40
|
+
Clean up while keeping tests green.
|
|
41
|
+
|
|
42
|
+
### 6. Verify
|
|
43
|
+
```bash
|
|
44
|
+
npm run test:coverage # Must show 100%
|
|
45
|
+
npm run lint # Must pass with 0 warnings
|
|
46
|
+
npm run typecheck # Must pass with 0 errors
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Guardrails
|
|
50
|
+
- NEVER start implementation before tests exist
|
|
51
|
+
- 100% coverage before marking done
|
|
52
|
+
- Follow `features/* → shared/*` dependency direction
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: review-branch
|
|
3
|
+
description: Review current branch changes against develop. Runs full code quality checklist.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Review Branch Skill
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
### 1. Get Changes
|
|
12
|
+
```bash
|
|
13
|
+
git diff develop...HEAD
|
|
14
|
+
```
|
|
15
|
+
Or for a specific branch:
|
|
16
|
+
```bash
|
|
17
|
+
git diff develop...<branch-name>
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 2. Check Each Changed File Against:
|
|
21
|
+
|
|
22
|
+
#### Code Quality
|
|
23
|
+
- Zero `any` types
|
|
24
|
+
- Zero comments
|
|
25
|
+
- Functions ≤ 20 lines, files ≤ 200 lines
|
|
26
|
+
- No magic numbers/strings
|
|
27
|
+
|
|
28
|
+
#### Tests
|
|
29
|
+
- Test files exist for every changed source file
|
|
30
|
+
- Tests cover new code paths
|
|
31
|
+
- No implementation-detail testing
|
|
32
|
+
|
|
33
|
+
#### Architecture
|
|
34
|
+
- Correct imports (features → shared only)
|
|
35
|
+
- Server Actions for mutations
|
|
36
|
+
- Edge runtime on AI routes
|
|
37
|
+
|
|
38
|
+
#### Security
|
|
39
|
+
- Input validation at boundaries
|
|
40
|
+
- Auth checks present
|
|
41
|
+
- No exposed secrets
|
|
42
|
+
|
|
43
|
+
### 3. Generate Report
|
|
44
|
+
```
|
|
45
|
+
## Branch Review: <branch-name>
|
|
46
|
+
|
|
47
|
+
### Changed Files
|
|
48
|
+
- [list each file]
|
|
49
|
+
|
|
50
|
+
### Issues Found
|
|
51
|
+
- [file:line]: [issue] → [fix]
|
|
52
|
+
|
|
53
|
+
### Verdict: PASS / FAIL
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 4. Run Automated Checks
|
|
57
|
+
```bash
|
|
58
|
+
npm run lint
|
|
59
|
+
npm run typecheck
|
|
60
|
+
npm run test:coverage
|
|
61
|
+
```
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: security-audit
|
|
3
|
+
description: Run a security audit on the codebase. Checks OWASP Top 10, RLS, secrets, and dependencies.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Security Audit Skill
|
|
8
|
+
|
|
9
|
+
## Process
|
|
10
|
+
|
|
11
|
+
### 1. Dependency Audit
|
|
12
|
+
```bash
|
|
13
|
+
npm audit
|
|
14
|
+
```
|
|
15
|
+
Categorize findings by: Critical / High / Medium / Low
|
|
16
|
+
|
|
17
|
+
### 2. Secret Scan
|
|
18
|
+
Search for hardcoded secrets:
|
|
19
|
+
```bash
|
|
20
|
+
grep -r "sk_" src/ --include="*.ts"
|
|
21
|
+
grep -r "apiKey\s*=" src/ --include="*.ts"
|
|
22
|
+
grep -r "password\s*=" src/ --include="*.ts"
|
|
23
|
+
```
|
|
24
|
+
Check `.env.example` has no real values.
|
|
25
|
+
|
|
26
|
+
### 3. RLS Verification
|
|
27
|
+
For each table in `src/shared/db/schema.ts`:
|
|
28
|
+
- Confirm RLS is enabled in Supabase dashboard
|
|
29
|
+
- Confirm explicit policies exist
|
|
30
|
+
|
|
31
|
+
### 4. Auth Coverage
|
|
32
|
+
- Verify `middleware.ts` protects all non-public routes
|
|
33
|
+
- Check every `route.ts` in protected features has auth check
|
|
34
|
+
- Verify `app/(protected)/layout.tsx` has server-side auth check
|
|
35
|
+
|
|
36
|
+
### 5. Input Validation
|
|
37
|
+
- Every `route.ts` has Zod schema validation
|
|
38
|
+
- Every `.action.ts` has Zod schema validation
|
|
39
|
+
|
|
40
|
+
### 6. XSS Check
|
|
41
|
+
```bash
|
|
42
|
+
grep -r "dangerouslySetInnerHTML" src/ --include="*.tsx"
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 7. Security Headers
|
|
46
|
+
Verify in `next.config.ts`:
|
|
47
|
+
- `Content-Security-Policy`
|
|
48
|
+
- `Strict-Transport-Security`
|
|
49
|
+
- `X-Frame-Options`
|
|
50
|
+
|
|
51
|
+
## Output Format
|
|
52
|
+
```
|
|
53
|
+
## Security Audit Report
|
|
54
|
+
|
|
55
|
+
### Critical 🔴
|
|
56
|
+
[findings]
|
|
57
|
+
|
|
58
|
+
### High 🟠
|
|
59
|
+
[findings]
|
|
60
|
+
|
|
61
|
+
### Medium 🟡
|
|
62
|
+
[findings]
|
|
63
|
+
|
|
64
|
+
### Low 🟢
|
|
65
|
+
[findings]
|
|
66
|
+
|
|
67
|
+
### Passed ✅
|
|
68
|
+
[clean checks]
|
|
69
|
+
```
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# =============================================================================
|
|
2
|
+
# REQUIRED — fill these in before running npm run dev
|
|
3
|
+
# =============================================================================
|
|
4
|
+
|
|
5
|
+
# Supabase — https://supabase.com > Project Settings > API
|
|
6
|
+
NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
|
|
7
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
|
8
|
+
|
|
9
|
+
# Supabase DB — https://supabase.com > Project Settings > Database > Connection string (URI)
|
|
10
|
+
DATABASE_URL=postgresql://postgres:[password]@db.your-project-id.supabase.co:5432/postgres
|
|
11
|
+
|
|
12
|
+
# MiniMax — https://www.minimaxi.chat > API Keys
|
|
13
|
+
MINIMAX_API_KEY=your-minimax-api-key
|
|
14
|
+
|
|
15
|
+
# Vercel AI Gateway — https://vercel.com > AI > Gateways
|
|
16
|
+
# Format: https://gateway.ai.vercel.app/v1/{team-id}/{gateway-id}
|
|
17
|
+
AI_GATEWAY_URL=https://gateway.ai.vercel.app/v1/your-team-id/your-gateway-id
|
|
18
|
+
|
|
19
|
+
# =============================================================================
|
|
20
|
+
# OPTIONAL
|
|
21
|
+
# =============================================================================
|
|
22
|
+
|
|
23
|
+
# App URL (used for OAuth callbacks)
|
|
24
|
+
NEXT_PUBLIC_APP_URL=http://localhost:3000
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Requirements
|
|
2
|
+
|
|
3
|
+
This directory contains feature requirements written in Given/When/Then format.
|
|
4
|
+
|
|
5
|
+
## Structure
|
|
6
|
+
- `README.md` — this file
|
|
7
|
+
- `template.md` — requirement template
|
|
8
|
+
- `auth.md` — authentication requirements
|
|
9
|
+
- `<feature>.md` — per-feature requirements
|
|
10
|
+
|
|
11
|
+
## Process
|
|
12
|
+
1. Define requirements BEFORE starting implementation
|
|
13
|
+
2. Each acceptance criterion maps to at least one test case
|
|
14
|
+
3. Validate implementation against criteria before marking done
|
|
15
|
+
4. Use the `business-intelligence` Cursor agent to help define requirements
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# Feature: Authentication
|
|
2
|
+
|
|
3
|
+
## User Story
|
|
4
|
+
As a user, I want to sign in with email and password so that I can access protected features.
|
|
5
|
+
|
|
6
|
+
## Acceptance Criteria
|
|
7
|
+
|
|
8
|
+
### AC1: Successful Login
|
|
9
|
+
**Given** I am on the login page
|
|
10
|
+
**When** I enter valid email and password
|
|
11
|
+
**Then** I am redirected to the home page and my session is established
|
|
12
|
+
|
|
13
|
+
### AC2: Invalid Credentials
|
|
14
|
+
**Given** I am on the login page
|
|
15
|
+
**When** I enter an incorrect email or password
|
|
16
|
+
**Then** I see an error message "Invalid login credentials"
|
|
17
|
+
|
|
18
|
+
### AC3: Validation Errors
|
|
19
|
+
**Given** I am on the login page
|
|
20
|
+
**When** I submit the form with an invalid email format
|
|
21
|
+
**Then** I see "Invalid email" validation error before the form is submitted
|
|
22
|
+
|
|
23
|
+
### AC4: Password Minimum Length
|
|
24
|
+
**Given** I am on the login page
|
|
25
|
+
**When** I enter a password shorter than 8 characters
|
|
26
|
+
**Then** I see "Password must be at least 8 characters" error
|
|
27
|
+
|
|
28
|
+
### AC5: Logout
|
|
29
|
+
**Given** I am authenticated
|
|
30
|
+
**When** I trigger logout
|
|
31
|
+
**Then** My session is cleared and I am redirected to the login page
|
|
32
|
+
|
|
33
|
+
### AC6: Protected Route Guard
|
|
34
|
+
**Given** I am not authenticated
|
|
35
|
+
**When** I navigate to any protected route
|
|
36
|
+
**Then** I am redirected to the login page
|
|
37
|
+
|
|
38
|
+
## Test Cases
|
|
39
|
+
- [ ] TC1: Valid credentials → redirect to home
|
|
40
|
+
- [ ] TC2: Wrong password → error message
|
|
41
|
+
- [ ] TC3: Empty email → validation error
|
|
42
|
+
- [ ] TC4: Invalid email format → validation error
|
|
43
|
+
- [ ] TC5: Short password → validation error
|
|
44
|
+
- [ ] TC6: Logout → redirect to login
|
|
45
|
+
- [ ] TC7: Unauthenticated → redirect to login
|
|
46
|
+
|
|
47
|
+
## Out of Scope
|
|
48
|
+
- Social OAuth (Google, GitHub) — can be added later
|
|
49
|
+
- Password reset flow — future enhancement
|
|
50
|
+
- MFA — future enhancement
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Feature: [Feature Name]
|
|
2
|
+
|
|
3
|
+
## User Story
|
|
4
|
+
As a [user type], I want [goal] so that [reason].
|
|
5
|
+
|
|
6
|
+
## Acceptance Criteria
|
|
7
|
+
|
|
8
|
+
### AC1: [Scenario Name]
|
|
9
|
+
**Given** [initial context]
|
|
10
|
+
**When** [action taken]
|
|
11
|
+
**Then** [expected outcome]
|
|
12
|
+
|
|
13
|
+
### AC2: [Error Scenario]
|
|
14
|
+
**Given** [initial context]
|
|
15
|
+
**When** [invalid action]
|
|
16
|
+
**Then** [error behavior]
|
|
17
|
+
|
|
18
|
+
## Test Cases
|
|
19
|
+
- [ ] TC1: [Happy path]
|
|
20
|
+
- [ ] TC2: [Validation error]
|
|
21
|
+
- [ ] TC3: [Auth error]
|
|
22
|
+
- [ ] TC4: [Edge case]
|
|
23
|
+
|
|
24
|
+
## Out of Scope
|
|
25
|
+
- [What this feature does NOT cover]
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# {{projectName}}
|
|
2
|
+
|
|
3
|
+
Full-stack Next.js 15 hackathon starter.
|
|
4
|
+
|
|
5
|
+
## Stack
|
|
6
|
+
|
|
7
|
+
| Layer | Tool |
|
|
8
|
+
|---|---|
|
|
9
|
+
| Framework | Next.js 15 + App Router |
|
|
10
|
+
| Database | Supabase (PostgreSQL) |
|
|
11
|
+
| Auth | Supabase Auth |
|
|
12
|
+
| ORM | Drizzle (schema + migrations) |
|
|
13
|
+
| Runtime Queries | supabase-js (RLS active) |
|
|
14
|
+
| State | TanStack Query v5 |
|
|
15
|
+
| Forms | React Hook Form + Zod |
|
|
16
|
+
| UI | shadcn/ui + Tailwind CSS v4 |
|
|
17
|
+
| AI | Vercel AI SDK + MiniMax |
|
|
18
|
+
| Testing | Vitest + Playwright |
|
|
19
|
+
|
|
20
|
+
## Getting Started
|
|
21
|
+
|
|
22
|
+
### 1. Add your API keys
|
|
23
|
+
|
|
24
|
+
`.env.local` was created automatically. Fill in the values:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
# Supabase — https://supabase.com > Project Settings > API
|
|
28
|
+
NEXT_PUBLIC_SUPABASE_URL=https://your-project-id.supabase.co
|
|
29
|
+
NEXT_PUBLIC_SUPABASE_ANON_KEY=your-anon-key
|
|
30
|
+
DATABASE_URL=postgresql://postgres:[password]@db.your-project-id.supabase.co:5432/postgres
|
|
31
|
+
|
|
32
|
+
# MiniMax — https://www.minimaxi.chat > API Keys
|
|
33
|
+
MINIMAX_API_KEY=your-minimax-api-key
|
|
34
|
+
|
|
35
|
+
# Vercel AI Gateway — https://vercel.com > AI > Gateways
|
|
36
|
+
AI_GATEWAY_URL=https://gateway.ai.vercel.app/v1/your-team-id/your-gateway-id
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### 2. Run the dev server
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm run dev
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### 3. Apply database migrations
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run db:generate
|
|
49
|
+
npm run db:migrate
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Scripts
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm run dev # Start dev server
|
|
56
|
+
npm run build # Production build
|
|
57
|
+
npm run lint # Lint (0 warnings allowed)
|
|
58
|
+
npm run typecheck # TypeScript check
|
|
59
|
+
npm run test # Unit tests
|
|
60
|
+
npm run test:coverage # Tests with coverage (100% required)
|
|
61
|
+
npm run test:e2e # Playwright e2e tests
|
|
62
|
+
npm run db:generate # Generate Drizzle migrations
|
|
63
|
+
npm run db:migrate # Apply migrations
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Environment Variables
|
|
67
|
+
|
|
68
|
+
| Variable | Where to get it |
|
|
69
|
+
|---|---|
|
|
70
|
+
| `NEXT_PUBLIC_SUPABASE_URL` | Supabase > Project Settings > API |
|
|
71
|
+
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Supabase > Project Settings > API |
|
|
72
|
+
| `DATABASE_URL` | Supabase > Project Settings > Database > URI |
|
|
73
|
+
| `MINIMAX_API_KEY` | minimaxi.chat > API Keys |
|
|
74
|
+
| `AI_GATEWAY_URL` | Vercel > AI > Gateways |
|
|
75
|
+
| `NEXT_PUBLIC_APP_URL` | Your deployment URL (default: `http://localhost:3000`) |
|
|
76
|
+
|
|
77
|
+
See `.env.example` for all required variables with comments.
|
|
78
|
+
|
|
79
|
+
## Architecture
|
|
80
|
+
|
|
81
|
+
Feature-based structure:
|
|
82
|
+
```
|
|
83
|
+
src/
|
|
84
|
+
├── app/ # Next.js routing + layouts
|
|
85
|
+
├── features/ # auth | chat | video | tts
|
|
86
|
+
├── shared/ # lib | db | components/ui
|
|
87
|
+
└── e2e/ # Playwright tests
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Dependency direction: `features/* → shared/*` (never reverse).
|
|
91
|
+
|
|
92
|
+
## Cursor AI Setup
|
|
93
|
+
|
|
94
|
+
Cursor rules, agents, and skills are preconfigured in `.cursor/`.
|
|
95
|
+
|
|
96
|
+
- **Rules**: always-on guardrails for coding standards, architecture, security
|
|
97
|
+
- **Agents**: specialized roles (technical-lead, frontend, test-qa, etc.)
|
|
98
|
+
- **Skills**: repeatable workflows (`/create-feature`, `/create-api-route`, etc.)
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://ui.shadcn.com/schema.json",
|
|
3
|
+
"style": "new-york",
|
|
4
|
+
"rsc": true,
|
|
5
|
+
"tsx": true,
|
|
6
|
+
"tailwind": {
|
|
7
|
+
"config": "",
|
|
8
|
+
"css": "src/app/globals.css",
|
|
9
|
+
"baseColor": "slate",
|
|
10
|
+
"cssVariables": true
|
|
11
|
+
},
|
|
12
|
+
"aliases": {
|
|
13
|
+
"components": "@/shared/components/ui",
|
|
14
|
+
"utils": "@/shared/lib/utils",
|
|
15
|
+
"ui": "@/shared/components/ui",
|
|
16
|
+
"lib": "@/shared/lib",
|
|
17
|
+
"hooks": "@/shared/hooks"
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import js from "@eslint/js";
|
|
2
|
+
import tseslint from "typescript-eslint";
|
|
3
|
+
import reactPlugin from "eslint-plugin-react";
|
|
4
|
+
import reactHooksPlugin from "eslint-plugin-react-hooks";
|
|
5
|
+
import nextPlugin from "@next/eslint-plugin-next";
|
|
6
|
+
import importPlugin from "eslint-plugin-import-x";
|
|
7
|
+
import vitestPlugin from "eslint-plugin-vitest";
|
|
8
|
+
import playwrightPlugin from "eslint-plugin-playwright";
|
|
9
|
+
|
|
10
|
+
export default tseslint.config(
|
|
11
|
+
js.configs.recommended,
|
|
12
|
+
...tseslint.configs.strictTypeChecked,
|
|
13
|
+
...tseslint.configs.stylisticTypeChecked,
|
|
14
|
+
{
|
|
15
|
+
languageOptions: {
|
|
16
|
+
parserOptions: {
|
|
17
|
+
projectService: true,
|
|
18
|
+
tsconfigRootDir: import.meta.dirname,
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
plugins: {
|
|
22
|
+
react: reactPlugin,
|
|
23
|
+
"react-hooks": reactHooksPlugin,
|
|
24
|
+
"@next/next": nextPlugin,
|
|
25
|
+
"import-x": importPlugin,
|
|
26
|
+
},
|
|
27
|
+
rules: {
|
|
28
|
+
"@typescript-eslint/no-explicit-any": "error",
|
|
29
|
+
"@typescript-eslint/no-unused-vars": ["error", { argsIgnorePattern: "^_" }],
|
|
30
|
+
"@typescript-eslint/consistent-type-imports": "error",
|
|
31
|
+
"@typescript-eslint/no-non-null-assertion": "error",
|
|
32
|
+
"no-console": "warn",
|
|
33
|
+
"react/react-in-jsx-scope": "off",
|
|
34
|
+
"react-hooks/rules-of-hooks": "error",
|
|
35
|
+
"react-hooks/exhaustive-deps": "error",
|
|
36
|
+
"import-x/order": [
|
|
37
|
+
"error",
|
|
38
|
+
{
|
|
39
|
+
groups: ["builtin", "external", "internal", "parent", "sibling", "index"],
|
|
40
|
+
"newlines-between": "always",
|
|
41
|
+
alphabetize: { order: "asc" },
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
},
|
|
45
|
+
settings: {
|
|
46
|
+
react: { version: "detect" },
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
files: ["**/*.test.ts", "**/*.test.tsx"],
|
|
51
|
+
plugins: { vitest: vitestPlugin },
|
|
52
|
+
rules: {
|
|
53
|
+
...vitestPlugin.configs.recommended.rules,
|
|
54
|
+
},
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
files: ["src/e2e/**/*.spec.ts"],
|
|
58
|
+
plugins: { playwright: playwrightPlugin },
|
|
59
|
+
rules: {
|
|
60
|
+
...playwrightPlugin.configs["flat/recommended"].rules,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
ignores: [".next/**", "node_modules/**", "dist/**"],
|
|
65
|
+
}
|
|
66
|
+
);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type NextRequest } from "next/server";
|
|
2
|
+
import { updateSession } from "@/shared/lib/supabase/middleware";
|
|
3
|
+
|
|
4
|
+
export async function middleware(request: NextRequest) {
|
|
5
|
+
return updateSession(request);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export const config = {
|
|
9
|
+
matcher: ["/((?!_next/static|_next/image|favicon.ico|.*\\.(?:svg|png|jpg|jpeg|gif|webp)$).*)"],
|
|
10
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { NextConfig } from "next";
|
|
2
|
+
|
|
3
|
+
const nextConfig: NextConfig = {
|
|
4
|
+
experimental: {
|
|
5
|
+
typedRoutes: true,
|
|
6
|
+
},
|
|
7
|
+
headers: async () => [
|
|
8
|
+
{
|
|
9
|
+
source: "/(.*)",
|
|
10
|
+
headers: [
|
|
11
|
+
{ key: "X-Frame-Options", value: "DENY" },
|
|
12
|
+
{ key: "X-Content-Type-Options", value: "nosniff" },
|
|
13
|
+
{ key: "Referrer-Policy", value: "strict-origin-when-cross-origin" },
|
|
14
|
+
{ key: "Strict-Transport-Security", value: "max-age=63072000; includeSubDomains; preload" },
|
|
15
|
+
{
|
|
16
|
+
key: "Content-Security-Policy",
|
|
17
|
+
value: [
|
|
18
|
+
"default-src 'self'",
|
|
19
|
+
"script-src 'self' 'unsafe-eval' 'unsafe-inline'",
|
|
20
|
+
"style-src 'self' 'unsafe-inline'",
|
|
21
|
+
"img-src 'self' data: blob: https:",
|
|
22
|
+
"font-src 'self'",
|
|
23
|
+
"connect-src 'self' https://*.supabase.co wss://*.supabase.co",
|
|
24
|
+
].join("; "),
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
},
|
|
28
|
+
],
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default nextConfig;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "{{projectName}}",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"scripts": {
|
|
6
|
+
"dev": "next dev --turbopack",
|
|
7
|
+
"build": "next build",
|
|
8
|
+
"start": "next start",
|
|
9
|
+
"lint": "eslint . --max-warnings 0",
|
|
10
|
+
"lint:fix": "eslint . --fix",
|
|
11
|
+
"typecheck": "tsc --noEmit",
|
|
12
|
+
"test": "vitest run",
|
|
13
|
+
"test:unit": "vitest run --reporter=verbose",
|
|
14
|
+
"test:watch": "vitest",
|
|
15
|
+
"test:coverage": "vitest run --coverage",
|
|
16
|
+
"test:e2e": "playwright test",
|
|
17
|
+
"db:generate": "drizzle-kit generate",
|
|
18
|
+
"db:migrate": "drizzle-kit migrate",
|
|
19
|
+
"db:studio": "drizzle-kit studio"
|
|
20
|
+
},
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"next": "^15",
|
|
23
|
+
"@supabase/supabase-js": "^2",
|
|
24
|
+
"@supabase/ssr": "^0",
|
|
25
|
+
"drizzle-orm": "^0.45",
|
|
26
|
+
"drizzle-zod": "^0.7",
|
|
27
|
+
"@tanstack/react-query": "^5",
|
|
28
|
+
"ai": "^4",
|
|
29
|
+
"@ai-sdk/react": "^1",
|
|
30
|
+
"react-hook-form": "^7",
|
|
31
|
+
"@hookform/resolvers": "^3",
|
|
32
|
+
"zod": "^3",
|
|
33
|
+
"react": "^19",
|
|
34
|
+
"react-dom": "^19"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"typescript": "^5",
|
|
38
|
+
"@types/node": "^22",
|
|
39
|
+
"@types/react": "^19",
|
|
40
|
+
"@types/react-dom": "^19",
|
|
41
|
+
"drizzle-kit": "^0.30",
|
|
42
|
+
"vitest": "^3",
|
|
43
|
+
"@vitejs/plugin-react": "latest",
|
|
44
|
+
"@testing-library/react": "^16",
|
|
45
|
+
"@testing-library/jest-dom": "latest",
|
|
46
|
+
"@testing-library/user-event": "latest",
|
|
47
|
+
"@vitest/coverage-v8": "latest",
|
|
48
|
+
"@playwright/test": "^1.49",
|
|
49
|
+
"tailwindcss": "^4",
|
|
50
|
+
"@tailwindcss/postcss": "^4",
|
|
51
|
+
"postcss": "^8",
|
|
52
|
+
"eslint": "^9",
|
|
53
|
+
"@eslint/js": "^9",
|
|
54
|
+
"typescript-eslint": "^8",
|
|
55
|
+
"@next/eslint-plugin-next": "^15",
|
|
56
|
+
"eslint-plugin-react": "latest",
|
|
57
|
+
"eslint-plugin-react-hooks": "latest",
|
|
58
|
+
"eslint-plugin-import-x": "latest",
|
|
59
|
+
"eslint-plugin-vitest": "latest",
|
|
60
|
+
"eslint-plugin-playwright": "latest"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { defineConfig, devices } from "@playwright/test";
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
testDir: "./src/e2e",
|
|
5
|
+
fullyParallel: true,
|
|
6
|
+
forbidOnly: !!process.env["CI"],
|
|
7
|
+
retries: process.env["CI"] ? 2 : 0,
|
|
8
|
+
workers: process.env["CI"] ? 1 : undefined,
|
|
9
|
+
reporter: "html",
|
|
10
|
+
use: {
|
|
11
|
+
baseURL: "http://localhost:3000",
|
|
12
|
+
trace: "on-first-retry",
|
|
13
|
+
},
|
|
14
|
+
projects: [
|
|
15
|
+
{ name: "chromium", use: { ...devices["Desktop Chrome"] } },
|
|
16
|
+
],
|
|
17
|
+
webServer: {
|
|
18
|
+
command: "npm run dev",
|
|
19
|
+
url: "http://localhost:3000",
|
|
20
|
+
reuseExistingServer: !process.env["CI"],
|
|
21
|
+
},
|
|
22
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { LoginForm } from "@/features/auth/components/login-form";
|
|
2
|
+
|
|
3
|
+
export default function LoginPage() {
|
|
4
|
+
return (
|
|
5
|
+
<main className="flex min-h-screen items-center justify-center">
|
|
6
|
+
<div className="w-full max-w-md p-8">
|
|
7
|
+
<h1 className="mb-8 text-2xl font-bold">Sign in</h1>
|
|
8
|
+
<LoginForm />
|
|
9
|
+
</div>
|
|
10
|
+
</main>
|
|
11
|
+
);
|
|
12
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { redirect } from "next/navigation";
|
|
2
|
+
import { createClient } from "@/shared/lib/supabase/server";
|
|
3
|
+
|
|
4
|
+
export default async function ProtectedLayout({
|
|
5
|
+
children,
|
|
6
|
+
}: {
|
|
7
|
+
children: React.ReactNode;
|
|
8
|
+
}) {
|
|
9
|
+
const supabase = await createClient();
|
|
10
|
+
const {
|
|
11
|
+
data: { user },
|
|
12
|
+
} = await supabase.auth.getUser();
|
|
13
|
+
|
|
14
|
+
if (!user) {
|
|
15
|
+
redirect("/login");
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return <>{children}</>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createClient } from "@/shared/lib/supabase/server";
|
|
2
|
+
import { ChatUi } from "@/features/chat/components/chat-ui";
|
|
3
|
+
|
|
4
|
+
export default async function HomePage() {
|
|
5
|
+
const supabase = await createClient();
|
|
6
|
+
const {
|
|
7
|
+
data: { user },
|
|
8
|
+
} = await supabase.auth.getUser();
|
|
9
|
+
|
|
10
|
+
return (
|
|
11
|
+
<main className="container mx-auto p-8">
|
|
12
|
+
<h1 className="mb-4 text-2xl font-bold">Welcome, {user?.email}</h1>
|
|
13
|
+
<ChatUi />
|
|
14
|
+
</main>
|
|
15
|
+
);
|
|
16
|
+
}
|