@react-spa-scaffold/mcp 2.2.0 → 2.4.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.
Files changed (138) hide show
  1. package/dist/constants.d.ts +4 -0
  2. package/dist/constants.d.ts.map +1 -1
  3. package/dist/constants.js +4 -0
  4. package/dist/constants.js.map +1 -1
  5. package/dist/features/definitions/api.d.ts.map +1 -1
  6. package/dist/features/definitions/api.js +2 -1
  7. package/dist/features/definitions/api.js.map +1 -1
  8. package/dist/features/definitions/database.d.ts +3 -0
  9. package/dist/features/definitions/database.d.ts.map +1 -0
  10. package/dist/features/definitions/database.js +45 -0
  11. package/dist/features/definitions/database.js.map +1 -0
  12. package/dist/features/definitions/deployment.d.ts +3 -0
  13. package/dist/features/definitions/deployment.d.ts.map +1 -0
  14. package/dist/features/definitions/deployment.js +14 -0
  15. package/dist/features/definitions/deployment.js.map +1 -0
  16. package/dist/features/definitions/electron.d.ts +3 -0
  17. package/dist/features/definitions/electron.d.ts.map +1 -0
  18. package/dist/features/definitions/electron.js +23 -0
  19. package/dist/features/definitions/electron.js.map +1 -0
  20. package/dist/features/definitions/index.d.ts +3 -0
  21. package/dist/features/definitions/index.d.ts.map +1 -1
  22. package/dist/features/definitions/index.js +3 -0
  23. package/dist/features/definitions/index.js.map +1 -1
  24. package/dist/features/registry.d.ts.map +1 -1
  25. package/dist/features/registry.js +4 -1
  26. package/dist/features/registry.js.map +1 -1
  27. package/dist/features/types.d.ts +1 -0
  28. package/dist/features/types.d.ts.map +1 -1
  29. package/dist/features/types.test.js +5 -2
  30. package/dist/features/types.test.js.map +1 -1
  31. package/dist/resources/docs.d.ts.map +1 -1
  32. package/dist/resources/docs.js +5 -0
  33. package/dist/resources/docs.js.map +1 -1
  34. package/dist/tools/add-features.js +1 -1
  35. package/dist/tools/add-features.js.map +1 -1
  36. package/dist/tools/get-features.test.js +7 -0
  37. package/dist/tools/get-features.test.js.map +1 -1
  38. package/dist/tools/get-scaffold.d.ts +1 -0
  39. package/dist/tools/get-scaffold.d.ts.map +1 -1
  40. package/dist/tools/get-scaffold.js +4 -1
  41. package/dist/tools/get-scaffold.js.map +1 -1
  42. package/dist/tools/get-scaffold.test.js +50 -0
  43. package/dist/tools/get-scaffold.test.js.map +1 -1
  44. package/dist/utils/docs.d.ts.map +1 -1
  45. package/dist/utils/docs.js +2 -0
  46. package/dist/utils/docs.js.map +1 -1
  47. package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
  48. package/dist/utils/scaffold/claude-md/index.js +4 -1
  49. package/dist/utils/scaffold/claude-md/index.js.map +1 -1
  50. package/dist/utils/scaffold/claude-md/sections.d.ts +3 -0
  51. package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
  52. package/dist/utils/scaffold/claude-md/sections.js +174 -2
  53. package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
  54. package/dist/utils/scaffold/compute.d.ts.map +1 -1
  55. package/dist/utils/scaffold/compute.js +4 -2
  56. package/dist/utils/scaffold/compute.js.map +1 -1
  57. package/dist/utils/scaffold/generators.d.ts +7 -2
  58. package/dist/utils/scaffold/generators.d.ts.map +1 -1
  59. package/dist/utils/scaffold/generators.js +100 -22
  60. package/dist/utils/scaffold/generators.js.map +1 -1
  61. package/package.json +1 -1
  62. package/templates/.env.example +40 -12
  63. package/templates/.github/workflows/ci.yml +49 -2
  64. package/templates/.github/workflows/deploy.yml +46 -0
  65. package/templates/CLAUDE.md +180 -1
  66. package/templates/docs/AUTHENTICATION.md +325 -0
  67. package/templates/docs/DEPLOYMENT.md +296 -0
  68. package/templates/docs/E2E_TESTING.md +81 -4
  69. package/templates/docs/SUPABASE_INTEGRATION.md +310 -0
  70. package/templates/docs/TESTING.md +195 -77
  71. package/templates/e2e/auth/auth.setup.ts +60 -0
  72. package/templates/e2e/fixtures/index.ts +11 -0
  73. package/templates/e2e/tests/profile.auth.spec.ts +103 -0
  74. package/templates/e2e/tests/profile.spec.ts +64 -0
  75. package/templates/e2e/tests/register-form.spec.ts +38 -0
  76. package/templates/forge.config.js +53 -0
  77. package/templates/gitignore +5 -0
  78. package/templates/package.json +13 -1
  79. package/templates/playwright.config.ts +33 -3
  80. package/templates/src/App.tsx +32 -19
  81. package/templates/src/components/layout/Header.test.tsx +17 -1
  82. package/templates/src/components/layout/Header.tsx +11 -0
  83. package/templates/src/components/shared/AccountButton/AccountButton.test.tsx +3 -3
  84. package/templates/src/components/shared/ProfileSync/ProfileSync.test.tsx +44 -0
  85. package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +104 -0
  86. package/templates/src/components/shared/ProfileSync/index.ts +1 -0
  87. package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.test.tsx +3 -3
  88. package/templates/src/components/shared/index.ts +1 -0
  89. package/templates/src/contexts/performanceContext.tsx +3 -3
  90. package/templates/src/contexts/queryContext.tsx +9 -8
  91. package/templates/src/contexts/supabaseContext.test.tsx +59 -0
  92. package/templates/src/contexts/supabaseContext.tsx +87 -0
  93. package/templates/src/hooks/index.ts +17 -0
  94. package/templates/src/hooks/supabase/index.ts +12 -0
  95. package/templates/src/hooks/supabase/useProfiles.test.tsx +207 -0
  96. package/templates/src/hooks/supabase/useProfiles.ts +213 -0
  97. package/templates/src/hooks/supabase/useSupabaseQuery.test.tsx +150 -0
  98. package/templates/src/hooks/supabase/useSupabaseQuery.ts +91 -0
  99. package/templates/src/lib/api.test.ts +30 -38
  100. package/templates/src/lib/api.ts +1 -7
  101. package/templates/src/lib/config.ts +54 -4
  102. package/templates/src/lib/env.ts +36 -14
  103. package/templates/src/lib/index.ts +4 -2
  104. package/templates/src/lib/routes.ts +1 -0
  105. package/templates/src/lib/sentry.ts +13 -10
  106. package/templates/src/lib/supabase/client.ts +58 -0
  107. package/templates/src/lib/supabase/index.ts +5 -0
  108. package/templates/src/main.ts +227 -0
  109. package/templates/src/main.tsx +32 -42
  110. package/templates/src/mocks/constants.ts +31 -0
  111. package/templates/src/mocks/fixtures/index.ts +3 -1
  112. package/templates/src/mocks/fixtures/profiles.ts +55 -0
  113. package/templates/src/mocks/fixtures/users.ts +91 -0
  114. package/templates/src/mocks/handlers/index.ts +2 -1
  115. package/templates/src/mocks/handlers/supabase.ts +64 -0
  116. package/templates/src/mocks/handlers/todos.ts +1 -1
  117. package/templates/src/mocks/index.ts +6 -0
  118. package/templates/src/pages/Profile.test.tsx +263 -0
  119. package/templates/src/pages/Profile.tsx +171 -0
  120. package/templates/src/pages/index.ts +1 -0
  121. package/templates/src/preload.ts +26 -0
  122. package/templates/src/stores/preferencesStore.ts +2 -1
  123. package/templates/src/test/clerkMock.tsx +49 -9
  124. package/templates/src/test/fetchMock.ts +58 -0
  125. package/templates/src/test/index.ts +49 -3
  126. package/templates/src/test/mocks.ts +128 -1
  127. package/templates/src/test/providers.tsx +7 -4
  128. package/templates/src/test/supabaseMock.ts +112 -0
  129. package/templates/src/test-setup.ts +26 -0
  130. package/templates/src/types/database.ts +46 -0
  131. package/templates/src/types/global.d.ts +28 -0
  132. package/templates/src/types/index.ts +1 -0
  133. package/templates/src/types/supabase.ts +167 -0
  134. package/templates/src/vite-env.d.ts +6 -0
  135. package/templates/supabase/migrations/20260104000000_create_profiles_table.sql +67 -0
  136. package/templates/vite.main.config.mjs +20 -0
  137. package/templates/vite.preload.config.mjs +17 -0
  138. package/templates/vite.renderer.config.mjs +52 -0
