nestjs-ddd-cli 2.2.0 → 3.2.1

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.
Files changed (254) hide show
  1. package/README.md +247 -408
  2. package/ddd.schema.json +111 -0
  3. package/dist/commands/aggregate-validator.d.ts +9 -0
  4. package/dist/commands/aggregate-validator.js +953 -0
  5. package/dist/commands/aggregate-validator.js.map +1 -0
  6. package/dist/commands/ai-assist.d.ts +8 -0
  7. package/dist/commands/ai-assist.js +337 -0
  8. package/dist/commands/ai-assist.js.map +1 -0
  9. package/dist/commands/api-contracts.d.ts +9 -0
  10. package/dist/commands/api-contracts.js +1368 -0
  11. package/dist/commands/api-contracts.js.map +1 -0
  12. package/dist/commands/api-docs.d.ts +8 -0
  13. package/dist/commands/api-docs.js +408 -0
  14. package/dist/commands/api-docs.js.map +1 -0
  15. package/dist/commands/api-versioning.d.ts +11 -0
  16. package/dist/commands/api-versioning.js +643 -0
  17. package/dist/commands/api-versioning.js.map +1 -0
  18. package/dist/commands/audit-logging.d.ts +9 -0
  19. package/dist/commands/audit-logging.js +1129 -0
  20. package/dist/commands/audit-logging.js.map +1 -0
  21. package/dist/commands/batch-generate.d.ts +10 -0
  22. package/dist/commands/batch-generate.js +405 -0
  23. package/dist/commands/batch-generate.js.map +1 -0
  24. package/dist/commands/caching-strategies.d.ts +9 -0
  25. package/dist/commands/caching-strategies.js +874 -0
  26. package/dist/commands/caching-strategies.js.map +1 -0
  27. package/dist/commands/code-analyzer.d.ts +42 -0
  28. package/dist/commands/code-analyzer.js +474 -0
  29. package/dist/commands/code-analyzer.js.map +1 -0
  30. package/dist/commands/database-seeding.d.ts +6 -0
  31. package/dist/commands/database-seeding.js +621 -0
  32. package/dist/commands/database-seeding.js.map +1 -0
  33. package/dist/commands/db-optimization.d.ts +7 -0
  34. package/dist/commands/db-optimization.js +687 -0
  35. package/dist/commands/db-optimization.js.map +1 -0
  36. package/dist/commands/dependency-graph.d.ts +6 -0
  37. package/dist/commands/dependency-graph.js +329 -0
  38. package/dist/commands/dependency-graph.js.map +1 -0
  39. package/dist/commands/doctor-enhanced.d.ts +22 -0
  40. package/dist/commands/doctor-enhanced.js +543 -0
  41. package/dist/commands/doctor-enhanced.js.map +1 -0
  42. package/dist/commands/doctor.d.ts +4 -0
  43. package/dist/commands/doctor.js +151 -0
  44. package/dist/commands/doctor.js.map +1 -0
  45. package/dist/commands/env-manager.d.ts +6 -0
  46. package/dist/commands/env-manager.js +419 -0
  47. package/dist/commands/env-manager.js.map +1 -0
  48. package/dist/commands/event-sourcing-full.d.ts +10 -0
  49. package/dist/commands/event-sourcing-full.js +1107 -0
  50. package/dist/commands/event-sourcing-full.js.map +1 -0
  51. package/dist/commands/feature-flags.d.ts +9 -0
  52. package/dist/commands/feature-flags.js +824 -0
  53. package/dist/commands/feature-flags.js.map +1 -0
  54. package/dist/commands/filter-dsl.d.ts +10 -0
  55. package/dist/commands/filter-dsl.js +1407 -0
  56. package/dist/commands/filter-dsl.js.map +1 -0
  57. package/dist/commands/generate-all.js +485 -32
  58. package/dist/commands/generate-all.js.map +1 -1
  59. package/dist/commands/generate-deployment.d.ts +8 -0
  60. package/dist/commands/generate-deployment.js +746 -0
  61. package/dist/commands/generate-deployment.js.map +1 -0
  62. package/dist/commands/generate-domain-service.d.ts +14 -0
  63. package/dist/commands/generate-domain-service.js +796 -0
  64. package/dist/commands/generate-domain-service.js.map +1 -0
  65. package/dist/commands/generate-entity.js +82 -24
  66. package/dist/commands/generate-entity.js.map +1 -1
  67. package/dist/commands/generate-from-schema.d.ts +56 -0
  68. package/dist/commands/generate-from-schema.js +222 -0
  69. package/dist/commands/generate-from-schema.js.map +1 -0
  70. package/dist/commands/generate-orchestrator.d.ts +14 -0
  71. package/dist/commands/generate-orchestrator.js +887 -0
  72. package/dist/commands/generate-orchestrator.js.map +1 -0
  73. package/dist/commands/generate-repository.d.ts +14 -0
  74. package/dist/commands/generate-repository.js +1019 -0
  75. package/dist/commands/generate-repository.js.map +1 -0
  76. package/dist/commands/generate-shared.d.ts +4 -0
  77. package/dist/commands/generate-shared.js +388 -0
  78. package/dist/commands/generate-shared.js.map +1 -0
  79. package/dist/commands/generate-value-object.d.ts +32 -0
  80. package/dist/commands/generate-value-object.js +700 -0
  81. package/dist/commands/generate-value-object.js.map +1 -0
  82. package/dist/commands/graphql-subscriptions.d.ts +6 -0
  83. package/dist/commands/graphql-subscriptions.js +607 -0
  84. package/dist/commands/graphql-subscriptions.js.map +1 -0
  85. package/dist/commands/graphql-types.d.ts +5 -0
  86. package/dist/commands/graphql-types.js +423 -0
  87. package/dist/commands/graphql-types.js.map +1 -0
  88. package/dist/commands/health-probes-advanced.d.ts +6 -0
  89. package/dist/commands/health-probes-advanced.js +655 -0
  90. package/dist/commands/health-probes-advanced.js.map +1 -0
  91. package/dist/commands/i18n-setup.d.ts +10 -0
  92. package/dist/commands/i18n-setup.js +677 -0
  93. package/dist/commands/i18n-setup.js.map +1 -0
  94. package/dist/commands/init-config.d.ts +6 -0
  95. package/dist/commands/init-config.js +370 -0
  96. package/dist/commands/init-config.js.map +1 -0
  97. package/dist/commands/init-project.js +56 -6
  98. package/dist/commands/init-project.js.map +1 -1
  99. package/dist/commands/interactive-scaffold.d.ts +5 -0
  100. package/dist/commands/interactive-scaffold.js +271 -0
  101. package/dist/commands/interactive-scaffold.js.map +1 -0
  102. package/dist/commands/metrics-prometheus.d.ts +6 -0
  103. package/dist/commands/metrics-prometheus.js +681 -0
  104. package/dist/commands/metrics-prometheus.js.map +1 -0
  105. package/dist/commands/migration-engine.d.ts +6 -0
  106. package/dist/commands/migration-engine.js +446 -0
  107. package/dist/commands/migration-engine.js.map +1 -0
  108. package/dist/commands/migration.d.ts +12 -0
  109. package/dist/commands/migration.js +484 -0
  110. package/dist/commands/migration.js.map +1 -0
  111. package/dist/commands/monorepo.d.ts +8 -0
  112. package/dist/commands/monorepo.js +483 -0
  113. package/dist/commands/monorepo.js.map +1 -0
  114. package/dist/commands/multi-database.d.ts +5 -0
  115. package/dist/commands/multi-database.js +439 -0
  116. package/dist/commands/multi-database.js.map +1 -0
  117. package/dist/commands/observability-tracing.d.ts +10 -0
  118. package/dist/commands/observability-tracing.js +740 -0
  119. package/dist/commands/observability-tracing.js.map +1 -0
  120. package/dist/commands/openapi-export.d.ts +8 -0
  121. package/dist/commands/openapi-export.js +359 -0
  122. package/dist/commands/openapi-export.js.map +1 -0
  123. package/dist/commands/perf-analyzer.d.ts +8 -0
  124. package/dist/commands/perf-analyzer.js +423 -0
  125. package/dist/commands/perf-analyzer.js.map +1 -0
  126. package/dist/commands/rate-limiting.d.ts +10 -0
  127. package/dist/commands/rate-limiting.js +953 -0
  128. package/dist/commands/rate-limiting.js.map +1 -0
  129. package/dist/commands/recipe-plugin.d.ts +56 -0
  130. package/dist/commands/recipe-plugin.js +315 -0
  131. package/dist/commands/recipe-plugin.js.map +1 -0
  132. package/dist/commands/recipe.d.ts +6 -0
  133. package/dist/commands/recipe.js +3941 -0
  134. package/dist/commands/recipe.js.map +1 -0
  135. package/dist/commands/recipes/elasticsearch.recipe.d.ts +1 -0
  136. package/dist/commands/recipes/elasticsearch.recipe.js +761 -0
  137. package/dist/commands/recipes/elasticsearch.recipe.js.map +1 -0
  138. package/dist/commands/recipes/event-sourcing.recipe.d.ts +1 -0
  139. package/dist/commands/recipes/event-sourcing.recipe.js +889 -0
  140. package/dist/commands/recipes/event-sourcing.recipe.js.map +1 -0
  141. package/dist/commands/recipes/index.d.ts +7 -0
  142. package/dist/commands/recipes/index.js +24 -0
  143. package/dist/commands/recipes/index.js.map +1 -0
  144. package/dist/commands/recipes/message-queue.recipe.d.ts +1 -0
  145. package/dist/commands/recipes/message-queue.recipe.js +706 -0
  146. package/dist/commands/recipes/message-queue.recipe.js.map +1 -0
  147. package/dist/commands/recipes/middleware.recipe.d.ts +1 -0
  148. package/dist/commands/recipes/middleware.recipe.js +383 -0
  149. package/dist/commands/recipes/middleware.recipe.js.map +1 -0
  150. package/dist/commands/recipes/multi-tenancy.recipe.d.ts +1 -0
  151. package/dist/commands/recipes/multi-tenancy.recipe.js +520 -0
  152. package/dist/commands/recipes/multi-tenancy.recipe.js.map +1 -0
  153. package/dist/commands/recipes/oauth2.recipe.d.ts +1 -0
  154. package/dist/commands/recipes/oauth2.recipe.js +472 -0
  155. package/dist/commands/recipes/oauth2.recipe.js.map +1 -0
  156. package/dist/commands/recipes/websocket.recipe.d.ts +1 -0
  157. package/dist/commands/recipes/websocket.recipe.js +453 -0
  158. package/dist/commands/recipes/websocket.recipe.js.map +1 -0
  159. package/dist/commands/resilience-patterns.d.ts +13 -0
  160. package/dist/commands/resilience-patterns.js +1029 -0
  161. package/dist/commands/resilience-patterns.js.map +1 -0
  162. package/dist/commands/security-patterns.d.ts +11 -0
  163. package/dist/commands/security-patterns.js +2233 -0
  164. package/dist/commands/security-patterns.js.map +1 -0
  165. package/dist/commands/template-debug.d.ts +27 -0
  166. package/dist/commands/template-debug.js +388 -0
  167. package/dist/commands/template-debug.js.map +1 -0
  168. package/dist/commands/test-factory-full.d.ts +9 -0
  169. package/dist/commands/test-factory-full.js +1570 -0
  170. package/dist/commands/test-factory-full.js.map +1 -0
  171. package/dist/commands/test-scaffold.d.ts +7 -0
  172. package/dist/commands/test-scaffold.js +621 -0
  173. package/dist/commands/test-scaffold.js.map +1 -0
  174. package/dist/index.js +1088 -0
  175. package/dist/index.js.map +1 -1
  176. package/dist/templates/ai-context/CLAUDE.md.hbs +158 -0
  177. package/dist/templates/ai-context/conventions.md.hbs +154 -0
  178. package/dist/templates/command/create-command.hbs +6 -14
  179. package/dist/templates/command/delete-command.hbs +19 -0
  180. package/dist/templates/command/update-command.hbs +24 -0
  181. package/dist/templates/controller/controller.hbs +64 -17
  182. package/dist/templates/dto/create-dto.hbs +29 -5
  183. package/dist/templates/dto/filter-dto.hbs +52 -0
  184. package/dist/templates/dto/filter-query.dto.hbs +148 -0
  185. package/dist/templates/dto/paginated-response.dto.hbs +29 -0
  186. package/dist/templates/dto/pagination-query.dto.hbs +30 -0
  187. package/dist/templates/dto/response-dto.hbs +38 -0
  188. package/dist/templates/dto/update-dto.hbs +11 -0
  189. package/dist/templates/entity/entity.hbs +32 -1
  190. package/dist/templates/event/domain-event.hbs +33 -7
  191. package/dist/templates/event/event-handler.hbs +40 -0
  192. package/dist/templates/exception/base-exceptions.hbs +69 -0
  193. package/dist/templates/exception/entity-not-found.exception.hbs +7 -0
  194. package/dist/templates/mapper/mapper.hbs +49 -24
  195. package/dist/templates/module/module.hbs +34 -10
  196. package/dist/templates/orm-entity/orm-entity.hbs +63 -12
  197. package/dist/templates/prisma/prisma-mapper.hbs +71 -0
  198. package/dist/templates/prisma/prisma-repository.hbs +114 -0
  199. package/dist/templates/prisma/prisma-schema.hbs +20 -0
  200. package/dist/templates/prisma/prisma-service.hbs +51 -0
  201. package/dist/templates/query/get-all.query.hbs +50 -0
  202. package/dist/templates/query/get-by-id.query.hbs +31 -0
  203. package/dist/templates/repository/repository.hbs +55 -13
  204. package/dist/templates/resolver/graphql-input.hbs +54 -0
  205. package/dist/templates/resolver/graphql-type.hbs +58 -0
  206. package/dist/templates/resolver/pagination-args.hbs +33 -0
  207. package/dist/templates/resolver/resolver.hbs +62 -0
  208. package/dist/templates/shared/prisma-query-builder.util.hbs +189 -0
  209. package/dist/templates/shared/query-builder.util.hbs +218 -0
  210. package/dist/templates/test/controller.spec.hbs +124 -0
  211. package/dist/templates/test/repository.spec.hbs +158 -0
  212. package/dist/templates/test/usecase.spec.hbs +116 -0
  213. package/dist/templates/usecase/create-usecase.hbs +19 -7
  214. package/dist/templates/usecase/delete-usecase.hbs +17 -0
  215. package/dist/templates/usecase/update-usecase.hbs +31 -0
  216. package/dist/utils/config.utils.d.ts +45 -0
  217. package/dist/utils/config.utils.js +211 -0
  218. package/dist/utils/config.utils.js.map +1 -0
  219. package/dist/utils/error.utils.d.ts +145 -0
  220. package/dist/utils/error.utils.js +422 -0
  221. package/dist/utils/error.utils.js.map +1 -0
  222. package/dist/utils/field.utils.d.ts +54 -0
  223. package/dist/utils/field.utils.js +389 -0
  224. package/dist/utils/field.utils.js.map +1 -0
  225. package/dist/utils/file.utils.d.ts +19 -8
  226. package/dist/utils/file.utils.js +135 -4
  227. package/dist/utils/file.utils.js.map +1 -1
  228. package/dist/utils/idempotency.utils.d.ts +123 -0
  229. package/dist/utils/idempotency.utils.js +444 -0
  230. package/dist/utils/idempotency.utils.js.map +1 -0
  231. package/dist/utils/naming.utils.js +26 -3
  232. package/dist/utils/naming.utils.js.map +1 -1
  233. package/dist/utils/performance.utils.d.ts +37 -0
  234. package/dist/utils/performance.utils.js +158 -0
  235. package/dist/utils/performance.utils.js.map +1 -0
  236. package/dist/utils/relation.utils.d.ts +92 -0
  237. package/dist/utils/relation.utils.js +388 -0
  238. package/dist/utils/relation.utils.js.map +1 -0
  239. package/dist/utils/rollback.utils.d.ts +49 -0
  240. package/dist/utils/rollback.utils.js +306 -0
  241. package/dist/utils/rollback.utils.js.map +1 -0
  242. package/dist/utils/schema.utils.d.ts +123 -0
  243. package/dist/utils/schema.utils.js +419 -0
  244. package/dist/utils/schema.utils.js.map +1 -0
  245. package/dist/utils/security.utils.d.ts +57 -0
  246. package/dist/utils/security.utils.js +315 -0
  247. package/dist/utils/security.utils.js.map +1 -0
  248. package/dist/utils/template-engine.utils.d.ts +80 -0
  249. package/dist/utils/template-engine.utils.js +463 -0
  250. package/dist/utils/template-engine.utils.js.map +1 -0
  251. package/dist/utils/validation-registry.utils.d.ts +160 -0
  252. package/dist/utils/validation-registry.utils.js +526 -0
  253. package/dist/utils/validation-registry.utils.js.map +1 -0
  254. package/package.json +3 -1
