@polymorphism-tech/morph-spec 4.16.0 → 4.18.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/CLAUDE.md +36 -0
- package/README.md +2 -2
- package/bin/morph-spec.js +3 -0
- package/bin/task-manager.js +999 -962
- package/claude-plugin.json +1 -1
- package/docs/CHEATSHEET.md +1 -1
- package/docs/QUICKSTART.md +1 -1
- package/framework/agents/ai-agents/vector-search-expert.md +247 -233
- package/framework/agents/backend/ms-agent-expert.md +1 -1
- package/framework/agents/backend/neon-expert.md +379 -0
- package/framework/agents/frontend/gsap-animator.md +131 -0
- package/framework/agents/integrations/neon-auth.md +151 -0
- package/framework/agents.json +1183 -1085
- package/framework/commands/morph-preflight.md +200 -192
- package/framework/hooks/claude-code/session-start/inject-morph-context.js +1 -1
- package/framework/rules/gsap-standards.md +13 -0
- package/framework/rules/neon-auth-standards.md +13 -0
- package/framework/rules/neon-standards.md +26 -0
- package/framework/skills/level-0-meta/mcp-registry.json +1 -1
- package/framework/skills/level-0-meta/morph-agent-teams/SKILL.md +472 -472
- package/framework/skills/level-0-meta/morph-frontend-review/SKILL.md +1 -1
- package/framework/skills/level-0-meta/morph-init/SKILL.md +12 -12
- package/framework/skills/level-0-meta/morph-post-implementation/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-tool-usage-guide/SKILL.md +5 -5
- package/framework/skills/level-0-meta/morph-tool-usage-guide/references/tools-per-phase.md +7 -7
- package/framework/skills/level-1-workflows/morph-phase-codebase-analysis/SKILL.md +24 -24
- package/framework/skills/level-1-workflows/morph-phase-design/SKILL.md +12 -12
- package/framework/skills/level-1-workflows/morph-phase-implement/SKILL.md +9 -9
- package/framework/skills/level-1-workflows/morph-phase-plan/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-phase-tasks/references/task-planning-patterns.md +143 -143
- package/framework/skills/level-1-workflows/morph-phase-uiux/SKILL.md +1 -1
- package/framework/skills/level-1-workflows/morph-scope-escalation/SKILL.md +1 -1
- package/framework/standards/STANDARDS.json +197 -116
- package/framework/standards/ai-agents/production.md +1 -0
- package/framework/standards/ai-agents/setup.md +3 -2
- package/framework/standards/ai-agents/workflows.md +1 -0
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +4 -4
- package/framework/standards/backend/database/postgresql/database.md +63 -83
- package/framework/standards/backend/dotnet/scalar-ui.md +172 -172
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +274 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +2 -2
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +364 -0
- package/framework/standards/frontend/design-system/animations.md +9 -0
- package/framework/standards/frontend/gsap/gsap-core.md +463 -0
- package/framework/standards/frontend/gsap/gsap-plugins.md +294 -0
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +318 -0
- package/framework/standards/frontend/nextjs/motion-patterns.md +1 -0
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +47 -37
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +4 -8
- package/framework/standards/infrastructure/neon/neon-pgrag.md +239 -0
- package/framework/standards/infrastructure/neon/neon-pgvector.md +236 -0
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +310 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +186 -0
- package/framework/standards/integration/mcp/mcp-tools.md +32 -34
- package/framework/templates/README.md +20 -20
- package/framework/templates/REGISTRY.json +54 -101
- package/framework/templates/code/dotnet/abstractions/api-endpoint.cs +7 -7
- package/framework/templates/code/dotnet/backend/repository.cs +148 -148
- package/framework/templates/code/dotnet/contracts/contracts-vsa.cs +2 -2
- package/framework/templates/code/dotnet/database/vector-entity.cs +93 -0
- package/framework/templates/code/dotnet/feature/endpoint.cs.hbs +1 -1
- package/framework/templates/code/sql/{supabase-migration.sql → postgresql-migration.sql} +12 -21
- package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +13 -18
- package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +6 -9
- package/framework/templates/code/typescript/contracts.ts +167 -168
- package/framework/templates/context/CONTEXT.md +3 -3
- package/framework/templates/docs/proposal.md +6 -6
- package/framework/templates/docs/ui-components.md +1 -1
- package/framework/templates/feature/decisions.md +122 -121
- package/framework/templates/feature/recap.md +145 -145
- package/framework/templates/feature/tasks.md +199 -199
- package/framework/templates/frontend/nextjs/gsap-scroll-section.tsx.hbs +44 -0
- package/framework/templates/infrastructure/docker/docker-compose.template.yml +50 -67
- package/framework/templates/infrastructure/docker/dockerfile-api.dockerfile +38 -38
- package/framework/templates/infrastructure/docker/dockerfile-web.dockerfile +48 -48
- package/framework/templates/infrastructure/docker/easypanel.template.json +46 -54
- package/framework/templates/infrastructure/github/README.md +56 -56
- package/framework/templates/integrations/neon-auth-config.cs +133 -0
- package/framework/templates/integrations/semantic-kernel-neon.cs +125 -0
- package/framework/workflows/README.md +1 -1
- package/framework/workflows/docs/full-morph.md +69 -69
- package/package.json +1 -1
- package/src/commands/agents/dispatch-agents.js +19 -15
- package/src/commands/state/advance-phase.js +5 -0
- package/src/commands/tasks/task.js +3 -0
- package/src/core/state/state-manager.js +45 -1
- package/src/core/templates/template-registry.js +1 -66
- package/src/lib/detectors/index.js +4 -4
- package/src/lib/generators/architecture-scanner.js +639 -640
- package/src/lib/installers/mcp-installer.js +1 -1
- package/src/lib/tasks/task-classifier.js +15 -1
- package/src/lib/tasks/test-runner.js +5 -1
- package/src/lib/validators/content/content-validator.js +9 -10
- package/src/lib/validators/validation-runner.js +7 -5
- package/framework/agents/backend/supabase-expert.md +0 -256
- package/framework/agents/integrations/clerk-auth.md +0 -109
- package/framework/rules/clerk-standards.md +0 -13
- package/framework/rules/supabase-standards.md +0 -29
- package/framework/standards/backend/integrations/clerk/clerk-auth.md +0 -290
- package/framework/standards/infrastructure/supabase/mcp-setup.md +0 -142
- package/framework/standards/infrastructure/supabase/supabase-auth.md +0 -176
- package/framework/standards/infrastructure/supabase/supabase-custom-jwt.md +0 -206
- package/framework/standards/infrastructure/supabase/supabase-pgvector.md +0 -169
- package/framework/standards/infrastructure/supabase/supabase-rls.md +0 -184
- package/framework/standards/infrastructure/supabase/supabase-storage.md +0 -153
- package/framework/templates/integrations/clerk-config.cs +0 -217
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
# Clerk Authentication Integration Standard
|
|
2
|
-
|
|
3
|
-
> **Scope:** universal
|
|
4
|
-
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:** clerk, auth, authentication, nextjs, user management
|
|
6
|
-
> **Load When:** clerk authentication keywords detected
|
|
7
|
-
|
|
8
|
-
Modern authentication and user management for Next.js applications.
|
|
9
|
-
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
## Overview
|
|
13
|
-
|
|
14
|
-
Clerk provides:
|
|
15
|
-
- Pre-built UI components (SignIn, SignUp, UserProfile)
|
|
16
|
-
- Social auth (Google, GitHub, etc.)
|
|
17
|
-
- Multi-factor authentication
|
|
18
|
-
- Session management
|
|
19
|
-
- User metadata and organizations
|
|
20
|
-
|
|
21
|
-
**Primary Use Case:** Next.js 15 + App Router
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
## Core Principles
|
|
26
|
-
|
|
27
|
-
1. **Server-First**: Use server-side auth checks with `auth()` helper
|
|
28
|
-
2. **Route Protection**: Middleware-based protection for private routes
|
|
29
|
-
3. **Type Safety**: Use Clerk's TypeScript types
|
|
30
|
-
4. **Edge Compatible**: Works with Edge Runtime
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
## Installation & Setup
|
|
35
|
-
|
|
36
|
-
### Install Packages
|
|
37
|
-
|
|
38
|
-
```bash
|
|
39
|
-
npm install @clerk/nextjs
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
### Environment Variables
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
# .env.local
|
|
46
|
-
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY=pk_test_...
|
|
47
|
-
CLERK_SECRET_KEY=sk_test_...
|
|
48
|
-
NEXT_PUBLIC_CLERK_SIGN_IN_URL=/sign-in
|
|
49
|
-
NEXT_PUBLIC_CLERK_SIGN_UP_URL=/sign-up
|
|
50
|
-
NEXT_PUBLIC_CLERK_AFTER_SIGN_IN_URL=/dashboard
|
|
51
|
-
NEXT_PUBLIC_CLERK_AFTER_SIGN_UP_URL=/onboarding
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
### Root Layout Wrapper
|
|
55
|
-
|
|
56
|
-
```typescript
|
|
57
|
-
// app/layout.tsx
|
|
58
|
-
import { ClerkProvider } from '@clerk/nextjs';
|
|
59
|
-
|
|
60
|
-
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
|
61
|
-
return (
|
|
62
|
-
<ClerkProvider>
|
|
63
|
-
<html lang="en">
|
|
64
|
-
<body>{children}</body>
|
|
65
|
-
</html>
|
|
66
|
-
</ClerkProvider>
|
|
67
|
-
);
|
|
68
|
-
}
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
### Middleware Protection
|
|
72
|
-
|
|
73
|
-
```typescript
|
|
74
|
-
// middleware.ts
|
|
75
|
-
import { authMiddleware } from '@clerk/nextjs';
|
|
76
|
-
|
|
77
|
-
export default authMiddleware({
|
|
78
|
-
publicRoutes: ['/', '/api/webhooks/clerk'],
|
|
79
|
-
ignoredRoutes: ['/api/public']
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
export const config = {
|
|
83
|
-
matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)'],
|
|
84
|
-
};
|
|
85
|
-
```
|
|
86
|
-
|
|
87
|
-
---
|
|
88
|
-
|
|
89
|
-
## Common Patterns
|
|
90
|
-
|
|
91
|
-
### Sign In Page
|
|
92
|
-
|
|
93
|
-
```typescript
|
|
94
|
-
// app/sign-in/[[...sign-in]]/page.tsx
|
|
95
|
-
import { SignIn } from '@clerk/nextjs';
|
|
96
|
-
|
|
97
|
-
export default function SignInPage() {
|
|
98
|
-
return (
|
|
99
|
-
<div className="flex items-center justify-center min-h-screen">
|
|
100
|
-
<SignIn />
|
|
101
|
-
</div>
|
|
102
|
-
);
|
|
103
|
-
}
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
### Server-Side Auth Check
|
|
107
|
-
|
|
108
|
-
```typescript
|
|
109
|
-
// app/dashboard/page.tsx
|
|
110
|
-
import { auth } from '@clerk/nextjs';
|
|
111
|
-
import { redirect } from 'next/navigation';
|
|
112
|
-
|
|
113
|
-
export default async function DashboardPage() {
|
|
114
|
-
const { userId } = auth();
|
|
115
|
-
|
|
116
|
-
if (!userId) {
|
|
117
|
-
redirect('/sign-in');
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return <div>Dashboard for user {userId}</div>;
|
|
121
|
-
}
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
### Client Component with User Data
|
|
125
|
-
|
|
126
|
-
```typescript
|
|
127
|
-
// components/UserProfile.tsx
|
|
128
|
-
'use client';
|
|
129
|
-
import { useUser } from '@clerk/nextjs';
|
|
130
|
-
|
|
131
|
-
export function UserProfile() {
|
|
132
|
-
const { user, isLoaded, isSignedIn } = useUser();
|
|
133
|
-
|
|
134
|
-
if (!isLoaded) return <div>Loading...</div>;
|
|
135
|
-
if (!isSignedIn) return <div>Not signed in</div>;
|
|
136
|
-
|
|
137
|
-
return (
|
|
138
|
-
<div>
|
|
139
|
-
<p>Welcome, {user.firstName}!</p>
|
|
140
|
-
<p>Email: {user.primaryEmailAddress?.emailAddress}</p>
|
|
141
|
-
</div>
|
|
142
|
-
);
|
|
143
|
-
}
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
### API Route Protection
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
// app/api/protected/route.ts
|
|
150
|
-
import { auth } from '@clerk/nextjs';
|
|
151
|
-
|
|
152
|
-
export async function GET() {
|
|
153
|
-
const { userId } = auth();
|
|
154
|
-
|
|
155
|
-
if (!userId) {
|
|
156
|
-
return new Response('Unauthorized', { status: 401 });
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// Fetch user-specific data
|
|
160
|
-
const data = await fetchUserData(userId);
|
|
161
|
-
|
|
162
|
-
return Response.json(data);
|
|
163
|
-
}
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
---
|
|
167
|
-
|
|
168
|
-
## Webhook Integration
|
|
169
|
-
|
|
170
|
-
### Setup Webhook Endpoint
|
|
171
|
-
|
|
172
|
-
```typescript
|
|
173
|
-
// app/api/webhooks/clerk/route.ts
|
|
174
|
-
import { Webhook } from 'svix';
|
|
175
|
-
import { headers } from 'next/headers';
|
|
176
|
-
|
|
177
|
-
export async function POST(req: Request) {
|
|
178
|
-
const WEBHOOK_SECRET = process.env.CLERK_WEBHOOK_SECRET!;
|
|
179
|
-
|
|
180
|
-
const headerPayload = headers();
|
|
181
|
-
const svix_id = headerPayload.get('svix-id');
|
|
182
|
-
const svix_timestamp = headerPayload.get('svix-timestamp');
|
|
183
|
-
const svix_signature = headerPayload.get('svix-signature');
|
|
184
|
-
|
|
185
|
-
if (!svix_id || !svix_timestamp || !svix_signature) {
|
|
186
|
-
return new Response('Missing svix headers', { status: 400 });
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const payload = await req.json();
|
|
190
|
-
const body = JSON.stringify(payload);
|
|
191
|
-
|
|
192
|
-
const wh = new Webhook(WEBHOOK_SECRET);
|
|
193
|
-
|
|
194
|
-
let evt;
|
|
195
|
-
try {
|
|
196
|
-
evt = wh.verify(body, {
|
|
197
|
-
'svix-id': svix_id,
|
|
198
|
-
'svix-timestamp': svix_timestamp,
|
|
199
|
-
'svix-signature': svix_signature,
|
|
200
|
-
});
|
|
201
|
-
} catch (err) {
|
|
202
|
-
return new Response('Invalid signature', { status: 400 });
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// Handle events
|
|
206
|
-
switch (evt.type) {
|
|
207
|
-
case 'user.created':
|
|
208
|
-
await handleUserCreated(evt.data);
|
|
209
|
-
break;
|
|
210
|
-
case 'user.updated':
|
|
211
|
-
await handleUserUpdated(evt.data);
|
|
212
|
-
break;
|
|
213
|
-
case 'user.deleted':
|
|
214
|
-
await handleUserDeleted(evt.data);
|
|
215
|
-
break;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
return new Response('OK');
|
|
219
|
-
}
|
|
220
|
-
```
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
## Supabase Integration
|
|
225
|
-
|
|
226
|
-
### Sync Users to Supabase
|
|
227
|
-
|
|
228
|
-
```typescript
|
|
229
|
-
// lib/clerk/sync-user.ts
|
|
230
|
-
import { createClient } from '@supabase/supabase-js';
|
|
231
|
-
|
|
232
|
-
async function handleUserCreated(userData: any) {
|
|
233
|
-
const supabase = createClient(
|
|
234
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
235
|
-
process.env.SUPABASE_SERVICE_ROLE_KEY!
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
await supabase.from('profiles').insert({
|
|
239
|
-
clerk_user_id: userData.id,
|
|
240
|
-
email: userData.email_addresses[0].email_address,
|
|
241
|
-
first_name: userData.first_name,
|
|
242
|
-
last_name: userData.last_name,
|
|
243
|
-
avatar_url: userData.image_url
|
|
244
|
-
});
|
|
245
|
-
}
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
|
-
## Best Practices
|
|
251
|
-
|
|
252
|
-
### Custom Claims
|
|
253
|
-
|
|
254
|
-
```typescript
|
|
255
|
-
// Add custom claims in Clerk Dashboard or via API
|
|
256
|
-
const { sessionClaims } = auth();
|
|
257
|
-
const role = sessionClaims?.metadata?.role as string;
|
|
258
|
-
|
|
259
|
-
if (role !== 'admin') {
|
|
260
|
-
return new Response('Forbidden', { status: 403 });
|
|
261
|
-
}
|
|
262
|
-
```
|
|
263
|
-
|
|
264
|
-
### Organizations
|
|
265
|
-
|
|
266
|
-
```typescript
|
|
267
|
-
import { auth } from '@clerk/nextjs';
|
|
268
|
-
|
|
269
|
-
const { orgId, orgRole } = auth();
|
|
270
|
-
|
|
271
|
-
if (!orgId) {
|
|
272
|
-
redirect('/create-organization');
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
if (orgRole !== 'admin') {
|
|
276
|
-
return <div>Admin access required</div>;
|
|
277
|
-
}
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
---
|
|
281
|
-
|
|
282
|
-
## References
|
|
283
|
-
|
|
284
|
-
- [Clerk Next.js Documentation](https://clerk.com/docs/nextjs)
|
|
285
|
-
- [Clerk API Reference](https://clerk.com/docs/reference/backend-api)
|
|
286
|
-
- [Webhooks Guide](https://clerk.com/docs/integrations/webhooks)
|
|
287
|
-
|
|
288
|
-
---
|
|
289
|
-
|
|
290
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,142 +0,0 @@
|
|
|
1
|
-
# Supabase Local Development Guide
|
|
2
|
-
|
|
3
|
-
> **Scope:** nextjs-supabase
|
|
4
|
-
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:** supabase, cli, local, docker, setup, migrations, psql
|
|
6
|
-
> **Load When:** supabase setup keywords detected
|
|
7
|
-
|
|
8
|
-
Local-first database management via Supabase CLI and psql — always targeting the local Docker instance.
|
|
9
|
-
|
|
10
|
-
## CRITICAL WARNING
|
|
11
|
-
|
|
12
|
-
```
|
|
13
|
-
==========================================================
|
|
14
|
-
NEVER connect to a PRODUCTION Supabase project.
|
|
15
|
-
|
|
16
|
-
All database operations MUST target the local Docker
|
|
17
|
-
instance started via `npx supabase start`.
|
|
18
|
-
|
|
19
|
-
Production schema may diverge from local (renamed tables,
|
|
20
|
-
columns, migrations). Local is the single source of truth
|
|
21
|
-
during development.
|
|
22
|
-
==========================================================
|
|
23
|
-
```
|
|
24
|
-
|
|
25
|
-
## Setup
|
|
26
|
-
|
|
27
|
-
### 1. Install Supabase CLI
|
|
28
|
-
|
|
29
|
-
```bash
|
|
30
|
-
npm install -g supabase
|
|
31
|
-
# or use npx (no global install needed)
|
|
32
|
-
npx supabase --version
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### 2. Start Local Instance
|
|
36
|
-
|
|
37
|
-
```bash
|
|
38
|
-
npx supabase start
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
This starts PostgreSQL, GoTrue (auth), Storage, and other services in Docker containers.
|
|
42
|
-
|
|
43
|
-
### 3. Get Connection Details
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
npx supabase status
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
Output includes:
|
|
50
|
-
- **DB URL**: `postgresql://postgres:postgres@127.0.0.1:54322/postgres` (port may vary)
|
|
51
|
-
- **API URL**: `http://127.0.0.1:54321`
|
|
52
|
-
- **anon key** and **service_role key** for local testing
|
|
53
|
-
|
|
54
|
-
> **Important:** The port (54322) may vary when running multiple Supabase instances. Always use `npx supabase status` to get the current DB URL — never hardcode it.
|
|
55
|
-
|
|
56
|
-
## Common CLI Commands
|
|
57
|
-
|
|
58
|
-
### Schema Inspection
|
|
59
|
-
|
|
60
|
-
```bash
|
|
61
|
-
# Full schema dump
|
|
62
|
-
npx supabase db dump --local --schema public
|
|
63
|
-
|
|
64
|
-
# List tables (from dump output)
|
|
65
|
-
npx supabase db dump --local --schema public 2>/dev/null | grep "CREATE TABLE"
|
|
66
|
-
|
|
67
|
-
# Detailed table schema via psql
|
|
68
|
-
psql $DB_URL -c "SELECT column_name, data_type, is_nullable, column_default \
|
|
69
|
-
FROM information_schema.columns WHERE table_name = 'TABLE_NAME' ORDER BY ordinal_position"
|
|
70
|
-
|
|
71
|
-
# Foreign key relationships
|
|
72
|
-
psql $DB_URL -c "SELECT tc.table_name, kcu.column_name, ccu.table_name AS foreign_table, \
|
|
73
|
-
ccu.column_name AS foreign_column FROM information_schema.table_constraints tc \
|
|
74
|
-
JOIN information_schema.key_column_usage kcu ON tc.constraint_name = kcu.constraint_name \
|
|
75
|
-
JOIN information_schema.constraint_column_usage ccu ON tc.constraint_name = ccu.constraint_name \
|
|
76
|
-
WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_name = 'TABLE_NAME'"
|
|
77
|
-
|
|
78
|
-
# RLS policies
|
|
79
|
-
psql $DB_URL -c "SELECT tablename, policyname, cmd, qual FROM pg_policies WHERE schemaname = 'public'"
|
|
80
|
-
|
|
81
|
-
# Indexes
|
|
82
|
-
psql $DB_URL -c "SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'TABLE_NAME'"
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Migrations
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
# Create a new migration
|
|
89
|
-
npx supabase migration new add_status_to_leads
|
|
90
|
-
|
|
91
|
-
# Apply pending migrations to local
|
|
92
|
-
npx supabase migration up --local
|
|
93
|
-
|
|
94
|
-
# List applied migrations
|
|
95
|
-
npx supabase migration list --local
|
|
96
|
-
|
|
97
|
-
# Reset local database (destructive — re-applies all migrations)
|
|
98
|
-
npx supabase db reset
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
### Data Operations
|
|
102
|
-
|
|
103
|
-
```bash
|
|
104
|
-
# Run arbitrary SQL against local
|
|
105
|
-
psql $DB_URL -c "SELECT * FROM leads LIMIT 5"
|
|
106
|
-
|
|
107
|
-
# Seed data
|
|
108
|
-
npx supabase db seed --local
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
## Usage with MORPH-SPEC Workflow
|
|
112
|
-
|
|
113
|
-
During MORPH-SPEC phases, the Supabase CLI is used for:
|
|
114
|
-
|
|
115
|
-
| Phase | CLI Usage | Commands |
|
|
116
|
-
|-------|-----------|----------|
|
|
117
|
-
| **Phase 2 (Design)** | Inspect existing schema to inform spec | `npx supabase status` → `psql` queries |
|
|
118
|
-
| **Phase 4 (Tasks)** | Verify migration dependencies | `npx supabase migration list --local` |
|
|
119
|
-
| **Phase 5 (Implement)** | Apply migrations, verify RLS, test queries | `npx supabase migration up --local` + `psql` |
|
|
120
|
-
| **Post-Implementation** | Verify RLS policies are correct | `psql` pg_policies queries |
|
|
121
|
-
|
|
122
|
-
### Example Workflow
|
|
123
|
-
|
|
124
|
-
```
|
|
125
|
-
1. Design phase: `npx supabase status` → `psql $DB_URL` to inspect schema
|
|
126
|
-
2. Write migration SQL in supabase/migrations/
|
|
127
|
-
3. Apply: `npx supabase migration up --local`
|
|
128
|
-
4. Verify: `psql $DB_URL -c "SELECT policyname, cmd FROM pg_policies WHERE tablename = 'search_logs'"`
|
|
129
|
-
5. Test: `psql $DB_URL -c "SELECT * FROM search_logs LIMIT 5"`
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
## Best Practices
|
|
133
|
-
|
|
134
|
-
1. **Always use local Supabase** (`npx supabase start`) — never connect to remote/production
|
|
135
|
-
2. **Use `npx supabase status`** to detect the DB URL dynamically — port may vary
|
|
136
|
-
3. **Write migrations as SQL files** in `supabase/migrations/` — apply via CLI
|
|
137
|
-
4. **Verify RLS after every table change** — missing policies are a CRITICAL security issue
|
|
138
|
-
5. **Reset local DB when needed** — `npx supabase db reset` re-applies all migrations cleanly
|
|
139
|
-
|
|
140
|
-
---
|
|
141
|
-
|
|
142
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,176 +0,0 @@
|
|
|
1
|
-
# Supabase Authentication Standard
|
|
2
|
-
|
|
3
|
-
> **Scope:** nextjs-supabase
|
|
4
|
-
> **Layer:** 2 (on keyword)
|
|
5
|
-
> **Keywords:** supabase, auth, rls, user, authentication
|
|
6
|
-
> **Load When:** supabase auth keywords detected
|
|
7
|
-
|
|
8
|
-
Stack: Next.js 15 + Supabase + .NET Backend
|
|
9
|
-
|
|
10
|
-
## Core Rules
|
|
11
|
-
|
|
12
|
-
- NEVER use `supabase.auth.getSession()` on server -- reads from cookies without validation
|
|
13
|
-
- ALWAYS use `supabase.auth.getUser()` on server -- validates JWT with Supabase
|
|
14
|
-
- NEVER expose `service_role` key on frontend -- bypasses RLS
|
|
15
|
-
- ALWAYS use `@supabase/ssr` for Next.js -- not `@supabase/auth-helpers-nextjs` (deprecated)
|
|
16
|
-
- ALWAYS use PKCE flow for SSR auth
|
|
17
|
-
|
|
18
|
-
## Client Setup
|
|
19
|
-
|
|
20
|
-
### Browser Client
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
// lib/supabase/client.ts
|
|
24
|
-
import { createBrowserClient } from "@supabase/ssr";
|
|
25
|
-
|
|
26
|
-
export function createClient() {
|
|
27
|
-
return createBrowserClient(
|
|
28
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
29
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
|
30
|
-
);
|
|
31
|
-
}
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
### Server Client
|
|
35
|
-
|
|
36
|
-
```ts
|
|
37
|
-
// lib/supabase/server.ts
|
|
38
|
-
import { createServerClient } from "@supabase/ssr";
|
|
39
|
-
import { cookies } from "next/headers";
|
|
40
|
-
|
|
41
|
-
export async function createClient() {
|
|
42
|
-
const cookieStore = await cookies();
|
|
43
|
-
return createServerClient(
|
|
44
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
45
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
46
|
-
{
|
|
47
|
-
cookies: {
|
|
48
|
-
getAll() { return cookieStore.getAll(); },
|
|
49
|
-
setAll(cookiesToSet) {
|
|
50
|
-
cookiesToSet.forEach(({ name, value, options }) =>
|
|
51
|
-
cookieStore.set(name, value, options));
|
|
52
|
-
},
|
|
53
|
-
},
|
|
54
|
-
}
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
## Auth Flows
|
|
60
|
-
|
|
61
|
-
```ts
|
|
62
|
-
// Email/Password sign up
|
|
63
|
-
await supabase.auth.signUp({ email, password,
|
|
64
|
-
options: { emailRedirectTo: `${origin}/auth/callback` } });
|
|
65
|
-
|
|
66
|
-
// Email/Password sign in
|
|
67
|
-
await supabase.auth.signInWithPassword({ email, password });
|
|
68
|
-
|
|
69
|
-
// OAuth (Google / GitHub)
|
|
70
|
-
await supabase.auth.signInWithOAuth({
|
|
71
|
-
provider: "google", // or "github"
|
|
72
|
-
options: { redirectTo: `${origin}/auth/callback`,
|
|
73
|
-
queryParams: { access_type: "offline", prompt: "consent" } } // Google-specific
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
// Magic Link
|
|
77
|
-
await supabase.auth.signInWithOtp({ email,
|
|
78
|
-
options: { emailRedirectTo: `${origin}/auth/callback` } });
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
## Auth Callback Route (PKCE)
|
|
82
|
-
|
|
83
|
-
```ts
|
|
84
|
-
// app/auth/callback/route.ts
|
|
85
|
-
import { createClient } from "@/lib/supabase/server";
|
|
86
|
-
import { NextResponse } from "next/server";
|
|
87
|
-
|
|
88
|
-
export async function GET(request: Request) {
|
|
89
|
-
const { searchParams, origin } = new URL(request.url);
|
|
90
|
-
const code = searchParams.get("code");
|
|
91
|
-
const next = searchParams.get("next") ?? "/dashboard";
|
|
92
|
-
if (code) {
|
|
93
|
-
const supabase = await createClient();
|
|
94
|
-
const { error } = await supabase.auth.exchangeCodeForSession(code);
|
|
95
|
-
if (!error) return NextResponse.redirect(`${origin}${next}`);
|
|
96
|
-
}
|
|
97
|
-
return NextResponse.redirect(`${origin}/auth/error`);
|
|
98
|
-
}
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
## Middleware Pattern
|
|
102
|
-
|
|
103
|
-
```ts
|
|
104
|
-
// middleware.ts
|
|
105
|
-
import { createServerClient } from "@supabase/ssr";
|
|
106
|
-
import { NextResponse, type NextRequest } from "next/server";
|
|
107
|
-
|
|
108
|
-
export async function middleware(request: NextRequest) {
|
|
109
|
-
let supabaseResponse = NextResponse.next({ request });
|
|
110
|
-
const supabase = createServerClient(
|
|
111
|
-
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
112
|
-
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
|
|
113
|
-
{
|
|
114
|
-
cookies: {
|
|
115
|
-
getAll() { return request.cookies.getAll(); },
|
|
116
|
-
setAll(cookiesToSet) {
|
|
117
|
-
cookiesToSet.forEach(({ name, value, options }) => {
|
|
118
|
-
request.cookies.set(name, value);
|
|
119
|
-
supabaseResponse.cookies.set(name, value, options);
|
|
120
|
-
});
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
}
|
|
124
|
-
);
|
|
125
|
-
const { data: { user } } = await supabase.auth.getUser();
|
|
126
|
-
if (!user && request.nextUrl.pathname.startsWith("/dashboard"))
|
|
127
|
-
return NextResponse.redirect(new URL("/login", request.url));
|
|
128
|
-
return supabaseResponse;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export const config = {
|
|
132
|
-
matcher: ["/((?!_next/static|_next/image|favicon.ico|api/webhooks).*)"],
|
|
133
|
-
};
|
|
134
|
-
```
|
|
135
|
-
|
|
136
|
-
## .NET JWT Validation
|
|
137
|
-
|
|
138
|
-
```csharp
|
|
139
|
-
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
|
140
|
-
.AddJwtBearer(options => {
|
|
141
|
-
options.TokenValidationParameters = new TokenValidationParameters {
|
|
142
|
-
ValidateIssuer = true,
|
|
143
|
-
ValidIssuer = $"https://{supabaseProjectRef}.supabase.co/auth/v1",
|
|
144
|
-
ValidateAudience = true,
|
|
145
|
-
ValidAudience = "authenticated",
|
|
146
|
-
ValidateIssuerSigningKey = true,
|
|
147
|
-
IssuerSigningKey = new SymmetricSecurityKey(
|
|
148
|
-
Encoding.UTF8.GetBytes(supabaseJwtSecret)),
|
|
149
|
-
ValidateLifetime = true,
|
|
150
|
-
ClockSkew = TimeSpan.FromSeconds(30)
|
|
151
|
-
};
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
// Extract user ID: maps to auth.uid()
|
|
155
|
-
var userId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
## Environment Variables
|
|
159
|
-
|
|
160
|
-
| Variable | Where | Purpose |
|
|
161
|
-
|----------|-------|---------|
|
|
162
|
-
| `NEXT_PUBLIC_SUPABASE_URL` | Frontend | Supabase project URL |
|
|
163
|
-
| `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Frontend | Public anon key (respects RLS) |
|
|
164
|
-
| `SUPABASE_SERVICE_ROLE_KEY` | Backend ONLY | Bypasses RLS -- NEVER on frontend |
|
|
165
|
-
| `SUPABASE_JWT_SECRET` | Backend ONLY | JWT validation secret |
|
|
166
|
-
|
|
167
|
-
## Common Mistakes
|
|
168
|
-
|
|
169
|
-
| Wrong | Right | Why |
|
|
170
|
-
|-------|-------|-----|
|
|
171
|
-
| `getSession()` on server | `getUser()` on server | getSession reads unvalidated cookie data |
|
|
172
|
-
| `@supabase/auth-helpers-nextjs` | `@supabase/ssr` | auth-helpers is deprecated |
|
|
173
|
-
| `service_role` in `NEXT_PUBLIC_*` | `anon` key in `NEXT_PUBLIC_*` | service_role bypasses all RLS |
|
|
174
|
-
| Implicit flow for SSR | PKCE flow with code exchange | Implicit exposes tokens in URL fragments |
|
|
175
|
-
| Auth only in page components | Auth check in middleware.ts | Middleware prevents flash of content |
|
|
176
|
-
| Missing `setAll` in cookie config | Both `getAll` and `setAll` | Session refresh silently fails without setAll |
|