@breadstone/archipel-mcp 0.0.21 → 0.0.22
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/data/guides/cryptography-and-otp.md +2 -2
- package/data/guides/database-setup.md +4 -3
- package/data/guides/email-templates.md +3 -3
- package/data/guides/getting-started.md +6 -6
- package/data/guides/resource-management.md +9 -9
- 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-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.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 +6 -3
- 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/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} +44 -47
- 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 +1 -1
- 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 +4 -3
- package/src/tools/registerGetDtoPatternTool.d.ts +2 -1
- package/src/tools/registerGetDtoPatternTool.js +4 -3
- package/src/tools/registerGetErrorHandlingPatternTool.d.ts +2 -1
- package/src/tools/registerGetErrorHandlingPatternTool.js +4 -3
- package/src/tools/registerGetGuardPatternTool.d.ts +2 -1
- package/src/tools/registerGetGuardPatternTool.js +4 -3
- package/src/tools/registerGetMappingPatternTool.d.ts +2 -1
- package/src/tools/registerGetMappingPatternTool.js +5 -4
- package/src/tools/registerGetModulePatternTool.d.ts +2 -1
- package/src/tools/registerGetModulePatternTool.js +5 -4
- package/src/tools/registerGetQueryPatternTool.d.ts +2 -1
- package/src/tools/registerGetQueryPatternTool.js +5 -4
- package/src/tools/registerGetRepositoryPatternTool.d.ts +2 -1
- package/src/tools/registerGetRepositoryPatternTool.js +5 -4
- package/src/tools/registerGetTestingPatternTool.d.ts +2 -1
- package/src/tools/registerGetTestingPatternTool.js +4 -3
- 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
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Repository Pattern
|
|
3
|
+
description: RepositoryBase subclasses, DelegateArgs/DelegateReturnTypes helpers, module registration, and service injection.
|
|
4
|
+
order: 8
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Archipel Repository Pattern
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
Every Prisma model gets a dedicated **Repository class** extending `RepositoryBase`.
|
|
12
|
+
Repositories are the ONLY code that touches Prisma delegates. Services never call
|
|
13
|
+
Prisma directly — they call `repository.execute(someQuery(...))` or
|
|
14
|
+
`repository.executeTransactional(someTxQuery(...))`.
|
|
15
|
+
|
|
16
|
+
## RepositoryBase Signature
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { RepositoryBase } from '@breadstone/archipel-platform-database';
|
|
20
|
+
|
|
21
|
+
export abstract class RepositoryBase<
|
|
22
|
+
TDelegate extends Record<RepositoryOperations, (args: any) => Promise<any>>,
|
|
23
|
+
TArgs extends Record<RepositoryOperations, unknown>,
|
|
24
|
+
TReturn extends Record<RepositoryOperations, unknown>,
|
|
25
|
+
TEntity = unknown
|
|
26
|
+
>
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Generic Parameters
|
|
30
|
+
|
|
31
|
+
| Param | Purpose | Example |
|
|
32
|
+
|-------|---------|---------|
|
|
33
|
+
| `TDelegate` | Prisma delegate type from generated client | `Prisma.UserDelegate` |
|
|
34
|
+
| `TArgs` | Argument shapes for each operation | `Prisma.UserFindManyArgs`, etc. |
|
|
35
|
+
| `TReturn` | Return shapes for each operation | `User`, `User[]`, etc. |
|
|
36
|
+
| `TEntity` | Domain entity interface (optional) | `IUserEntity` |
|
|
37
|
+
|
|
38
|
+
### Helper Types
|
|
39
|
+
|
|
40
|
+
```typescript
|
|
41
|
+
import { DelegateArgs, DelegateReturnTypes } from '@breadstone/archipel-platform-database';
|
|
42
|
+
|
|
43
|
+
// These extract args and return types from a delegate automatically:
|
|
44
|
+
type UserArgs = DelegateArgs<Prisma.UserDelegate>;
|
|
45
|
+
type UserReturns = DelegateReturnTypes<Prisma.UserDelegate>;
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## How to Create a Repository
|
|
49
|
+
|
|
50
|
+
### Step 1: Create the repository class
|
|
51
|
+
|
|
52
|
+
```typescript
|
|
53
|
+
// repositories/UserRepository.ts
|
|
54
|
+
import { Injectable } from '@nestjs/common';
|
|
55
|
+
import { Prisma } from '@prisma/client';
|
|
56
|
+
import {
|
|
57
|
+
DatabaseService,
|
|
58
|
+
DelegateArgs,
|
|
59
|
+
DelegateReturnTypes,
|
|
60
|
+
RepositoryBase,
|
|
61
|
+
} from '@breadstone/archipel-platform-database';
|
|
62
|
+
|
|
63
|
+
@Injectable()
|
|
64
|
+
export class UserRepository extends RepositoryBase<
|
|
65
|
+
Prisma.UserDelegate,
|
|
66
|
+
DelegateArgs<Prisma.UserDelegate>,
|
|
67
|
+
DelegateReturnTypes<Prisma.UserDelegate>,
|
|
68
|
+
IUserEntity
|
|
69
|
+
> {
|
|
70
|
+
|
|
71
|
+
// #region Ctor
|
|
72
|
+
|
|
73
|
+
public constructor(db: DatabaseService) {
|
|
74
|
+
super(db, db.client.user);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// #endregion
|
|
78
|
+
|
|
79
|
+
}
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The constructor receives `DatabaseService` and passes both the service and the
|
|
83
|
+
Prisma delegate (`db.client.user`) to the base class.
|
|
84
|
+
|
|
85
|
+
### Step 2: Register as a provider in a module
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
@Module({
|
|
89
|
+
imports: [DatabaseModule.register()],
|
|
90
|
+
providers: [UserRepository, UserService],
|
|
91
|
+
exports: [UserService],
|
|
92
|
+
})
|
|
93
|
+
export class UserModule {}
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Step 3: Inject into a service
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
@Injectable()
|
|
100
|
+
export class UserService {
|
|
101
|
+
|
|
102
|
+
// #region Fields
|
|
103
|
+
|
|
104
|
+
private readonly _userRepository: UserRepository;
|
|
105
|
+
|
|
106
|
+
// #endregion
|
|
107
|
+
|
|
108
|
+
// #region Ctor
|
|
109
|
+
|
|
110
|
+
public constructor(userRepository: UserRepository) {
|
|
111
|
+
this._userRepository = userRepository;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// #endregion
|
|
115
|
+
|
|
116
|
+
// #region Methods
|
|
117
|
+
|
|
118
|
+
public async findById(id: string): Promise<IUserEntity | null> {
|
|
119
|
+
return this._userRepository.execute(findUserByIdQuery(id));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
public async findAll(skip = 0, take = 20): Promise<IUserEntity[]> {
|
|
123
|
+
return this._userRepository.execute(findAllUsersQuery(skip, take));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// #endregion
|
|
127
|
+
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## Key Methods on RepositoryBase
|
|
132
|
+
|
|
133
|
+
### execute(query)
|
|
134
|
+
|
|
135
|
+
Primary method for data access. Delegates to a query object.
|
|
136
|
+
|
|
137
|
+
```typescript
|
|
138
|
+
public execute<TResult>(query: IRepositoryQuery<TDelegate, TResult>): Promise<TResult>
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### executeTransactional(query)
|
|
142
|
+
|
|
143
|
+
For multi-model operations within a transaction.
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
public executeTransactional<TResult>(query: ITransactionalRepositoryQuery<TResult>): Promise<TResult>
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Direct Methods (for legacy or internal use only)
|
|
150
|
+
|
|
151
|
+
The base class also exposes `findUnique`, `findMany`, `findFirst`, `create`,
|
|
152
|
+
`createMany`, `update`, `updateMany`, `upsert`, `delete`, `deleteMany`,
|
|
153
|
+
`aggregate`, `count`, `groupBy`, `merge`.
|
|
154
|
+
|
|
155
|
+
**Important:** New code should use `execute(query)` exclusively.
|
|
156
|
+
Direct method calls are available but violate the query pattern convention.
|
|
157
|
+
|
|
158
|
+
### findMany with Pagination
|
|
159
|
+
|
|
160
|
+
```typescript
|
|
161
|
+
// Direct method (legacy)
|
|
162
|
+
const results = await this._repository.findMany(args, { page: 1, perPage: 20 });
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
## Error Handling
|
|
166
|
+
|
|
167
|
+
All Prisma errors are caught by the base class `tryCatch` wrapper and re-thrown
|
|
168
|
+
as `RepositoryError`:
|
|
169
|
+
|
|
170
|
+
- `PrismaClientKnownRequestError` → `RepositoryError` with code `'R_KNOWN'`
|
|
171
|
+
- `PrismaClientUnknownRequestError` → `RepositoryError` with code `'R_UNKNOWN'`
|
|
172
|
+
- Other errors are re-thrown unchanged
|
|
173
|
+
|
|
174
|
+
The global `RepositoryExceptionFilter` catches `RepositoryError` and returns
|
|
175
|
+
HTTP 500 with a structured envelope.
|
|
176
|
+
|
|
177
|
+
## Query Integration
|
|
178
|
+
|
|
179
|
+
Repositories work with query factories from the query pattern:
|
|
180
|
+
|
|
181
|
+
```typescript
|
|
182
|
+
// queries/findUserByIdQuery.ts
|
|
183
|
+
import { Prisma } from '@prisma/client';
|
|
184
|
+
import { query } from '@breadstone/archipel-platform-database';
|
|
185
|
+
|
|
186
|
+
export function findUserByIdQuery(id: string) {
|
|
187
|
+
return query<Prisma.UserDelegate, IUserEntity | null>(
|
|
188
|
+
'findUserById',
|
|
189
|
+
(model) => model.findFirst({
|
|
190
|
+
where: { id },
|
|
191
|
+
select: { id: true, email: true, name: true, createdAt: true },
|
|
192
|
+
}),
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Service usage:
|
|
197
|
+
const user = await this._userRepository.execute(findUserByIdQuery(id));
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Rules
|
|
201
|
+
|
|
202
|
+
1. **One repository per Prisma model** — e.g. `UserRepository`, `OrderRepository`.
|
|
203
|
+
2. **Constructor receives `DatabaseService`** and passes the delegate via `db.client.<model>`.
|
|
204
|
+
3. **Use `DelegateArgs` and `DelegateReturnTypes`** helper types for the RepositoryBase generics.
|
|
205
|
+
4. **New code uses `execute(query)`** — never direct calls like `repository.findFirst()` from services.
|
|
206
|
+
5. **Register as provider** in the feature module, import `DatabaseModule.register()`.
|
|
207
|
+
6. **Repositories are `@Injectable()`** — they participate in NestJS DI.
|
|
208
|
+
7. **Domain entity interface as fourth generic** (`TEntity`) is optional but recommended.
|
|
@@ -1,35 +1,34 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
exports.TESTING_PATTERN_KNOWLEDGE = `# Archipel Testing Pattern
|
|
1
|
+
---
|
|
2
|
+
title: Testing Pattern
|
|
3
|
+
description: Vitest conventions, mocking patterns for repositories/services/delegates, AAA structure, and test naming conventions.
|
|
4
|
+
order: 9
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Archipel Testing Pattern
|
|
9
8
|
|
|
10
9
|
## Overview
|
|
11
10
|
|
|
12
11
|
Archipel uses **Vitest** for unit and integration tests with **vi mocking**. Tests follow
|
|
13
|
-
the Arrange-Act-Assert (AAA) pattern with descriptive
|
|
12
|
+
the Arrange-Act-Assert (AAA) pattern with descriptive `describe`/`it` blocks. Each test
|
|
14
13
|
file covers a single class or module.
|
|
15
14
|
|
|
16
15
|
## Test Framework
|
|
17
16
|
|
|
18
17
|
- **Runner:** Vitest
|
|
19
|
-
- **Mocking:**
|
|
20
|
-
- **Assertions:** Vitest built-in (
|
|
18
|
+
- **Mocking:** `vi.fn()`, `vi.mock()`, `vi.spyOn()`
|
|
19
|
+
- **Assertions:** Vitest built-in (`expect`)
|
|
21
20
|
- **E2E:** Supertest against a bootstrapped NestJS app
|
|
22
21
|
|
|
23
22
|
## File Naming & Location
|
|
24
23
|
|
|
25
24
|
| Test Type | File Pattern | Location |
|
|
26
25
|
|-----------|-------------|----------|
|
|
27
|
-
| Unit test |
|
|
28
|
-
| E2E test |
|
|
26
|
+
| Unit test | `*.spec.ts` | Next to the source file |
|
|
27
|
+
| E2E test | `*.e2e-spec.ts` | `test/` directory |
|
|
29
28
|
|
|
30
29
|
## Basic Test Structure
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
```typescript
|
|
33
32
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
34
33
|
|
|
35
34
|
describe('UserService', () => {
|
|
@@ -88,13 +87,13 @@ describe('UserService', () => {
|
|
|
88
87
|
// #endregion
|
|
89
88
|
|
|
90
89
|
});
|
|
91
|
-
|
|
90
|
+
```
|
|
92
91
|
|
|
93
92
|
## Mocking Patterns
|
|
94
93
|
|
|
95
94
|
### Mocking Prisma Delegates (for Repository Tests)
|
|
96
95
|
|
|
97
|
-
|
|
96
|
+
```typescript
|
|
98
97
|
vi.mock('@prisma/client/runtime/client', () => ({
|
|
99
98
|
PrismaClientKnownRequestError: class PrismaClientKnownRequestError extends Error {
|
|
100
99
|
public code: string;
|
|
@@ -117,11 +116,11 @@ vi.mock('@prisma/client/runtime/client', () => ({
|
|
|
117
116
|
}
|
|
118
117
|
},
|
|
119
118
|
}));
|
|
120
|
-
|
|
119
|
+
```
|
|
121
120
|
|
|
122
121
|
### Creating a Mock Delegate
|
|
123
122
|
|
|
124
|
-
|
|
123
|
+
```typescript
|
|
125
124
|
type MockDelegate = Record<
|
|
126
125
|
| 'aggregate' | 'count' | 'create' | 'createMany' | 'createManyAndReturn'
|
|
127
126
|
| 'delete' | 'deleteMany' | 'findFirst' | 'findFirstOrThrow'
|
|
@@ -150,31 +149,31 @@ function createMockDelegate(): MockDelegate {
|
|
|
150
149
|
findUniqueOrThrow: vi.fn(),
|
|
151
150
|
};
|
|
152
151
|
}
|
|
153
|
-
|
|
152
|
+
```
|
|
154
153
|
|
|
155
154
|
### Mocking DatabaseService
|
|
156
155
|
|
|
157
|
-
|
|
156
|
+
```typescript
|
|
158
157
|
const mockDb = {
|
|
159
158
|
transactionCallback: vi.fn(),
|
|
160
159
|
client: {
|
|
161
160
|
user: createMockDelegate(),
|
|
162
161
|
},
|
|
163
162
|
} as unknown as DatabaseService;
|
|
164
|
-
|
|
163
|
+
```
|
|
165
164
|
|
|
166
165
|
### Mocking a Query
|
|
167
166
|
|
|
168
|
-
|
|
167
|
+
```typescript
|
|
169
168
|
const mockQuery = {
|
|
170
169
|
name: 'findUserById',
|
|
171
170
|
run: vi.fn().mockResolvedValue({ id: '1', email: 'test@example.com' }),
|
|
172
171
|
};
|
|
173
|
-
|
|
172
|
+
```
|
|
174
173
|
|
|
175
174
|
## Repository Test Pattern
|
|
176
175
|
|
|
177
|
-
|
|
176
|
+
```typescript
|
|
178
177
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
|
179
178
|
|
|
180
179
|
// Mock Prisma runtime errors BEFORE importing test subject
|
|
@@ -228,11 +227,11 @@ describe('UserRepository', () => {
|
|
|
228
227
|
});
|
|
229
228
|
|
|
230
229
|
});
|
|
231
|
-
|
|
230
|
+
```
|
|
232
231
|
|
|
233
232
|
## Service Test Pattern
|
|
234
233
|
|
|
235
|
-
|
|
234
|
+
```typescript
|
|
236
235
|
describe('ProductService', () => {
|
|
237
236
|
|
|
238
237
|
let service: ProductService;
|
|
@@ -268,11 +267,11 @@ describe('ProductService', () => {
|
|
|
268
267
|
});
|
|
269
268
|
|
|
270
269
|
});
|
|
271
|
-
|
|
270
|
+
```
|
|
272
271
|
|
|
273
272
|
## Mapping Profile Test Pattern
|
|
274
273
|
|
|
275
|
-
|
|
274
|
+
```typescript
|
|
276
275
|
describe('ProductMappingProfile', () => {
|
|
277
276
|
|
|
278
277
|
let mappingService: MappingService;
|
|
@@ -304,23 +303,23 @@ describe('ProductMappingProfile', () => {
|
|
|
304
303
|
});
|
|
305
304
|
|
|
306
305
|
});
|
|
307
|
-
|
|
306
|
+
```
|
|
308
307
|
|
|
309
308
|
## Test Conventions
|
|
310
309
|
|
|
311
310
|
### Comments
|
|
312
311
|
|
|
313
|
-
|
|
312
|
+
```typescript
|
|
314
313
|
// Use AAA comments:
|
|
315
314
|
// arrange — set up test data and mocks
|
|
316
315
|
// act — call the method under test
|
|
317
316
|
// act & assert — when the assertion is on the thrown error
|
|
318
317
|
// assert — verify the outcome
|
|
319
|
-
|
|
318
|
+
```
|
|
320
319
|
|
|
321
320
|
### Regions
|
|
322
321
|
|
|
323
|
-
|
|
322
|
+
```typescript
|
|
324
323
|
describe('MyClass', () => {
|
|
325
324
|
|
|
326
325
|
// #region Fields
|
|
@@ -334,23 +333,21 @@ describe('MyClass', () => {
|
|
|
334
333
|
// #endregion
|
|
335
334
|
|
|
336
335
|
});
|
|
337
|
-
|
|
336
|
+
```
|
|
338
337
|
|
|
339
338
|
### Naming
|
|
340
339
|
|
|
341
|
-
-
|
|
342
|
-
-
|
|
340
|
+
- `describe` blocks: Class name or method name
|
|
341
|
+
- `it` blocks: `'should <expected behavior> when <condition>'`
|
|
343
342
|
|
|
344
343
|
## Rules
|
|
345
344
|
|
|
346
345
|
1. **Every public service method needs at least one happy-path and one failure-path test.**
|
|
347
|
-
2. **Use
|
|
346
|
+
2. **Use `vi.restoreAllMocks()` in `beforeEach`** — prevent mock leakage between tests.
|
|
348
347
|
3. **Mock at the boundary** — mock repositories in service tests, mock delegates in repository tests.
|
|
349
348
|
4. **AAA pattern** — clearly separate arrange, act, assert with comments.
|
|
350
|
-
5.
|
|
349
|
+
5. **`vi.mock()` for modules must appear before imports** of the mocked module.
|
|
351
350
|
6. **No real database in unit tests** — use mocks. Use Testcontainers for integration tests.
|
|
352
351
|
7. **Assert on behavior, not implementation** — verify return values and thrown errors, not internal calls
|
|
353
352
|
(unless the call IS the behavior, e.g., verifying a repository was called).
|
|
354
|
-
8. **Use
|
|
355
|
-
`;
|
|
356
|
-
//# sourceMappingURL=testingPattern.js.map
|
|
353
|
+
8. **Use `mockResolvedValue` / `mockRejectedValue`** for async mocks.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@breadstone/archipel-mcp",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.22",
|
|
4
4
|
"description": "MCP server providing Archipel platform knowledge — documentation, query patterns, and coding conventions — to AI development tools.",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"main": "./src/main.js",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { IPatternDoc } from './models/IPatternDoc';
|
|
2
|
+
/**
|
|
3
|
+
* Loads and indexes Archipel code patterns from the `.docs/patterns/`
|
|
4
|
+
* directory (monorepo dev) or from the bundled `data/patterns/` directory
|
|
5
|
+
* (published npm package).
|
|
6
|
+
*/
|
|
7
|
+
export declare class PatternsLoader {
|
|
8
|
+
private readonly _patterns;
|
|
9
|
+
get patterns(): ReadonlyMap<string, IPatternDoc>;
|
|
10
|
+
load(): void;
|
|
11
|
+
private resolvePatternsDirectory;
|
|
12
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PatternsLoader = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const gray_matter_1 = tslib_1.__importDefault(require("gray-matter"));
|
|
6
|
+
const fs = tslib_1.__importStar(require("node:fs"));
|
|
7
|
+
const path = tslib_1.__importStar(require("node:path"));
|
|
8
|
+
/**
|
|
9
|
+
* Loads and indexes Archipel code patterns from the `.docs/patterns/`
|
|
10
|
+
* directory (monorepo dev) or from the bundled `data/patterns/` directory
|
|
11
|
+
* (published npm package).
|
|
12
|
+
*/
|
|
13
|
+
class PatternsLoader {
|
|
14
|
+
// #region Fields
|
|
15
|
+
_patterns = new Map();
|
|
16
|
+
// #endregion
|
|
17
|
+
// #region Properties
|
|
18
|
+
get patterns() {
|
|
19
|
+
return this._patterns;
|
|
20
|
+
}
|
|
21
|
+
// #endregion
|
|
22
|
+
// #region Methods
|
|
23
|
+
load() {
|
|
24
|
+
const patternsDirectory = this.resolvePatternsDirectory();
|
|
25
|
+
if (!patternsDirectory) {
|
|
26
|
+
process.stderr.write('[archipel-mcp] Warning: Could not locate patterns directory.\n');
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
const files = fs.readdirSync(patternsDirectory).filter((file) => file.endsWith('.md') && file !== 'index.md');
|
|
30
|
+
for (const file of files) {
|
|
31
|
+
const filePath = path.join(patternsDirectory, file);
|
|
32
|
+
const raw = fs.readFileSync(filePath, 'utf-8');
|
|
33
|
+
const parsed = (0, gray_matter_1.default)(raw);
|
|
34
|
+
const name = path.basename(file, '.md');
|
|
35
|
+
this._patterns.set(name, {
|
|
36
|
+
name,
|
|
37
|
+
title: parsed.data['title'] || name,
|
|
38
|
+
description: parsed.data['description'] || '',
|
|
39
|
+
order: parsed.data['order'] || 999,
|
|
40
|
+
content: parsed.content.trim(),
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
process.stderr.write(`[archipel-mcp] Loaded ${this._patterns.size} pattern(s)\n`);
|
|
44
|
+
}
|
|
45
|
+
resolvePatternsDirectory() {
|
|
46
|
+
// 1. Bundled data (published npm package)
|
|
47
|
+
const bundledPath = path.resolve(__dirname, '..', 'data', 'patterns');
|
|
48
|
+
if (fs.existsSync(bundledPath)) {
|
|
49
|
+
return bundledPath;
|
|
50
|
+
}
|
|
51
|
+
// 2. Workspace root (monorepo dev)
|
|
52
|
+
const cwdPath = path.resolve(process.cwd(), '.docs', 'patterns');
|
|
53
|
+
if (fs.existsSync(cwdPath)) {
|
|
54
|
+
return cwdPath;
|
|
55
|
+
}
|
|
56
|
+
// 3. Relative from compiled source (dist/tools/mcp/src/)
|
|
57
|
+
const relativePath = path.resolve(__dirname, '..', '..', '..', '..', '.docs', 'patterns');
|
|
58
|
+
if (fs.existsSync(relativePath)) {
|
|
59
|
+
return relativePath;
|
|
60
|
+
}
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
exports.PatternsLoader = PatternsLoader;
|
|
65
|
+
//# sourceMappingURL=PatternsLoader.js.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates a full mapping example (input interface, key, profile, module registration,
|
|
3
|
+
* controller usage) for a given entity and its target response class.
|
|
4
|
+
*/
|
|
5
|
+
export declare function MAPPING_PATTERN_WITH_CONTEXT(entityName: string, fields: string[], responseName: string): string;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.MAPPING_PATTERN_WITH_CONTEXT = MAPPING_PATTERN_WITH_CONTEXT;
|
|
4
|
+
/**
|
|
5
|
+
* Generates a full mapping example (input interface, key, profile, module registration,
|
|
6
|
+
* controller usage) for a given entity and its target response class.
|
|
7
|
+
*/
|
|
8
|
+
function MAPPING_PATTERN_WITH_CONTEXT(entityName, fields, responseName) {
|
|
9
|
+
return `
|
|
10
|
+
## Example: Mapping for "${entityName}" → "${responseName}"
|
|
11
|
+
|
|
12
|
+
Given an entity \`${entityName}\` with fields: ${fields.map((f) => `\`${f}\``).join(', ')}
|
|
13
|
+
|
|
14
|
+
### 1. Input Interface
|
|
15
|
+
|
|
16
|
+
\`\`\`typescript
|
|
17
|
+
// models/mapping-inputs/I${entityName}MappingInput.ts
|
|
18
|
+
|
|
19
|
+
export interface I${entityName}MappingInput {
|
|
20
|
+
${fields.map((f) => ` readonly ${f}: ${inferMappingFieldType(f)};`).join('\n')}
|
|
21
|
+
}
|
|
22
|
+
\`\`\`
|
|
23
|
+
|
|
24
|
+
### 2. Mapping Key
|
|
25
|
+
|
|
26
|
+
\`\`\`typescript
|
|
27
|
+
// constants/MappingKeys.ts
|
|
28
|
+
import { createMappingKey } from '@breadstone/archipel-platform-mapping';
|
|
29
|
+
import { I${entityName}MappingInput } from '../models/mapping-inputs/I${entityName}MappingInput';
|
|
30
|
+
import { ${responseName} } from '../dtos/${responseName}';
|
|
31
|
+
|
|
32
|
+
export const ${toConstantCase(entityName)}_MAPPING_KEY = createMappingKey<I${entityName}MappingInput, ${responseName}>('${toDotNotation(entityName)}');
|
|
33
|
+
\`\`\`
|
|
34
|
+
|
|
35
|
+
### 3. Mapping Profile
|
|
36
|
+
|
|
37
|
+
\`\`\`typescript
|
|
38
|
+
// mappers/${entityName}MappingProfile.ts
|
|
39
|
+
import { MappingProfileBase, IMappingBuilder } from '@breadstone/archipel-platform-mapping';
|
|
40
|
+
import { ${toConstantCase(entityName)}_MAPPING_KEY } from '../constants/MappingKeys';
|
|
41
|
+
import { I${entityName}MappingInput } from '../models/mapping-inputs/I${entityName}MappingInput';
|
|
42
|
+
import { ${responseName} } from '../dtos/${responseName}';
|
|
43
|
+
|
|
44
|
+
export class ${entityName}MappingProfile extends MappingProfileBase {
|
|
45
|
+
|
|
46
|
+
// #region Methods
|
|
47
|
+
|
|
48
|
+
public configure(builder: IMappingBuilder): void {
|
|
49
|
+
builder.createKeyedMap(${toConstantCase(entityName)}_MAPPING_KEY, (input: I${entityName}MappingInput) => {
|
|
50
|
+
const response = new ${responseName}();
|
|
51
|
+
${fields.map((f) => ` response.${f} = input.${f};`).join('\n')}
|
|
52
|
+
|
|
53
|
+
return response;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// #endregion
|
|
58
|
+
|
|
59
|
+
}
|
|
60
|
+
\`\`\`
|
|
61
|
+
|
|
62
|
+
### 4. Module Registration
|
|
63
|
+
|
|
64
|
+
\`\`\`typescript
|
|
65
|
+
import { MappingModule } from '@breadstone/archipel-platform-mapping';
|
|
66
|
+
import { ${entityName}MappingProfile } from './mappers/${entityName}MappingProfile';
|
|
67
|
+
|
|
68
|
+
@Module({
|
|
69
|
+
imports: [
|
|
70
|
+
MappingModule.withProfiles([${entityName}MappingProfile]),
|
|
71
|
+
],
|
|
72
|
+
})
|
|
73
|
+
export class ${entityName}Module {}
|
|
74
|
+
\`\`\`
|
|
75
|
+
|
|
76
|
+
### 5. Controller Usage
|
|
77
|
+
|
|
78
|
+
\`\`\`typescript
|
|
79
|
+
// In a controller method:
|
|
80
|
+
const entity = await this._${lowerFirst(entityName)}Service.findById(id);
|
|
81
|
+
const response = this._mappingService.map(${toConstantCase(entityName)}_MAPPING_KEY, entity);
|
|
82
|
+
\`\`\`
|
|
83
|
+
`;
|
|
84
|
+
}
|
|
85
|
+
function inferMappingFieldType(fieldName) {
|
|
86
|
+
if (fieldName === 'id')
|
|
87
|
+
return 'string';
|
|
88
|
+
if (fieldName.endsWith('Id'))
|
|
89
|
+
return 'string';
|
|
90
|
+
if (fieldName.endsWith('At') || fieldName === 'createdAt' || fieldName === 'updatedAt')
|
|
91
|
+
return 'Date';
|
|
92
|
+
if (fieldName.startsWith('is') || fieldName.startsWith('has'))
|
|
93
|
+
return 'boolean';
|
|
94
|
+
if (fieldName === 'count' || fieldName === 'quantity' || fieldName === 'amount' || fieldName === 'price')
|
|
95
|
+
return 'number';
|
|
96
|
+
return 'string';
|
|
97
|
+
}
|
|
98
|
+
function toConstantCase(name) {
|
|
99
|
+
return name.replace(/([a-z])([A-Z])/g, '$1_$2').toUpperCase();
|
|
100
|
+
}
|
|
101
|
+
function toDotNotation(name) {
|
|
102
|
+
return name.replace(/([a-z])([A-Z])/g, '$1.$2').toLowerCase();
|
|
103
|
+
}
|
|
104
|
+
function lowerFirst(str) {
|
|
105
|
+
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=mappingPatternGenerator.js.map
|