@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
|
@@ -1,367 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
/**
|
|
3
|
-
* Static knowledge content for the Archipel repository class pattern.
|
|
4
|
-
* Returned by the get-repository-pattern tool.
|
|
5
|
-
*/
|
|
6
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.REPOSITORY_PATTERN_WITH_MODEL = exports.REPOSITORY_PATTERN_KNOWLEDGE = void 0;
|
|
8
|
-
exports.REPOSITORY_PATTERN_KNOWLEDGE = `# Archipel Repository Pattern
|
|
9
|
-
|
|
10
|
-
## Overview
|
|
11
|
-
|
|
12
|
-
Every Prisma model gets a dedicated **Repository class** extending \`RepositoryBase\`.
|
|
13
|
-
Repositories are the ONLY code that touches Prisma delegates. Services never call
|
|
14
|
-
Prisma directly — they call \`repository.execute(someQuery(...))\` or
|
|
15
|
-
\`repository.executeTransactional(someTxQuery(...))\`.
|
|
16
|
-
|
|
17
|
-
## RepositoryBase Signature
|
|
18
|
-
|
|
19
|
-
\`\`\`typescript
|
|
20
|
-
import { RepositoryBase } from '@breadstone/archipel-platform-database';
|
|
21
|
-
|
|
22
|
-
export abstract class RepositoryBase<
|
|
23
|
-
TDelegate extends Record<RepositoryOperations, (args: any) => Promise<any>>,
|
|
24
|
-
TArgs extends Record<RepositoryOperations, unknown>,
|
|
25
|
-
TReturn extends Record<RepositoryOperations, unknown>,
|
|
26
|
-
TEntity = unknown
|
|
27
|
-
>
|
|
28
|
-
\`\`\`
|
|
29
|
-
|
|
30
|
-
### Generic Parameters
|
|
31
|
-
|
|
32
|
-
| Param | Purpose | Example |
|
|
33
|
-
|-------|---------|---------|
|
|
34
|
-
| \`TDelegate\` | Prisma delegate type from generated client | \`Prisma.UserDelegate\` |
|
|
35
|
-
| \`TArgs\` | Argument shapes for each operation | \`Prisma.UserFindManyArgs\`, etc. |
|
|
36
|
-
| \`TReturn\` | Return shapes for each operation | \`User\`, \`User[]\`, etc. |
|
|
37
|
-
| \`TEntity\` | Domain entity interface (optional) | \`IUserEntity\` |
|
|
38
|
-
|
|
39
|
-
### Helper Types
|
|
40
|
-
|
|
41
|
-
\`\`\`typescript
|
|
42
|
-
import { DelegateArgs, DelegateReturnTypes } from '@breadstone/archipel-platform-database';
|
|
43
|
-
|
|
44
|
-
// These extract args and return types from a delegate automatically:
|
|
45
|
-
type UserArgs = DelegateArgs<Prisma.UserDelegate>;
|
|
46
|
-
type UserReturns = DelegateReturnTypes<Prisma.UserDelegate>;
|
|
47
|
-
\`\`\`
|
|
48
|
-
|
|
49
|
-
## How to Create a Repository
|
|
50
|
-
|
|
51
|
-
### Step 1: Create the repository class
|
|
52
|
-
|
|
53
|
-
\`\`\`typescript
|
|
54
|
-
// repositories/UserRepository.ts
|
|
55
|
-
import { Injectable } from '@nestjs/common';
|
|
56
|
-
import { Prisma } from '@prisma/client';
|
|
57
|
-
import {
|
|
58
|
-
DatabaseService,
|
|
59
|
-
DelegateArgs,
|
|
60
|
-
DelegateReturnTypes,
|
|
61
|
-
RepositoryBase,
|
|
62
|
-
} from '@breadstone/archipel-platform-database';
|
|
63
|
-
|
|
64
|
-
@Injectable()
|
|
65
|
-
export class UserRepository extends RepositoryBase<
|
|
66
|
-
Prisma.UserDelegate,
|
|
67
|
-
DelegateArgs<Prisma.UserDelegate>,
|
|
68
|
-
DelegateReturnTypes<Prisma.UserDelegate>,
|
|
69
|
-
IUserEntity
|
|
70
|
-
> {
|
|
71
|
-
|
|
72
|
-
// #region Ctor
|
|
73
|
-
|
|
74
|
-
public constructor(db: DatabaseService) {
|
|
75
|
-
super(db, db.client.user);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
// #endregion
|
|
79
|
-
|
|
80
|
-
}
|
|
81
|
-
\`\`\`
|
|
82
|
-
|
|
83
|
-
The constructor receives \`DatabaseService\` and passes both the service and the
|
|
84
|
-
Prisma delegate (\`db.client.user\`) to the base class.
|
|
85
|
-
|
|
86
|
-
### Step 2: Register as a provider in a module
|
|
87
|
-
|
|
88
|
-
\`\`\`typescript
|
|
89
|
-
@Module({
|
|
90
|
-
imports: [DatabaseModule.register()],
|
|
91
|
-
providers: [UserRepository, UserService],
|
|
92
|
-
exports: [UserService],
|
|
93
|
-
})
|
|
94
|
-
export class UserModule {}
|
|
95
|
-
\`\`\`
|
|
96
|
-
|
|
97
|
-
### Step 3: Inject into a service
|
|
98
|
-
|
|
99
|
-
\`\`\`typescript
|
|
100
|
-
@Injectable()
|
|
101
|
-
export class UserService {
|
|
102
|
-
|
|
103
|
-
// #region Fields
|
|
104
|
-
|
|
105
|
-
private readonly _userRepository: UserRepository;
|
|
106
|
-
|
|
107
|
-
// #endregion
|
|
108
|
-
|
|
109
|
-
// #region Ctor
|
|
110
|
-
|
|
111
|
-
public constructor(userRepository: UserRepository) {
|
|
112
|
-
this._userRepository = userRepository;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// #endregion
|
|
116
|
-
|
|
117
|
-
// #region Methods
|
|
118
|
-
|
|
119
|
-
public async findById(id: string): Promise<IUserEntity | null> {
|
|
120
|
-
return this._userRepository.execute(findUserByIdQuery(id));
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
public async findAll(skip = 0, take = 20): Promise<IUserEntity[]> {
|
|
124
|
-
return this._userRepository.execute(findAllUsersQuery(skip, take));
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
// #endregion
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
\`\`\`
|
|
131
|
-
|
|
132
|
-
## Key Methods on RepositoryBase
|
|
133
|
-
|
|
134
|
-
### execute(query)
|
|
135
|
-
|
|
136
|
-
Primary method for data access. Delegates to a query object.
|
|
137
|
-
|
|
138
|
-
\`\`\`typescript
|
|
139
|
-
public execute<TResult>(query: IRepositoryQuery<TDelegate, TResult>): Promise<TResult>
|
|
140
|
-
\`\`\`
|
|
141
|
-
|
|
142
|
-
### executeTransactional(query)
|
|
143
|
-
|
|
144
|
-
For multi-model operations within a transaction.
|
|
145
|
-
|
|
146
|
-
\`\`\`typescript
|
|
147
|
-
public executeTransactional<TResult>(query: ITransactionalRepositoryQuery<TResult>): Promise<TResult>
|
|
148
|
-
\`\`\`
|
|
149
|
-
|
|
150
|
-
### Direct Methods (for legacy or internal use only)
|
|
151
|
-
|
|
152
|
-
The base class also exposes \`findUnique\`, \`findMany\`, \`findFirst\`, \`create\`,
|
|
153
|
-
\`createMany\`, \`update\`, \`updateMany\`, \`upsert\`, \`delete\`, \`deleteMany\`,
|
|
154
|
-
\`aggregate\`, \`count\`, \`groupBy\`, \`merge\`.
|
|
155
|
-
|
|
156
|
-
**Important:** New code should use \`execute(query)\` exclusively.
|
|
157
|
-
Direct method calls are available but violate the query pattern convention.
|
|
158
|
-
|
|
159
|
-
### findMany with Pagination
|
|
160
|
-
|
|
161
|
-
\`\`\`typescript
|
|
162
|
-
// Direct method (legacy)
|
|
163
|
-
const results = await this._repository.findMany(args, { page: 1, perPage: 20 });
|
|
164
|
-
\`\`\`
|
|
165
|
-
|
|
166
|
-
## Error Handling
|
|
167
|
-
|
|
168
|
-
All Prisma errors are caught by the base class \`tryCatch\` wrapper and re-thrown
|
|
169
|
-
as \`RepositoryError\`:
|
|
170
|
-
|
|
171
|
-
- \`PrismaClientKnownRequestError\` → \`RepositoryError\` with code \`'R_KNOWN'\`
|
|
172
|
-
- \`PrismaClientUnknownRequestError\` → \`RepositoryError\` with code \`'R_UNKNOWN'\`
|
|
173
|
-
- Other errors are re-thrown unchanged
|
|
174
|
-
|
|
175
|
-
The global \`RepositoryExceptionFilter\` catches \`RepositoryError\` and returns
|
|
176
|
-
HTTP 500 with a structured envelope.
|
|
177
|
-
|
|
178
|
-
## Query Integration
|
|
179
|
-
|
|
180
|
-
Repositories work with query factories from the query pattern:
|
|
181
|
-
|
|
182
|
-
\`\`\`typescript
|
|
183
|
-
// queries/findUserByIdQuery.ts
|
|
184
|
-
import { Prisma } from '@prisma/client';
|
|
185
|
-
import { query } from '@breadstone/archipel-platform-database';
|
|
186
|
-
|
|
187
|
-
export function findUserByIdQuery(id: string) {
|
|
188
|
-
return query<Prisma.UserDelegate, IUserEntity | null>(
|
|
189
|
-
'findUserById',
|
|
190
|
-
(model) => model.findFirst({
|
|
191
|
-
where: { id },
|
|
192
|
-
select: { id: true, email: true, name: true, createdAt: true },
|
|
193
|
-
}),
|
|
194
|
-
);
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
// Service usage:
|
|
198
|
-
const user = await this._userRepository.execute(findUserByIdQuery(id));
|
|
199
|
-
\`\`\`
|
|
200
|
-
|
|
201
|
-
## Rules
|
|
202
|
-
|
|
203
|
-
1. **One repository per Prisma model** — e.g. \`UserRepository\`, \`OrderRepository\`.
|
|
204
|
-
2. **Constructor receives \`DatabaseService\`** and passes the delegate via \`db.client.<model>\`.
|
|
205
|
-
3. **Use \`DelegateArgs\` and \`DelegateReturnTypes\`** helper types for the RepositoryBase generics.
|
|
206
|
-
4. **New code uses \`execute(query)\`** — never direct calls like \`repository.findFirst()\` from services.
|
|
207
|
-
5. **Register as provider** in the feature module, import \`DatabaseModule.register()\`.
|
|
208
|
-
6. **Repositories are \`@Injectable()\`** — they participate in NestJS DI.
|
|
209
|
-
7. **Domain entity interface as fourth generic** (\`TEntity\`) is optional but recommended.
|
|
210
|
-
`;
|
|
211
|
-
const REPOSITORY_PATTERN_WITH_MODEL = (modelName, fields) => `
|
|
212
|
-
## Example: Repository for "${modelName}"
|
|
213
|
-
|
|
214
|
-
Given a Prisma model \`${modelName}\` with fields: ${fields.map((f) => `\`${f}\``).join(', ')}
|
|
215
|
-
|
|
216
|
-
### Repository Class
|
|
217
|
-
|
|
218
|
-
\`\`\`typescript
|
|
219
|
-
// repositories/${modelName}Repository.ts
|
|
220
|
-
import { Injectable } from '@nestjs/common';
|
|
221
|
-
import { Prisma } from '@prisma/client';
|
|
222
|
-
import {
|
|
223
|
-
DatabaseService,
|
|
224
|
-
DelegateArgs,
|
|
225
|
-
DelegateReturnTypes,
|
|
226
|
-
RepositoryBase,
|
|
227
|
-
} from '@breadstone/archipel-platform-database';
|
|
228
|
-
import { I${modelName}Entity } from '../models/entities/I${modelName}Entity';
|
|
229
|
-
|
|
230
|
-
@Injectable()
|
|
231
|
-
export class ${modelName}Repository extends RepositoryBase<
|
|
232
|
-
Prisma.${modelName}Delegate,
|
|
233
|
-
DelegateArgs<Prisma.${modelName}Delegate>,
|
|
234
|
-
DelegateReturnTypes<Prisma.${modelName}Delegate>,
|
|
235
|
-
I${modelName}Entity
|
|
236
|
-
> {
|
|
237
|
-
|
|
238
|
-
// #region Ctor
|
|
239
|
-
|
|
240
|
-
public constructor(db: DatabaseService) {
|
|
241
|
-
super(db, db.client.${lowerFirst(modelName)});
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// #endregion
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
\`\`\`
|
|
248
|
-
|
|
249
|
-
### Entity Interface
|
|
250
|
-
|
|
251
|
-
\`\`\`typescript
|
|
252
|
-
// models/entities/I${modelName}Entity.ts
|
|
253
|
-
export interface I${modelName}Entity {
|
|
254
|
-
${fields.map((f) => ` readonly ${f}: ${inferFieldType(f)};`).join('\n')}
|
|
255
|
-
}
|
|
256
|
-
\`\`\`
|
|
257
|
-
|
|
258
|
-
### Query Factories
|
|
259
|
-
|
|
260
|
-
\`\`\`typescript
|
|
261
|
-
// queries/find${modelName}ByIdQuery.ts
|
|
262
|
-
import { Prisma } from '@prisma/client';
|
|
263
|
-
import { query } from '@breadstone/archipel-platform-database';
|
|
264
|
-
import { I${modelName}Entity } from '../models/entities/I${modelName}Entity';
|
|
265
|
-
|
|
266
|
-
export function find${modelName}ByIdQuery(id: string) {
|
|
267
|
-
return query<Prisma.${modelName}Delegate, I${modelName}Entity | null>(
|
|
268
|
-
'find${modelName}ById',
|
|
269
|
-
(model) => model.findFirst({
|
|
270
|
-
where: { id },
|
|
271
|
-
select: { ${fields.map((f) => `${f}: true`).join(', ')} },
|
|
272
|
-
}),
|
|
273
|
-
);
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
export function findAll${modelName}sQuery(skip = 0, take = 20) {
|
|
277
|
-
return query<Prisma.${modelName}Delegate, I${modelName}Entity[]>(
|
|
278
|
-
'findAll${modelName}s',
|
|
279
|
-
(model) => model.findMany({
|
|
280
|
-
skip,
|
|
281
|
-
take,
|
|
282
|
-
select: { ${fields.map((f) => `${f}: true`).join(', ')} },
|
|
283
|
-
orderBy: { ${fields.includes('createdAt') ? 'createdAt' : fields[0]}: 'desc' },
|
|
284
|
-
}),
|
|
285
|
-
);
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
export function count${modelName}sQuery() {
|
|
289
|
-
return query<Prisma.${modelName}Delegate, number>(
|
|
290
|
-
'count${modelName}s',
|
|
291
|
-
(model) => model.count(),
|
|
292
|
-
);
|
|
293
|
-
}
|
|
294
|
-
\`\`\`
|
|
295
|
-
|
|
296
|
-
### Module Registration
|
|
297
|
-
|
|
298
|
-
\`\`\`typescript
|
|
299
|
-
import { DatabaseModule } from '@breadstone/archipel-platform-database';
|
|
300
|
-
import { ${modelName}Repository } from './repositories/${modelName}Repository';
|
|
301
|
-
import { ${modelName}Service } from './services/${modelName}Service';
|
|
302
|
-
|
|
303
|
-
@Module({
|
|
304
|
-
imports: [DatabaseModule.register()],
|
|
305
|
-
providers: [${modelName}Repository, ${modelName}Service],
|
|
306
|
-
exports: [${modelName}Service],
|
|
307
|
-
})
|
|
308
|
-
export class ${modelName}Module {}
|
|
309
|
-
\`\`\`
|
|
310
|
-
|
|
311
|
-
### Service Usage
|
|
312
|
-
|
|
313
|
-
\`\`\`typescript
|
|
314
|
-
@Injectable()
|
|
315
|
-
export class ${modelName}Service {
|
|
316
|
-
|
|
317
|
-
// #region Fields
|
|
318
|
-
|
|
319
|
-
private readonly _${lowerFirst(modelName)}Repository: ${modelName}Repository;
|
|
320
|
-
|
|
321
|
-
// #endregion
|
|
322
|
-
|
|
323
|
-
// #region Ctor
|
|
324
|
-
|
|
325
|
-
public constructor(${lowerFirst(modelName)}Repository: ${modelName}Repository) {
|
|
326
|
-
this._${lowerFirst(modelName)}Repository = ${lowerFirst(modelName)}Repository;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// #endregion
|
|
330
|
-
|
|
331
|
-
// #region Methods
|
|
332
|
-
|
|
333
|
-
public async findById(id: string): Promise<I${modelName}Entity | null> {
|
|
334
|
-
return this._${lowerFirst(modelName)}Repository.execute(find${modelName}ByIdQuery(id));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
public async findAll(skip = 0, take = 20): Promise<I${modelName}Entity[]> {
|
|
338
|
-
return this._${lowerFirst(modelName)}Repository.execute(findAll${modelName}sQuery(skip, take));
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
public async count(): Promise<number> {
|
|
342
|
-
return this._${lowerFirst(modelName)}Repository.execute(count${modelName}sQuery());
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// #endregion
|
|
346
|
-
|
|
347
|
-
}
|
|
348
|
-
\`\`\`
|
|
349
|
-
`;
|
|
350
|
-
exports.REPOSITORY_PATTERN_WITH_MODEL = REPOSITORY_PATTERN_WITH_MODEL;
|
|
351
|
-
function inferFieldType(fieldName) {
|
|
352
|
-
if (fieldName === 'id')
|
|
353
|
-
return 'string';
|
|
354
|
-
if (fieldName.endsWith('Id'))
|
|
355
|
-
return 'string';
|
|
356
|
-
if (fieldName.endsWith('At') || fieldName === 'createdAt' || fieldName === 'updatedAt')
|
|
357
|
-
return 'Date';
|
|
358
|
-
if (fieldName.startsWith('is') || fieldName.startsWith('has'))
|
|
359
|
-
return 'boolean';
|
|
360
|
-
if (fieldName === 'count' || fieldName === 'quantity' || fieldName === 'amount' || fieldName === 'price')
|
|
361
|
-
return 'number';
|
|
362
|
-
return 'string';
|
|
363
|
-
}
|
|
364
|
-
function lowerFirst(str) {
|
|
365
|
-
return str.charAt(0).toLowerCase() + str.slice(1);
|
|
366
|
-
}
|
|
367
|
-
//# sourceMappingURL=repositoryPattern.js.map
|
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Static knowledge content for the Archipel testing pattern.
|
|
3
|
-
* Returned by the get-testing-pattern tool.
|
|
4
|
-
*/
|
|
5
|
-
export declare const TESTING_PATTERN_KNOWLEDGE = "# Archipel Testing Pattern\n\n## Overview\n\nArchipel uses **Vitest** for unit and integration tests with **vi mocking**. Tests follow\nthe Arrange-Act-Assert (AAA) pattern with descriptive `describe`/`it` blocks. Each test\nfile covers a single class or module.\n\n## Test Framework\n\n- **Runner:** Vitest\n- **Mocking:** `vi.fn()`, `vi.mock()`, `vi.spyOn()`\n- **Assertions:** Vitest built-in (`expect`)\n- **E2E:** Supertest against a bootstrapped NestJS app\n\n## File Naming & Location\n\n| Test Type | File Pattern | Location |\n|-----------|-------------|----------|\n| Unit test | `*.spec.ts` | Next to the source file |\n| E2E test | `*.e2e-spec.ts` | `test/` directory |\n\n## Basic Test Structure\n\n```typescript\nimport { beforeEach, describe, expect, it, vi } from 'vitest';\n\ndescribe('UserService', () => {\n\n // #region Fields\n\n let service: UserService;\n let mockRepository: { execute: ReturnType<typeof vi.fn> };\n let mockMappingService: { map: ReturnType<typeof vi.fn> };\n\n // #endregion\n\n // #region Methods\n\n beforeEach(() => {\n vi.restoreAllMocks();\n\n mockRepository = {\n execute: vi.fn(),\n };\n\n mockMappingService = {\n map: vi.fn(),\n };\n\n service = new UserService(\n mockRepository as unknown as UserRepository,\n mockMappingService as unknown as MappingService,\n );\n });\n\n describe('findById', () => {\n it('should return entity when user exists', async () => {\n // arrange\n const mockUser = { id: '1', email: 'test@example.com', name: 'Test' };\n mockRepository.execute.mockResolvedValue(mockUser);\n\n // act\n const result = await service.findById('1');\n\n // assert\n expect(result).toEqual(mockUser);\n expect(mockRepository.execute).toHaveBeenCalledOnce();\n });\n\n it('should throw UserNotFoundError when user does not exist', async () => {\n // arrange\n mockRepository.execute.mockResolvedValue(null);\n\n // act & assert\n await expect(() => service.findById('nonexistent'))\n .rejects.toThrow(UserNotFoundError);\n });\n });\n\n // #endregion\n\n});\n```\n\n## Mocking Patterns\n\n### Mocking Prisma Delegates (for Repository Tests)\n\n```typescript\nvi.mock('@prisma/client/runtime/client', () => ({\n PrismaClientKnownRequestError: class PrismaClientKnownRequestError extends Error {\n public code: string;\n public meta?: Record<string, unknown>;\n public clientVersion: string;\n public constructor(message: string, opts: { code: string; clientVersion: string; meta?: Record<string, unknown> }) {\n super(message);\n this.name = 'PrismaClientKnownRequestError';\n this.code = opts.code;\n this.clientVersion = opts.clientVersion;\n this.meta = opts.meta;\n }\n },\n PrismaClientUnknownRequestError: class PrismaClientUnknownRequestError extends Error {\n public clientVersion: string;\n public constructor(message: string, opts: { clientVersion: string }) {\n super(message);\n this.name = 'PrismaClientUnknownRequestError';\n this.clientVersion = opts.clientVersion;\n }\n },\n}));\n```\n\n### Creating a Mock Delegate\n\n```typescript\ntype MockDelegate = Record<\n | 'aggregate' | 'count' | 'create' | 'createMany' | 'createManyAndReturn'\n | 'delete' | 'deleteMany' | 'findFirst' | 'findFirstOrThrow'\n | 'findMany' | 'findUnique' | 'findUniqueOrThrow'\n | 'groupBy' | 'update' | 'updateMany' | 'upsert',\n ReturnType<typeof vi.fn>\n>;\n\nfunction createMockDelegate(): MockDelegate {\n return {\n findUnique: vi.fn(),\n findFirst: vi.fn(),\n findMany: vi.fn(),\n create: vi.fn(),\n createMany: vi.fn(),\n createManyAndReturn: vi.fn(),\n update: vi.fn(),\n updateMany: vi.fn(),\n upsert: vi.fn(),\n delete: vi.fn(),\n deleteMany: vi.fn(),\n aggregate: vi.fn(),\n count: vi.fn(),\n groupBy: vi.fn(),\n findFirstOrThrow: vi.fn(),\n findUniqueOrThrow: vi.fn(),\n };\n}\n```\n\n### Mocking DatabaseService\n\n```typescript\nconst mockDb = {\n transactionCallback: vi.fn(),\n client: {\n user: createMockDelegate(),\n },\n} as unknown as DatabaseService;\n```\n\n### Mocking a Query\n\n```typescript\nconst mockQuery = {\n name: 'findUserById',\n run: vi.fn().mockResolvedValue({ id: '1', email: 'test@example.com' }),\n};\n```\n\n## Repository Test Pattern\n\n```typescript\nimport { beforeEach, describe, expect, it, vi } from 'vitest';\n\n// Mock Prisma runtime errors BEFORE importing test subject\nvi.mock('@prisma/client/runtime/client', () => ({ /* ... */ }));\nimport { PrismaClientKnownRequestError } from '@prisma/client/runtime/client';\n\ndescribe('UserRepository', () => {\n\n let repository: TestRepository;\n let mockModel: MockDelegate;\n let mockDb: DatabaseService;\n\n beforeEach(() => {\n vi.restoreAllMocks();\n mockModel = createMockDelegate();\n mockDb = { transactionCallback: vi.fn() } as unknown as DatabaseService;\n repository = new TestRepository(mockDb, mockModel);\n });\n\n describe('execute', () => {\n it('should execute a query against the model', async () => {\n // arrange\n const mockQuery = {\n name: 'testQuery',\n run: vi.fn().mockResolvedValue({ id: '1' }),\n };\n\n // act\n const result = await repository.execute(mockQuery);\n\n // assert\n expect(mockQuery.run).toHaveBeenCalledWith(mockModel);\n expect(result).toEqual({ id: '1' });\n });\n\n it('should wrap PrismaClientKnownRequestError as RepositoryError', async () => {\n // arrange\n const prismaError = new PrismaClientKnownRequestError('Not found', {\n code: 'P2025',\n clientVersion: '6.0.0',\n });\n const mockQuery = {\n name: 'failingQuery',\n run: vi.fn().mockRejectedValue(prismaError),\n };\n\n // act & assert\n await expect(() => repository.execute(mockQuery))\n .rejects.toThrow(RepositoryError);\n });\n });\n\n});\n```\n\n## Service Test Pattern\n\n```typescript\ndescribe('ProductService', () => {\n\n let service: ProductService;\n let mockRepo: { execute: ReturnType<typeof vi.fn> };\n\n beforeEach(() => {\n vi.restoreAllMocks();\n mockRepo = { execute: vi.fn() };\n service = new ProductService(mockRepo as unknown as ProductRepository);\n });\n\n describe('findById', () => {\n it('should return entity when found', async () => {\n // arrange\n const expected = { id: '1', name: 'Product A', barcode: '123' };\n mockRepo.execute.mockResolvedValue(expected);\n\n // act\n const result = await service.findById('1');\n\n // assert\n expect(result).toEqual(expected);\n });\n\n it('should throw domain error when not found', async () => {\n // arrange\n mockRepo.execute.mockResolvedValue(null);\n\n // act & assert\n await expect(() => service.findById('unknown'))\n .rejects.toThrow(ProductNotFoundError);\n });\n });\n\n});\n```\n\n## Mapping Profile Test Pattern\n\n```typescript\ndescribe('ProductMappingProfile', () => {\n\n let mappingService: MappingService;\n\n beforeEach(() => {\n mappingService = new MappingService();\n const profile = new ProductMappingProfile();\n mappingService.registerProfile(profile);\n });\n\n it('should map input to response correctly', () => {\n // arrange\n const input: IProductMappingInput = {\n id: '1',\n barcode: '123',\n name: 'Product A',\n categoryId: 'cat-1',\n createdAt: new Date('2026-01-01'),\n };\n\n // act\n const result = mappingService.map(PRODUCT_MAPPING_KEY, input);\n\n // assert\n expect(result).toBeInstanceOf(ProductResponse);\n expect(result.id).toBe('1');\n expect(result.barcode).toBe('123');\n expect(result.name).toBe('Product A');\n });\n\n});\n```\n\n## Test Conventions\n\n### Comments\n\n```typescript\n// Use AAA comments:\n// arrange \u2014 set up test data and mocks\n// act \u2014 call the method under test\n// act & assert \u2014 when the assertion is on the thrown error\n// assert \u2014 verify the outcome\n```\n\n### Regions\n\n```typescript\ndescribe('MyClass', () => {\n\n // #region Fields\n let service: MyService;\n // #endregion\n\n // #region Methods\n beforeEach(() => { ... });\n\n describe('methodName', () => { ... });\n // #endregion\n\n});\n```\n\n### Naming\n\n- `describe` blocks: Class name or method name\n- `it` blocks: `'should <expected behavior> when <condition>'`\n\n## Rules\n\n1. **Every public service method needs at least one happy-path and one failure-path test.**\n2. **Use `vi.restoreAllMocks()` in `beforeEach`** \u2014 prevent mock leakage between tests.\n3. **Mock at the boundary** \u2014 mock repositories in service tests, mock delegates in repository tests.\n4. **AAA pattern** \u2014 clearly separate arrange, act, assert with comments.\n5. **`vi.mock()` for modules must appear before imports** of the mocked module.\n6. **No real database in unit tests** \u2014 use mocks. Use Testcontainers for integration tests.\n7. **Assert on behavior, not implementation** \u2014 verify return values and thrown errors, not internal calls\n (unless the call IS the behavior, e.g., verifying a repository was called).\n8. **Use `mockResolvedValue` / `mockRejectedValue`** for async mocks.\n";
|