@polymorphism-tech/morph-spec 7.0.0 → 7.1.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 +367 -367
- package/framework/agents.json +132 -127
- package/framework/rubrics/backend.json +41 -36
- package/framework/rules/ai-agents-standards.md +45 -15
- package/framework/rules/neon-standards.md +25 -26
- package/framework/standards/STANDARDS.json +1729 -1133
- package/framework/standards/ai-agents/agent-session.md +160 -0
- package/framework/standards/ai-agents/batching.md +193 -0
- package/framework/standards/ai-agents/context-providers.md +188 -0
- package/framework/standards/ai-agents/mcp-server.md +250 -0
- package/framework/standards/ai-agents/mcp-tools.md +197 -0
- package/framework/standards/ai-agents/middleware-patterns.md +207 -0
- package/framework/standards/ai-agents/modalities-image-gen.md +173 -0
- package/framework/standards/ai-agents/modalities-speech-to-text.md +171 -0
- package/framework/standards/ai-agents/modalities-text-to-speech.md +181 -0
- package/framework/standards/ai-agents/modalities-vision.md +201 -0
- package/framework/standards/ai-agents/multi-agent-patterns.md +155 -0
- package/framework/standards/ai-agents/production.md +114 -258
- package/framework/standards/ai-agents/prompts-in-db.md +214 -0
- package/framework/standards/ai-agents/providers/model-registry.md +158 -0
- package/framework/standards/ai-agents/rag-custom-pgvector.md +243 -0
- package/framework/standards/ai-agents/service-tier-flex.md +135 -0
- package/framework/standards/ai-agents/setup.md +142 -316
- package/framework/standards/ai-agents/structured-output.md +249 -0
- package/framework/standards/ai-agents/sweet-spot.md +109 -0
- package/framework/standards/ai-agents/workflows.md +114 -284
- package/framework/standards/architecture/vertical-slice/vertical-slice.md +433 -429
- package/framework/standards/backend/api/validation.md +92 -88
- package/framework/standards/backend/database/postgresql/database.md +336 -332
- package/framework/standards/backend/database/vector-search-rag.md +599 -541
- package/framework/standards/backend/dotnet/fluent-validation-vsa.md +4 -0
- package/framework/standards/backend/dotnet/result-pattern.md +4 -0
- package/framework/standards/backend/dotnet/scalar-ui.md +176 -172
- package/framework/standards/backend/dotnet/scrutor.md +4 -0
- package/framework/standards/backend/dotnet/vsa-handler-patterns.md +278 -274
- package/framework/standards/backend/integrations/asaas/asaas-api.md +260 -216
- package/framework/standards/backend/integrations/hangfire/hangfire-jobs.md +354 -350
- package/framework/standards/backend/integrations/neon-auth/neon-auth.md +30 -19
- package/framework/standards/backend/integrations/resend/resend-email.md +400 -385
- package/framework/standards/data/nosql/cache/redis.md +106 -97
- package/framework/standards/data/nosql/cosmos-db.md +127 -118
- package/framework/standards/data/vector-search/rag-chunking.md +113 -104
- package/framework/standards/frontend/design-system/ai-image-generation.md +4 -0
- package/framework/standards/frontend/design-system/animations.md +164 -160
- package/framework/standards/frontend/design-system/naming.md +68 -64
- package/framework/standards/frontend/gsap/gsap-core.md +14 -8
- package/framework/standards/frontend/gsap/gsap-plugins.md +26 -21
- package/framework/standards/frontend/gsap/gsap-scrolltrigger.md +9 -7
- package/framework/standards/frontend/nextjs/app-router.md +127 -123
- package/framework/standards/frontend/nextjs/components.md +136 -132
- package/framework/standards/frontend/nextjs/data-fetching.md +130 -126
- package/framework/standards/frontend/nextjs/forms.md +134 -128
- package/framework/standards/frontend/nextjs/motion-patterns.md +328 -322
- package/framework/standards/frontend/nextjs/naming-conventions.md +71 -67
- package/framework/standards/frontend/nextjs/nextjs-patterns.md +229 -225
- package/framework/standards/frontend/nextjs/project-structure.md +106 -102
- package/framework/standards/frontend/nextjs/state-management.md +76 -72
- package/framework/standards/frontend/nextjs/testing.md +115 -111
- package/framework/standards/infrastructure/docker/easypanel-deploy.md +196 -192
- package/framework/standards/infrastructure/neon/neon-pgrag.md +6 -2
- package/framework/standards/infrastructure/neon/neon-pgvector.md +4 -0
- package/framework/standards/infrastructure/neon/neon-setup.md +5 -1
- package/framework/standards/integration/mcp/mcp-tools.md +471 -462
- package/framework/templates/REGISTRY.json +1745 -1239
- package/framework/templates/code/dotnet/ai-agents/AgentBootstrap.cs.template +39 -0
- package/framework/templates/code/dotnet/ai-agents/EmbeddingService.cs.template +68 -0
- package/framework/templates/code/dotnet/ai-agents/ModelRegistry.cs.template +117 -0
- package/framework/templates/code/dotnet/ai-agents/PromptRepository.cs.template +95 -0
- package/framework/templates/code/dotnet/ai-agents/ResilientToolMiddleware.cs.template +61 -0
- package/framework/templates/code/dotnet/ai-agents/VectorSearchTool.cs.template +107 -0
- package/framework/templates/code/dotnet/ai-agents/model-registry.json.template +58 -0
- package/framework/templates/code/sql/ai-agents/agent-prompts-migration.sql.template +58 -0
- package/framework/templates/code/sql/ai-agents/pgvector-embeddings-migration.sql.template +91 -0
- package/package.json +89 -83
- package/src/core/templates/template-registry.js +289 -256
- package/src/lib/generators/context-generator.js +516 -518
- package/src/lib/generators/scaffold-generator.js +23 -26
- package/framework/standards/infrastructure/neon/neon-semantic-kernel.md +0 -310
- package/framework/templates/integrations/semantic-kernel-neon.cs +0 -125
|
@@ -1,97 +1,106 @@
|
|
|
1
|
-
# Data Standard: Redis Cache (Azure Cache for Redis)
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
//
|
|
42
|
-
await _cache.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
```
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
1
|
+
# Data Standard: Redis Cache (Azure Cache for Redis)
|
|
2
|
+
|
|
3
|
+
> **Scope:** data-nosql-cache
|
|
4
|
+
> **Layer:** 2
|
|
5
|
+
> **Keywords:** redis, cache, distributed-cache, session, rate-limiting, stackexchange-redis, azure-cache
|
|
6
|
+
> **Load When:** caching, session storage, or rate limiting is in scope
|
|
7
|
+
|
|
8
|
+
**Verified against:** .NET 10 + StackExchange.Redis 2.x + Microsoft.Extensions.Caching.StackExchangeRedis. Last-verified: 2026-05-20.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
Distributed cache for session data, rate limiting, and frequently-read data.
|
|
14
|
+
|
|
15
|
+
## Setup
|
|
16
|
+
```xml
|
|
17
|
+
<PackageReference Include="StackExchange.Redis" Version="2.*" />
|
|
18
|
+
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="9.*" />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
```csharp
|
|
22
|
+
// Program.cs
|
|
23
|
+
builder.Services.AddStackExchangeRedisCache(opts =>
|
|
24
|
+
{
|
|
25
|
+
opts.Configuration = config["Redis:ConnectionString"]; // from Key Vault
|
|
26
|
+
opts.InstanceName = "myapp:"; // Prefix to avoid key collisions
|
|
27
|
+
});
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Cache-Aside Pattern
|
|
31
|
+
```csharp
|
|
32
|
+
public class CachedProductService : IProductService
|
|
33
|
+
{
|
|
34
|
+
private readonly IDistributedCache _cache;
|
|
35
|
+
private readonly IProductRepository _repository;
|
|
36
|
+
|
|
37
|
+
public async Task<Product?> GetAsync(Guid id, CancellationToken ct = default)
|
|
38
|
+
{
|
|
39
|
+
var cacheKey = $"product:{id}";
|
|
40
|
+
|
|
41
|
+
// Try cache first
|
|
42
|
+
var cached = await _cache.GetStringAsync(cacheKey, ct);
|
|
43
|
+
if (cached != null)
|
|
44
|
+
return JsonSerializer.Deserialize<Product>(cached);
|
|
45
|
+
|
|
46
|
+
// Cache miss → read from DB
|
|
47
|
+
var product = await _repository.GetAsync(id, ct);
|
|
48
|
+
if (product == null) return null;
|
|
49
|
+
|
|
50
|
+
// Cache with TTL
|
|
51
|
+
await _cache.SetStringAsync(cacheKey,
|
|
52
|
+
JsonSerializer.Serialize(product),
|
|
53
|
+
new DistributedCacheEntryOptions
|
|
54
|
+
{
|
|
55
|
+
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15),
|
|
56
|
+
SlidingExpiration = TimeSpan.FromMinutes(5)
|
|
57
|
+
}, ct);
|
|
58
|
+
|
|
59
|
+
return product;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
public async Task InvalidateAsync(Guid id, CancellationToken ct = default)
|
|
63
|
+
=> await _cache.RemoveAsync($"product:{id}", ct);
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## TTL Strategy
|
|
68
|
+
| Data Type | TTL | Rationale |
|
|
69
|
+
|-----------|-----|-----------|
|
|
70
|
+
| User session | 30 min sliding | Active session window |
|
|
71
|
+
| Product catalog | 15 min absolute | Inventory changes |
|
|
72
|
+
| Rate limit counters | 1 min | Per-minute limiting |
|
|
73
|
+
| Computed aggregates | 1 hour | Expensive query results |
|
|
74
|
+
| Reference data | 24 hours | Config rarely changes |
|
|
75
|
+
|
|
76
|
+
## Key Naming Convention
|
|
77
|
+
```
|
|
78
|
+
{service}:{entity}:{id}
|
|
79
|
+
{service}:{entity}:list:{filter}
|
|
80
|
+
{service}:ratelimit:{userId}:{endpoint}
|
|
81
|
+
|
|
82
|
+
Examples:
|
|
83
|
+
product:detail:550e8400-e29b-41d4-a716
|
|
84
|
+
product:list:category:electronics
|
|
85
|
+
auth:ratelimit:user123:login
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Rate Limiting Pattern
|
|
89
|
+
```csharp
|
|
90
|
+
public async Task<bool> IsRateLimitedAsync(string userId, string action, int maxPerMinute)
|
|
91
|
+
{
|
|
92
|
+
var key = $"ratelimit:{action}:{userId}:{DateTime.UtcNow:yyyyMMddHHmm}";
|
|
93
|
+
var db = _redis.GetDatabase();
|
|
94
|
+
|
|
95
|
+
var count = await db.StringIncrementAsync(key);
|
|
96
|
+
if (count == 1) await db.KeyExpireAsync(key, TimeSpan.FromMinutes(1));
|
|
97
|
+
|
|
98
|
+
return count > maxPerMinute;
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
## Anti-Patterns
|
|
103
|
+
- Never cache sensitive data (passwords, tokens, PII) without encryption
|
|
104
|
+
- Never use Redis as primary store — it's a cache, not a database
|
|
105
|
+
- Never omit TTL — unbounded keys fill memory
|
|
106
|
+
- Never catch RedisException silently — degrade gracefully to DB
|
|
@@ -1,118 +1,127 @@
|
|
|
1
|
-
# Data Standard: Azure Cosmos DB
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
[JsonPropertyName("
|
|
109
|
-
public
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
1
|
+
# Data Standard: Azure Cosmos DB
|
|
2
|
+
|
|
3
|
+
> **Scope:** data-nosql
|
|
4
|
+
> **Layer:** 2
|
|
5
|
+
> **Keywords:** cosmos-db, nosql, document-database, event-store, partition-key, azure-cosmos
|
|
6
|
+
> **Load When:** a NoSQL document store, event store, or globally distributed data is in scope
|
|
7
|
+
|
|
8
|
+
**Verified against:** .NET 10 + Microsoft.Azure.Cosmos SDK v3. Last-verified: 2026-05-20.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## Overview
|
|
13
|
+
NoSQL document database — use for high-throughput, globally distributed, or schema-flexible data.
|
|
14
|
+
|
|
15
|
+
## When to Use Cosmos DB
|
|
16
|
+
✅ Event store (append-only, high write throughput)
|
|
17
|
+
✅ User activity/session data (variable schema)
|
|
18
|
+
✅ Multi-region writes required
|
|
19
|
+
✅ JSON documents with flexible schema
|
|
20
|
+
❌ Relational data with complex joins (use Azure SQL)
|
|
21
|
+
❌ Small datasets (cost inefficient)
|
|
22
|
+
|
|
23
|
+
## Container Design
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"partitionKey": "/tenantId",
|
|
27
|
+
"indexingPolicy": {
|
|
28
|
+
"includedPaths": [{ "path": "/*" }],
|
|
29
|
+
"excludedPaths": [
|
|
30
|
+
{ "path": "/largePayload/*" },
|
|
31
|
+
{ "path": "/_etag/?" }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Partition Key Strategy
|
|
38
|
+
- High cardinality: userId, tenantId, orderId
|
|
39
|
+
- Even distribution: avoid hot partitions (e.g., /status = "active" for 90% of docs)
|
|
40
|
+
- Query alignment: partition key should appear in most queries
|
|
41
|
+
|
|
42
|
+
## SDK Setup
|
|
43
|
+
```csharp
|
|
44
|
+
// Program.cs
|
|
45
|
+
builder.Services.AddSingleton(sp =>
|
|
46
|
+
new CosmosClient(
|
|
47
|
+
config["CosmosDb:ConnectionString"],
|
|
48
|
+
new CosmosClientOptions
|
|
49
|
+
{
|
|
50
|
+
SerializerOptions = new CosmosSerializationOptions
|
|
51
|
+
{
|
|
52
|
+
PropertyNamingPolicy = CosmosPropertyNamingPolicy.CamelCase
|
|
53
|
+
},
|
|
54
|
+
ApplicationName = "MyApp"
|
|
55
|
+
}));
|
|
56
|
+
|
|
57
|
+
builder.Services.AddSingleton<ICosmosRepository<Order>>(sp =>
|
|
58
|
+
new CosmosRepository<Order>(
|
|
59
|
+
sp.GetRequiredService<CosmosClient>(),
|
|
60
|
+
config["CosmosDb:DatabaseId"],
|
|
61
|
+
"orders"));
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Repository Pattern
|
|
65
|
+
```csharp
|
|
66
|
+
public class CosmosRepository<T> where T : CosmosDocument
|
|
67
|
+
{
|
|
68
|
+
private readonly Container _container;
|
|
69
|
+
|
|
70
|
+
public async Task<T?> GetAsync(string id, string partitionKey)
|
|
71
|
+
{
|
|
72
|
+
try
|
|
73
|
+
{
|
|
74
|
+
var response = await _container.ReadItemAsync<T>(id, new PartitionKey(partitionKey));
|
|
75
|
+
return response.Resource;
|
|
76
|
+
}
|
|
77
|
+
catch (CosmosException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
|
|
78
|
+
{
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public async Task<IReadOnlyList<T>> QueryAsync(string sql, Dictionary<string, object>? parameters = null)
|
|
84
|
+
{
|
|
85
|
+
var query = new QueryDefinition(sql);
|
|
86
|
+
if (parameters != null)
|
|
87
|
+
foreach (var (k, v) in parameters) query = query.WithParameter(k, v);
|
|
88
|
+
|
|
89
|
+
var results = new List<T>();
|
|
90
|
+
using var feed = _container.GetItemQueryIterator<T>(query);
|
|
91
|
+
while (feed.HasMoreResults)
|
|
92
|
+
{
|
|
93
|
+
var page = await feed.ReadNextAsync();
|
|
94
|
+
results.AddRange(page);
|
|
95
|
+
}
|
|
96
|
+
return results;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
public async Task UpsertAsync(T document)
|
|
100
|
+
=> await _container.UpsertItemAsync(document, new PartitionKey(document.PartitionKey));
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Document Base Class
|
|
105
|
+
```csharp
|
|
106
|
+
public abstract class CosmosDocument
|
|
107
|
+
{
|
|
108
|
+
[JsonPropertyName("id")]
|
|
109
|
+
public string Id { get; set; } = Guid.NewGuid().ToString();
|
|
110
|
+
|
|
111
|
+
[JsonPropertyName("_partitionKey")]
|
|
112
|
+
public abstract string PartitionKey { get; }
|
|
113
|
+
|
|
114
|
+
[JsonPropertyName("type")]
|
|
115
|
+
public string Type => GetType().Name;
|
|
116
|
+
|
|
117
|
+
[JsonPropertyName("createdAt")]
|
|
118
|
+
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Cost Optimization
|
|
123
|
+
- Enable TTL on transient data (session, temp tokens)
|
|
124
|
+
- Use serverless mode for dev/test environments
|
|
125
|
+
- Index only queried paths (exclude large blobs)
|
|
126
|
+
- Batch operations with TransactionalBatch for same partition
|
|
127
|
+
- Use bulk execution for large imports
|