@polymorphism-tech/morph-spec 4.15.0 → 4.17.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/README.md +2 -2
- package/bin/morph-spec.js +4 -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 +2 -1
- package/framework/agents/backend/neon-expert.md +397 -0
- package/framework/agents/frontend/gsap-animator.md +131 -0
- package/framework/agents.json +1175 -1077
- 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-standards.md +25 -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-init/SKILL.md +30 -11
- 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 +184 -107
- 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 +58 -78
- 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 +1 -1
- package/framework/standards/backend/integrations/clerk/clerk-auth.md +23 -21
- 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 +18 -18
- package/framework/templates/REGISTRY.json +49 -96
- 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} +8 -17
- package/framework/templates/code/sql/{supabase-migration.template.sql → postgresql-migration.template.sql} +10 -13
- package/framework/templates/code/sql/{rls-policy.sql → postgresql-rls-policy.sql} +8 -8
- 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 +121 -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/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/project/setup-infra-cmd.js +1 -1
- 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 +2 -2
- package/src/lib/generators/architecture-scanner.js +640 -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/src/scripts/setup-infra.js +60 -30
- package/framework/agents/backend/supabase-expert.md +0 -256
- package/framework/rules/supabase-standards.md +0 -29
- 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
|
@@ -1,148 +1,148 @@
|
|
|
1
|
-
// ============================================================
|
|
2
|
-
// REPOSITORY + UNIT OF WORK TEMPLATE (VSA Pattern)
|
|
3
|
-
// Generated by MORPH Framework
|
|
4
|
-
// ============================================================
|
|
5
|
-
|
|
6
|
-
using System.Data.Common;
|
|
7
|
-
using Microsoft.EntityFrameworkCore;
|
|
8
|
-
using {{pascalCase PROJECT_NAME}}.Database;
|
|
9
|
-
|
|
10
|
-
namespace {{pascalCase PROJECT_NAME}}.Repository;
|
|
11
|
-
|
|
12
|
-
// ============================================================
|
|
13
|
-
// IRepository<T> — Generic repository contract
|
|
14
|
-
// ============================================================
|
|
15
|
-
|
|
16
|
-
public interface IRepository<T> where T : class
|
|
17
|
-
{
|
|
18
|
-
Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
|
|
19
|
-
Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
|
|
20
|
-
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
|
|
21
|
-
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
|
|
22
|
-
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
|
|
23
|
-
IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams);
|
|
24
|
-
Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
|
|
25
|
-
void RemoveRange(IEnumerable<T> entities);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// ============================================================
|
|
29
|
-
// IUnitOfWork — Transaction boundary contract
|
|
30
|
-
// ============================================================
|
|
31
|
-
|
|
32
|
-
public interface IUnitOfWork
|
|
33
|
-
{
|
|
34
|
-
int Commit();
|
|
35
|
-
void Rollback();
|
|
36
|
-
Task<int> CommitAsync(CancellationToken cancellationToken = default);
|
|
37
|
-
Task RollbackAsync();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// ============================================================
|
|
41
|
-
// Repository<T> — EF Core generic repository implementation
|
|
42
|
-
// ============================================================
|
|
43
|
-
|
|
44
|
-
public class Repository<T> : IRepository<T> where T : class
|
|
45
|
-
{
|
|
46
|
-
private readonly ApplicationDbContext _context;
|
|
47
|
-
private readonly DbSet<T> _dbSet;
|
|
48
|
-
|
|
49
|
-
public Repository(ApplicationDbContext context)
|
|
50
|
-
{
|
|
51
|
-
_context = context;
|
|
52
|
-
_dbSet = _context.Set<T>();
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
public async Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default)
|
|
56
|
-
{
|
|
57
|
-
return await _dbSet.ToListAsync(cancellationToken);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
public async Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default)
|
|
61
|
-
{
|
|
62
|
-
return await _dbSet.FindAsync(new[] { id }, cancellationToken);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
public async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
|
|
66
|
-
{
|
|
67
|
-
await _dbSet.AddAsync(entity, cancellationToken);
|
|
68
|
-
return entity;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
public Task UpdateAsync(T entity, CancellationToken cancellationToken = default)
|
|
72
|
-
{
|
|
73
|
-
_dbSet.Update(entity);
|
|
74
|
-
return Task.CompletedTask;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
public Task DeleteAsync(T entity, CancellationToken cancellationToken = default)
|
|
78
|
-
{
|
|
79
|
-
_dbSet.Remove(entity);
|
|
80
|
-
return Task.CompletedTask;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams)
|
|
84
|
-
{
|
|
85
|
-
return _dbSet
|
|
86
|
-
.FromSqlRaw(query, dbParams)
|
|
87
|
-
.AsNoTracking()
|
|
88
|
-
.AsEnumerable();
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
public async Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default)
|
|
92
|
-
{
|
|
93
|
-
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
public void RemoveRange(IEnumerable<T> entities)
|
|
97
|
-
{
|
|
98
|
-
_dbSet.RemoveRange(entities);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// ============================================================
|
|
103
|
-
// UnitOfWork — EF Core transaction management
|
|
104
|
-
// ============================================================
|
|
105
|
-
|
|
106
|
-
public class UnitOfWork : IUnitOfWork
|
|
107
|
-
{
|
|
108
|
-
private readonly ApplicationDbContext _dataContext;
|
|
109
|
-
|
|
110
|
-
public UnitOfWork(ApplicationDbContext dataContext)
|
|
111
|
-
{
|
|
112
|
-
_dataContext = dataContext;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
public int Commit()
|
|
116
|
-
{
|
|
117
|
-
return _dataContext.SaveChanges();
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
public async Task<int> CommitAsync(CancellationToken cancellationToken = default)
|
|
121
|
-
{
|
|
122
|
-
return await _dataContext.SaveChangesAsync(cancellationToken);
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
public void Rollback()
|
|
126
|
-
{
|
|
127
|
-
_dataContext.Dispose();
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
public async Task RollbackAsync()
|
|
131
|
-
{
|
|
132
|
-
await _dataContext.DisposeAsync();
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// ============================================================
|
|
137
|
-
// DI REGISTRATION
|
|
138
|
-
// ============================================================
|
|
139
|
-
//
|
|
140
|
-
// In Program.cs or a DI extension method:
|
|
141
|
-
//
|
|
142
|
-
// builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
143
|
-
// options.UseSqlServer(connectionString));
|
|
144
|
-
//
|
|
145
|
-
// builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
|
146
|
-
// builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
|
147
|
-
//
|
|
148
|
-
// ============================================================
|
|
1
|
+
// ============================================================
|
|
2
|
+
// REPOSITORY + UNIT OF WORK TEMPLATE (VSA Pattern)
|
|
3
|
+
// Generated by MORPH Framework
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
using System.Data.Common;
|
|
7
|
+
using Microsoft.EntityFrameworkCore;
|
|
8
|
+
using {{pascalCase PROJECT_NAME}}.Database;
|
|
9
|
+
|
|
10
|
+
namespace {{pascalCase PROJECT_NAME}}.Repository;
|
|
11
|
+
|
|
12
|
+
// ============================================================
|
|
13
|
+
// IRepository<T> — Generic repository contract
|
|
14
|
+
// ============================================================
|
|
15
|
+
|
|
16
|
+
public interface IRepository<T> where T : class
|
|
17
|
+
{
|
|
18
|
+
Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default);
|
|
19
|
+
Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default);
|
|
20
|
+
Task<T> AddAsync(T entity, CancellationToken cancellationToken = default);
|
|
21
|
+
Task UpdateAsync(T entity, CancellationToken cancellationToken = default);
|
|
22
|
+
Task DeleteAsync(T entity, CancellationToken cancellationToken = default);
|
|
23
|
+
IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams);
|
|
24
|
+
Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default);
|
|
25
|
+
void RemoveRange(IEnumerable<T> entities);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// ============================================================
|
|
29
|
+
// IUnitOfWork — Transaction boundary contract
|
|
30
|
+
// ============================================================
|
|
31
|
+
|
|
32
|
+
public interface IUnitOfWork
|
|
33
|
+
{
|
|
34
|
+
int Commit();
|
|
35
|
+
void Rollback();
|
|
36
|
+
Task<int> CommitAsync(CancellationToken cancellationToken = default);
|
|
37
|
+
Task RollbackAsync(CancellationToken cancellationToken = default);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// ============================================================
|
|
41
|
+
// Repository<T> — EF Core generic repository implementation
|
|
42
|
+
// ============================================================
|
|
43
|
+
|
|
44
|
+
public class Repository<T> : IRepository<T> where T : class
|
|
45
|
+
{
|
|
46
|
+
private readonly ApplicationDbContext _context;
|
|
47
|
+
private readonly DbSet<T> _dbSet;
|
|
48
|
+
|
|
49
|
+
public Repository(ApplicationDbContext context)
|
|
50
|
+
{
|
|
51
|
+
_context = context;
|
|
52
|
+
_dbSet = _context.Set<T>();
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
public async Task<IEnumerable<T>> GetAllAsync(CancellationToken cancellationToken = default)
|
|
56
|
+
{
|
|
57
|
+
return await _dbSet.ToListAsync(cancellationToken);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public async Task<T?> GetByIdAsync(object id, CancellationToken cancellationToken = default)
|
|
61
|
+
{
|
|
62
|
+
return await _dbSet.FindAsync(new[] { id }, cancellationToken);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
public async Task<T> AddAsync(T entity, CancellationToken cancellationToken = default)
|
|
66
|
+
{
|
|
67
|
+
await _dbSet.AddAsync(entity, cancellationToken);
|
|
68
|
+
return entity;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
public Task UpdateAsync(T entity, CancellationToken cancellationToken = default)
|
|
72
|
+
{
|
|
73
|
+
_dbSet.Update(entity);
|
|
74
|
+
return Task.CompletedTask;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
public Task DeleteAsync(T entity, CancellationToken cancellationToken = default)
|
|
78
|
+
{
|
|
79
|
+
_dbSet.Remove(entity);
|
|
80
|
+
return Task.CompletedTask;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public IEnumerable<T> ExecuteQuery(string query, DbParameter[] dbParams)
|
|
84
|
+
{
|
|
85
|
+
return _dbSet
|
|
86
|
+
.FromSqlRaw(query, dbParams)
|
|
87
|
+
.AsNoTracking()
|
|
88
|
+
.AsEnumerable();
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
public async Task AddRangeAsync(IEnumerable<T> entities, CancellationToken cancellationToken = default)
|
|
92
|
+
{
|
|
93
|
+
await _dbSet.AddRangeAsync(entities, cancellationToken);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public void RemoveRange(IEnumerable<T> entities)
|
|
97
|
+
{
|
|
98
|
+
_dbSet.RemoveRange(entities);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ============================================================
|
|
103
|
+
// UnitOfWork — EF Core transaction management
|
|
104
|
+
// ============================================================
|
|
105
|
+
|
|
106
|
+
public class UnitOfWork : IUnitOfWork
|
|
107
|
+
{
|
|
108
|
+
private readonly ApplicationDbContext _dataContext;
|
|
109
|
+
|
|
110
|
+
public UnitOfWork(ApplicationDbContext dataContext)
|
|
111
|
+
{
|
|
112
|
+
_dataContext = dataContext;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
public int Commit()
|
|
116
|
+
{
|
|
117
|
+
return _dataContext.SaveChanges();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
public async Task<int> CommitAsync(CancellationToken cancellationToken = default)
|
|
121
|
+
{
|
|
122
|
+
return await _dataContext.SaveChangesAsync(cancellationToken);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
public void Rollback()
|
|
126
|
+
{
|
|
127
|
+
_dataContext.Dispose();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
public async Task RollbackAsync(CancellationToken cancellationToken = default)
|
|
131
|
+
{
|
|
132
|
+
await _dataContext.DisposeAsync();
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ============================================================
|
|
137
|
+
// DI REGISTRATION
|
|
138
|
+
// ============================================================
|
|
139
|
+
//
|
|
140
|
+
// In Program.cs or a DI extension method:
|
|
141
|
+
//
|
|
142
|
+
// builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
|
143
|
+
// options.UseSqlServer(connectionString));
|
|
144
|
+
//
|
|
145
|
+
// builder.Services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
|
|
146
|
+
// builder.Services.AddScoped<IUnitOfWork, UnitOfWork>();
|
|
147
|
+
//
|
|
148
|
+
// ============================================================
|
|
@@ -34,7 +34,7 @@ namespace {{NAMESPACE}}.Abstractions
|
|
|
34
34
|
{
|
|
35
35
|
public interface IApiEndpoint
|
|
36
36
|
{
|
|
37
|
-
void MapEndpoint(
|
|
37
|
+
void MapEndpoint(IEndpointRouteBuilder app);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -235,7 +235,7 @@ using {{NAMESPACE}}.Extensions;
|
|
|
235
235
|
|
|
236
236
|
internal sealed class Create{{pascalCase FEATURE_NAME}}Endpoint : IApiEndpoint
|
|
237
237
|
{
|
|
238
|
-
public void MapEndpoint(
|
|
238
|
+
public void MapEndpoint(IEndpointRouteBuilder app)
|
|
239
239
|
{
|
|
240
240
|
app.MapPost("{{ROUTE}}", async (
|
|
241
241
|
IHandler<Create{{pascalCase FEATURE_NAME}}Request, Result<Create{{pascalCase FEATURE_NAME}}Response>> handler,
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// PGVECTOR ENTITY + CONFIGURATION TEMPLATE
|
|
3
|
+
// Generated by MORPH Framework
|
|
4
|
+
// ============================================================
|
|
5
|
+
|
|
6
|
+
using System.ComponentModel.DataAnnotations.Schema;
|
|
7
|
+
using Microsoft.EntityFrameworkCore;
|
|
8
|
+
using Microsoft.EntityFrameworkCore.Metadata.Builders;
|
|
9
|
+
using Pgvector;
|
|
10
|
+
|
|
11
|
+
#nullable disable
|
|
12
|
+
|
|
13
|
+
namespace {{NAMESPACE}}.Domain.Entities;
|
|
14
|
+
|
|
15
|
+
/// <summary>
|
|
16
|
+
/// Entity with vector embedding for similarity search via pgvector.
|
|
17
|
+
/// Dimensions: 1536 (text-embedding-3-small). Adjust if using a different model.
|
|
18
|
+
/// </summary>
|
|
19
|
+
public class {{pascalCase FEATURE_NAME}}
|
|
20
|
+
{
|
|
21
|
+
public Guid Id { get; set; }
|
|
22
|
+
public string Title { get; set; } = string.Empty;
|
|
23
|
+
public string Content { get; set; } = string.Empty;
|
|
24
|
+
public string? Metadata { get; set; }
|
|
25
|
+
|
|
26
|
+
[Column(TypeName = "vector(1536)")]
|
|
27
|
+
public Vector? Embedding { get; set; }
|
|
28
|
+
|
|
29
|
+
public DateTimeOffset CreatedAt { get; set; } = DateTimeOffset.UtcNow;
|
|
30
|
+
public DateTimeOffset? UpdatedAt { get; set; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// ============================================================
|
|
34
|
+
// ENTITY TYPE CONFIGURATION
|
|
35
|
+
// ============================================================
|
|
36
|
+
|
|
37
|
+
namespace {{NAMESPACE}}.Infrastructure.Data.Configurations;
|
|
38
|
+
|
|
39
|
+
public class {{pascalCase FEATURE_NAME}}Configuration : IEntityTypeConfiguration<{{pascalCase FEATURE_NAME}}>
|
|
40
|
+
{
|
|
41
|
+
public void Configure(EntityTypeBuilder<{{pascalCase FEATURE_NAME}}> builder)
|
|
42
|
+
{
|
|
43
|
+
builder.ToTable("{{snakeCase FEATURE_NAME}}s");
|
|
44
|
+
builder.HasKey(e => e.Id);
|
|
45
|
+
builder.Property(e => e.Id).HasDefaultValueSql("gen_random_uuid()");
|
|
46
|
+
builder.Property(e => e.Title).IsRequired().HasMaxLength(200);
|
|
47
|
+
builder.Property(e => e.Content).IsRequired();
|
|
48
|
+
builder.Property(e => e.Metadata).HasColumnType("jsonb");
|
|
49
|
+
builder.Property(e => e.Embedding).HasColumnType("vector(1536)");
|
|
50
|
+
builder.Property(e => e.CreatedAt).HasDefaultValueSql("now()");
|
|
51
|
+
|
|
52
|
+
// HNSW index for cosine similarity search
|
|
53
|
+
builder.HasIndex(e => e.Embedding)
|
|
54
|
+
.HasMethod("hnsw")
|
|
55
|
+
.HasOperators("vector_cosine_ops")
|
|
56
|
+
.HasStorageParameter("m", 16)
|
|
57
|
+
.HasStorageParameter("ef_construction", 64);
|
|
58
|
+
|
|
59
|
+
builder.HasIndex(e => e.Title);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============================================================
|
|
64
|
+
// SETUP INSTRUCTIONS
|
|
65
|
+
// ============================================================
|
|
66
|
+
//
|
|
67
|
+
// 1. Install NuGet packages:
|
|
68
|
+
//
|
|
69
|
+
// dotnet add package Pgvector.EntityFrameworkCore
|
|
70
|
+
// dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL
|
|
71
|
+
//
|
|
72
|
+
// 2. Configure NpgsqlDataSource with vector support in Program.cs:
|
|
73
|
+
//
|
|
74
|
+
// builder.Services.AddNpgsql<AppDbContext>(
|
|
75
|
+
// connectionString,
|
|
76
|
+
// npgsqlOptions => npgsqlOptions.UseVector());
|
|
77
|
+
//
|
|
78
|
+
// 3. Register in DbContext:
|
|
79
|
+
//
|
|
80
|
+
// public DbSet<{{pascalCase FEATURE_NAME}}> {{pascalCase FEATURE_NAME}}s => Set<{{pascalCase FEATURE_NAME}}>();
|
|
81
|
+
//
|
|
82
|
+
// protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
83
|
+
// {
|
|
84
|
+
// modelBuilder.HasPostgresExtension("vector");
|
|
85
|
+
// modelBuilder.ApplyConfiguration(new {{pascalCase FEATURE_NAME}}Configuration());
|
|
86
|
+
// }
|
|
87
|
+
//
|
|
88
|
+
// 4. Create and apply migration:
|
|
89
|
+
//
|
|
90
|
+
// dotnet ef migrations add Add{{pascalCase FEATURE_NAME}}WithVector -p src/{{NAMESPACE}}.Infrastructure -s src/{{NAMESPACE}}.Web
|
|
91
|
+
// dotnet ef database update -p src/{{NAMESPACE}}.Infrastructure -s src/{{NAMESPACE}}.Web
|
|
92
|
+
//
|
|
93
|
+
// ============================================================
|
|
@@ -5,7 +5,7 @@ namespace {{pascalCase PROJECT_NAME}}.Features.{{pascalCase FEATURE_NAME}}Featur
|
|
|
5
5
|
|
|
6
6
|
internal sealed class {{pascalCase SLICE_NAME}}Endpoint : IApiEndpoint
|
|
7
7
|
{
|
|
8
|
-
public void MapEndpoint(
|
|
8
|
+
public void MapEndpoint(IEndpointRouteBuilder app)
|
|
9
9
|
{
|
|
10
10
|
app.MapPost("{{kebabCase FEATURE_NAME}}s", async (IHandler<{{pascalCase SLICE_NAME}}Request, Result<{{pascalCase SLICE_NAME}}Response>> handler, {{pascalCase SLICE_NAME}}Request command, CancellationToken cancellationToken) =>
|
|
11
11
|
{
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
-- ============================================================
|
|
2
2
|
-- Migration: {{titleCase FEATURE_NAME}}
|
|
3
|
-
-- Stack: Next.js +
|
|
3
|
+
-- Stack: Next.js + Neon
|
|
4
4
|
-- Generated by MORPH Framework
|
|
5
5
|
-- Date: {{DATE}}
|
|
6
|
-
-- Run: supabase db push
|
|
7
6
|
-- ============================================================
|
|
8
7
|
|
|
9
8
|
-- ============================================================
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
|
|
13
12
|
create table if not exists public.{{TABLE_NAME}} (
|
|
14
13
|
id uuid primary key default gen_random_uuid(),
|
|
15
|
-
user_id
|
|
14
|
+
user_id text not null,
|
|
16
15
|
name text not null,
|
|
17
16
|
status text not null default 'draft'
|
|
18
17
|
check (status in ('draft', 'active', 'completed', 'archived', 'failed', 'cancelled')),
|
|
@@ -66,35 +65,27 @@ create trigger set_{{TABLE_NAME}}_updated_at
|
|
|
66
65
|
-- Enable RLS
|
|
67
66
|
alter table public.{{TABLE_NAME}} enable row level security;
|
|
68
67
|
|
|
69
|
-
-- Users can read their own rows
|
|
68
|
+
-- Users can read their own rows (Clerk JWT)
|
|
70
69
|
create policy "Users can read own {{TABLE_NAME}}"
|
|
71
70
|
on public.{{TABLE_NAME}}
|
|
72
71
|
for select
|
|
73
|
-
using (
|
|
72
|
+
using (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
74
73
|
|
|
75
74
|
-- Users can insert rows for themselves
|
|
76
75
|
create policy "Users can insert own {{TABLE_NAME}}"
|
|
77
76
|
on public.{{TABLE_NAME}}
|
|
78
77
|
for insert
|
|
79
|
-
with check (
|
|
78
|
+
with check (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
80
79
|
|
|
81
80
|
-- Users can update their own rows
|
|
82
81
|
create policy "Users can update own {{TABLE_NAME}}"
|
|
83
82
|
on public.{{TABLE_NAME}}
|
|
84
83
|
for update
|
|
85
|
-
using (
|
|
86
|
-
with check (
|
|
84
|
+
using (current_setting('request.jwt.claims')::json->>'sub' = user_id)
|
|
85
|
+
with check (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
87
86
|
|
|
88
87
|
-- Users can delete their own rows
|
|
89
88
|
create policy "Users can delete own {{TABLE_NAME}}"
|
|
90
89
|
on public.{{TABLE_NAME}}
|
|
91
90
|
for delete
|
|
92
|
-
using (
|
|
93
|
-
|
|
94
|
-
-- ============================================================
|
|
95
|
-
-- 5. GRANTS
|
|
96
|
-
-- ============================================================
|
|
97
|
-
|
|
98
|
-
-- Grant access to authenticated users (via Supabase API)
|
|
99
|
-
grant select, insert, update, delete on public.{{TABLE_NAME}} to authenticated;
|
|
100
|
-
grant usage on schema public to authenticated;
|
|
91
|
+
using (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
@@ -19,7 +19,7 @@ create table if not exists public.{{TABLE_NAME}} (
|
|
|
19
19
|
is_active boolean default true not null,
|
|
20
20
|
|
|
21
21
|
-- Foreign keys (example)
|
|
22
|
-
-- user_id
|
|
22
|
+
-- user_id text not null,
|
|
23
23
|
|
|
24
24
|
-- Timestamps
|
|
25
25
|
created_at timestamptz default now() not null,
|
|
@@ -70,30 +70,27 @@ create policy "{{TABLE_NAME}}_select_policy"
|
|
|
70
70
|
on public.{{TABLE_NAME}} for select
|
|
71
71
|
using (true);
|
|
72
72
|
|
|
73
|
-
-- Policy: Users can only insert their own records (
|
|
73
|
+
-- Policy: Users can only insert their own records (Clerk JWT)
|
|
74
74
|
-- create policy "{{TABLE_NAME}}_insert_policy"
|
|
75
75
|
-- on public.{{TABLE_NAME}} for insert
|
|
76
|
-
-- with check (
|
|
76
|
+
-- with check (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
77
77
|
|
|
78
|
-
-- Policy: Users can only update their own records (
|
|
78
|
+
-- Policy: Users can only update their own records (Clerk JWT)
|
|
79
79
|
-- create policy "{{TABLE_NAME}}_update_policy"
|
|
80
80
|
-- on public.{{TABLE_NAME}} for update
|
|
81
|
-
-- using (
|
|
82
|
-
-- with check (
|
|
81
|
+
-- using (current_setting('request.jwt.claims')::json->>'sub' = user_id)
|
|
82
|
+
-- with check (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
83
83
|
|
|
84
|
-
-- Policy: Users can only delete their own records (
|
|
84
|
+
-- Policy: Users can only delete their own records (Clerk JWT)
|
|
85
85
|
-- create policy "{{TABLE_NAME}}_delete_policy"
|
|
86
86
|
-- on public.{{TABLE_NAME}} for delete
|
|
87
|
-
-- using (
|
|
87
|
+
-- using (current_setting('request.jwt.claims')::json->>'sub' = user_id);
|
|
88
88
|
|
|
89
|
-
-- Policy: Admins have full access (
|
|
89
|
+
-- Policy: Admins have full access (Clerk JWT)
|
|
90
90
|
-- create policy "{{TABLE_NAME}}_admin_policy"
|
|
91
91
|
-- on public.{{TABLE_NAME}} for all
|
|
92
92
|
-- using (
|
|
93
|
-
--
|
|
94
|
-
-- select 1 from public.user_roles
|
|
95
|
-
-- where user_id = auth.uid() and role = 'admin'
|
|
96
|
-
-- )
|
|
93
|
+
-- current_setting('request.jwt.claims')::json->>'role' = 'admin'
|
|
97
94
|
-- );
|
|
98
95
|
|
|
99
96
|
-- ========================================
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
-- ============================================================
|
|
2
2
|
-- RLS Policy: {{titleCase FEATURE_NAME}}
|
|
3
|
-
-- Stack: Next.js +
|
|
3
|
+
-- Stack: Next.js + Neon
|
|
4
4
|
-- Generated by MORPH Framework
|
|
5
5
|
-- Date: {{DATE}}
|
|
6
6
|
-- ============================================================
|
|
@@ -42,16 +42,16 @@ create policy "{{POLICY_NAME}}_delete"
|
|
|
42
42
|
-- ============================================================
|
|
43
43
|
-- Common USING / WITH CHECK expressions:
|
|
44
44
|
--
|
|
45
|
-
-- Owner-based:
|
|
46
|
-
--
|
|
45
|
+
-- Owner-based (Clerk JWT):
|
|
46
|
+
-- current_setting('request.jwt.claims')::json->>'sub' = user_id
|
|
47
47
|
--
|
|
48
|
-
-- Role-based:
|
|
49
|
-
--
|
|
48
|
+
-- Role-based (Clerk JWT):
|
|
49
|
+
-- current_setting('request.jwt.claims')::json->>'role' = 'admin'
|
|
50
50
|
--
|
|
51
|
-
-- Organization-based:
|
|
52
|
-
-- org_id
|
|
51
|
+
-- Organization-based (Clerk JWT):
|
|
52
|
+
-- org_id = (current_setting('request.jwt.claims')::json->>'org_id')
|
|
53
53
|
--
|
|
54
54
|
-- Public read, owner write:
|
|
55
55
|
-- SELECT using (true)
|
|
56
|
-
-- INSERT/UPDATE/DELETE using (
|
|
56
|
+
-- INSERT/UPDATE/DELETE using (current_setting('request.jwt.claims')::json->>'sub' = user_id)
|
|
57
57
|
-- ============================================================
|