@@ -0,0 +1,325 @@
1
+ # Authentication
2
+
3
+ Clerk authentication integration with shadcn theming and Supabase token injection.
4
+ For quick-start usage, see [CLAUDE.md](../CLAUDE.md#authentication-clerk).
5
+
6
+ ---
7
+
8
+ ## Why Clerk
9
+
10
+ This project uses Clerk instead of Supabase Auth for:
11
+
12
+ - **Production-ready UI** - Pre-built `SignInButton`, `UserButton` components with modal flows
13
+ - **Superior OAuth** - 20+ providers vs ~10, dashboard configuration vs code
14
+ - **Automatic session management** - Cross-tab sync, token refresh handled transparently
15
+ - **Separation of concerns** - Clerk = authentication (who), Supabase = authorization + data (what)
16
+
17
+ ---
18
+
19
+ ## Architecture
20
+
21
+ ```
22
+ ClerkProvider (ClerkThemeProvider wrapper)
23
+
24
+ ├── useAuth() → { isLoaded, isSignedIn, userId, getToken }
25
+ ├── useUser() → { user: { id, email, fullName, imageUrl } }
26
+ └── useSession() → { session.getToken() } → JWT Token
27
+
28
+
29
+ SupabaseProvider injects token
30
+
31
+
32
+ Supabase validates JWT
33
+ auth.uid() = Clerk user_id
34
+ ```
35
+
36
+ **Authentication Flow:**
37
+
38
+ 1. User clicks Sign In → Clerk modal opens
39
+ 2. User authenticates (email, OAuth, etc.)
40
+ 3. Clerk creates session, `useAuth()` returns `isSignedIn: true`
41
+ 4. `useSession().getToken()` provides JWT for Supabase
42
+ 5. RLS policies grant access via `auth.uid()`
43
+
44
+ ---
45
+
46
+ ## Setup
47
+
48
+ ### 1. Environment Variable
49
+
50
+ ```bash
51
+ VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
52
+ ```
53
+
54
+ Get from [Clerk Dashboard](https://dashboard.clerk.com) → API Keys.
55
+
56
+ ### 2. Dashboard Setup
57
+
58
+ 1. Create application at [clerk.com](https://clerk.com)
59
+ 2. Configure sign-in methods (Email, Google, GitHub, etc.)
60
+ 3. **For Supabase**: Integrations → Supabase → Activate (adds `role: authenticated` claim)
61
+ 4. Copy Clerk domain → Supabase Dashboard → Authentication → Add Clerk provider
62
+
63
+ ### 3. Provider Hierarchy
64
+
65
+ ```tsx
66
+ // main.tsx - SupabaseProvider MUST be inside ClerkProvider
67
+ <ClerkThemeProvider publishableKey={CLERK_PUBLISHABLE_KEY}>
68
+ <SupabaseProvider>
69
+ <App />
70
+ </SupabaseProvider>
71
+ </ClerkThemeProvider>
72
+ ```
73
+
74
+ ---
75
+
76
+ ## File Structure
77
+
78
+ ```
79
+ src/
80
+ ├── contexts/
81
+ │ └── clerkContext.tsx # ClerkThemeProvider with shadcn theme
82
+ ├── components/shared/
83
+ │ ├── AccountButton/ # Sign in / User button
84
+ │ ├── ProtectedRoute/ # Auth guard wrapper
85
+ │ └── ProfileSync/ # Auto-sync Clerk → Supabase
86
+ ├── test/
87
+ │ └── clerkMock.tsx # Comprehensive Clerk mocks
88
+ ├── mocks/
89
+ │ ├── constants.ts # Mock user/session constants
90
+ │ └── fixtures/users.ts # Mock user factories
91
+ └── index.css # Includes @clerk/themes/shadcn.css
92
+ ```
93
+
94
+ ---
95
+
96
+ ## Theme Configuration
97
+
98
+ ### ClerkThemeProvider
99
+
100
+ ```typescript
101
+ // src/contexts/clerkContext.tsx
102
+ import { ClerkProvider } from '@clerk/react-router';
103
+ import { shadcn } from '@clerk/themes';
104
+
105
+ const appearance: Appearance = {
106
+ baseTheme: shadcn,
107
+ variables: {
108
+ fontFamily: '"Inter Variable", sans-serif',
109
+ borderRadius: '0.45rem',
110
+ },
111
+ elements: {
112
+ modalBackdrop: 'backdrop-blur-sm',
113
+ modalContent: 'sm:max-w-md max-sm:min-h-svh max-sm:min-w-full max-sm:rounded-none',
114
+ card: 'max-sm:rounded-none max-sm:shadow-none',
115
+ },
116
+ };
117
+
118
+ export function ClerkThemeProvider({ children, publishableKey }: Props) {
119
+ return (
120
+ <ClerkProvider publishableKey={publishableKey} afterSignOutUrl="/" appearance={appearance}>
121
+ {children}
122
+ </ClerkProvider>
123
+ );
124
+ }
125
+ ```
126
+
127
+ ### CSS Import
128
+
129
+ ```css
130
+ /* src/index.css */
131
+ @import '@clerk/themes/shadcn.css';
132
+ ```
133
+
134
+ Enables automatic light/dark mode adaptation with shadcn CSS variables.
135
+
136
+ ---
137
+
138
+ ## Components
139
+
140
+ | Component | Location | Purpose |
141
+ | -------------------- | ----------------------------------- | ---------------------------------------------------- |
142
+ | `ClerkThemeProvider` | `contexts/clerkContext.tsx` | Clerk wrapper with shadcn theme |
143
+ | `AccountButton` | `components/shared/AccountButton/` | Sign-in button (logged out) / UserButton (logged in) |
144
+ | `ProtectedRoute` | `components/shared/ProtectedRoute/` | Auth guard, redirects to sign-in |
145
+ | `ProfileSync` | `components/shared/ProfileSync/` | Auto-syncs Clerk user → Supabase profiles |
146
+
147
+ ### AccountButton
148
+
149
+ Shows `SignInButton` when logged out, `UserButton` when logged in. Displays skeleton while loading.
150
+
151
+ ### ProtectedRoute
152
+
153
+ ```tsx
154
+ <Route
155
+ path="/dashboard"
156
+ element={
157
+ <ProtectedRoute>
158
+ <DashboardPage />
159
+ </ProtectedRoute>
160
+ }
161
+ />
162
+ ```
163
+
164
+ Returns `<PageLoading />` while checking auth, `<RedirectToSignIn />` if not authenticated.
165
+
166
+ ### ProfileSync
167
+
168
+ Invisible component that syncs Clerk user data to Supabase on sign-in:
169
+
170
+ - `id` → Clerk user ID
171
+ - `email` → Primary email
172
+ - `full_name` → Full name
173
+ - `avatar_url` → Profile image
174
+
175
+ ---
176
+
177
+ ## Hooks Reference
178
+
179
+ All hooks imported from `@clerk/react-router`:
180
+
181
+ | Hook | Key Returns | Use Case |
182
+ | -------------- | ------------------------------------------------------------- | -------------------------------------------- |
183
+ | `useAuth()` | `isLoaded`, `isSignedIn`, `userId`, `sessionId`, `getToken()` | Auth state without user details |
184
+ | `useUser()` | `isLoaded`, `user` | User profile (id, email, fullName, imageUrl) |
185
+ | `useSession()` | `isLoaded`, `session.getToken()` | JWT token for API calls |
186
+ | `useClerk()` | `signOut({ redirectUrl })` | Programmatic sign-out |
187
+
188
+ ### User Object Properties
189
+
190
+ | Property | Type | Description |
191
+ | ---------------------------------------- | ---------------- | ------------- |
192
+ | `user.id` | `string` | Clerk user ID |
193
+ | `user.primaryEmailAddress?.emailAddress` | `string` | Email |
194
+ | `user.fullName` | `string \| null` | Full name |
195
+ | `user.imageUrl` | `string` | Avatar URL |
196
+
197
+ For usage examples, see [CLAUDE.md](../CLAUDE.md#authentication-clerk).
198
+
199
+ ---
200
+
201
+ ## Supabase Integration
202
+
203
+ Clerk tokens are injected into Supabase for authenticated database access:
204
+
205
+ ```typescript
206
+ // src/contexts/supabaseContext.tsx
207
+ const { session } = useSession();
208
+
209
+ const supabase = useMemo(
210
+ () =>
211
+ createSupabaseClient(async () => {
212
+ if (!session) return null;
213
+ return session.getToken(); // Clerk JWT injected
214
+ }),
215
+ [session?.id], // Only recreate on sign in/out, not every render
216
+ );
217
+ ```
218
+
219
+ **Key points:**
220
+
221
+ - JWT includes `sub` (user ID) and `role: authenticated` claims
222
+ - Supabase `auth.uid()` equals Clerk user ID
223
+ - RLS policies enforce user-scoped access
224
+
225
+ See [SUPABASE_INTEGRATION.md](./SUPABASE_INTEGRATION.md) for full details.
226
+
227
+ ---
228
+
229
+ ## Testing
230
+
231
+ ### Mock Utilities
232
+
233
+ Import from `@/test`:
234
+
235
+ | Utility | Purpose |
236
+ | ---------------------------- | ---------------------------------------- |
237
+ | `setMockClerkSignedIn(bool)` | Set sign-in status |
238
+ | `setMockClerkLoaded(bool)` | Set loading state |
239
+ | `setMockClerkState({ ... })` | Set multiple values |
240
+ | `setMockClerkUser({ ... })` | Customize mock user |
241
+ | `resetClerkMocks()` | Reset to defaults (call in `beforeEach`) |
242
+
243
+ ### Mock Components
244
+
245
+ | Component | Test ID |
246
+ | ------------------ | --------------------- |
247
+ | `SignInButton` | `sign-in-button` |
248
+ | `SignUpButton` | `sign-up-button` |
249
+ | `UserButton` | `user-button` |
250
+ | `RedirectToSignIn` | `redirect-to-sign-in` |
251
+
252
+ ### Mock Constants & Fixtures
253
+
254
+ ```typescript
255
+ // src/mocks/constants.ts
256
+ export const MOCK_USER = {
257
+ id: 'user_123',
258
+ email: 'test@example.com',
259
+ fullName: 'Test User',
260
+ avatarUrl: 'https://example.com/avatar.jpg',
261
+ };
262
+ export const MOCK_SESSION_ID = 'sess_123';
263
+ export const MOCK_AUTH_TOKEN = 'mock-auth-token';
264
+ ```
265
+
266
+ ```typescript
267
+ // src/mocks/fixtures/users.ts
268
+ import { createUser, createUsers } from '@/test';
269
+
270
+ const user = createUser({ fullName: 'Jane Doe' }); // Single user with overrides
271
+ const users = createUsers(3); // Array of mock users
272
+ ```
273
+
274
+ ### E2E Testing
275
+
276
+ For Playwright tests requiring authentication, use `@clerk/testing`:
277
+
278
+ ```bash
279
+ CLERK_SECRET_KEY=sk_test_xxxxx
280
+ E2E_CLERK_USER_USERNAME=test@example.com
281
+ E2E_CLERK_USER_PASSWORD=your-password
282
+ ```
283
+
284
+ See [E2E_TESTING.md](./E2E_TESTING.md#authenticated-testing) for full details.
285
+
286
+ ---
287
+
288
+ ## Troubleshooting
289
+
290
+ | Issue | Cause | Fix |
291
+ | ------------------------------------------- | -------------------------- | ----------------------------------------------------- |
292
+ | "Missing VITE_CLERK_PUBLISHABLE_KEY" | Env var not set | Add to `.env`, restart dev server |
293
+ | UI not matching theme | Missing CSS import | Add `@import '@clerk/themes/shadcn.css'` to index.css |
294
+ | "useAuth must be used within ClerkProvider" | Component outside provider | Check `main.tsx` provider order |
295
+ | Modal not opening | Missing `mode="modal"` | Use `<SignInButton mode="modal">` |
296
+ | Supabase not getting tokens | Wrong provider order | SupabaseProvider must be inside ClerkProvider |
297
+ | User data undefined | Checking before loaded | Wait for `isLoaded === true` before accessing user |
298
+
299
+ ### Debug Auth State
300
+
301
+ ```typescript
302
+ const { isLoaded, isSignedIn, userId } = useAuth();
303
+ console.log({ isLoaded, isSignedIn, userId });
304
+ ```
305
+
306
+ ### Debug Token Claims
307
+
308
+ ```typescript
309
+ const { session } = useSession();
310
+ const token = await session?.getToken();
311
+ if (token) {
312
+ const payload = JSON.parse(atob(token.split('.')[1]));
313
+ console.log(payload); // { sub: "user_xxx", role: "authenticated", ... }
314
+ }
315
+ ```
316
+
317
+ ---
318
+
319
+ ## Resources
320
+
321
+ - [Clerk Documentation](https://clerk.com/docs)
322
+ - [Clerk React Router Integration](https://clerk.com/docs/references/react-router/overview)
323
+ - [Clerk Supabase Integration](https://clerk.com/docs/integrations/databases/supabase)
324
+ - [Clerk Appearance Customization](https://clerk.com/docs/customization/overview)
325
+ - [SUPABASE_INTEGRATION.md](./SUPABASE_INTEGRATION.md) - Database integration with Clerk tokens
@@ -0,0 +1,296 @@
1
+ # Deployment Guide
2
+
3
+ Automated deployment to Netlify with GitHub Actions for preview and production environments.
4
+
5
+ ---
6
+
7
+ ## Overview
8
+
9
+ ```
10
+ PR Created/Updated → GitHub Actions → Build → Netlify Preview
11
+
12
+ Comment with preview URL on PR
13
+
14
+ Push to main → GitHub Actions → Build → Netlify Production
15
+ ```
16
+
17
+ **Features:**
18
+
19
+ - Automatic preview deploys for pull requests
20
+ - Production deploys on push to main/master
21
+ - PR comments with preview URLs
22
+ - Manual deploy via workflow_dispatch (with environment selector)
23
+ - Security headers pre-configured
24
+ - Sentry source map upload (when configured)
25
+ - **CI-gated**: Deploy only runs after all checks pass
26
+
27
+ **Note:** Enable branch protection rules to require CI to pass before merging to main.
28
+
29
+ ### How It Works
30
+
31
+ ```
32
+ Push/PR → CI Workflow → lint, typecheck, security, build, test, e2e → deploy
33
+
34
+ (all must pass)
35
+ ```
36
+
37
+ **Two workflows:**
38
+
39
+ 1. **CI workflow** (`ci.yml`): Runs all checks, then deploys if all pass
40
+ 2. **Manual deploy** (`deploy.yml`): Emergency escape hatch, skips CI checks
41
+
42
+ **Two deployment modes:**
43
+
44
+ 1. **GitHub Actions (primary)**: Builds in CI, uploads pre-built files to Netlify
45
+ 2. **Netlify CLI (fallback)**: Use `netlify build` and `netlify deploy` locally
46
+
47
+ The `netlify.toml` file configures both:
48
+
49
+ - `[build]` section: Used by Netlify CLI and `netlify dev`
50
+ - `[[headers]]` and `[[redirects]]`: Applied at CDN level regardless of build method
51
+
52
+ ---
53
+
54
+ ## Netlify Setup
55
+
56
+ ### 1. Create Netlify Site
57
+
58
+ 1. Go to [app.netlify.com](https://app.netlify.com) and sign in
59
+ 2. Click "Add new site" → "Import an existing project"
60
+ 3. Connect your GitHub repository
61
+ 4. Configure build settings (auto-detected from `netlify.toml`):
62
+ - Build command: `npm run build`
63
+ - Publish directory: `dist`
64
+ 5. Click "Deploy site"
65
+
66
+ ### 2. Get API Credentials
67
+
68
+ 1. **Personal Access Token** (for `NETLIFY_AUTH_TOKEN`):
69
+ - User Settings → Applications → Personal access tokens
70
+ - Click "New access token", name it, and copy the token
71
+
72
+ 2. **Site ID** (for `NETLIFY_SITE_ID`):
73
+ - Site Settings → General → Site details → Site ID
74
+
75
+ ### 3. Add GitHub Secrets
76
+
77
+ Go to your repository → Settings → Secrets and variables → Actions → New repository secret:
78
+
79
+ | Secret Name | Description |
80
+ | -------------------- | -------------------------------- |
81
+ | `NETLIFY_AUTH_TOKEN` | Personal access token from above |
82
+ | `NETLIFY_SITE_ID` | Site ID from above |
83
+
84
+ ---
85
+
86
+ ## Environment Variables
87
+
88
+ ### Build-Time Variables
89
+
90
+ Set in Netlify Dashboard → Site Settings → Environment variables:
91
+
92
+ | Variable | Required | Description |
93
+ | ---------------------------- | ------------- | --------------------- |
94
+ | `VITE_CLERK_PUBLISHABLE_KEY` | If using auth | Clerk publishable key |
95
+ | `VITE_SUPABASE_DATABASE_URL` | If using db | Supabase project URL |
96
+ | `VITE_SUPABASE_ANON_KEY` | If using db | Supabase anon key |
97
+
98
+ ### Context-Specific Variables
99
+
100
+ Use Netlify CLI to set variables for specific contexts:
101
+
102
+ ```bash
103
+ # Set for all contexts
104
+ netlify env:set VAR_NAME value
105
+
106
+ # Set for production only
107
+ netlify env:set VAR_NAME value --context production
108
+
109
+ # Set for deploy previews only
110
+ netlify env:set VAR_NAME value --context deploy-preview
111
+ ```
112
+
113
+ ---
114
+
115
+ ## Supabase Integration
116
+
117
+ If using the database feature, connect Supabase to Netlify for automatic environment variable sync.
118
+
119
+ ### Extension Setup
120
+
121
+ 1. **Netlify Dashboard** → Extensions → Search "Supabase" → Install
122
+ 2. **Project Settings** → General → Supabase → Connect
123
+ 3. Authorize with Supabase and select your project
124
+ 4. For Vite projects:
125
+ - Framework: Select "Other"
126
+ - Environment variable prefix: Enter `VITE_`
127
+
128
+ ### Auto-Configured Variables
129
+
130
+ After connecting, these are automatically injected:
131
+
132
+ | Variable | Description |
133
+ | ---------------------------- | ---------------------------------------- |
134
+ | `VITE_SUPABASE_DATABASE_URL` | Project URL |
135
+ | `VITE_SUPABASE_ANON_KEY` | Client API key |
136
+ | `SUPABASE_SERVICE_ROLE_KEY` | Server-side only (not exposed to client) |
137
+
138
+ ### Local Development
139
+
140
+ Run `netlify dev` to inject Supabase variables locally:
141
+
142
+ ```bash
143
+ npm install -g netlify-cli
144
+ netlify login
145
+ netlify link # Link to your Netlify site
146
+ netlify dev # Starts dev server with injected env vars
147
+ ```
148
+
149
+ ---
150
+
151
+ ## Preview Deploys
152
+
153
+ Every pull request automatically gets a preview deployment:
154
+
155
+ 1. Open a PR against main/master
156
+ 2. GitHub Actions builds and deploys to Netlify
157
+ 3. Bot comments on PR with preview URL
158
+ 4. Preview updates on each push to the PR
159
+ 5. Preview is deleted when PR is closed
160
+
161
+ ### Preview URL Pattern
162
+
163
+ - PR previews: `https://pr-{number}--{site-name}.netlify.app`
164
+ - Branch deploys: `https://{branch}--{site-name}.netlify.app`
165
+
166
+ ---
167
+
168
+ ## Production Deploys
169
+
170
+ Pushing to main/master triggers production deployment:
171
+
172
+ 1. Deploy workflow builds the app
173
+ 2. Deploys to production URL: `https://your-site.netlify.app`
174
+
175
+ **Important:** Enable branch protection rules on main/master to require CI to pass before merging. This ensures production only gets code that passed all checks.
176
+
177
+ ### Manual Deploys
178
+
179
+ Use the manual deploy workflow for emergency or ad-hoc deploys (skips CI checks):
180
+
181
+ 1. Go to Actions → Deploy (Manual) → Run workflow
182
+ 2. Select branch
183
+ 3. Choose environment: `preview` or `production`
184
+ 4. Click "Run workflow"
185
+
186
+ > **Warning:** Manual deploys skip CI checks. Use only for emergencies or hotfixes.
187
+
188
+ ---
189
+
190
+ ## Configuration
191
+
192
+ ### netlify.toml
193
+
194
+ The `netlify.toml` file in your project root configures:
195
+
196
+ - **Build settings**: Command and publish directory
197
+ - **Redirects**: SPA fallback to index.html
198
+ - **Headers**: Security headers and caching rules
199
+ - **Context overrides**: Environment-specific settings
200
+
201
+ ### Customizing Headers
202
+
203
+ Add custom headers in `netlify.toml`:
204
+
205
+ ```toml
206
+ [[headers]]
207
+ for = "/api/*"
208
+ [headers.values]
209
+ Access-Control-Allow-Origin = "https://example.com"
210
+ ```
211
+
212
+ ### Redirect Rules
213
+
214
+ Add redirects before the SPA fallback:
215
+
216
+ ```toml
217
+ [[redirects]]
218
+ from = "/old-path"
219
+ to = "/new-path"
220
+ status = 301
221
+
222
+ # SPA fallback (keep last)
223
+ [[redirects]]
224
+ from = "/*"
225
+ to = "/index.html"
226
+ status = 200
227
+ ```
228
+
229
+ ---
230
+
231
+ ## CLI Commands
232
+
233
+ ```bash
234
+ # Install Netlify CLI
235
+ npm install -g netlify-cli
236
+
237
+ # Login and link
238
+ netlify login
239
+ netlify link
240
+
241
+ # Local development with Netlify env vars
242
+ netlify dev
243
+
244
+ # Manual deploys
245
+ npm run deploy:preview # Deploy preview build
246
+ npm run deploy:prod # Deploy to production
247
+
248
+ # Environment variables
249
+ netlify env:list # List all variables
250
+ netlify env:set KEY value # Set variable
251
+ netlify env:get KEY # Get variable value
252
+ netlify env:unset KEY # Remove variable
253
+
254
+ # Build locally
255
+ netlify build # Test production build
256
+ netlify build --context deploy-preview # Test preview build
257
+
258
+ # Check status
259
+ netlify status
260
+ ```
261
+
262
+ ---
263
+
264
+ ## Troubleshooting
265
+
266
+ | Issue | Cause | Solution |
267
+ | ---------------------------------- | ---------------------------- | --------------------------------------------- |
268
+ | Deploy fails with "Site not found" | Missing `NETLIFY_SITE_ID` | Add secret to GitHub repository |
269
+ | Deploy fails with "Unauthorized" | Invalid `NETLIFY_AUTH_TOKEN` | Regenerate token in Netlify |
270
+ | Preview not commenting on PR | Missing permissions | Check workflow has `pull-requests: write` |
271
+ | Env vars undefined in build | Not set in Netlify | Add to Netlify Dashboard or use `netlify env` |
272
+ | 404 on page refresh | SPA fallback not working | Check `netlify.toml` has `/* -> /index.html` |
273
+ | Production deploy not triggered | CI workflow failed | Check CI workflow status first |
274
+
275
+ ### Debug Build Locally
276
+
277
+ Test production build locally:
278
+
279
+ ```bash
280
+ npm run build
281
+ npx serve dist
282
+
283
+ # Or with Netlify CLI:
284
+ netlify build
285
+ netlify deploy --dir=dist
286
+ ```
287
+
288
+ ---
289
+
290
+ ## Resources
291
+
292
+ - [Netlify Documentation](https://docs.netlify.com/)
293
+ - [Netlify CLI Reference](https://cli.netlify.com/)
294
+ - [File-based Configuration](https://docs.netlify.com/configure-builds/file-based-configuration/)
295
+ - [GitHub Actions for Netlify](https://github.com/nwtgck/actions-netlify)
296
+ - [Netlify Supabase Extension](https://www.netlify.com/integrations/supabase/)