@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.
- package/dist/constants.d.ts +4 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +4 -0
- package/dist/constants.js.map +1 -1
- package/dist/features/definitions/api.d.ts.map +1 -1
- package/dist/features/definitions/api.js +2 -1
- package/dist/features/definitions/api.js.map +1 -1
- package/dist/features/definitions/database.d.ts +3 -0
- package/dist/features/definitions/database.d.ts.map +1 -0
- package/dist/features/definitions/database.js +45 -0
- package/dist/features/definitions/database.js.map +1 -0
- package/dist/features/definitions/deployment.d.ts +3 -0
- package/dist/features/definitions/deployment.d.ts.map +1 -0
- package/dist/features/definitions/deployment.js +14 -0
- package/dist/features/definitions/deployment.js.map +1 -0
- package/dist/features/definitions/electron.d.ts +3 -0
- package/dist/features/definitions/electron.d.ts.map +1 -0
- package/dist/features/definitions/electron.js +23 -0
- package/dist/features/definitions/electron.js.map +1 -0
- package/dist/features/definitions/index.d.ts +3 -0
- package/dist/features/definitions/index.d.ts.map +1 -1
- package/dist/features/definitions/index.js +3 -0
- package/dist/features/definitions/index.js.map +1 -1
- package/dist/features/registry.d.ts.map +1 -1
- package/dist/features/registry.js +4 -1
- package/dist/features/registry.js.map +1 -1
- package/dist/features/types.d.ts +1 -0
- package/dist/features/types.d.ts.map +1 -1
- package/dist/features/types.test.js +5 -2
- package/dist/features/types.test.js.map +1 -1
- package/dist/resources/docs.d.ts.map +1 -1
- package/dist/resources/docs.js +5 -0
- package/dist/resources/docs.js.map +1 -1
- package/dist/tools/add-features.js +1 -1
- package/dist/tools/add-features.js.map +1 -1
- package/dist/tools/get-features.test.js +7 -0
- package/dist/tools/get-features.test.js.map +1 -1
- package/dist/tools/get-scaffold.d.ts +1 -0
- package/dist/tools/get-scaffold.d.ts.map +1 -1
- package/dist/tools/get-scaffold.js +4 -1
- package/dist/tools/get-scaffold.js.map +1 -1
- package/dist/tools/get-scaffold.test.js +50 -0
- package/dist/tools/get-scaffold.test.js.map +1 -1
- package/dist/utils/docs.d.ts.map +1 -1
- package/dist/utils/docs.js +2 -0
- package/dist/utils/docs.js.map +1 -1
- package/dist/utils/scaffold/claude-md/index.d.ts.map +1 -1
- package/dist/utils/scaffold/claude-md/index.js +4 -1
- package/dist/utils/scaffold/claude-md/index.js.map +1 -1
- package/dist/utils/scaffold/claude-md/sections.d.ts +3 -0
- package/dist/utils/scaffold/claude-md/sections.d.ts.map +1 -1
- package/dist/utils/scaffold/claude-md/sections.js +174 -2
- package/dist/utils/scaffold/claude-md/sections.js.map +1 -1
- package/dist/utils/scaffold/compute.d.ts.map +1 -1
- package/dist/utils/scaffold/compute.js +4 -2
- package/dist/utils/scaffold/compute.js.map +1 -1
- package/dist/utils/scaffold/generators.d.ts +7 -2
- package/dist/utils/scaffold/generators.d.ts.map +1 -1
- package/dist/utils/scaffold/generators.js +100 -22
- package/dist/utils/scaffold/generators.js.map +1 -1
- package/package.json +1 -1
- package/templates/.env.example +40 -12
- package/templates/.github/workflows/ci.yml +49 -2
- package/templates/.github/workflows/deploy.yml +46 -0
- package/templates/CLAUDE.md +180 -1
- package/templates/docs/AUTHENTICATION.md +325 -0
- package/templates/docs/DEPLOYMENT.md +296 -0
- package/templates/docs/E2E_TESTING.md +81 -4
- package/templates/docs/SUPABASE_INTEGRATION.md +310 -0
- package/templates/docs/TESTING.md +195 -77
- package/templates/e2e/auth/auth.setup.ts +60 -0
- package/templates/e2e/fixtures/index.ts +11 -0
- package/templates/e2e/tests/profile.auth.spec.ts +103 -0
- package/templates/e2e/tests/profile.spec.ts +64 -0
- package/templates/e2e/tests/register-form.spec.ts +38 -0
- package/templates/forge.config.js +53 -0
- package/templates/gitignore +5 -0
- package/templates/package.json +13 -1
- package/templates/playwright.config.ts +33 -3
- package/templates/src/App.tsx +32 -19
- package/templates/src/components/layout/Header.test.tsx +17 -1
- package/templates/src/components/layout/Header.tsx +11 -0
- package/templates/src/components/shared/AccountButton/AccountButton.test.tsx +3 -3
- package/templates/src/components/shared/ProfileSync/ProfileSync.test.tsx +44 -0
- package/templates/src/components/shared/ProfileSync/ProfileSync.tsx +104 -0
- package/templates/src/components/shared/ProfileSync/index.ts +1 -0
- package/templates/src/components/shared/ProtectedRoute/ProtectedRoute.test.tsx +3 -3
- package/templates/src/components/shared/index.ts +1 -0
- package/templates/src/contexts/performanceContext.tsx +3 -3
- package/templates/src/contexts/queryContext.tsx +9 -8
- package/templates/src/contexts/supabaseContext.test.tsx +59 -0
- package/templates/src/contexts/supabaseContext.tsx +87 -0
- package/templates/src/hooks/index.ts +17 -0
- package/templates/src/hooks/supabase/index.ts +12 -0
- package/templates/src/hooks/supabase/useProfiles.test.tsx +207 -0
- package/templates/src/hooks/supabase/useProfiles.ts +213 -0
- package/templates/src/hooks/supabase/useSupabaseQuery.test.tsx +150 -0
- package/templates/src/hooks/supabase/useSupabaseQuery.ts +91 -0
- package/templates/src/lib/api.test.ts +30 -38
- package/templates/src/lib/api.ts +1 -7
- package/templates/src/lib/config.ts +54 -4
- package/templates/src/lib/env.ts +36 -14
- package/templates/src/lib/index.ts +4 -2
- package/templates/src/lib/routes.ts +1 -0
- package/templates/src/lib/sentry.ts +13 -10
- package/templates/src/lib/supabase/client.ts +58 -0
- package/templates/src/lib/supabase/index.ts +5 -0
- package/templates/src/main.ts +227 -0
- package/templates/src/main.tsx +32 -42
- package/templates/src/mocks/constants.ts +31 -0
- package/templates/src/mocks/fixtures/index.ts +3 -1
- package/templates/src/mocks/fixtures/profiles.ts +55 -0
- package/templates/src/mocks/fixtures/users.ts +91 -0
- package/templates/src/mocks/handlers/index.ts +2 -1
- package/templates/src/mocks/handlers/supabase.ts +64 -0
- package/templates/src/mocks/handlers/todos.ts +1 -1
- package/templates/src/mocks/index.ts +6 -0
- package/templates/src/pages/Profile.test.tsx +263 -0
- package/templates/src/pages/Profile.tsx +171 -0
- package/templates/src/pages/index.ts +1 -0
- package/templates/src/preload.ts +26 -0
- package/templates/src/stores/preferencesStore.ts +2 -1
- package/templates/src/test/clerkMock.tsx +49 -9
- package/templates/src/test/fetchMock.ts +58 -0
- package/templates/src/test/index.ts +49 -3
- package/templates/src/test/mocks.ts +128 -1
- package/templates/src/test/providers.tsx +7 -4
- package/templates/src/test/supabaseMock.ts +112 -0
- package/templates/src/test-setup.ts +26 -0
- package/templates/src/types/database.ts +46 -0
- package/templates/src/types/global.d.ts +28 -0
- package/templates/src/types/index.ts +1 -0
- package/templates/src/types/supabase.ts +167 -0
- package/templates/src/vite-env.d.ts +6 -0
- package/templates/supabase/migrations/20260104000000_create_profiles_table.sql +67 -0
- package/templates/vite.main.config.mjs +20 -0
- package/templates/vite.preload.config.mjs +17 -0
- package/templates/vite.renderer.config.mjs +52 -0
package/templates/.env.example
CHANGED
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ─────────────────────────────────────────────────────────────
|
|
2
|
+
# Application (required)
|
|
3
|
+
# ─────────────────────────────────────────────────────────────
|
|
2
4
|
VITE_APP_NAME="My App"
|
|
3
5
|
VITE_APP_URL=http://localhost:5173
|
|
4
|
-
|
|
5
|
-
# Optional: Base URL for deployment subdirectory
|
|
6
|
-
# VITE_BASE_URL=/
|
|
6
|
+
VITE_API_URL=https://jsonplaceholder.typicode.com
|
|
7
7
|
|
|
8
8
|
# ─────────────────────────────────────────────────────────────
|
|
9
|
-
# Sentry Error Tracking (
|
|
9
|
+
# Sentry Error Tracking (required)
|
|
10
10
|
# ─────────────────────────────────────────────────────────────
|
|
11
|
-
# Set to 'false' to disable Sentry entirely (opt-out)
|
|
12
|
-
# Enabled by default when DSN is provided
|
|
13
|
-
# VITE_SENTRY_ENABLED=true
|
|
14
|
-
|
|
15
11
|
# Runtime DSN for error reporting (client-side, safe to expose)
|
|
16
|
-
#
|
|
12
|
+
# Get from: https://sentry.io/settings/projects/YOUR_PROJECT/keys/
|
|
13
|
+
VITE_SENTRY_DSN=https://xxxxx@o123456.ingest.sentry.io/789
|
|
14
|
+
VITE_SENTRY_ENABLED=true
|
|
17
15
|
|
|
18
16
|
# CI/CD secrets for source map upload (set in GitHub Secrets):
|
|
19
17
|
# - SENTRY_AUTH_TOKEN: API token for uploading source maps
|
|
@@ -21,7 +19,37 @@ VITE_APP_URL=http://localhost:5173
|
|
|
21
19
|
# - SENTRY_PROJECT: Sentry project slug
|
|
22
20
|
|
|
23
21
|
# ─────────────────────────────────────────────────────────────
|
|
24
|
-
# Clerk Authentication
|
|
22
|
+
# Clerk Authentication (required)
|
|
25
23
|
# ─────────────────────────────────────────────────────────────
|
|
26
24
|
# Get your Publishable Key from: https://dashboard.clerk.com/~/api-keys
|
|
27
|
-
VITE_CLERK_PUBLISHABLE_KEY=
|
|
25
|
+
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
|
|
26
|
+
|
|
27
|
+
# ─────────────────────────────────────────────────────────────
|
|
28
|
+
# Supabase Database (required)
|
|
29
|
+
# ─────────────────────────────────────────────────────────────
|
|
30
|
+
# Get your Project URL and API Key from:
|
|
31
|
+
# https://supabase.com/dashboard/project/YOUR_PROJECT/settings/api
|
|
32
|
+
#
|
|
33
|
+
# For Netlify deployments, these can be auto-configured via the
|
|
34
|
+
# Netlify Supabase extension: Extensions > Supabase > Connect
|
|
35
|
+
VITE_SUPABASE_DATABASE_URL=https://your-project.supabase.co
|
|
36
|
+
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
37
|
+
|
|
38
|
+
# ─────────────────────────────────────────────────────────────
|
|
39
|
+
# Performance Testing (required)
|
|
40
|
+
# ─────────────────────────────────────────────────────────────
|
|
41
|
+
VITE_PERF_TEST=false
|
|
42
|
+
|
|
43
|
+
# Supabase CLI (required for npm run db:types)
|
|
44
|
+
# Project ID is the subdomain from your Supabase URL (e.g., abc123xyz from https://abc123xyz.supabase.co)
|
|
45
|
+
SUPABASE_PROJECT_ID=your-project-id
|
|
46
|
+
|
|
47
|
+
# ─────────────────────────────────────────────────────────────
|
|
48
|
+
# E2E Testing (optional - for authenticated Playwright tests)
|
|
49
|
+
# ─────────────────────────────────────────────────────────────
|
|
50
|
+
# Create a test user in Clerk and provide credentials here
|
|
51
|
+
# Required for running: npx playwright test --project=authenticated
|
|
52
|
+
# E2E_CLERK_USER_USERNAME=test@example.com
|
|
53
|
+
# E2E_CLERK_USER_PASSWORD=your-test-password
|
|
54
|
+
# CLERK_SECRET_KEY=sk_test_xxxxx
|
|
55
|
+
|
|
@@ -20,6 +20,7 @@ jobs:
|
|
|
20
20
|
- uses: ./.github/actions/setup-node-deps
|
|
21
21
|
- run: npm run lint
|
|
22
22
|
- run: npm run format:check
|
|
23
|
+
- run: npm run sync:check
|
|
23
24
|
|
|
24
25
|
typecheck:
|
|
25
26
|
name: Type Check
|
|
@@ -41,7 +42,7 @@ jobs:
|
|
|
41
42
|
|
|
42
43
|
build:
|
|
43
44
|
name: Build
|
|
44
|
-
needs: [lint, typecheck]
|
|
45
|
+
needs: [lint, typecheck, security]
|
|
45
46
|
runs-on: ubuntu-latest
|
|
46
47
|
timeout-minutes: 10
|
|
47
48
|
steps:
|
|
@@ -96,7 +97,7 @@ jobs:
|
|
|
96
97
|
upload-on: failure
|
|
97
98
|
- type: performance
|
|
98
99
|
project: performance
|
|
99
|
-
command: PERF_TEST=true npx playwright test --project=performance
|
|
100
|
+
command: PERF_TEST=true PERF_CI=true npx playwright test --project=performance
|
|
100
101
|
report-name: performance-report
|
|
101
102
|
upload-on: always
|
|
102
103
|
steps:
|
|
@@ -106,6 +107,9 @@ jobs:
|
|
|
106
107
|
with:
|
|
107
108
|
name: dist
|
|
108
109
|
path: dist/
|
|
110
|
+
- name: Rebuild with performance tracking
|
|
111
|
+
if: matrix.type == 'performance'
|
|
112
|
+
run: VITE_PERF_TEST=true npm run build
|
|
109
113
|
- name: Get Playwright version
|
|
110
114
|
id: playwright-version
|
|
111
115
|
run: echo "version=$(npm ls @playwright/test --json | jq -r '.dependencies["@playwright/test"].version')" >> $GITHUB_OUTPUT
|
|
@@ -131,3 +135,46 @@ jobs:
|
|
|
131
135
|
name: ${{ matrix.report-name }}
|
|
132
136
|
path: playwright-report/
|
|
133
137
|
retention-days: ${{ matrix.type == 'performance' && 14 || 7 }}
|
|
138
|
+
|
|
139
|
+
deploy:
|
|
140
|
+
name: Deploy
|
|
141
|
+
needs: [build, test, test-e2e]
|
|
142
|
+
runs-on: ubuntu-latest
|
|
143
|
+
timeout-minutes: 10
|
|
144
|
+
permissions:
|
|
145
|
+
pull-requests: write
|
|
146
|
+
deployments: write
|
|
147
|
+
steps:
|
|
148
|
+
- uses: actions/download-artifact@v6
|
|
149
|
+
with:
|
|
150
|
+
name: dist
|
|
151
|
+
path: dist/
|
|
152
|
+
|
|
153
|
+
- name: Deploy Preview
|
|
154
|
+
if: github.event_name == 'pull_request'
|
|
155
|
+
uses: nwtgck/actions-netlify@v3
|
|
156
|
+
with:
|
|
157
|
+
publish-dir: './dist'
|
|
158
|
+
production-deploy: false
|
|
159
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
160
|
+
deploy-message: 'Preview deploy from PR #${{ github.event.number }}'
|
|
161
|
+
enable-pull-request-comment: true
|
|
162
|
+
enable-commit-comment: false
|
|
163
|
+
overwrites-pull-request-comment: true
|
|
164
|
+
alias: pr-${{ github.event.number }}
|
|
165
|
+
env:
|
|
166
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
167
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
168
|
+
|
|
169
|
+
- name: Deploy Production
|
|
170
|
+
if: github.event_name == 'push'
|
|
171
|
+
uses: nwtgck/actions-netlify@v3
|
|
172
|
+
with:
|
|
173
|
+
publish-dir: './dist'
|
|
174
|
+
production-deploy: true
|
|
175
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
176
|
+
deploy-message: 'Production deploy from ${{ github.sha }}'
|
|
177
|
+
enable-commit-comment: true
|
|
178
|
+
env:
|
|
179
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
180
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
name: Deploy (Manual)
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
inputs:
|
|
6
|
+
environment:
|
|
7
|
+
description: 'Deploy environment'
|
|
8
|
+
required: true
|
|
9
|
+
default: 'preview'
|
|
10
|
+
type: choice
|
|
11
|
+
options:
|
|
12
|
+
- preview
|
|
13
|
+
- production
|
|
14
|
+
|
|
15
|
+
permissions:
|
|
16
|
+
contents: read
|
|
17
|
+
deployments: write
|
|
18
|
+
|
|
19
|
+
jobs:
|
|
20
|
+
deploy:
|
|
21
|
+
name: Deploy (${{ inputs.environment }})
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
timeout-minutes: 15
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v6
|
|
26
|
+
|
|
27
|
+
- uses: ./.github/actions/setup-node-deps
|
|
28
|
+
|
|
29
|
+
- name: Build
|
|
30
|
+
run: npm run build
|
|
31
|
+
env:
|
|
32
|
+
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
|
|
33
|
+
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
|
|
34
|
+
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
|
|
35
|
+
|
|
36
|
+
- name: Deploy
|
|
37
|
+
uses: nwtgck/actions-netlify@v3
|
|
38
|
+
with:
|
|
39
|
+
publish-dir: './dist'
|
|
40
|
+
production-deploy: ${{ inputs.environment == 'production' }}
|
|
41
|
+
github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
42
|
+
deploy-message: 'Manual ${{ inputs.environment }} deploy from ${{ github.sha }}'
|
|
43
|
+
enable-commit-comment: ${{ inputs.environment == 'production' }}
|
|
44
|
+
env:
|
|
45
|
+
NETLIFY_AUTH_TOKEN: ${{ secrets.NETLIFY_AUTH_TOKEN }}
|
|
46
|
+
NETLIFY_SITE_ID: ${{ secrets.NETLIFY_SITE_ID }}
|
package/templates/CLAUDE.md
CHANGED
|
@@ -19,6 +19,11 @@ npm run e2e:mobile # Playwright E2E (mobile)
|
|
|
19
19
|
npm run e2e:all # Playwright E2E (all viewports)
|
|
20
20
|
npm run e2e:perf # Performance regression tests
|
|
21
21
|
npm run i18n:extract # Extract translations to .po
|
|
22
|
+
npm run db:types # Generate Supabase TypeScript types
|
|
23
|
+
npm run db:push # Push database migrations
|
|
24
|
+
npm run db:studio # Open Supabase Studio
|
|
25
|
+
npm run sync:check # Check monorepo dependency versions
|
|
26
|
+
npm run sync:fix # Auto-fix version mismatches
|
|
22
27
|
```
|
|
23
28
|
|
|
24
29
|
## Project Structure
|
|
@@ -213,10 +218,184 @@ import { render, mockMatchMedia, server } from '@/test';
|
|
|
213
218
|
|
|
214
219
|
MSW handlers auto-reset after each test.
|
|
215
220
|
|
|
221
|
+
## Authentication (Clerk)
|
|
222
|
+
|
|
223
|
+
When the auth feature is enabled, Clerk authentication is required.
|
|
224
|
+
|
|
225
|
+
### Setup
|
|
226
|
+
|
|
227
|
+
1. Create an account at [clerk.com](https://clerk.com)
|
|
228
|
+
2. Get your Publishable Key from the dashboard
|
|
229
|
+
3. Copy `.env.example` to `.env` and set your key:
|
|
230
|
+
```
|
|
231
|
+
VITE_CLERK_PUBLISHABLE_KEY=pk_test_xxxxx
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Usage
|
|
235
|
+
|
|
236
|
+
```tsx
|
|
237
|
+
// Protect routes that require authentication
|
|
238
|
+
import { ProtectedRoute } from '@/components/shared';
|
|
239
|
+
|
|
240
|
+
<Route
|
|
241
|
+
path="/dashboard"
|
|
242
|
+
element={
|
|
243
|
+
<ProtectedRoute>
|
|
244
|
+
<DashboardPage />
|
|
245
|
+
</ProtectedRoute>
|
|
246
|
+
}
|
|
247
|
+
/>;
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
```tsx
|
|
251
|
+
// Conditional rendering based on auth state
|
|
252
|
+
import { SignedIn, SignedOut, UserButton, SignInButton } from '@clerk/react-router';
|
|
253
|
+
|
|
254
|
+
<SignedIn>
|
|
255
|
+
<UserButton />
|
|
256
|
+
</SignedIn>
|
|
257
|
+
<SignedOut>
|
|
258
|
+
<SignInButton mode="modal">
|
|
259
|
+
<Button>Sign In</Button>
|
|
260
|
+
</SignInButton>
|
|
261
|
+
</SignedOut>
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Testing
|
|
265
|
+
|
|
266
|
+
Clerk is automatically mocked in tests. Use test utilities to control auth state:
|
|
267
|
+
|
|
268
|
+
```tsx
|
|
269
|
+
import { setMockClerkSignedIn, resetClerkMocks } from '@/test';
|
|
270
|
+
|
|
271
|
+
beforeEach(() => resetClerkMocks());
|
|
272
|
+
|
|
273
|
+
it('shows sign-in when not authenticated', () => {
|
|
274
|
+
setMockClerkSignedIn(false);
|
|
275
|
+
// ...
|
|
276
|
+
});
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
## Database (Supabase)
|
|
280
|
+
|
|
281
|
+
Supabase provides PostgreSQL database with Row Level Security (RLS), integrated with Clerk authentication.
|
|
282
|
+
|
|
283
|
+
### Setup
|
|
284
|
+
|
|
285
|
+
1. Create a project at [supabase.com](https://supabase.com)
|
|
286
|
+
2. Configure Clerk as third-party auth provider:
|
|
287
|
+
- Supabase Dashboard → Authentication → Providers → Third-Party Auth → Add Clerk
|
|
288
|
+
3. Enable Supabase integration in Clerk:
|
|
289
|
+
- Clerk Dashboard → Integrations → Supabase → Activate
|
|
290
|
+
4. Set environment variables in `.env`:
|
|
291
|
+
```
|
|
292
|
+
VITE_SUPABASE_DATABASE_URL=https://your-project.supabase.co
|
|
293
|
+
VITE_SUPABASE_ANON_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Database Commands
|
|
297
|
+
|
|
298
|
+
```bash
|
|
299
|
+
npm run db:types # Generate TypeScript types from schema
|
|
300
|
+
npm run db:push # Push migrations to database
|
|
301
|
+
npm run db:reset # Reset database (WARNING: destructive)
|
|
302
|
+
npm run db:studio # Open Supabase Studio
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
### Usage
|
|
306
|
+
|
|
307
|
+
```tsx
|
|
308
|
+
import { useSupabase, useSupabaseQuery, useProfile } from '@/hooks';
|
|
309
|
+
|
|
310
|
+
// Direct client access
|
|
311
|
+
const supabase = useSupabase();
|
|
312
|
+
const { data } = await supabase.from('profiles').select();
|
|
313
|
+
|
|
314
|
+
// TanStack Query wrapper for automatic caching
|
|
315
|
+
const { data, isLoading } = useSupabaseQuery({
|
|
316
|
+
table: 'profiles',
|
|
317
|
+
queryKey: ['current'],
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
// Convenience hook for current user's profile
|
|
321
|
+
const { profile, isLoading, exists } = useProfile();
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Profile Mutations
|
|
325
|
+
|
|
326
|
+
```tsx
|
|
327
|
+
import { useUpsertProfile, useUpdateProfile, useDeleteProfile } from '@/hooks';
|
|
328
|
+
|
|
329
|
+
// Create or update profile (upsert)
|
|
330
|
+
const upsertProfile = useUpsertProfile();
|
|
331
|
+
await upsertProfile.mutateAsync({ id: userId, email: 'user@example.com' });
|
|
332
|
+
|
|
333
|
+
// Update current user's profile
|
|
334
|
+
const updateProfile = useUpdateProfile();
|
|
335
|
+
await updateProfile.mutateAsync({ full_name: 'John' });
|
|
336
|
+
|
|
337
|
+
// Delete current user's profile
|
|
338
|
+
const deleteProfile = useDeleteProfile();
|
|
339
|
+
await deleteProfile.mutateAsync();
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Auto-Sync with ProfileSync
|
|
343
|
+
|
|
344
|
+
```tsx
|
|
345
|
+
import { ProfileSync } from '@/components/shared';
|
|
346
|
+
|
|
347
|
+
// Add to your app to auto-sync Clerk user data to Supabase
|
|
348
|
+
function App() {
|
|
349
|
+
return (
|
|
350
|
+
<>
|
|
351
|
+
<ProfileSync />
|
|
352
|
+
<Routes>...</Routes>
|
|
353
|
+
</>
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
### Row Level Security (RLS)
|
|
359
|
+
|
|
360
|
+
All tables should have RLS enabled. Policies use `auth.uid()` which equals the Clerk user_id:
|
|
361
|
+
|
|
362
|
+
```sql
|
|
363
|
+
-- Users can only access their own data
|
|
364
|
+
CREATE POLICY "Users can view own profile"
|
|
365
|
+
ON profiles FOR SELECT TO authenticated
|
|
366
|
+
USING (id = auth.uid());
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
### Testing
|
|
370
|
+
|
|
371
|
+
Supabase context is mocked in tests with state controls:
|
|
372
|
+
|
|
373
|
+
```tsx
|
|
374
|
+
import { render, setMockSupabaseData, setMockSupabaseError, createProfile, resetSupabaseMocks } from '@/test';
|
|
375
|
+
|
|
376
|
+
beforeEach(() => resetSupabaseMocks());
|
|
377
|
+
|
|
378
|
+
it('displays profile data', async () => {
|
|
379
|
+
setMockSupabaseData([createProfile({ full_name: 'Test User' })]);
|
|
380
|
+
render(<ProfileCard />);
|
|
381
|
+
// Assert profile is displayed
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
it('handles error', async () => {
|
|
385
|
+
setMockSupabaseError({ message: 'Failed', code: 'ERROR' });
|
|
386
|
+
render(<ProfileCard />);
|
|
387
|
+
// Assert error state
|
|
388
|
+
});
|
|
389
|
+
```
|
|
390
|
+
|
|
216
391
|
## Common Gotchas
|
|
217
392
|
|
|
218
393
|
1. **Node.js >= 22.0.0** required (check `.nvmrc`)
|
|
219
394
|
2. **Conventional commits** enforced by commitlint
|
|
220
|
-
3. **Context hooks throw** outside provider (e.g., `useMobileContext()`)
|
|
395
|
+
3. **Context hooks throw** outside provider (e.g., `useMobileContext()`, `useSupabase()`)
|
|
221
396
|
4. **Barrel exports** in each directory via `index.ts`
|
|
222
397
|
5. **UI components** import directly: `@/components/ui/button` (no barrel)
|
|
398
|
+
6. **Clerk auth required** when auth feature is enabled - set `VITE_CLERK_PUBLISHABLE_KEY` in `.env`
|
|
399
|
+
7. **Supabase requires Clerk** - SupabaseProvider must be inside ClerkProvider
|
|
400
|
+
8. **RLS policies required** - All Supabase tables should have Row Level Security enabled
|
|
401
|
+
9. **Monorepo deps** - Run `npm run sync:check` to detect version mismatches across packages
|