@@ -0,0 +1,1019 @@
1
+ "use strict";
2
+ /**
3
+ * Abstract Repository Pattern Generator
4
+ * Generates type-safe repository interfaces and implementations
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.generateRepository = generateRepository;
44
+ exports.setupRepositoryInfrastructure = setupRepositoryInfrastructure;
45
+ const fs = __importStar(require("fs"));
46
+ const path = __importStar(require("path"));
47
+ const chalk_1 = __importDefault(require("chalk"));
48
+ async function generateRepository(entityName, basePath, options = {}) {
49
+ console.log(chalk_1.default.bold.blue('\n📦 Generating Repository Pattern\n'));
50
+ const moduleName = options.module || 'shared';
51
+ const orm = options.orm || 'typeorm';
52
+ const repoPath = path.join(basePath, 'src', moduleName, 'infrastructure', 'repositories');
53
+ if (!fs.existsSync(repoPath)) {
54
+ fs.mkdirSync(repoPath, { recursive: true });
55
+ }
56
+ // Generate repository interface
57
+ const interfaceContent = generateRepositoryInterface(entityName);
58
+ const interfaceFile = path.join(repoPath, `${toKebabCase(entityName)}.repository.interface.ts`);
59
+ fs.writeFileSync(interfaceFile, interfaceContent);
60
+ console.log(chalk_1.default.green(` ✓ Created ${interfaceFile}`));
61
+ // Generate implementation
62
+ const implContent = orm === 'prisma'
63
+ ? generatePrismaRepository(entityName)
64
+ : generateTypeORMRepository(entityName);
65
+ const implFile = path.join(repoPath, `${toKebabCase(entityName)}.repository.ts`);
66
+ fs.writeFileSync(implFile, implContent);
67
+ console.log(chalk_1.default.green(` ✓ Created ${implFile}`));
68
+ console.log(chalk_1.default.bold.green('\n✅ Repository generated successfully!\n'));
69
+ }
70
+ function generateRepositoryInterface(entityName) {
71
+ const className = toPascalCase(entityName);
72
+ return `import { ${className} } from '@domain/${toKebabCase(entityName)}.entity';
73
+ import { Specification } from '@shared/specifications/specification';
74
+
75
+ /**
76
+ * ${className} Repository Interface
77
+ * Defines the contract for ${className} persistence operations
78
+ */
79
+ export interface I${className}Repository {
80
+ /**
81
+ * Find entity by ID
82
+ */
83
+ findById(id: string): Promise<${className} | null>;
84
+
85
+ /**
86
+ * Find all entities
87
+ */
88
+ findAll(): Promise<${className}[]>;
89
+
90
+ /**
91
+ * Find entities matching specification
92
+ */
93
+ findBySpec(spec: Specification<${className}>): Promise<${className}[]>;
94
+
95
+ /**
96
+ * Find one entity matching specification
97
+ */
98
+ findOneBySpec(spec: Specification<${className}>): Promise<${className} | null>;
99
+
100
+ /**
101
+ * Count entities matching specification
102
+ */
103
+ countBySpec(spec: Specification<${className}>): Promise<number>;
104
+
105
+ /**
106
+ * Check if any entity matches specification
107
+ */
108
+ exists(spec: Specification<${className}>): Promise<boolean>;
109
+
110
+ /**
111
+ * Save entity (create or update)
112
+ */
113
+ save(entity: ${className}): Promise<${className}>;
114
+
115
+ /**
116
+ * Save multiple entities
117
+ */
118
+ saveMany(entities: ${className}[]): Promise<${className}[]>;
119
+
120
+ /**
121
+ * Delete entity by ID
122
+ */
123
+ delete(id: string): Promise<void>;
124
+
125
+ /**
126
+ * Delete entities matching specification
127
+ */
128
+ deleteBySpec(spec: Specification<${className}>): Promise<number>;
129
+
130
+ /**
131
+ * Perform operation in transaction
132
+ */
133
+ transaction<T>(operation: (repo: I${className}Repository) => Promise<T>): Promise<T>;
134
+ }
135
+
136
+ /**
137
+ * Repository query options
138
+ */
139
+ export interface QueryOptions<T> {
140
+ where?: Partial<T>;
141
+ orderBy?: { field: keyof T; direction: 'ASC' | 'DESC' }[];
142
+ skip?: number;
143
+ take?: number;
144
+ relations?: string[];
145
+ }
146
+
147
+ /**
148
+ * Paginated result
149
+ */
150
+ export interface PaginatedResult<T> {
151
+ items: T[];
152
+ total: number;
153
+ page: number;
154
+ pageSize: number;
155
+ totalPages: number;
156
+ hasNext: boolean;
157
+ hasPrev: boolean;
158
+ }
159
+ `;
160
+ }
161
+ function generateTypeORMRepository(entityName) {
162
+ const className = toPascalCase(entityName);
163
+ const varName = toCamelCase(entityName);
164
+ return `import { Injectable } from '@nestjs/common';
165
+ import { InjectRepository } from '@nestjs/typeorm';
166
+ import { Repository, DataSource, EntityManager } from 'typeorm';
167
+ import { ${className} } from '@domain/${toKebabCase(entityName)}.entity';
168
+ import { I${className}Repository, QueryOptions, PaginatedResult } from './${toKebabCase(entityName)}.repository.interface';
169
+ import { Specification } from '@shared/specifications/specification';
170
+ import { SpecificationVisitor } from '@shared/specifications/specification.visitor';
171
+
172
+ /**
173
+ * TypeORM implementation of ${className} Repository
174
+ */
175
+ @Injectable()
176
+ export class ${className}Repository implements I${className}Repository {
177
+ constructor(
178
+ @InjectRepository(${className})
179
+ private readonly repository: Repository<${className}>,
180
+ private readonly dataSource: DataSource,
181
+ ) {}
182
+
183
+ async findById(id: string): Promise<${className} | null> {
184
+ return this.repository.findOne({ where: { id } as any });
185
+ }
186
+
187
+ async findAll(): Promise<${className}[]> {
188
+ return this.repository.find();
189
+ }
190
+
191
+ async findBySpec(spec: Specification<${className}>): Promise<${className}[]> {
192
+ const qb = this.repository.createQueryBuilder('entity');
193
+ const visitor = new TypeORMSpecVisitor(qb);
194
+ spec.accept(visitor);
195
+ return qb.getMany();
196
+ }
197
+
198
+ async findOneBySpec(spec: Specification<${className}>): Promise<${className} | null> {
199
+ const results = await this.findBySpec(spec);
200
+ return results[0] || null;
201
+ }
202
+
203
+ async countBySpec(spec: Specification<${className}>): Promise<number> {
204
+ const qb = this.repository.createQueryBuilder('entity');
205
+ const visitor = new TypeORMSpecVisitor(qb);
206
+ spec.accept(visitor);
207
+ return qb.getCount();
208
+ }
209
+
210
+ async exists(spec: Specification<${className}>): Promise<boolean> {
211
+ const count = await this.countBySpec(spec);
212
+ return count > 0;
213
+ }
214
+
215
+ async save(entity: ${className}): Promise<${className}> {
216
+ return this.repository.save(entity);
217
+ }
218
+
219
+ async saveMany(entities: ${className}[]): Promise<${className}[]> {
220
+ return this.repository.save(entities);
221
+ }
222
+
223
+ async delete(id: string): Promise<void> {
224
+ await this.repository.delete(id);
225
+ }
226
+
227
+ async deleteBySpec(spec: Specification<${className}>): Promise<number> {
228
+ const entities = await this.findBySpec(spec);
229
+ if (entities.length === 0) return 0;
230
+
231
+ await this.repository.remove(entities);
232
+ return entities.length;
233
+ }
234
+
235
+ async transaction<T>(operation: (repo: I${className}Repository) => Promise<T>): Promise<T> {
236
+ return this.dataSource.transaction(async (manager: EntityManager) => {
237
+ const transactionalRepo = new TransactionalRepository(
238
+ manager.getRepository(${className}),
239
+ this.dataSource,
240
+ );
241
+ return operation(transactionalRepo);
242
+ });
243
+ }
244
+
245
+ /**
246
+ * Find with pagination
247
+ */
248
+ async findPaginated(
249
+ options: QueryOptions<${className}>,
250
+ page: number = 1,
251
+ pageSize: number = 10,
252
+ ): Promise<PaginatedResult<${className}>> {
253
+ const qb = this.repository.createQueryBuilder('entity');
254
+
255
+ if (options.where) {
256
+ Object.entries(options.where).forEach(([key, value]) => {
257
+ qb.andWhere(\`entity.\${key} = :\${key}\`, { [key]: value });
258
+ });
259
+ }
260
+
261
+ if (options.orderBy) {
262
+ options.orderBy.forEach(({ field, direction }) => {
263
+ qb.addOrderBy(\`entity.\${String(field)}\`, direction);
264
+ });
265
+ }
266
+
267
+ if (options.relations) {
268
+ options.relations.forEach(relation => {
269
+ qb.leftJoinAndSelect(\`entity.\${relation}\`, relation);
270
+ });
271
+ }
272
+
273
+ const total = await qb.getCount();
274
+ const items = await qb
275
+ .skip((page - 1) * pageSize)
276
+ .take(pageSize)
277
+ .getMany();
278
+
279
+ const totalPages = Math.ceil(total / pageSize);
280
+
281
+ return {
282
+ items,
283
+ total,
284
+ page,
285
+ pageSize,
286
+ totalPages,
287
+ hasNext: page < totalPages,
288
+ hasPrev: page > 1,
289
+ };
290
+ }
291
+ }
292
+
293
+ /**
294
+ * Transactional repository wrapper
295
+ */
296
+ class TransactionalRepository implements I${className}Repository {
297
+ constructor(
298
+ private readonly repository: Repository<${className}>,
299
+ private readonly dataSource: DataSource,
300
+ ) {}
301
+
302
+ async findById(id: string): Promise<${className} | null> {
303
+ return this.repository.findOne({ where: { id } as any });
304
+ }
305
+
306
+ async findAll(): Promise<${className}[]> {
307
+ return this.repository.find();
308
+ }
309
+
310
+ async findBySpec(spec: Specification<${className}>): Promise<${className}[]> {
311
+ const qb = this.repository.createQueryBuilder('entity');
312
+ const visitor = new TypeORMSpecVisitor(qb);
313
+ spec.accept(visitor);
314
+ return qb.getMany();
315
+ }
316
+
317
+ async findOneBySpec(spec: Specification<${className}>): Promise<${className} | null> {
318
+ const results = await this.findBySpec(spec);
319
+ return results[0] || null;
320
+ }
321
+
322
+ async countBySpec(spec: Specification<${className}>): Promise<number> {
323
+ const qb = this.repository.createQueryBuilder('entity');
324
+ const visitor = new TypeORMSpecVisitor(qb);
325
+ spec.accept(visitor);
326
+ return qb.getCount();
327
+ }
328
+
329
+ async exists(spec: Specification<${className}>): Promise<boolean> {
330
+ const count = await this.countBySpec(spec);
331
+ return count > 0;
332
+ }
333
+
334
+ async save(entity: ${className}): Promise<${className}> {
335
+ return this.repository.save(entity);
336
+ }
337
+
338
+ async saveMany(entities: ${className}[]): Promise<${className}[]> {
339
+ return this.repository.save(entities);
340
+ }
341
+
342
+ async delete(id: string): Promise<void> {
343
+ await this.repository.delete(id);
344
+ }
345
+
346
+ async deleteBySpec(spec: Specification<${className}>): Promise<number> {
347
+ const entities = await this.findBySpec(spec);
348
+ if (entities.length === 0) return 0;
349
+
350
+ await this.repository.remove(entities);
351
+ return entities.length;
352
+ }
353
+
354
+ async transaction<T>(operation: (repo: I${className}Repository) => Promise<T>): Promise<T> {
355
+ return operation(this);
356
+ }
357
+ }
358
+
359
+ /**
360
+ * TypeORM Specification Visitor
361
+ */
362
+ class TypeORMSpecVisitor<T> implements SpecificationVisitor<T> {
363
+ constructor(private readonly qb: any) {}
364
+
365
+ visitAnd(specs: Specification<T>[]): void {
366
+ specs.forEach(spec => spec.accept(this));
367
+ }
368
+
369
+ visitOr(specs: Specification<T>[]): void {
370
+ const conditions = specs.map((spec, i) => {
371
+ const tempVisitor = new TypeORMSpecVisitor(this.qb);
372
+ spec.accept(tempVisitor);
373
+ return \`cond\${i}\`;
374
+ });
375
+ this.qb.orWhere(conditions.join(' OR '));
376
+ }
377
+
378
+ visitNot(spec: Specification<T>): void {
379
+ // Implementation depends on the spec type
380
+ }
381
+
382
+ visitProperty(field: string, operator: string, value: any): void {
383
+ const paramName = \`\${field}_\${Date.now()}\`;
384
+ switch (operator) {
385
+ case 'eq':
386
+ this.qb.andWhere(\`entity.\${field} = :\${paramName}\`, { [paramName]: value });
387
+ break;
388
+ case 'neq':
389
+ this.qb.andWhere(\`entity.\${field} != :\${paramName}\`, { [paramName]: value });
390
+ break;
391
+ case 'gt':
392
+ this.qb.andWhere(\`entity.\${field} > :\${paramName}\`, { [paramName]: value });
393
+ break;
394
+ case 'gte':
395
+ this.qb.andWhere(\`entity.\${field} >= :\${paramName}\`, { [paramName]: value });
396
+ break;
397
+ case 'lt':
398
+ this.qb.andWhere(\`entity.\${field} < :\${paramName}\`, { [paramName]: value });
399
+ break;
400
+ case 'lte':
401
+ this.qb.andWhere(\`entity.\${field} <= :\${paramName}\`, { [paramName]: value });
402
+ break;
403
+ case 'like':
404
+ this.qb.andWhere(\`entity.\${field} LIKE :\${paramName}\`, { [paramName]: \`%\${value}%\` });
405
+ break;
406
+ case 'in':
407
+ this.qb.andWhere(\`entity.\${field} IN (:\${paramName})\`, { [paramName]: value });
408
+ break;
409
+ }
410
+ }
411
+ }
412
+ `;
413
+ }
414
+ function generatePrismaRepository(entityName) {
415
+ const className = toPascalCase(entityName);
416
+ const varName = toCamelCase(entityName);
417
+ return `import { Injectable } from '@nestjs/common';
418
+ import { PrismaService } from '@shared/prisma/prisma.service';
419
+ import { ${className} } from '@domain/${toKebabCase(entityName)}.entity';
420
+ import { I${className}Repository, QueryOptions, PaginatedResult } from './${toKebabCase(entityName)}.repository.interface';
421
+ import { Specification } from '@shared/specifications/specification';
422
+
423
+ /**
424
+ * Prisma implementation of ${className} Repository
425
+ */
426
+ @Injectable()
427
+ export class ${className}Repository implements I${className}Repository {
428
+ constructor(private readonly prisma: PrismaService) {}
429
+
430
+ async findById(id: string): Promise<${className} | null> {
431
+ const result = await this.prisma.${varName}.findUnique({
432
+ where: { id },
433
+ });
434
+ return result ? this.toDomain(result) : null;
435
+ }
436
+
437
+ async findAll(): Promise<${className}[]> {
438
+ const results = await this.prisma.${varName}.findMany();
439
+ return results.map(r => this.toDomain(r));
440
+ }
441
+
442
+ async findBySpec(spec: Specification<${className}>): Promise<${className}[]> {
443
+ const where = this.specToWhere(spec);
444
+ const results = await this.prisma.${varName}.findMany({ where });
445
+ return results.map(r => this.toDomain(r));
446
+ }
447
+
448
+ async findOneBySpec(spec: Specification<${className}>): Promise<${className} | null> {
449
+ const where = this.specToWhere(spec);
450
+ const result = await this.prisma.${varName}.findFirst({ where });
451
+ return result ? this.toDomain(result) : null;
452
+ }
453
+
454
+ async countBySpec(spec: Specification<${className}>): Promise<number> {
455
+ const where = this.specToWhere(spec);
456
+ return this.prisma.${varName}.count({ where });
457
+ }
458
+
459
+ async exists(spec: Specification<${className}>): Promise<boolean> {
460
+ const count = await this.countBySpec(spec);
461
+ return count > 0;
462
+ }
463
+
464
+ async save(entity: ${className}): Promise<${className}> {
465
+ const data = this.toDatabase(entity);
466
+ const result = await this.prisma.${varName}.upsert({
467
+ where: { id: entity.id },
468
+ create: data,
469
+ update: data,
470
+ });
471
+ return this.toDomain(result);
472
+ }
473
+
474
+ async saveMany(entities: ${className}[]): Promise<${className}[]> {
475
+ const results = await Promise.all(
476
+ entities.map(entity => this.save(entity))
477
+ );
478
+ return results;
479
+ }
480
+
481
+ async delete(id: string): Promise<void> {
482
+ await this.prisma.${varName}.delete({ where: { id } });
483
+ }
484
+
485
+ async deleteBySpec(spec: Specification<${className}>): Promise<number> {
486
+ const where = this.specToWhere(spec);
487
+ const result = await this.prisma.${varName}.deleteMany({ where });
488
+ return result.count;
489
+ }
490
+
491
+ async transaction<T>(operation: (repo: I${className}Repository) => Promise<T>): Promise<T> {
492
+ return this.prisma.$transaction(async (tx) => {
493
+ const transactionalRepo = new Transactional${className}Repository(tx as any);
494
+ return operation(transactionalRepo);
495
+ });
496
+ }
497
+
498
+ async findPaginated(
499
+ options: QueryOptions<${className}>,
500
+ page: number = 1,
501
+ pageSize: number = 10,
502
+ ): Promise<PaginatedResult<${className}>> {
503
+ const where = options.where || {};
504
+ const orderBy = options.orderBy?.map(o => ({ [o.field]: o.direction.toLowerCase() })) || [];
505
+
506
+ const [items, total] = await Promise.all([
507
+ this.prisma.${varName}.findMany({
508
+ where,
509
+ orderBy,
510
+ skip: (page - 1) * pageSize,
511
+ take: pageSize,
512
+ include: options.relations?.reduce((acc, r) => ({ ...acc, [r]: true }), {}),
513
+ }),
514
+ this.prisma.${varName}.count({ where }),
515
+ ]);
516
+
517
+ const totalPages = Math.ceil(total / pageSize);
518
+
519
+ return {
520
+ items: items.map(i => this.toDomain(i)),
521
+ total,
522
+ page,
523
+ pageSize,
524
+ totalPages,
525
+ hasNext: page < totalPages,
526
+ hasPrev: page > 1,
527
+ };
528
+ }
529
+
530
+ private toDomain(data: any): ${className} {
531
+ // Map database record to domain entity
532
+ return new ${className}(data);
533
+ }
534
+
535
+ private toDatabase(entity: ${className}): any {
536
+ // Map domain entity to database record
537
+ return { ...entity };
538
+ }
539
+
540
+ private specToWhere(spec: Specification<${className}>): any {
541
+ // Convert specification to Prisma where clause
542
+ return {};
543
+ }
544
+ }
545
+
546
+ /**
547
+ * Transactional repository wrapper for Prisma
548
+ */
549
+ class Transactional${className}Repository implements I${className}Repository {
550
+ constructor(private readonly tx: any) {}
551
+
552
+ // Implement all methods using this.tx instead of prisma
553
+ async findById(id: string): Promise<${className} | null> {
554
+ const result = await this.tx.${varName}.findUnique({ where: { id } });
555
+ return result ? this.toDomain(result) : null;
556
+ }
557
+
558
+ async findAll(): Promise<${className}[]> {
559
+ const results = await this.tx.${varName}.findMany();
560
+ return results.map((r: any) => this.toDomain(r));
561
+ }
562
+
563
+ async findBySpec(spec: Specification<${className}>): Promise<${className}[]> {
564
+ return [];
565
+ }
566
+
567
+ async findOneBySpec(spec: Specification<${className}>): Promise<${className} | null> {
568
+ return null;
569
+ }
570
+
571
+ async countBySpec(spec: Specification<${className}>): Promise<number> {
572
+ return 0;
573
+ }
574
+
575
+ async exists(spec: Specification<${className}>): Promise<boolean> {
576
+ return false;
577
+ }
578
+
579
+ async save(entity: ${className}): Promise<${className}> {
580
+ const data = this.toDatabase(entity);
581
+ const result = await this.tx.${varName}.upsert({
582
+ where: { id: entity.id },
583
+ create: data,
584
+ update: data,
585
+ });
586
+ return this.toDomain(result);
587
+ }
588
+
589
+ async saveMany(entities: ${className}[]): Promise<${className}[]> {
590
+ return Promise.all(entities.map(e => this.save(e)));
591
+ }
592
+
593
+ async delete(id: string): Promise<void> {
594
+ await this.tx.${varName}.delete({ where: { id } });
595
+ }
596
+
597
+ async deleteBySpec(spec: Specification<${className}>): Promise<number> {
598
+ return 0;
599
+ }
600
+
601
+ async transaction<T>(operation: (repo: I${className}Repository) => Promise<T>): Promise<T> {
602
+ return operation(this);
603
+ }
604
+
605
+ private toDomain(data: any): ${className} {
606
+ return new ${className}(data);
607
+ }
608
+
609
+ private toDatabase(entity: ${className}): any {
610
+ return { ...entity };
611
+ }
612
+ }
613
+ `;
614
+ }
615
+ /**
616
+ * Setup base repository infrastructure
617
+ */
618
+ async function setupRepositoryInfrastructure(basePath, options = {}) {
619
+ console.log(chalk_1.default.bold.blue('\n📦 Setting up Repository Infrastructure\n'));
620
+ const sharedPath = path.join(basePath, 'src/shared');
621
+ const specPath = path.join(sharedPath, 'specifications');
622
+ const repoPath = path.join(sharedPath, 'repositories');
623
+ // Create directories
624
+ [specPath, repoPath].forEach(p => {
625
+ if (!fs.existsSync(p)) {
626
+ fs.mkdirSync(p, { recursive: true });
627
+ }
628
+ });
629
+ // Generate specification pattern
630
+ const specContent = generateSpecificationPattern();
631
+ fs.writeFileSync(path.join(specPath, 'specification.ts'), specContent);
632
+ console.log(chalk_1.default.green(` ✓ Created specification pattern`));
633
+ // Generate base repository
634
+ const baseRepoContent = generateBaseRepository();
635
+ fs.writeFileSync(path.join(repoPath, 'base.repository.ts'), baseRepoContent);
636
+ console.log(chalk_1.default.green(` ✓ Created base repository`));
637
+ // Generate unit of work
638
+ const uowContent = generateUnitOfWork();
639
+ fs.writeFileSync(path.join(repoPath, 'unit-of-work.ts'), uowContent);
640
+ console.log(chalk_1.default.green(` ✓ Created unit of work`));
641
+ console.log(chalk_1.default.bold.green('\n✅ Repository infrastructure ready!\n'));
642
+ }
643
+ function generateSpecificationPattern() {
644
+ return `/**
645
+ * Specification Pattern
646
+ * Type-safe query specifications for repositories
647
+ */
648
+
649
+ /**
650
+ * Base specification interface
651
+ */
652
+ export interface Specification<T> {
653
+ isSatisfiedBy(candidate: T): boolean;
654
+ accept(visitor: SpecificationVisitor<T>): void;
655
+ }
656
+
657
+ /**
658
+ * Specification visitor interface
659
+ */
660
+ export interface SpecificationVisitor<T> {
661
+ visitAnd(specs: Specification<T>[]): void;
662
+ visitOr(specs: Specification<T>[]): void;
663
+ visitNot(spec: Specification<T>): void;
664
+ visitProperty(field: string, operator: string, value: any): void;
665
+ }
666
+
667
+ /**
668
+ * Base specification class
669
+ */
670
+ export abstract class BaseSpecification<T> implements Specification<T> {
671
+ abstract isSatisfiedBy(candidate: T): boolean;
672
+ abstract accept(visitor: SpecificationVisitor<T>): void;
673
+
674
+ and(other: Specification<T>): Specification<T> {
675
+ return new AndSpecification([this, other]);
676
+ }
677
+
678
+ or(other: Specification<T>): Specification<T> {
679
+ return new OrSpecification([this, other]);
680
+ }
681
+
682
+ not(): Specification<T> {
683
+ return new NotSpecification(this);
684
+ }
685
+ }
686
+
687
+ /**
688
+ * AND specification
689
+ */
690
+ export class AndSpecification<T> extends BaseSpecification<T> {
691
+ constructor(private readonly specs: Specification<T>[]) {
692
+ super();
693
+ }
694
+
695
+ isSatisfiedBy(candidate: T): boolean {
696
+ return this.specs.every(spec => spec.isSatisfiedBy(candidate));
697
+ }
698
+
699
+ accept(visitor: SpecificationVisitor<T>): void {
700
+ visitor.visitAnd(this.specs);
701
+ }
702
+ }
703
+
704
+ /**
705
+ * OR specification
706
+ */
707
+ export class OrSpecification<T> extends BaseSpecification<T> {
708
+ constructor(private readonly specs: Specification<T>[]) {
709
+ super();
710
+ }
711
+
712
+ isSatisfiedBy(candidate: T): boolean {
713
+ return this.specs.some(spec => spec.isSatisfiedBy(candidate));
714
+ }
715
+
716
+ accept(visitor: SpecificationVisitor<T>): void {
717
+ visitor.visitOr(this.specs);
718
+ }
719
+ }
720
+
721
+ /**
722
+ * NOT specification
723
+ */
724
+ export class NotSpecification<T> extends BaseSpecification<T> {
725
+ constructor(private readonly spec: Specification<T>) {
726
+ super();
727
+ }
728
+
729
+ isSatisfiedBy(candidate: T): boolean {
730
+ return !this.spec.isSatisfiedBy(candidate);
731
+ }
732
+
733
+ accept(visitor: SpecificationVisitor<T>): void {
734
+ visitor.visitNot(this.spec);
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Property specification
740
+ */
741
+ export class PropertySpecification<T, K extends keyof T> extends BaseSpecification<T> {
742
+ constructor(
743
+ private readonly field: K,
744
+ private readonly operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'in',
745
+ private readonly value: T[K] | T[K][],
746
+ ) {
747
+ super();
748
+ }
749
+
750
+ isSatisfiedBy(candidate: T): boolean {
751
+ const fieldValue = candidate[this.field];
752
+
753
+ switch (this.operator) {
754
+ case 'eq':
755
+ return fieldValue === this.value;
756
+ case 'neq':
757
+ return fieldValue !== this.value;
758
+ case 'gt':
759
+ return fieldValue > (this.value as T[K]);
760
+ case 'gte':
761
+ return fieldValue >= (this.value as T[K]);
762
+ case 'lt':
763
+ return fieldValue < (this.value as T[K]);
764
+ case 'lte':
765
+ return fieldValue <= (this.value as T[K]);
766
+ case 'like':
767
+ return String(fieldValue).includes(String(this.value));
768
+ case 'in':
769
+ return (this.value as T[K][]).includes(fieldValue);
770
+ default:
771
+ return false;
772
+ }
773
+ }
774
+
775
+ accept(visitor: SpecificationVisitor<T>): void {
776
+ visitor.visitProperty(String(this.field), this.operator, this.value);
777
+ }
778
+ }
779
+
780
+ /**
781
+ * Specification builder for fluent API
782
+ */
783
+ export class SpecificationBuilder<T> {
784
+ private specs: Specification<T>[] = [];
785
+
786
+ where<K extends keyof T>(field: K, operator: 'eq' | 'neq' | 'gt' | 'gte' | 'lt' | 'lte' | 'like' | 'in', value: T[K] | T[K][]): this {
787
+ this.specs.push(new PropertySpecification(field, operator, value));
788
+ return this;
789
+ }
790
+
791
+ equals<K extends keyof T>(field: K, value: T[K]): this {
792
+ return this.where(field, 'eq', value);
793
+ }
794
+
795
+ notEquals<K extends keyof T>(field: K, value: T[K]): this {
796
+ return this.where(field, 'neq', value);
797
+ }
798
+
799
+ greaterThan<K extends keyof T>(field: K, value: T[K]): this {
800
+ return this.where(field, 'gt', value);
801
+ }
802
+
803
+ lessThan<K extends keyof T>(field: K, value: T[K]): this {
804
+ return this.where(field, 'lt', value);
805
+ }
806
+
807
+ contains<K extends keyof T>(field: K, value: string): this {
808
+ return this.where(field, 'like', value as unknown as T[K]);
809
+ }
810
+
811
+ in<K extends keyof T>(field: K, values: T[K][]): this {
812
+ return this.where(field, 'in', values);
813
+ }
814
+
815
+ build(): Specification<T> {
816
+ if (this.specs.length === 0) {
817
+ return new TrueSpecification();
818
+ }
819
+ if (this.specs.length === 1) {
820
+ return this.specs[0];
821
+ }
822
+ return new AndSpecification(this.specs);
823
+ }
824
+ }
825
+
826
+ /**
827
+ * Always true specification
828
+ */
829
+ export class TrueSpecification<T> extends BaseSpecification<T> {
830
+ isSatisfiedBy(_candidate: T): boolean {
831
+ return true;
832
+ }
833
+
834
+ accept(_visitor: SpecificationVisitor<T>): void {
835
+ // No-op
836
+ }
837
+ }
838
+
839
+ /**
840
+ * Create a specification builder
841
+ */
842
+ export function spec<T>(): SpecificationBuilder<T> {
843
+ return new SpecificationBuilder<T>();
844
+ }
845
+ `;
846
+ }
847
+ function generateBaseRepository() {
848
+ return `/**
849
+ * Base Repository Interface
850
+ * Generic repository contract for all entities
851
+ */
852
+
853
+ import { Specification } from '../specifications/specification';
854
+
855
+ /**
856
+ * Generic repository interface
857
+ */
858
+ export interface IRepository<T, ID = string> {
859
+ findById(id: ID): Promise<T | null>;
860
+ findAll(): Promise<T[]>;
861
+ findBySpec(spec: Specification<T>): Promise<T[]>;
862
+ findOneBySpec(spec: Specification<T>): Promise<T | null>;
863
+ countBySpec(spec: Specification<T>): Promise<number>;
864
+ exists(spec: Specification<T>): Promise<boolean>;
865
+ save(entity: T): Promise<T>;
866
+ saveMany(entities: T[]): Promise<T[]>;
867
+ delete(id: ID): Promise<void>;
868
+ deleteBySpec(spec: Specification<T>): Promise<number>;
869
+ }
870
+
871
+ /**
872
+ * Read-only repository interface
873
+ */
874
+ export interface IReadRepository<T, ID = string> {
875
+ findById(id: ID): Promise<T | null>;
876
+ findAll(): Promise<T[]>;
877
+ findBySpec(spec: Specification<T>): Promise<T[]>;
878
+ findOneBySpec(spec: Specification<T>): Promise<T | null>;
879
+ countBySpec(spec: Specification<T>): Promise<number>;
880
+ exists(spec: Specification<T>): Promise<boolean>;
881
+ }
882
+
883
+ /**
884
+ * Write-only repository interface
885
+ */
886
+ export interface IWriteRepository<T, ID = string> {
887
+ save(entity: T): Promise<T>;
888
+ saveMany(entities: T[]): Promise<T[]>;
889
+ delete(id: ID): Promise<void>;
890
+ deleteBySpec(spec: Specification<T>): Promise<number>;
891
+ }
892
+
893
+ /**
894
+ * Aggregate repository with event publishing
895
+ */
896
+ export interface IAggregateRepository<T, ID = string> extends IRepository<T, ID> {
897
+ saveWithEvents(aggregate: T): Promise<T>;
898
+ }
899
+ `;
900
+ }
901
+ function generateUnitOfWork() {
902
+ return `/**
903
+ * Unit of Work Pattern
904
+ * Manages transactions across multiple repositories
905
+ */
906
+
907
+ import { Injectable } from '@nestjs/common';
908
+ import { DataSource, EntityManager, QueryRunner } from 'typeorm';
909
+
910
+ /**
911
+ * Unit of Work interface
912
+ */
913
+ export interface IUnitOfWork {
914
+ begin(): Promise<void>;
915
+ commit(): Promise<void>;
916
+ rollback(): Promise<void>;
917
+ getRepository<T>(entityClass: new () => T): any;
918
+ }
919
+
920
+ /**
921
+ * TypeORM Unit of Work implementation
922
+ */
923
+ @Injectable()
924
+ export class UnitOfWork implements IUnitOfWork {
925
+ private queryRunner: QueryRunner | null = null;
926
+
927
+ constructor(private readonly dataSource: DataSource) {}
928
+
929
+ async begin(): Promise<void> {
930
+ this.queryRunner = this.dataSource.createQueryRunner();
931
+ await this.queryRunner.connect();
932
+ await this.queryRunner.startTransaction();
933
+ }
934
+
935
+ async commit(): Promise<void> {
936
+ if (!this.queryRunner) {
937
+ throw new Error('Transaction not started');
938
+ }
939
+ await this.queryRunner.commitTransaction();
940
+ await this.queryRunner.release();
941
+ this.queryRunner = null;
942
+ }
943
+
944
+ async rollback(): Promise<void> {
945
+ if (!this.queryRunner) {
946
+ throw new Error('Transaction not started');
947
+ }
948
+ await this.queryRunner.rollbackTransaction();
949
+ await this.queryRunner.release();
950
+ this.queryRunner = null;
951
+ }
952
+
953
+ getRepository<T>(entityClass: new () => T): any {
954
+ if (!this.queryRunner) {
955
+ throw new Error('Transaction not started');
956
+ }
957
+ return this.queryRunner.manager.getRepository(entityClass);
958
+ }
959
+
960
+ get manager(): EntityManager {
961
+ if (!this.queryRunner) {
962
+ throw new Error('Transaction not started');
963
+ }
964
+ return this.queryRunner.manager;
965
+ }
966
+ }
967
+
968
+ /**
969
+ * Unit of Work decorator
970
+ */
971
+ export function Transactional(): MethodDecorator {
972
+ return function (
973
+ target: any,
974
+ propertyKey: string | symbol,
975
+ descriptor: PropertyDescriptor,
976
+ ) {
977
+ const originalMethod = descriptor.value;
978
+
979
+ descriptor.value = async function (...args: any[]) {
980
+ const unitOfWork: UnitOfWork = (this as any).unitOfWork;
981
+
982
+ if (!unitOfWork) {
983
+ throw new Error('UnitOfWork not injected');
984
+ }
985
+
986
+ await unitOfWork.begin();
987
+
988
+ try {
989
+ const result = await originalMethod.apply(this, args);
990
+ await unitOfWork.commit();
991
+ return result;
992
+ } catch (error) {
993
+ await unitOfWork.rollback();
994
+ throw error;
995
+ }
996
+ };
997
+
998
+ return descriptor;
999
+ };
1000
+ }
1001
+ `;
1002
+ }
1003
+ // Helper functions
1004
+ function toKebabCase(str) {
1005
+ return str
1006
+ .replace(/([a-z])([A-Z])/g, '$1-$2')
1007
+ .replace(/[\\s_]+/g, '-')
1008
+ .toLowerCase();
1009
+ }
1010
+ function toPascalCase(str) {
1011
+ return str
1012
+ .replace(/[-_\\s]+(.)?/g, (_, c) => c ? c.toUpperCase() : '')
1013
+ .replace(/^(.)/, c => c.toUpperCase());
1014
+ }
1015
+ function toCamelCase(str) {
1016
+ const pascal = toPascalCase(str);
1017
+ return pascal.charAt(0).toLowerCase() + pascal.slice(1);
1018
+ }
1019
+ //# sourceMappingURL=generate-repository.js.map