@breadstone/archipel-mcp 0.0.21 → 0.0.23
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 +32 -13
- package/data/guides/ai-text-generation.md +16 -24
- package/data/guides/cryptography-and-otp.md +2 -2
- package/data/guides/database-setup.md +4 -3
- package/data/guides/email-templates.md +52 -42
- package/data/guides/getting-started.md +6 -6
- package/data/guides/resource-management.md +9 -9
- package/data/packages/platform-authentication/api/Interface.IMfaSubjectUpdate.md +1 -1
- package/data/packages/platform-authentication/api/index.md +1 -1
- package/data/packages/platform-blob-storage/api/Variable.AWS_S3_PROVIDER_OPTIONS.md +1 -1
- package/data/packages/platform-blob-storage/api/Variable.AZURE_BLOB_PROVIDER_OPTIONS.md +1 -1
- package/data/packages/platform-blob-storage/api/Variable.BLOB_PROVIDER.md +1 -1
- package/data/packages/platform-blob-storage/api/Variable.VERCEL_BLOB_PROVIDER_OPTIONS.md +1 -1
- package/data/packages/platform-caching/api/Class.RedisLayeredCache.md +3 -3
- package/data/packages/platform-core/api/Class.ErrorTemplateService.md +1 -1
- package/data/packages/platform-core/api/Class.HostService.md +1 -1
- package/data/packages/platform-core/api/Variable.ID_GENERATOR_TOKEN.md +1 -1
- package/data/packages/platform-core/api/index.md +0 -27
- package/data/packages/platform-core/index.md +7 -4
- package/data/packages/platform-cryptography/api/Class.BcryptService.md +4 -4
- package/data/packages/platform-cryptography/api/Class.OtpService.md +6 -6
- package/data/packages/platform-cryptography/api/Variable.BCRYPT_OPTIONS.md +2 -2
- package/data/packages/platform-cryptography/api/Variable.MAX_BCRYPT_PASSWORD_BYTES.md +1 -1
- package/data/packages/platform-cryptography/api/Variable.MIN_BCRYPT_ROUNDS.md +1 -1
- package/data/packages/platform-cryptography/api/Variable.OTP_OPTIONS.md +2 -2
- package/data/packages/platform-cryptography/api/Variable.OTP_SERVICE_TOKEN.md +2 -2
- package/data/packages/platform-cryptography/api/Variable.TOTP_EPOCH_TOLERANCE.md +1 -1
- package/data/packages/platform-cryptography/api/index.md +3 -3
- package/data/packages/platform-documents/api/Class.BaseDocumentRenderer.md +32 -1
- package/data/packages/platform-documents/api/Class.DocumentEngine.md +4 -4
- package/data/packages/platform-documents/api/Class.DocumentModule.md +2 -2
- package/data/packages/platform-documents/api/Class.DocxDocumentRenderer2.md +333 -0
- package/data/packages/platform-documents/api/Class.PdfDocumentRenderer.md +355 -0
- package/data/packages/platform-documents/api/Interface.IDocumentModuleOptions.md +31 -5
- package/data/packages/platform-documents/api/Variable.DOCUMENT_MODULE_OPTIONS.md +1 -1
- package/data/packages/platform-documents/api/Variable.DOCUMENT_PARSER_TOKEN.md +1 -1
- package/data/packages/platform-documents/api/Variable.DOCUMENT_RENDERER_TOKEN.md +1 -1
- package/data/packages/platform-documents/api/Variable.IMAGE_PROCESSOR_TOKEN.md +1 -1
- package/data/packages/platform-documents/api/index.md +2 -0
- package/data/packages/platform-esigning/api/Class.EsigningClientPort.md +1 -1
- package/data/packages/platform-health/index.md +128 -0
- package/data/packages/platform-mailing/api/Class.DeliveryStrategyBase.md +0 -1
- package/data/packages/platform-mailing/api/Class.MailModule.md +1 -1
- package/data/packages/platform-mailing/api/Class.MailVerificationService.md +1 -1
- package/data/packages/platform-mailing/api/Class.TemplateFetchStrategyBase.md +0 -5
- package/data/packages/platform-mailing/api/Variable.SMTP_CONFIG_ENTRIES.md +14 -0
- package/data/packages/platform-mailing/api/Variable.SMTP_HOST.md +14 -0
- package/data/packages/platform-mailing/api/Variable.SMTP_PASSWORD.md +14 -0
- package/data/packages/platform-mailing/api/Variable.SMTP_PORT.md +14 -0
- package/data/packages/platform-mailing/api/Variable.SMTP_SECURE.md +14 -0
- package/data/packages/platform-mailing/api/Variable.SMTP_USER.md +14 -0
- package/data/packages/platform-mailing/api/index.md +7 -4
- package/data/packages/platform-mapping/index.md +121 -0
- package/data/packages/platform-mcp/api/Variable.MCP_MODULE_OPTIONS.md +1 -1
- package/data/packages/platform-openapi/api/Class.SwaggerMultiDocumentService.md +4 -4
- package/data/packages/platform-resources/index.md +135 -0
- package/data/packages/platform-telemetry/api/Class.OtelSdkHolder.md +1 -1
- package/data/packages/platform-telemetry/api/Variable.TELEMETRY_ENABLED.md +1 -1
- package/data/packages/platform-telemetry/api/Variable.TELEMETRY_FACADE.md +1 -1
- package/data/packages/platform-telemetry/api/Variable.TELEMETRY_OPTIONS.md +1 -1
- package/{src/knowledge/configPattern.js → data/patterns/config-pattern.md} +46 -49
- package/{src/knowledge/dtoPattern.js → data/patterns/dto-pattern.md} +58 -61
- package/{src/knowledge/errorHandlingPattern.js → data/patterns/error-handling-pattern.md} +35 -38
- package/{src/knowledge/guardPattern.js → data/patterns/guard-pattern.md} +35 -38
- package/{src/knowledge/mappingPattern.js → data/patterns/mapping-pattern.md} +43 -144
- package/data/patterns/module-pattern.md +182 -0
- package/data/patterns/query-pattern.md +137 -0
- package/data/patterns/repository-pattern.md +208 -0
- package/{src/knowledge/testingPattern.js → data/patterns/testing-pattern.md} +37 -40
- package/package.json +2 -2
- package/src/PatternsLoader.d.ts +12 -0
- package/src/PatternsLoader.js +65 -0
- package/src/generators/mappingPatternGenerator.d.ts +5 -0
- package/src/generators/mappingPatternGenerator.js +107 -0
- package/src/generators/modulePatternGenerator.d.ts +5 -0
- package/src/generators/modulePatternGenerator.js +107 -0
- package/src/generators/queryPatternGenerator.d.ts +4 -0
- package/src/generators/queryPatternGenerator.js +83 -0
- package/src/generators/repositoryPatternGenerator.d.ts +5 -0
- package/src/generators/repositoryPatternGenerator.js +165 -0
- package/src/main.js +15 -9
- package/src/models/IPatternDoc.d.ts +15 -0
- package/src/models/IPatternDoc.js +3 -0
- package/src/tools/registerGetConfigPatternTool.d.ts +2 -1
- package/src/tools/registerGetConfigPatternTool.js +5 -4
- package/src/tools/registerGetDtoPatternTool.d.ts +2 -1
- package/src/tools/registerGetDtoPatternTool.js +5 -4
- package/src/tools/registerGetErrorHandlingPatternTool.d.ts +2 -1
- package/src/tools/registerGetErrorHandlingPatternTool.js +5 -4
- package/src/tools/registerGetGuardPatternTool.d.ts +2 -1
- package/src/tools/registerGetGuardPatternTool.js +5 -4
- package/src/tools/registerGetMappingPatternTool.d.ts +2 -1
- package/src/tools/registerGetMappingPatternTool.js +6 -5
- package/src/tools/registerGetModulePatternTool.d.ts +2 -1
- package/src/tools/registerGetModulePatternTool.js +6 -5
- package/src/tools/registerGetQueryPatternTool.d.ts +2 -1
- package/src/tools/registerGetQueryPatternTool.js +6 -5
- package/src/tools/registerGetRepositoryPatternTool.d.ts +2 -1
- package/src/tools/registerGetRepositoryPatternTool.js +6 -5
- package/src/tools/registerGetTestingPatternTool.d.ts +2 -1
- package/src/tools/registerGetTestingPatternTool.js +5 -4
- package/data/packages/platform-core/api/Class.BlobResourceStrategy.md +0 -195
- package/data/packages/platform-core/api/Class.EmbeddedResourceStrategy.md +0 -215
- package/data/packages/platform-core/api/Class.FileResourceStrategy.md +0 -192
- package/data/packages/platform-core/api/Class.HealthModule.md +0 -42
- package/data/packages/platform-core/api/Class.HealthOrchestrator.md +0 -64
- package/data/packages/platform-core/api/Class.MappingBuilder.md +0 -110
- package/data/packages/platform-core/api/Class.MappingModule.md +0 -46
- package/data/packages/platform-core/api/Class.MappingNotRegisteredError.md +0 -56
- package/data/packages/platform-core/api/Class.MappingProfileBase.md +0 -52
- package/data/packages/platform-core/api/Class.MappingService.md +0 -284
- package/data/packages/platform-core/api/Class.ResourceManager.md +0 -565
- package/data/packages/platform-core/api/Class.ResourceModule.md +0 -46
- package/data/packages/platform-core/api/Class.TypeMappingNotRegisteredError.md +0 -57
- package/data/packages/platform-core/api/Function.createMappingKey.md +0 -39
- package/data/packages/platform-core/api/Interface.IBlobResourceStrategyConfig.md +0 -28
- package/data/packages/platform-core/api/Interface.IBlobServiceAdapter.md +0 -40
- package/data/packages/platform-core/api/Interface.IFileResourceStrategyConfig.md +0 -72
- package/data/packages/platform-core/api/Interface.IHealthCheckResult.md +0 -46
- package/data/packages/platform-core/api/Interface.IHealthIndicator.md +0 -41
- package/data/packages/platform-core/api/Interface.IMappingBuilder.md +0 -76
- package/data/packages/platform-core/api/Interface.IMappingKey.md +0 -58
- package/data/packages/platform-core/api/Interface.IMappingProfile.md +0 -32
- package/data/packages/platform-core/api/Interface.IResourceManagerConfig.md +0 -89
- package/data/packages/platform-core/api/Interface.IResourceMetadata.md +0 -94
- package/data/packages/platform-core/api/Interface.IResourceResult.md +0 -34
- package/data/packages/platform-core/api/Interface.IResourceStrategy.md +0 -134
- package/data/packages/platform-core/api/Variable.HEALTH_INDICATORS_TOKEN.md +0 -14
- package/data/packages/platform-mailing/api/Class.BlobTemplateFetchStrategy.md +0 -60
- package/data/packages/platform-mailing/api/Class.FileTemplateFetchStrategy.md +0 -58
- package/data/packages/platform-mailing/api/Class.LogDeliveryStrategy.md +0 -71
- package/src/knowledge/configPattern.d.ts +0 -5
- package/src/knowledge/dtoPattern.d.ts +0 -5
- package/src/knowledge/errorHandlingPattern.d.ts +0 -5
- package/src/knowledge/guardPattern.d.ts +0 -5
- package/src/knowledge/mappingPattern.d.ts +0 -6
- package/src/knowledge/modulePattern.d.ts +0 -6
- package/src/knowledge/modulePattern.js +0 -283
- package/src/knowledge/queryPattern.d.ts +0 -6
- package/src/knowledge/queryPattern.js +0 -215
- package/src/knowledge/repositoryPattern.d.ts +0 -6
- package/src/knowledge/repositoryPattern.js +0 -367
- package/src/knowledge/testingPattern.d.ts +0 -5
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.ERROR_HANDLING_PATTERN_KNOWLEDGE = `# Archipel Error Handling Pattern
|
|
1
|
+
---
|
|
2
|
+
title: Error Handling Pattern
|
|
3
|
+
description: Domain errors, RepositoryError, exception filters, and structured error response envelopes.
|
|
4
|
+
order: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Archipel Error Handling Pattern
|
|
9
8
|
|
|
10
9
|
## Overview
|
|
11
10
|
|
|
@@ -15,17 +14,17 @@ the HTTP translation. This keeps business logic clean and error responses consis
|
|
|
15
14
|
|
|
16
15
|
## Layer Architecture
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
```
|
|
19
18
|
Service → throws DomainError → ExceptionFilter → HTTP Response Envelope
|
|
20
|
-
|
|
19
|
+
```
|
|
21
20
|
|
|
22
21
|
## Base Error Classes
|
|
23
22
|
|
|
24
23
|
### RepositoryError
|
|
25
24
|
|
|
26
|
-
Thrown when any Prisma operation fails. Automatically wrapped by
|
|
25
|
+
Thrown when any Prisma operation fails. Automatically wrapped by `RepositoryBase.tryCatch()`.
|
|
27
26
|
|
|
28
|
-
|
|
27
|
+
```typescript
|
|
29
28
|
// models/errors/RepositoryError.ts
|
|
30
29
|
export class RepositoryError extends Error {
|
|
31
30
|
|
|
@@ -46,13 +45,13 @@ export class RepositoryError extends Error {
|
|
|
46
45
|
}
|
|
47
46
|
|
|
48
47
|
}
|
|
49
|
-
|
|
48
|
+
```
|
|
50
49
|
|
|
51
50
|
### MappingError (abstract)
|
|
52
51
|
|
|
53
52
|
Base for mapping-related errors.
|
|
54
53
|
|
|
55
|
-
|
|
54
|
+
```typescript
|
|
56
55
|
export abstract class MappingError extends Error {
|
|
57
56
|
public readonly code: string;
|
|
58
57
|
|
|
@@ -61,17 +60,17 @@ export abstract class MappingError extends Error {
|
|
|
61
60
|
this.code = code;
|
|
62
61
|
}
|
|
63
62
|
}
|
|
64
|
-
|
|
63
|
+
```
|
|
65
64
|
|
|
66
65
|
Concrete variants:
|
|
67
|
-
-
|
|
68
|
-
-
|
|
66
|
+
- `MappingNotRegisteredError` — code: `'MAPPING_NOT_REGISTERED'`
|
|
67
|
+
- `TypeMappingNotRegisteredError` — code: `'TYPE_MAPPING_NOT_REGISTERED'`
|
|
69
68
|
|
|
70
69
|
## Exception Filters
|
|
71
70
|
|
|
72
71
|
### RepositoryExceptionFilter
|
|
73
72
|
|
|
74
|
-
|
|
73
|
+
```typescript
|
|
75
74
|
import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common';
|
|
76
75
|
import { Request, Response } from 'express';
|
|
77
76
|
import { RepositoryError } from '../models/errors/RepositoryError';
|
|
@@ -93,19 +92,19 @@ export class RepositoryExceptionFilter implements ExceptionFilter<RepositoryErro
|
|
|
93
92
|
}
|
|
94
93
|
|
|
95
94
|
}
|
|
96
|
-
|
|
95
|
+
```
|
|
97
96
|
|
|
98
97
|
### Registration (in main.ts or AppModule)
|
|
99
98
|
|
|
100
|
-
|
|
99
|
+
```typescript
|
|
101
100
|
app.useGlobalFilters(new RepositoryExceptionFilter());
|
|
102
|
-
|
|
101
|
+
```
|
|
103
102
|
|
|
104
103
|
## How to Create a Domain Error
|
|
105
104
|
|
|
106
105
|
### Step 1: Define the error class
|
|
107
106
|
|
|
108
|
-
|
|
107
|
+
```typescript
|
|
109
108
|
// models/errors/UserNotFoundError.ts
|
|
110
109
|
export class UserNotFoundError extends Error {
|
|
111
110
|
|
|
@@ -113,7 +112,7 @@ export class UserNotFoundError extends Error {
|
|
|
113
112
|
public readonly userId: string;
|
|
114
113
|
|
|
115
114
|
public constructor(userId: string) {
|
|
116
|
-
super(
|
|
115
|
+
super(`User not found: ${userId}`);
|
|
117
116
|
Object.setPrototypeOf(this, UserNotFoundError.prototype);
|
|
118
117
|
this.name = 'UserNotFoundError';
|
|
119
118
|
this.userId = userId;
|
|
@@ -124,11 +123,11 @@ export class UserNotFoundError extends Error {
|
|
|
124
123
|
}
|
|
125
124
|
|
|
126
125
|
}
|
|
127
|
-
|
|
126
|
+
```
|
|
128
127
|
|
|
129
128
|
### Step 2: Create an exception filter
|
|
130
129
|
|
|
131
|
-
|
|
130
|
+
```typescript
|
|
132
131
|
// filters/UserNotFoundExceptionFilter.ts
|
|
133
132
|
import { ArgumentsHost, Catch, ExceptionFilter, HttpStatus } from '@nestjs/common';
|
|
134
133
|
import { Request, Response } from 'express';
|
|
@@ -152,20 +151,20 @@ export class UserNotFoundExceptionFilter implements ExceptionFilter<UserNotFound
|
|
|
152
151
|
}
|
|
153
152
|
|
|
154
153
|
}
|
|
155
|
-
|
|
154
|
+
```
|
|
156
155
|
|
|
157
156
|
### Step 3: Register the filter globally
|
|
158
157
|
|
|
159
|
-
|
|
158
|
+
```typescript
|
|
160
159
|
app.useGlobalFilters(
|
|
161
160
|
new RepositoryExceptionFilter(),
|
|
162
161
|
new UserNotFoundExceptionFilter(),
|
|
163
162
|
);
|
|
164
|
-
|
|
163
|
+
```
|
|
165
164
|
|
|
166
165
|
### Step 4: Throw from a service
|
|
167
166
|
|
|
168
|
-
|
|
167
|
+
```typescript
|
|
169
168
|
@Injectable()
|
|
170
169
|
export class UserService {
|
|
171
170
|
|
|
@@ -180,13 +179,13 @@ export class UserService {
|
|
|
180
179
|
}
|
|
181
180
|
|
|
182
181
|
}
|
|
183
|
-
|
|
182
|
+
```
|
|
184
183
|
|
|
185
184
|
## Response Envelope Convention
|
|
186
185
|
|
|
187
186
|
All error responses follow the same shape:
|
|
188
187
|
|
|
189
|
-
|
|
188
|
+
```json
|
|
190
189
|
{
|
|
191
190
|
"statusCode": 404,
|
|
192
191
|
"timestamp": "2026-04-05T12:00:00.000Z",
|
|
@@ -194,11 +193,11 @@ All error responses follow the same shape:
|
|
|
194
193
|
"message": "User not found: abc-123",
|
|
195
194
|
"path": "/api/v1/users/abc-123"
|
|
196
195
|
}
|
|
197
|
-
|
|
196
|
+
```
|
|
198
197
|
|
|
199
198
|
## Prisma Error Code Reference
|
|
200
199
|
|
|
201
|
-
Common Prisma error codes wrapped by
|
|
200
|
+
Common Prisma error codes wrapped by `RepositoryError`:
|
|
202
201
|
|
|
203
202
|
| Code | Meaning |
|
|
204
203
|
|------|---------|
|
|
@@ -211,9 +210,7 @@ Common Prisma error codes wrapped by \`RepositoryError\`:
|
|
|
211
210
|
|
|
212
211
|
1. **Services throw domain errors** — never raw NestJS HttpException in service layer.
|
|
213
212
|
2. **Exception filters map to HTTP** — one filter per domain error type.
|
|
214
|
-
3. **Error classes include a machine-readable
|
|
215
|
-
4. **Use
|
|
213
|
+
3. **Error classes include a machine-readable `code`** — used for client-side error handling.
|
|
214
|
+
4. **Use `Object.setPrototypeOf`** and `Error.captureStackTrace` in custom Error subclasses.
|
|
216
215
|
5. **Don't leak internal details** — the filter should not expose Prisma error codes or stack traces.
|
|
217
|
-
6. **Register filters globally** in the bootstrap function or via
|
|
218
|
-
`;
|
|
219
|
-
//# sourceMappingURL=errorHandlingPattern.js.map
|
|
216
|
+
6. **Register filters globally** in the bootstrap function or via `APP_FILTER` provider.
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.GUARD_PATTERN_KNOWLEDGE = `# Archipel Guard & Interceptor Pattern
|
|
1
|
+
---
|
|
2
|
+
title: Guard & Interceptor Pattern
|
|
3
|
+
description: Metadata decorators, FeatureGuard, RolesGuard, FeatureUsageInterceptor, and the full decorator-guard-interceptor trio.
|
|
4
|
+
order: 4
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Archipel Guard & Interceptor Pattern
|
|
9
8
|
|
|
10
9
|
## Overview
|
|
11
10
|
|
|
@@ -18,39 +17,39 @@ This creates a reusable trio: **Decorator → Guard → Interceptor**.
|
|
|
18
17
|
|
|
19
18
|
## Guard Architecture
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
```
|
|
22
21
|
@RequiresFeature('create-posts') ← Decorator sets metadata
|
|
23
22
|
@UseGuards(JwtAuthGuard, FeatureGuard) ← Guard reads metadata, checks access
|
|
24
23
|
@UseInterceptors(FeatureUsageInterceptor) ← Interceptor records usage after success
|
|
25
|
-
|
|
24
|
+
```
|
|
26
25
|
|
|
27
26
|
## Available Guards
|
|
28
27
|
|
|
29
28
|
| Guard | Strategy | Purpose |
|
|
30
29
|
|-------|----------|---------|
|
|
31
|
-
|
|
|
32
|
-
|
|
|
33
|
-
|
|
|
34
|
-
|
|
|
35
|
-
|
|
|
36
|
-
|
|
|
30
|
+
| `JwtAuthGuard` | jwt | Validates JWT bearer token |
|
|
31
|
+
| `LocalAuthGuard` | local | Validates username + password |
|
|
32
|
+
| `AnonymousAuthGuard` | anonymous | Allows unauthenticated access with a seed user |
|
|
33
|
+
| `GithubAuthGuard` | github | OAuth via GitHub |
|
|
34
|
+
| `RolesGuard` | metadata | Checks user roles against required roles |
|
|
35
|
+
| `FeatureGuard` | metadata + port | Checks feature access via FeatureAccessPort |
|
|
37
36
|
|
|
38
37
|
## The Feature Guard Pattern (Full Example)
|
|
39
38
|
|
|
40
39
|
### Step 1: Metadata Decorator
|
|
41
40
|
|
|
42
|
-
|
|
41
|
+
```typescript
|
|
43
42
|
import { SetMetadata } from '@nestjs/common';
|
|
44
43
|
|
|
45
44
|
export const FEATURE_KEY_METADATA = 'feature_key';
|
|
46
45
|
|
|
47
46
|
export const RequiresFeature = (featureKey: string) =>
|
|
48
47
|
SetMetadata(FEATURE_KEY_METADATA, featureKey);
|
|
49
|
-
|
|
48
|
+
```
|
|
50
49
|
|
|
51
50
|
### Step 2: Guard
|
|
52
51
|
|
|
53
|
-
|
|
52
|
+
```typescript
|
|
54
53
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
|
55
54
|
import { Reflector } from '@nestjs/core';
|
|
56
55
|
import { FeatureAccessPort } from '../contracts/FeatureAccessPort';
|
|
@@ -99,11 +98,11 @@ export class FeatureGuard implements CanActivate {
|
|
|
99
98
|
// #endregion
|
|
100
99
|
|
|
101
100
|
}
|
|
102
|
-
|
|
101
|
+
```
|
|
103
102
|
|
|
104
103
|
### Step 3: Interceptor (records usage after success)
|
|
105
104
|
|
|
106
|
-
|
|
105
|
+
```typescript
|
|
107
106
|
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
|
|
108
107
|
import { Reflector } from '@nestjs/core';
|
|
109
108
|
import { Observable } from 'rxjs';
|
|
@@ -152,11 +151,11 @@ export class FeatureUsageInterceptor implements NestInterceptor {
|
|
|
152
151
|
// #endregion
|
|
153
152
|
|
|
154
153
|
}
|
|
155
|
-
|
|
154
|
+
```
|
|
156
155
|
|
|
157
156
|
### Step 4: Controller Usage
|
|
158
157
|
|
|
159
|
-
|
|
158
|
+
```typescript
|
|
160
159
|
@Controller('/posts')
|
|
161
160
|
export class PostsController {
|
|
162
161
|
|
|
@@ -171,11 +170,11 @@ export class PostsController {
|
|
|
171
170
|
}
|
|
172
171
|
|
|
173
172
|
}
|
|
174
|
-
|
|
173
|
+
```
|
|
175
174
|
|
|
176
175
|
## Roles Guard Pattern
|
|
177
176
|
|
|
178
|
-
|
|
177
|
+
```typescript
|
|
179
178
|
// Decorator
|
|
180
179
|
export const Roles = (...roles: string[]) => SetMetadata('roles', roles);
|
|
181
180
|
|
|
@@ -202,11 +201,11 @@ export class RolesGuard implements CanActivate {
|
|
|
202
201
|
@Roles('admin', 'superadmin')
|
|
203
202
|
@UseGuards(JwtAuthGuard, RolesGuard)
|
|
204
203
|
public async getAdminPanel(): Promise<AdminResponse> { ... }
|
|
205
|
-
|
|
204
|
+
```
|
|
206
205
|
|
|
207
206
|
## Telemetry Interceptor
|
|
208
207
|
|
|
209
|
-
|
|
208
|
+
```typescript
|
|
210
209
|
@Injectable()
|
|
211
210
|
export class OperationTelemetryInterceptor implements NestInterceptor {
|
|
212
211
|
|
|
@@ -220,24 +219,24 @@ export class OperationTelemetryInterceptor implements NestInterceptor {
|
|
|
220
219
|
}
|
|
221
220
|
|
|
222
221
|
}
|
|
223
|
-
|
|
222
|
+
```
|
|
224
223
|
|
|
225
224
|
## How to Create a Custom Guard + Interceptor Pair
|
|
226
225
|
|
|
227
226
|
### Step 1: Define the metadata key and decorator
|
|
228
227
|
|
|
229
|
-
|
|
228
|
+
```typescript
|
|
230
229
|
export const RATE_LIMIT_METADATA = 'rate_limit';
|
|
231
230
|
export const RateLimit = (maxRequests: number) => SetMetadata(RATE_LIMIT_METADATA, maxRequests);
|
|
232
|
-
|
|
231
|
+
```
|
|
233
232
|
|
|
234
233
|
### Step 2: Create the guard
|
|
235
234
|
|
|
236
|
-
Read metadata via
|
|
235
|
+
Read metadata via `Reflector`, check the condition, return boolean.
|
|
237
236
|
|
|
238
237
|
### Step 3: Create the interceptor (optional)
|
|
239
238
|
|
|
240
|
-
Use
|
|
239
|
+
Use `tap()` in the RxJS pipeline to perform post-handler side effects.
|
|
241
240
|
|
|
242
241
|
### Step 4: Register in the module
|
|
243
242
|
|
|
@@ -245,12 +244,10 @@ Export guard and interceptor from the library module so consumers can apply them
|
|
|
245
244
|
|
|
246
245
|
## Rules
|
|
247
246
|
|
|
248
|
-
1. **Guards decide access** — return
|
|
247
|
+
1. **Guards decide access** — return `true` or `false`, never throw directly.
|
|
249
248
|
2. **Interceptors handle side effects** — recording, logging, transforming responses.
|
|
250
249
|
3. **Metadata decorators are the glue** — they attach config to handlers, read by guards/interceptors.
|
|
251
|
-
4. **Auth guards extend Passport's AuthGuard** — e.g.
|
|
250
|
+
4. **Auth guards extend Passport's AuthGuard** — e.g. `AuthGuard('jwt')`.
|
|
252
251
|
5. **Feature guards delegate to ports** — the guard itself holds no business logic.
|
|
253
|
-
6. **Guard order matters** —
|
|
254
|
-
7. **Interceptors use
|
|
255
|
-
`;
|
|
256
|
-
//# sourceMappingURL=guardPattern.js.map
|
|
252
|
+
6. **Guard order matters** — `@UseGuards(AuthGuard, FeatureGuard)`: auth runs first.
|
|
253
|
+
7. **Interceptors use `tap()`** for post-handler logic, `map()` for response transformation.
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.MAPPING_PATTERN_KNOWLEDGE = `# Archipel Mapping Pattern
|
|
1
|
+
---
|
|
2
|
+
title: Mapping Pattern
|
|
3
|
+
description: Branded type-safe mapping keys, MappingProfileBase, MappingService, and MappingModule registration.
|
|
4
|
+
order: 5
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Archipel Mapping Pattern
|
|
9
8
|
|
|
10
9
|
## Overview
|
|
11
10
|
|
|
12
11
|
In Archipel, all object-to-object mapping goes through a central **MappingService** with
|
|
13
12
|
**branded, type-safe mapping keys**. This guarantees compile-time type safety: the key
|
|
14
|
-
encodes both the input and output type, so calling
|
|
15
|
-
statically checked — the compiler verifies that
|
|
13
|
+
encodes both the input and output type, so calling `mappingService.map(KEY, input)` is
|
|
14
|
+
statically checked — the compiler verifies that `input` matches the key's input type and
|
|
16
15
|
infers the return type automatically.
|
|
17
16
|
|
|
18
17
|
**Rule:** Services MUST NOT inline-create response objects. They return entities.
|
|
@@ -22,32 +21,32 @@ Controllers use the MappingService to convert entities to response DTOs.
|
|
|
22
21
|
|
|
23
22
|
### IMappingKey<TInput, TOutput>
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
```typescript
|
|
26
25
|
export interface IMappingKey<TInput, TOutput> {
|
|
27
26
|
readonly _brand: 'MappingKey';
|
|
28
27
|
readonly _input: TInput;
|
|
29
28
|
readonly _output: TOutput;
|
|
30
29
|
readonly key: string;
|
|
31
30
|
}
|
|
32
|
-
|
|
31
|
+
```
|
|
33
32
|
|
|
34
|
-
The
|
|
33
|
+
The `_input` and `_output` fields are phantom types — they exist only for compile-time
|
|
35
34
|
type encoding and are never accessed at runtime.
|
|
36
35
|
|
|
37
36
|
### createMappingKey<TInput, TOutput>
|
|
38
37
|
|
|
39
|
-
|
|
40
|
-
import { createMappingKey } from '@breadstone/archipel-platform-
|
|
38
|
+
```typescript
|
|
39
|
+
import { createMappingKey } from '@breadstone/archipel-platform-mapping';
|
|
41
40
|
|
|
42
41
|
export const PRODUCT_MAPPING_KEY = createMappingKey<IProductMappingInput, ProductResponse>('food.product');
|
|
43
|
-
|
|
42
|
+
```
|
|
44
43
|
|
|
45
|
-
The string argument
|
|
46
|
-
|
|
44
|
+
The string argument `'food.product'` is the unique registry key. Convention:
|
|
45
|
+
`<domain>.<entity>` in lowercase dot-notation.
|
|
47
46
|
|
|
48
47
|
## MappingService API
|
|
49
48
|
|
|
50
|
-
|
|
49
|
+
```typescript
|
|
51
50
|
// Key-based mapping (preferred)
|
|
52
51
|
const response = mappingService.map(PRODUCT_MAPPING_KEY, input);
|
|
53
52
|
// → TOutput is inferred from the key, TInput is validated
|
|
@@ -58,7 +57,7 @@ const maybeResponse = mappingService.tryMap(PRODUCT_MAPPING_KEY, input);
|
|
|
58
57
|
// Type-based mapping (alternative)
|
|
59
58
|
const response = mappingService.mapTypes(sourceInstance, DestinationClass);
|
|
60
59
|
const responses = mappingService.mapTypesArray(sourceArray, DestinationClass);
|
|
61
|
-
|
|
60
|
+
```
|
|
62
61
|
|
|
63
62
|
## How to Create a Mapping
|
|
64
63
|
|
|
@@ -67,7 +66,7 @@ const responses = mappingService.mapTypesArray(sourceArray, DestinationClass);
|
|
|
67
66
|
The input interface represents the shape of data coming from the repository query.
|
|
68
67
|
Property names MUST match the Prisma select shape exactly.
|
|
69
68
|
|
|
70
|
-
|
|
69
|
+
```typescript
|
|
71
70
|
// models/mapping-inputs/IProductMappingInput.ts
|
|
72
71
|
export interface IProductMappingInput {
|
|
73
72
|
readonly id: string;
|
|
@@ -76,28 +75,28 @@ export interface IProductMappingInput {
|
|
|
76
75
|
readonly categoryId: string;
|
|
77
76
|
readonly createdAt: Date;
|
|
78
77
|
}
|
|
79
|
-
|
|
78
|
+
```
|
|
80
79
|
|
|
81
80
|
### Step 2: Create a branded mapping key
|
|
82
81
|
|
|
83
82
|
Each key is an **exported constant** — never inline magic strings.
|
|
84
83
|
|
|
85
|
-
|
|
84
|
+
```typescript
|
|
86
85
|
// constants/MappingKeys.ts
|
|
87
|
-
import { createMappingKey } from '@breadstone/archipel-platform-
|
|
86
|
+
import { createMappingKey } from '@breadstone/archipel-platform-mapping';
|
|
88
87
|
import { IProductMappingInput } from '../models/mapping-inputs/IProductMappingInput';
|
|
89
88
|
import { ProductResponse } from '../dtos/ProductResponse';
|
|
90
89
|
|
|
91
90
|
export const PRODUCT_MAPPING_KEY = createMappingKey<IProductMappingInput, ProductResponse>('food.product');
|
|
92
|
-
|
|
91
|
+
```
|
|
93
92
|
|
|
94
93
|
### Step 3: Create a mapping profile
|
|
95
94
|
|
|
96
|
-
A profile extends
|
|
95
|
+
A profile extends `MappingProfileBase` and registers one or more mappings via the builder.
|
|
97
96
|
|
|
98
|
-
|
|
97
|
+
```typescript
|
|
99
98
|
// mappers/ProductMappingProfile.ts
|
|
100
|
-
import { MappingProfileBase, IMappingBuilder } from '@breadstone/archipel-platform-
|
|
99
|
+
import { MappingProfileBase, IMappingBuilder } from '@breadstone/archipel-platform-mapping';
|
|
101
100
|
import { PRODUCT_MAPPING_KEY } from '../constants/MappingKeys';
|
|
102
101
|
import { IProductMappingInput } from '../models/mapping-inputs/IProductMappingInput';
|
|
103
102
|
import { ProductResponse } from '../dtos/ProductResponse';
|
|
@@ -122,12 +121,12 @@ export class ProductMappingProfile extends MappingProfileBase {
|
|
|
122
121
|
// #endregion
|
|
123
122
|
|
|
124
123
|
}
|
|
125
|
-
|
|
124
|
+
```
|
|
126
125
|
|
|
127
126
|
### Step 4: Register the profile in the module
|
|
128
127
|
|
|
129
|
-
|
|
130
|
-
import { MappingModule } from '@breadstone/archipel-platform-
|
|
128
|
+
```typescript
|
|
129
|
+
import { MappingModule } from '@breadstone/archipel-platform-mapping';
|
|
131
130
|
import { ProductMappingProfile } from './mappers/ProductMappingProfile';
|
|
132
131
|
|
|
133
132
|
@Module({
|
|
@@ -136,11 +135,11 @@ import { ProductMappingProfile } from './mappers/ProductMappingProfile';
|
|
|
136
135
|
],
|
|
137
136
|
})
|
|
138
137
|
export class ProductModule {}
|
|
139
|
-
|
|
138
|
+
```
|
|
140
139
|
|
|
141
140
|
### Step 5: Use in controller (NOT in service)
|
|
142
141
|
|
|
143
|
-
|
|
142
|
+
```typescript
|
|
144
143
|
@Controller('/products')
|
|
145
144
|
export class ProductController {
|
|
146
145
|
|
|
@@ -179,7 +178,7 @@ export class ProductController {
|
|
|
179
178
|
// #endregion
|
|
180
179
|
|
|
181
180
|
}
|
|
182
|
-
|
|
181
|
+
```
|
|
183
182
|
|
|
184
183
|
## MappingBuilder Methods
|
|
185
184
|
|
|
@@ -187,29 +186,29 @@ The builder supports two registration flavors:
|
|
|
187
186
|
|
|
188
187
|
### createKeyedMap (preferred)
|
|
189
188
|
|
|
190
|
-
|
|
189
|
+
```typescript
|
|
191
190
|
builder.createKeyedMap(SOME_MAPPING_KEY, (input: TInput) => {
|
|
192
191
|
const output = new TOutput();
|
|
193
192
|
// ... map fields
|
|
194
193
|
return output;
|
|
195
194
|
});
|
|
196
|
-
|
|
195
|
+
```
|
|
197
196
|
|
|
198
197
|
### createMap (type-based)
|
|
199
198
|
|
|
200
|
-
|
|
199
|
+
```typescript
|
|
201
200
|
builder.createMap(SourceClass, DestinationClass, (source: SourceClass) => {
|
|
202
201
|
const dest = new DestinationClass();
|
|
203
202
|
// ... map fields
|
|
204
203
|
return dest;
|
|
205
204
|
});
|
|
206
|
-
|
|
205
|
+
```
|
|
207
206
|
|
|
208
207
|
## Error Handling
|
|
209
208
|
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
- Both extend
|
|
209
|
+
- `MappingNotRegisteredError` — thrown when a keyed mapping is not found
|
|
210
|
+
- `TypeMappingNotRegisteredError` — thrown when a type-based mapping is not found
|
|
211
|
+
- Both extend `MappingError` with a machine-readable `code` field
|
|
213
212
|
|
|
214
213
|
## Rules
|
|
215
214
|
|
|
@@ -217,106 +216,6 @@ builder.createMap(SourceClass, DestinationClass, (source: SourceClass) => {
|
|
|
217
216
|
2. **Services return entities, controllers map to responses** — services MUST NOT create response objects.
|
|
218
217
|
3. **One mapping key per entity→response pair** — use descriptive dot notation for the key string.
|
|
219
218
|
4. **Input interfaces match Prisma select shape** — property names must align exactly.
|
|
220
|
-
5. **Profiles extend MappingProfileBase** and are registered via
|
|
221
|
-
6. **Use
|
|
222
|
-
7. **Mapping functions instantiate response classes** — use
|
|
223
|
-
`;
|
|
224
|
-
const MAPPING_PATTERN_WITH_CONTEXT = (entityName, fields, responseName) => `
|
|
225
|
-
## Example: Mapping for "${entityName}" → "${responseName}"
|
|
226
|
-
|
|
227
|
-
Given an entity \`${entityName}\` with fields: ${fields.map((f) => `\`${f}\``).join(', ')}
|
|
228
|
-
|
|
229
|
-
### 1. Input Interface
|
|
230
|
-
|
|
231
|
-
\`\`\`typescript
|
|
232
|
-
// models/mapping-inputs/I${entityName}MappingInput.ts
|
|
233
|
-
|
|
234
|
-
export interface I${entityName}MappingInput {
|
|
235
|
-
${fields.map((f) => ` readonly ${f}: ${inferMappingFieldType(f)};`).join('\n')}
|
|
236
|
-
}
|
|
237
|
-
\`\`\`
|
|
238
|
-
|
|
239
|
-
### 2. Mapping Key
|
|
240
|
-
|
|
241
|
-
\`\`\`typescript
|
|
242
|
-
// constants/MappingKeys.ts
|
|
243
|
-
import { createMappingKey } from '@breadstone/archipel-platform-core';
|
|
244
|
-
import { I${entityName}MappingInput } from '../models/mapping-inputs/I${entityName}MappingInput';
|
|
245
|
-
import { ${responseName} } from '../dtos/${responseName}';
|
|
246
|
-
|
|
247
|
-
export const ${toConstantCase(entityName)}_MAPPING_KEY = createMappingKey<I${entityName}MappingInput, ${responseName}>('${toDotNotation(entityName)}');
|
|
248
|
-
\`\`\`
|
|
249
|
-
|
|
250
|
-
### 3. Mapping Profile
|
|
251
|
-
|
|
252
|
-
\`\`\`typescript
|
|
253
|
-
// mappers/${entityName}MappingProfile.ts
|
|
254
|
-
import { MappingProfileBase, IMappingBuilder } from '@breadstone/archipel-platform-core';
|
|
255
|
-
import { ${toConstantCase(entityName)}_MAPPING_KEY } from '../constants/MappingKeys';
|
|
256
|
-
import { I${entityName}MappingInput } from '../models/mapping-inputs/I${entityName}MappingInput';
|
|
257
|
-
import { ${responseName} } from '../dtos/${responseName}';
|
|
258
|
-
|
|
259
|
-
export class ${entityName}MappingProfile extends MappingProfileBase {
|
|
260
|
-
|
|
261
|
-
// #region Methods
|
|
262
|
-
|
|
263
|
-
public configure(builder: IMappingBuilder): void {
|
|
264
|
-
builder.createKeyedMap(${toConstantCase(entityName)}_MAPPING_KEY, (input: I${entityName}MappingInput) => {
|
|
265
|
-
const response = new ${responseName}();
|
|
266
|
-
${fields.map((f) => ` response.${f} = input.${f};`).join('\n')}
|
|
267
|
-
|
|
268
|
-
return response;
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// #endregion
|
|
273
|
-
|
|
274
|
-
}
|
|
275
|
-
\`\`\`
|
|
276
|
-
|
|
277
|
-
### 4. Module Registration
|
|
278
|
-
|
|
279
|
-
\`\`\`typescript
|
|
280
|
-
import { MappingModule } from '@breadstone/archipel-platform-core';
|
|
281
|
-
import { ${entityName}MappingProfile } from './mappers/${entityName}MappingProfile';
|
|
282
|
-
|
|
283
|
-
@Module({
|
|
284
|
-
imports: [
|
|
285
|
-
MappingModule.withProfiles([${entityName}MappingProfile]),
|
|
286
|
-
],
|
|
287
|
-
})
|
|
288
|
-
export class ${entityName}Module {}
|
|
289
|
-
\`\`\`
|
|
290
|
-
|
|
291
|
-
### 5. Controller Usage
|
|
292
|
-
|
|
293
|
-
\`\`\`typescript
|
|
294
|
-
// In a controller method:
|
|
295
|
-
const entity = await this._${lowerFirst(entityName)}Service.findById(id);
|
|
296
|
-
const response = this._mappingService.map(${toConstantCase(entityName)}_MAPPING_KEY, entity);
|
|
297
|
-
\`\`\`
|
|
298
|
-
`;
|
|
299
|
-
exports.MAPPING_PATTERN_WITH_CONTEXT = MAPPING_PATTERN_WITH_CONTEXT;
|
|
300
|
-
function inferMappingFieldType(fieldName) {
|
|
301
|
-
if (fieldName === 'id')
|
|
302
|
-
return 'string';
|
|
303
|
-
if (fieldName.endsWith('Id'))
|
|
304
|
-
return 'string';
|
|
305
|
-
if (fieldName.endsWith('At') || fieldName === 'createdAt' || fieldName === 'updatedAt')
|
|
306
|
-
return 'Date';
|
|
307
|
-
if (fieldName.startsWith('is') || fieldName.startsWith('has'))
|
|
308
|
-
return 'boolean';
|
|
309
|
-
if (fieldName === 'count' || fieldName === 'quantity' || fieldName === 'amount' || fieldName === 'price')
|
|
310
|
-
return 'number';
|
|
311
|
-
return 'string';
|
|
312
|
-
}
|
|
313
|
-
function toConstantCase(name) {
|
|
314
|
-
return name.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
315
|
-
}
|
|
316
|
-
function toDotNotation(name) {
|
|
317
|
-
return name.replace(/([a-z])([A-Z])/g, '$1.$2').toLowerCase();
|
|
318
|
-
}
|
|
319
|
-
function lowerFirst(str) {
|
|
320
|
-
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
321
|
-
}
|
|
322
|
-
//# sourceMappingURL=mappingPattern.js.map
|
|
219
|
+
5. **Profiles extend MappingProfileBase** and are registered via `MappingModule.withProfiles()`.
|
|
220
|
+
6. **Use `createKeyedMap` over `createMap`** — branded keys provide better type safety.
|
|
221
|
+
7. **Mapping functions instantiate response classes** — use `new ResponseClass()` and assign fields.
|