@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
|
@@ -26,7 +26,7 @@ export function loadMcpRegistry() {
|
|
|
26
26
|
/**
|
|
27
27
|
* Filter MCPs by stack relevance
|
|
28
28
|
* @param {Object} registry - Full MCP registry
|
|
29
|
-
* @param {string} stack - Detected project stack (e.g., 'nextjs-
|
|
29
|
+
* @param {string} stack - Detected project stack (e.g., 'nextjs-neon', 'blazor')
|
|
30
30
|
* @returns {Object} Filtered map of mcpName → mcpEntry
|
|
31
31
|
*/
|
|
32
32
|
export function filterByStack(registry, stack) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Task classifier — identifies test/validation tasks by title.
|
|
2
|
+
* Task classifier — identifies test/validation and infrastructure tasks by title.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
const TEST_TASK_PATTERN = /\b(tests?|testing|specs?|coverage|e2e|unit|integration|integra[cç][aã]o|valida[cç][aã]o|cobertura|testes?)\b/i;
|
|
@@ -14,3 +14,17 @@ export function isTestTask(taskTitle) {
|
|
|
14
14
|
if (!taskTitle) return false;
|
|
15
15
|
return TEST_TASK_PATTERN.test(taskTitle);
|
|
16
16
|
}
|
|
17
|
+
|
|
18
|
+
const INFRA_TASK_PATTERN = /\b(infrastructure|infra|cleanup|migration|migra[cç][aã]o|config|setup|ci\/cd|devops|deploy|docker|packages?|dependencies|tags)\b/i;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Returns true if the task title indicates an infrastructure/cleanup/migration task.
|
|
22
|
+
* Used to skip contract validation and suppress breaking change warnings.
|
|
23
|
+
*
|
|
24
|
+
* @param {string} taskTitle
|
|
25
|
+
* @returns {boolean}
|
|
26
|
+
*/
|
|
27
|
+
export function isInfrastructureTask(taskTitle) {
|
|
28
|
+
if (!taskTitle) return false;
|
|
29
|
+
return INFRA_TASK_PATTERN.test(taskTitle);
|
|
30
|
+
}
|
|
@@ -68,9 +68,13 @@ export function runTestSuite(projectPath = '.', opts = {}) {
|
|
|
68
68
|
});
|
|
69
69
|
return { passed: true, output: output ?? '', exitCode: 0 };
|
|
70
70
|
} catch (err) {
|
|
71
|
+
const output = (err.stdout || '') + (err.stderr || '');
|
|
72
|
+
const BUILD_FAIL = /Build FAILED|error MSB\d+|error CS\d+|Could not find.*\.csproj/i;
|
|
73
|
+
const buildFailed = BUILD_FAIL.test(output);
|
|
71
74
|
return {
|
|
72
75
|
passed: false,
|
|
73
|
-
|
|
76
|
+
buildFailed,
|
|
77
|
+
output,
|
|
74
78
|
exitCode: err.status ?? 1
|
|
75
79
|
};
|
|
76
80
|
}
|
|
@@ -23,18 +23,17 @@ export function validateSpecContent(specPath) {
|
|
|
23
23
|
|
|
24
24
|
const content = readFileSync(specPath, 'utf8');
|
|
25
25
|
|
|
26
|
-
// Required sections
|
|
27
|
-
// "## Functional Requirements" and "## Technical Architecture")
|
|
26
|
+
// Required sections (flexible regex handles numbered variants like '## 1. Overview')
|
|
28
27
|
const requiredSections = [
|
|
29
|
-
'## Overview',
|
|
30
|
-
'## Functional Requirements',
|
|
31
|
-
'## Technical',
|
|
32
|
-
'## Data Model',
|
|
28
|
+
{ label: '## Overview', pattern: /^##\s+(?:\d+\.?\s+)?Overview/i },
|
|
29
|
+
{ label: '## Functional Requirements', pattern: /^##\s+(?:\d+\.?\s+)?Functional Requirements/i },
|
|
30
|
+
{ label: '## Technical', pattern: /^##\s+(?:\d+\.?\s+)?Technical/i },
|
|
31
|
+
{ label: '## Data Model', pattern: /^##\s+(?:\d+\.?\s+)?Data Model/i },
|
|
33
32
|
];
|
|
34
33
|
|
|
35
34
|
const lines = content.split('\n');
|
|
36
|
-
const missing = requiredSections.filter(
|
|
37
|
-
!lines.some(line =>
|
|
35
|
+
const missing = requiredSections.filter(s =>
|
|
36
|
+
!lines.some(line => s.pattern.test(line))
|
|
38
37
|
);
|
|
39
38
|
|
|
40
39
|
// Additional quality checks
|
|
@@ -44,8 +43,8 @@ export function validateSpecContent(specPath) {
|
|
|
44
43
|
// Missing sections are warnings, not errors — not every spec needs all sections
|
|
45
44
|
// (e.g., frontend-only features may not have Data Model, CLI tools may not have Technical Architecture)
|
|
46
45
|
if (missing.length > 0) {
|
|
47
|
-
missing.forEach(
|
|
48
|
-
warnings.push(`Missing recommended section: ${
|
|
46
|
+
missing.forEach(s => {
|
|
47
|
+
warnings.push(`Missing recommended section: ${s.label}`);
|
|
49
48
|
});
|
|
50
49
|
}
|
|
51
50
|
|
|
@@ -119,11 +119,13 @@ export async function runValidation(projectPath, featureName, options = {}) {
|
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
// Always run contract compliance if the contracts file exists (stack-aware)
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
122
|
+
if (!options.skipContracts) {
|
|
123
|
+
const _profileForContracts = getStackProfile(projectPath);
|
|
124
|
+
const contractsType = resolveContractsOutputType(_profileForContracts.stack);
|
|
125
|
+
const contractsPath = getAbsoluteOutputPath(projectPath, featureName, contractsType);
|
|
126
|
+
if (existsSync(contractsPath) && !_profileForContracts.isNextjs) {
|
|
127
|
+
validatorIds.push('contract-compliance');
|
|
128
|
+
}
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
// Deduplicate
|
|
@@ -1,256 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: supabase-expert
|
|
3
|
-
description: Supabase & PostgreSQL specialist for RLS policies, auth integration, pgvector, Realtime subscriptions, Edge Functions, and migration management. Use when designing database schemas, implementing Row Level Security, setting up auth flows, or working with pgvector embeddings.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
-
stacks:
|
|
6
|
-
- supabase
|
|
7
|
-
color: teal
|
|
8
|
-
effort: medium
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Supabase & PostgreSQL Specialist
|
|
12
|
-
|
|
13
|
-
Expert in Supabase auth, Row Level Security, Realtime, Edge Functions, and pgvector.
|
|
14
|
-
|
|
15
|
-
> **Ref:** `framework/standards/infrastructure/supabase/` — RLS, auth, storage, pgvector standards
|
|
16
|
-
|
|
17
|
-
## Connection Pooling
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
# Direct connection (migrations only)
|
|
21
|
-
postgresql://postgres:[password]@db.[project].supabase.co:5432/postgres
|
|
22
|
-
|
|
23
|
-
# PgBouncer (application — ALWAYS use this)
|
|
24
|
-
postgresql://postgres.[project]:[password]@aws-0-[region].pooler.supabase.com:6543/postgres
|
|
25
|
-
```
|
|
26
|
-
|
|
27
|
-
**Rule:** Application code ALWAYS connects via PgBouncer (port 6543). Direct connections (port 5432) only for migrations and schema changes.
|
|
28
|
-
|
|
29
|
-
---
|
|
30
|
-
|
|
31
|
-
## Row Level Security (RLS)
|
|
32
|
-
|
|
33
|
-
### Enable RLS on every table
|
|
34
|
-
|
|
35
|
-
```sql
|
|
36
|
-
ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
|
|
37
|
-
```
|
|
38
|
-
|
|
39
|
-
### Common RLS Patterns
|
|
40
|
-
|
|
41
|
-
```sql
|
|
42
|
-
-- User owns the row
|
|
43
|
-
CREATE POLICY "Users can view own documents"
|
|
44
|
-
ON public.documents FOR SELECT
|
|
45
|
-
USING (auth.uid() = user_id);
|
|
46
|
-
|
|
47
|
-
-- User can insert own rows
|
|
48
|
-
CREATE POLICY "Users can insert own documents"
|
|
49
|
-
ON public.documents FOR INSERT
|
|
50
|
-
WITH CHECK (auth.uid() = user_id);
|
|
51
|
-
|
|
52
|
-
-- User can update own rows
|
|
53
|
-
CREATE POLICY "Users can update own documents"
|
|
54
|
-
ON public.documents FOR UPDATE
|
|
55
|
-
USING (auth.uid() = user_id)
|
|
56
|
-
WITH CHECK (auth.uid() = user_id);
|
|
57
|
-
|
|
58
|
-
-- User can delete own rows
|
|
59
|
-
CREATE POLICY "Users can delete own documents"
|
|
60
|
-
ON public.documents FOR DELETE
|
|
61
|
-
USING (auth.uid() = user_id);
|
|
62
|
-
|
|
63
|
-
-- Service role bypass (for backend API)
|
|
64
|
-
CREATE POLICY "Service role full access"
|
|
65
|
-
ON public.documents FOR ALL
|
|
66
|
-
USING (auth.role() = 'service_role');
|
|
67
|
-
|
|
68
|
-
-- Team-based access
|
|
69
|
-
CREATE POLICY "Team members can view"
|
|
70
|
-
ON public.documents FOR SELECT
|
|
71
|
-
USING (
|
|
72
|
-
team_id IN (
|
|
73
|
-
SELECT team_id FROM public.team_members
|
|
74
|
-
WHERE user_id = auth.uid()
|
|
75
|
-
)
|
|
76
|
-
);
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
### RLS with Clerk JWT (custom claims)
|
|
80
|
-
|
|
81
|
-
```sql
|
|
82
|
-
-- Extract Clerk user ID from JWT
|
|
83
|
-
CREATE OR REPLACE FUNCTION auth.clerk_user_id()
|
|
84
|
-
RETURNS TEXT AS $$
|
|
85
|
-
SELECT coalesce(
|
|
86
|
-
current_setting('request.jwt.claims', true)::json->>'sub',
|
|
87
|
-
''
|
|
88
|
-
);
|
|
89
|
-
$$ LANGUAGE sql STABLE;
|
|
90
|
-
|
|
91
|
-
-- Use in RLS policies
|
|
92
|
-
CREATE POLICY "Clerk users own rows"
|
|
93
|
-
ON public.documents FOR ALL
|
|
94
|
-
USING (clerk_user_id = auth.clerk_user_id());
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
---
|
|
98
|
-
|
|
99
|
-
## Migrations
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
# Create new migration
|
|
103
|
-
npx supabase migration new create_documents_table
|
|
104
|
-
|
|
105
|
-
# Apply locally
|
|
106
|
-
npx supabase db reset
|
|
107
|
-
|
|
108
|
-
# Push to remote
|
|
109
|
-
npx supabase db push
|
|
110
|
-
|
|
111
|
-
# Diff local vs remote
|
|
112
|
-
npx supabase db diff --use-migra
|
|
113
|
-
|
|
114
|
-
# Lint for common issues
|
|
115
|
-
npx supabase db lint --level warning
|
|
116
|
-
```
|
|
117
|
-
|
|
118
|
-
### Migration file structure
|
|
119
|
-
|
|
120
|
-
```sql
|
|
121
|
-
-- supabase/migrations/20240101000000_create_documents_table.sql
|
|
122
|
-
|
|
123
|
-
CREATE TABLE public.documents (
|
|
124
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
125
|
-
user_id UUID NOT NULL REFERENCES auth.users(id) ON DELETE CASCADE,
|
|
126
|
-
title TEXT NOT NULL,
|
|
127
|
-
content TEXT,
|
|
128
|
-
created_at TIMESTAMPTZ DEFAULT now() NOT NULL,
|
|
129
|
-
updated_at TIMESTAMPTZ DEFAULT now() NOT NULL
|
|
130
|
-
);
|
|
131
|
-
|
|
132
|
-
-- Always enable RLS
|
|
133
|
-
ALTER TABLE public.documents ENABLE ROW LEVEL SECURITY;
|
|
134
|
-
|
|
135
|
-
-- Always add indexes for filtered columns
|
|
136
|
-
CREATE INDEX idx_documents_user_id ON public.documents(user_id);
|
|
137
|
-
CREATE INDEX idx_documents_created_at ON public.documents(created_at DESC);
|
|
138
|
-
|
|
139
|
-
-- RLS policies
|
|
140
|
-
CREATE POLICY "Users manage own documents"
|
|
141
|
-
ON public.documents FOR ALL
|
|
142
|
-
USING (auth.uid() = user_id)
|
|
143
|
-
WITH CHECK (auth.uid() = user_id);
|
|
144
|
-
```
|
|
145
|
-
|
|
146
|
-
---
|
|
147
|
-
|
|
148
|
-
## pgvector (Embeddings)
|
|
149
|
-
|
|
150
|
-
### Setup
|
|
151
|
-
|
|
152
|
-
```sql
|
|
153
|
-
CREATE EXTENSION IF NOT EXISTS vector;
|
|
154
|
-
|
|
155
|
-
CREATE TABLE public.embeddings (
|
|
156
|
-
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
|
|
157
|
-
document_id UUID REFERENCES public.documents(id) ON DELETE CASCADE,
|
|
158
|
-
content TEXT NOT NULL,
|
|
159
|
-
embedding vector(1536) NOT NULL, -- OpenAI text-embedding-3-small
|
|
160
|
-
metadata JSONB DEFAULT '{}'::jsonb,
|
|
161
|
-
created_at TIMESTAMPTZ DEFAULT now() NOT NULL
|
|
162
|
-
);
|
|
163
|
-
|
|
164
|
-
-- HNSW index for fast similarity search (cosine distance)
|
|
165
|
-
CREATE INDEX idx_embeddings_vector ON public.embeddings
|
|
166
|
-
USING hnsw (embedding vector_cosine_ops)
|
|
167
|
-
WITH (m = 16, ef_construction = 64);
|
|
168
|
-
```
|
|
169
|
-
|
|
170
|
-
### Similarity search function
|
|
171
|
-
|
|
172
|
-
```sql
|
|
173
|
-
CREATE OR REPLACE FUNCTION match_embeddings(
|
|
174
|
-
query_embedding vector(1536),
|
|
175
|
-
match_threshold FLOAT DEFAULT 0.78,
|
|
176
|
-
match_count INT DEFAULT 5
|
|
177
|
-
)
|
|
178
|
-
RETURNS TABLE (
|
|
179
|
-
id UUID,
|
|
180
|
-
content TEXT,
|
|
181
|
-
metadata JSONB,
|
|
182
|
-
similarity FLOAT
|
|
183
|
-
)
|
|
184
|
-
LANGUAGE plpgsql AS $$
|
|
185
|
-
BEGIN
|
|
186
|
-
RETURN QUERY
|
|
187
|
-
SELECT
|
|
188
|
-
e.id,
|
|
189
|
-
e.content,
|
|
190
|
-
e.metadata,
|
|
191
|
-
1 - (e.embedding <=> query_embedding) AS similarity
|
|
192
|
-
FROM public.embeddings e
|
|
193
|
-
WHERE 1 - (e.embedding <=> query_embedding) > match_threshold
|
|
194
|
-
ORDER BY e.embedding <=> query_embedding
|
|
195
|
-
LIMIT match_count;
|
|
196
|
-
END;
|
|
197
|
-
$$;
|
|
198
|
-
```
|
|
199
|
-
|
|
200
|
-
---
|
|
201
|
-
|
|
202
|
-
## Realtime Subscriptions
|
|
203
|
-
|
|
204
|
-
```typescript
|
|
205
|
-
// Subscribe to changes on a table
|
|
206
|
-
const channel = supabase
|
|
207
|
-
.channel('documents-changes')
|
|
208
|
-
.on('postgres_changes',
|
|
209
|
-
{ event: '*', schema: 'public', table: 'documents', filter: `user_id=eq.${userId}` },
|
|
210
|
-
(payload) => {
|
|
211
|
-
console.log('Change:', payload.eventType, payload.new);
|
|
212
|
-
}
|
|
213
|
-
)
|
|
214
|
-
.subscribe();
|
|
215
|
-
|
|
216
|
-
// Cleanup
|
|
217
|
-
channel.unsubscribe();
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
**Rule:** Always filter subscriptions by user/team — never subscribe to full table changes.
|
|
221
|
-
|
|
222
|
-
---
|
|
223
|
-
|
|
224
|
-
## Storage (File Uploads)
|
|
225
|
-
|
|
226
|
-
```sql
|
|
227
|
-
-- Create bucket
|
|
228
|
-
INSERT INTO storage.buckets (id, name, public)
|
|
229
|
-
VALUES ('avatars', 'avatars', true);
|
|
230
|
-
|
|
231
|
-
-- RLS for storage
|
|
232
|
-
CREATE POLICY "Users upload own avatar"
|
|
233
|
-
ON storage.objects FOR INSERT
|
|
234
|
-
WITH CHECK (
|
|
235
|
-
bucket_id = 'avatars' AND
|
|
236
|
-
auth.uid()::text = (storage.foldername(name))[1]
|
|
237
|
-
);
|
|
238
|
-
```
|
|
239
|
-
|
|
240
|
-
---
|
|
241
|
-
|
|
242
|
-
## Checklist
|
|
243
|
-
|
|
244
|
-
- [ ] PgBouncer (port 6543) used for all application connections
|
|
245
|
-
- [ ] RLS enabled on ALL public tables
|
|
246
|
-
- [ ] RLS policies cover SELECT, INSERT, UPDATE, DELETE
|
|
247
|
-
- [ ] Service role policy exists for backend API access
|
|
248
|
-
- [ ] Migrations use `npx supabase migration new` (not manual SQL)
|
|
249
|
-
- [ ] Indexes on all frequently filtered columns
|
|
250
|
-
- [ ] pgvector: HNSW index with appropriate m/ef_construction values
|
|
251
|
-
- [ ] Realtime subscriptions filtered by user/team
|
|
252
|
-
- [ ] Storage buckets have RLS policies
|
|
253
|
-
|
|
254
|
-
---
|
|
255
|
-
|
|
256
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: clerk-auth
|
|
3
|
-
description: Clerk Auth specialist for Clerk SDK integration, social login, JWT session management, and user management in Next.js and .NET projects. Use when integrating Clerk authentication, implementing social login flows, managing user sessions with Clerk JWT, or protecting routes with Clerk middleware.
|
|
4
|
-
allowed-tools: Read, Write, Edit, Bash, Glob, Grep
|
|
5
|
-
stacks:
|
|
6
|
-
- clerk
|
|
7
|
-
color: teal
|
|
8
|
-
effort: medium
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# Clerk Auth
|
|
12
|
-
|
|
13
|
-
Autenticação SaaS com Clerk para .NET e Next.js. SDK: `Clerk.Net.AspNetCore.Security` (.NET) / `@clerk/nextjs` (Next.js).
|
|
14
|
-
|
|
15
|
-
## Setup
|
|
16
|
-
|
|
17
|
-
```csharp
|
|
18
|
-
// appsettings.json
|
|
19
|
-
{ "Clerk": { "SecretKey": "${CLERK_SECRET_KEY}", "PublishableKey": "pk_test_xxx" } }
|
|
20
|
-
|
|
21
|
-
// Program.cs
|
|
22
|
-
builder.Services.AddClerk(builder.Configuration);
|
|
23
|
-
builder.Services.AddAuthentication(ClerkAuthenticationDefaults.AuthenticationScheme)
|
|
24
|
-
.AddClerk(o => { o.Authority = "https://clerk.{instance}.com"; o.ValidAudiences = ["your-app-id"]; });
|
|
25
|
-
builder.Services.AddAuthorization();
|
|
26
|
-
app.UseAuthentication();
|
|
27
|
-
app.UseAuthorization();
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
## Protected Endpoints
|
|
31
|
-
|
|
32
|
-
```csharp
|
|
33
|
-
// Minimal API
|
|
34
|
-
app.MapGet("/api/profile", async (ClaimsPrincipal user, IClerkClient clerk) =>
|
|
35
|
-
{
|
|
36
|
-
var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
|
|
37
|
-
if (userId is null) return Results.Unauthorized();
|
|
38
|
-
var u = await clerk.Users.GetUserAsync(userId);
|
|
39
|
-
return Results.Ok(new { u.Id, Email = u.EmailAddresses.FirstOrDefault()?.EmailAddress });
|
|
40
|
-
}).RequireAuthorization();
|
|
41
|
-
|
|
42
|
-
// Controller: same pattern with [Authorize] + User.FindFirstValue()
|
|
43
|
-
// Role-based: [Authorize(Roles = "admin")]
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Next.js Integration
|
|
47
|
-
|
|
48
|
-
```tsx
|
|
49
|
-
// middleware.ts
|
|
50
|
-
import { clerkMiddleware } from '@clerk/nextjs/server';
|
|
51
|
-
export default clerkMiddleware();
|
|
52
|
-
export const config = { matcher: ['/((?!.*\\..*|_next).*)', '/'] };
|
|
53
|
-
|
|
54
|
-
// Protected page
|
|
55
|
-
import { auth } from '@clerk/nextjs/server';
|
|
56
|
-
export default async function DashboardPage() {
|
|
57
|
-
const { userId } = await auth();
|
|
58
|
-
if (!userId) redirect('/sign-in');
|
|
59
|
-
return <Dashboard />;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// Client component
|
|
63
|
-
'use client';
|
|
64
|
-
import { useUser } from '@clerk/nextjs';
|
|
65
|
-
const { user, isLoaded } = useUser();
|
|
66
|
-
```
|
|
67
|
-
|
|
68
|
-
## Client Operations (.NET)
|
|
69
|
-
|
|
70
|
-
```csharp
|
|
71
|
-
var user = await _clerk.Users.GetUserAsync(userId); // Get
|
|
72
|
-
var users = await _clerk.Users.GetUserListAsync(new() { Limit = 10 }); // List
|
|
73
|
-
await _clerk.Users.UpdateUserMetadataAsync(userId, new() // Update metadata
|
|
74
|
-
{ PublicMetadata = new Dictionary<string, object> { ["plan"] = "pro" } });
|
|
75
|
-
await _clerk.Users.DeleteUserAsync(userId); // Delete
|
|
76
|
-
```
|
|
77
|
-
|
|
78
|
-
## Webhooks
|
|
79
|
-
|
|
80
|
-
```csharp
|
|
81
|
-
[ApiController, Route("api/webhooks/clerk")]
|
|
82
|
-
public class ClerkWebhookController(IUserService users) : ControllerBase
|
|
83
|
-
{
|
|
84
|
-
[HttpPost]
|
|
85
|
-
public async Task<IActionResult> Handle([FromBody] ClerkWebhookPayload payload)
|
|
86
|
-
{
|
|
87
|
-
switch (payload.Type)
|
|
88
|
-
{
|
|
89
|
-
case "user.created": await users.SyncUserAsync(payload.Data); break;
|
|
90
|
-
case "user.updated": await users.UpdateUserAsync(payload.Data); break;
|
|
91
|
-
case "user.deleted": await users.DeleteUserAsync(payload.Data.Id); break;
|
|
92
|
-
}
|
|
93
|
-
return Ok();
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
## Checklist
|
|
99
|
-
|
|
100
|
-
- [ ] Secret Key in environment variables (not hardcoded)
|
|
101
|
-
- [ ] Authentication scheme configured
|
|
102
|
-
- [ ] Authorization policies defined
|
|
103
|
-
- [ ] Webhook endpoint + signature validation
|
|
104
|
-
- [ ] Redirect after login configured
|
|
105
|
-
- [ ] Error handling for expired sessions
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
*MORPH-SPEC by Polymorphism Tech*
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
paths:
|
|
3
|
-
- "**/*.sql"
|
|
4
|
-
- "**/supabase/**"
|
|
5
|
-
- "**/migrations/**"
|
|
6
|
-
stacks:
|
|
7
|
-
- supabase
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
# Supabase & PostgreSQL Standards
|
|
11
|
-
|
|
12
|
-
@.morph/framework/standards/infrastructure/supabase/supabase-auth.md
|
|
13
|
-
@.morph/framework/standards/infrastructure/supabase/supabase-rls.md
|
|
14
|
-
@.morph/framework/standards/infrastructure/supabase/supabase-storage.md
|
|
15
|
-
@.morph/framework/standards/infrastructure/supabase/supabase-pgvector.md
|
|
16
|
-
@.morph/framework/standards/infrastructure/supabase/mcp-setup.md
|
|
17
|
-
@.morph/framework/standards/infrastructure/supabase/supabase-custom-jwt.md
|
|
18
|
-
@.morph/framework/standards/backend/database/postgresql/database.md
|
|
19
|
-
|
|
20
|
-
## SQL & Migration Templates
|
|
21
|
-
|
|
22
|
-
| Template | Path | Purpose |
|
|
23
|
-
|----------|------|---------|
|
|
24
|
-
| Migration (full) | `.morph/framework/templates/code/sql/supabase-migration.template.sql` | RLS + indexes |
|
|
25
|
-
| Migration (simple) | `.morph/framework/templates/code/sql/supabase-migration.sql` | Basic migration |
|
|
26
|
-
| RLS Policy | `.morph/framework/templates/code/sql/rls-policy.sql` | Row Level Security |
|
|
27
|
-
| TS Types | `.morph/framework/templates/code/typescript/contracts.ts` | Supabase DB types |
|
|
28
|
-
|
|
29
|
-
Read the template to see the exact pattern for migrations, RLS policies, and type generation.
|