nestjs-ddd-cli 2.2.1 → 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 +24 -5
  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,1029 @@
1
+ "use strict";
2
+ /**
3
+ * Circuit Breaker & Resilience Patterns Generator
4
+ * Implements retry logic, timeout handling, and fallback strategies
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.setupResiliencePatterns = setupResiliencePatterns;
44
+ const fs = __importStar(require("fs"));
45
+ const path = __importStar(require("path"));
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ async function setupResiliencePatterns(basePath, options = {}) {
48
+ console.log(chalk_1.default.bold.blue('\n🛡️ Setting up Resilience Patterns\n'));
49
+ const sharedPath = path.join(basePath, 'src/shared/resilience');
50
+ if (!fs.existsSync(sharedPath)) {
51
+ fs.mkdirSync(sharedPath, { recursive: true });
52
+ }
53
+ // Generate circuit breaker
54
+ fs.writeFileSync(path.join(sharedPath, 'circuit-breaker.ts'), generateCircuitBreaker());
55
+ console.log(chalk_1.default.green(` ✓ Created circuit breaker`));
56
+ // Generate retry strategy
57
+ fs.writeFileSync(path.join(sharedPath, 'retry.strategy.ts'), generateRetryStrategy());
58
+ console.log(chalk_1.default.green(` ✓ Created retry strategy`));
59
+ // Generate timeout handler
60
+ fs.writeFileSync(path.join(sharedPath, 'timeout.handler.ts'), generateTimeoutHandler());
61
+ console.log(chalk_1.default.green(` ✓ Created timeout handler`));
62
+ // Generate fallback decorator
63
+ fs.writeFileSync(path.join(sharedPath, 'fallback.decorator.ts'), generateFallbackDecorator());
64
+ console.log(chalk_1.default.green(` ✓ Created fallback decorator`));
65
+ // Generate bulkhead pattern
66
+ fs.writeFileSync(path.join(sharedPath, 'bulkhead.ts'), generateBulkhead());
67
+ console.log(chalk_1.default.green(` ✓ Created bulkhead pattern`));
68
+ // Generate resilience module
69
+ fs.writeFileSync(path.join(sharedPath, 'resilience.module.ts'), generateResilienceModule());
70
+ console.log(chalk_1.default.green(` ✓ Created resilience module`));
71
+ // Generate resilience decorators
72
+ fs.writeFileSync(path.join(sharedPath, 'resilience.decorators.ts'), generateResilienceDecorators());
73
+ console.log(chalk_1.default.green(` ✓ Created resilience decorators`));
74
+ console.log(chalk_1.default.bold.green('\n✅ Resilience patterns ready!\n'));
75
+ }
76
+ function generateCircuitBreaker() {
77
+ return `/**
78
+ * Circuit Breaker Implementation
79
+ * Prevents cascading failures in distributed systems
80
+ */
81
+
82
+ import { Logger } from '@nestjs/common';
83
+ import { EventEmitter2 } from '@nestjs/event-emitter';
84
+
85
+ export enum CircuitState {
86
+ CLOSED = 'CLOSED',
87
+ OPEN = 'OPEN',
88
+ HALF_OPEN = 'HALF_OPEN',
89
+ }
90
+
91
+ export interface CircuitBreakerOptions {
92
+ name: string;
93
+ failureThreshold: number;
94
+ successThreshold: number;
95
+ timeout: number; // ms before trying again
96
+ resetTimeout?: number;
97
+ onStateChange?: (from: CircuitState, to: CircuitState) => void;
98
+ onSuccess?: () => void;
99
+ onFailure?: (error: Error) => void;
100
+ }
101
+
102
+ export class CircuitBreaker {
103
+ private readonly logger = new Logger(CircuitBreaker.name);
104
+ private state: CircuitState = CircuitState.CLOSED;
105
+ private failureCount = 0;
106
+ private successCount = 0;
107
+ private lastFailureTime: number = 0;
108
+ private nextAttempt: number = 0;
109
+
110
+ constructor(
111
+ private readonly options: CircuitBreakerOptions,
112
+ private readonly eventEmitter?: EventEmitter2,
113
+ ) {}
114
+
115
+ get currentState(): CircuitState {
116
+ return this.state;
117
+ }
118
+
119
+ get isOpen(): boolean {
120
+ return this.state === CircuitState.OPEN;
121
+ }
122
+
123
+ get isClosed(): boolean {
124
+ return this.state === CircuitState.CLOSED;
125
+ }
126
+
127
+ get isHalfOpen(): boolean {
128
+ return this.state === CircuitState.HALF_OPEN;
129
+ }
130
+
131
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
132
+ if (this.state === CircuitState.OPEN) {
133
+ if (Date.now() < this.nextAttempt) {
134
+ throw new CircuitOpenError(
135
+ \`Circuit \${this.options.name} is open. Retry after \${this.nextAttempt - Date.now()}ms\`,
136
+ );
137
+ }
138
+ this.transitionTo(CircuitState.HALF_OPEN);
139
+ }
140
+
141
+ try {
142
+ const result = await fn();
143
+ this.onSuccess();
144
+ return result;
145
+ } catch (error) {
146
+ this.onFailure(error as Error);
147
+ throw error;
148
+ }
149
+ }
150
+
151
+ private onSuccess(): void {
152
+ this.failureCount = 0;
153
+
154
+ if (this.state === CircuitState.HALF_OPEN) {
155
+ this.successCount++;
156
+ if (this.successCount >= this.options.successThreshold) {
157
+ this.transitionTo(CircuitState.CLOSED);
158
+ }
159
+ }
160
+
161
+ this.options.onSuccess?.();
162
+ }
163
+
164
+ private onFailure(error: Error): void {
165
+ this.failureCount++;
166
+ this.lastFailureTime = Date.now();
167
+ this.successCount = 0;
168
+
169
+ if (this.state === CircuitState.HALF_OPEN) {
170
+ this.transitionTo(CircuitState.OPEN);
171
+ } else if (
172
+ this.state === CircuitState.CLOSED &&
173
+ this.failureCount >= this.options.failureThreshold
174
+ ) {
175
+ this.transitionTo(CircuitState.OPEN);
176
+ }
177
+
178
+ this.options.onFailure?.(error);
179
+ }
180
+
181
+ private transitionTo(newState: CircuitState): void {
182
+ const oldState = this.state;
183
+ this.state = newState;
184
+
185
+ if (newState === CircuitState.OPEN) {
186
+ this.nextAttempt = Date.now() + this.options.timeout;
187
+ }
188
+
189
+ if (newState === CircuitState.CLOSED) {
190
+ this.failureCount = 0;
191
+ this.successCount = 0;
192
+ }
193
+
194
+ this.logger.log(\`Circuit \${this.options.name}: \${oldState} -> \${newState}\`);
195
+
196
+ this.options.onStateChange?.(oldState, newState);
197
+ this.eventEmitter?.emit('circuit-breaker.state-change', {
198
+ name: this.options.name,
199
+ from: oldState,
200
+ to: newState,
201
+ timestamp: new Date(),
202
+ });
203
+ }
204
+
205
+ reset(): void {
206
+ this.transitionTo(CircuitState.CLOSED);
207
+ }
208
+
209
+ getStats(): CircuitBreakerStats {
210
+ return {
211
+ name: this.options.name,
212
+ state: this.state,
213
+ failureCount: this.failureCount,
214
+ successCount: this.successCount,
215
+ lastFailureTime: this.lastFailureTime ? new Date(this.lastFailureTime) : null,
216
+ nextAttempt: this.nextAttempt ? new Date(this.nextAttempt) : null,
217
+ };
218
+ }
219
+ }
220
+
221
+ export interface CircuitBreakerStats {
222
+ name: string;
223
+ state: CircuitState;
224
+ failureCount: number;
225
+ successCount: number;
226
+ lastFailureTime: Date | null;
227
+ nextAttempt: Date | null;
228
+ }
229
+
230
+ export class CircuitOpenError extends Error {
231
+ constructor(message: string) {
232
+ super(message);
233
+ this.name = 'CircuitOpenError';
234
+ }
235
+ }
236
+
237
+ /**
238
+ * Circuit Breaker Registry
239
+ */
240
+ export class CircuitBreakerRegistry {
241
+ private readonly breakers = new Map<string, CircuitBreaker>();
242
+
243
+ constructor(private readonly eventEmitter?: EventEmitter2) {}
244
+
245
+ getOrCreate(options: CircuitBreakerOptions): CircuitBreaker {
246
+ let breaker = this.breakers.get(options.name);
247
+ if (!breaker) {
248
+ breaker = new CircuitBreaker(options, this.eventEmitter);
249
+ this.breakers.set(options.name, breaker);
250
+ }
251
+ return breaker;
252
+ }
253
+
254
+ get(name: string): CircuitBreaker | undefined {
255
+ return this.breakers.get(name);
256
+ }
257
+
258
+ getAll(): CircuitBreaker[] {
259
+ return Array.from(this.breakers.values());
260
+ }
261
+
262
+ getAllStats(): CircuitBreakerStats[] {
263
+ return this.getAll().map(b => b.getStats());
264
+ }
265
+
266
+ resetAll(): void {
267
+ this.breakers.forEach(b => b.reset());
268
+ }
269
+ }
270
+ `;
271
+ }
272
+ function generateRetryStrategy() {
273
+ return `/**
274
+ * Retry Strategy Implementation
275
+ * Configurable retry logic with backoff strategies
276
+ */
277
+
278
+ import { Logger } from '@nestjs/common';
279
+
280
+ export interface RetryOptions {
281
+ maxAttempts: number;
282
+ delay: number; // base delay in ms
283
+ backoffMultiplier?: number;
284
+ maxDelay?: number;
285
+ retryOn?: (error: Error) => boolean;
286
+ onRetry?: (attempt: number, error: Error, delay: number) => void;
287
+ jitter?: boolean;
288
+ }
289
+
290
+ export type BackoffStrategy = 'fixed' | 'exponential' | 'linear' | 'fibonacci';
291
+
292
+ const DEFAULT_OPTIONS: RetryOptions = {
293
+ maxAttempts: 3,
294
+ delay: 1000,
295
+ backoffMultiplier: 2,
296
+ maxDelay: 30000,
297
+ jitter: true,
298
+ };
299
+
300
+ export class RetryStrategy {
301
+ private readonly logger = new Logger(RetryStrategy.name);
302
+ private readonly options: RetryOptions;
303
+
304
+ constructor(options: Partial<RetryOptions> = {}) {
305
+ this.options = { ...DEFAULT_OPTIONS, ...options };
306
+ }
307
+
308
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
309
+ let lastError: Error | null = null;
310
+
311
+ for (let attempt = 1; attempt <= this.options.maxAttempts; attempt++) {
312
+ try {
313
+ return await fn();
314
+ } catch (error) {
315
+ lastError = error as Error;
316
+
317
+ if (attempt === this.options.maxAttempts) {
318
+ break;
319
+ }
320
+
321
+ if (this.options.retryOn && !this.options.retryOn(lastError)) {
322
+ throw lastError;
323
+ }
324
+
325
+ const delay = this.calculateDelay(attempt);
326
+ this.logger.warn(
327
+ \`Attempt \${attempt} failed. Retrying in \${delay}ms...\`,
328
+ );
329
+
330
+ this.options.onRetry?.(attempt, lastError, delay);
331
+ await this.sleep(delay);
332
+ }
333
+ }
334
+
335
+ throw new RetryExhaustedError(
336
+ \`All \${this.options.maxAttempts} retry attempts failed\`,
337
+ lastError!,
338
+ );
339
+ }
340
+
341
+ private calculateDelay(attempt: number): number {
342
+ let delay = this.options.delay * Math.pow(this.options.backoffMultiplier || 2, attempt - 1);
343
+
344
+ if (this.options.maxDelay) {
345
+ delay = Math.min(delay, this.options.maxDelay);
346
+ }
347
+
348
+ if (this.options.jitter) {
349
+ delay = delay * (0.5 + Math.random());
350
+ }
351
+
352
+ return Math.floor(delay);
353
+ }
354
+
355
+ private sleep(ms: number): Promise<void> {
356
+ return new Promise(resolve => setTimeout(resolve, ms));
357
+ }
358
+ }
359
+
360
+ export class RetryExhaustedError extends Error {
361
+ constructor(message: string, public readonly lastError: Error) {
362
+ super(message);
363
+ this.name = 'RetryExhaustedError';
364
+ }
365
+ }
366
+
367
+ /**
368
+ * Retry with exponential backoff
369
+ */
370
+ export function retryWithBackoff<T>(
371
+ fn: () => Promise<T>,
372
+ options?: Partial<RetryOptions>,
373
+ ): Promise<T> {
374
+ return new RetryStrategy(options).execute(fn);
375
+ }
376
+
377
+ /**
378
+ * Retry decorator factory
379
+ */
380
+ export function Retry(options?: Partial<RetryOptions>): MethodDecorator {
381
+ return function (
382
+ target: any,
383
+ propertyKey: string | symbol,
384
+ descriptor: PropertyDescriptor,
385
+ ) {
386
+ const originalMethod = descriptor.value;
387
+ const strategy = new RetryStrategy(options);
388
+
389
+ descriptor.value = async function (...args: any[]) {
390
+ return strategy.execute(() => originalMethod.apply(this, args));
391
+ };
392
+
393
+ return descriptor;
394
+ };
395
+ }
396
+
397
+ /**
398
+ * Fibonacci backoff calculator
399
+ */
400
+ export function fibonacciDelay(attempt: number, baseDelay: number): number {
401
+ const fib = [1, 1];
402
+ for (let i = 2; i <= attempt; i++) {
403
+ fib[i] = fib[i - 1] + fib[i - 2];
404
+ }
405
+ return fib[attempt] * baseDelay;
406
+ }
407
+
408
+ /**
409
+ * Check if error is retryable
410
+ */
411
+ export function isRetryableError(error: Error): boolean {
412
+ const retryableCodes = ['ECONNRESET', 'ETIMEDOUT', 'ECONNREFUSED', 'EPIPE'];
413
+ const retryableMessages = ['timeout', 'network', 'connection', '503', '502', '429'];
414
+
415
+ if ((error as any).code && retryableCodes.includes((error as any).code)) {
416
+ return true;
417
+ }
418
+
419
+ const message = error.message.toLowerCase();
420
+ return retryableMessages.some(m => message.includes(m));
421
+ }
422
+ `;
423
+ }
424
+ function generateTimeoutHandler() {
425
+ return `/**
426
+ * Timeout Handler Implementation
427
+ * Prevents hanging operations with configurable timeouts
428
+ */
429
+
430
+ import { Logger } from '@nestjs/common';
431
+
432
+ export interface TimeoutOptions {
433
+ timeout: number; // ms
434
+ onTimeout?: () => void;
435
+ message?: string;
436
+ }
437
+
438
+ export class TimeoutHandler {
439
+ private readonly logger = new Logger(TimeoutHandler.name);
440
+
441
+ async execute<T>(fn: () => Promise<T>, options: TimeoutOptions): Promise<T> {
442
+ const { timeout, onTimeout, message } = options;
443
+
444
+ return new Promise<T>((resolve, reject) => {
445
+ const timer = setTimeout(() => {
446
+ onTimeout?.();
447
+ reject(new TimeoutError(message || \`Operation timed out after \${timeout}ms\`));
448
+ }, timeout);
449
+
450
+ fn()
451
+ .then(result => {
452
+ clearTimeout(timer);
453
+ resolve(result);
454
+ })
455
+ .catch(error => {
456
+ clearTimeout(timer);
457
+ reject(error);
458
+ });
459
+ });
460
+ }
461
+ }
462
+
463
+ export class TimeoutError extends Error {
464
+ constructor(message: string) {
465
+ super(message);
466
+ this.name = 'TimeoutError';
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Execute with timeout
472
+ */
473
+ export function withTimeout<T>(
474
+ fn: () => Promise<T>,
475
+ timeout: number,
476
+ message?: string,
477
+ ): Promise<T> {
478
+ return new TimeoutHandler().execute(fn, { timeout, message });
479
+ }
480
+
481
+ /**
482
+ * Timeout decorator factory
483
+ */
484
+ export function Timeout(ms: number, message?: string): MethodDecorator {
485
+ return function (
486
+ target: any,
487
+ propertyKey: string | symbol,
488
+ descriptor: PropertyDescriptor,
489
+ ) {
490
+ const originalMethod = descriptor.value;
491
+ const handler = new TimeoutHandler();
492
+
493
+ descriptor.value = async function (...args: any[]) {
494
+ return handler.execute(
495
+ () => originalMethod.apply(this, args),
496
+ { timeout: ms, message: message || \`\${String(propertyKey)} timed out after \${ms}ms\` },
497
+ );
498
+ };
499
+
500
+ return descriptor;
501
+ };
502
+ }
503
+
504
+ /**
505
+ * Race multiple promises with timeout
506
+ */
507
+ export async function raceWithTimeout<T>(
508
+ promises: Promise<T>[],
509
+ timeout: number,
510
+ ): Promise<T> {
511
+ const timeoutPromise = new Promise<never>((_, reject) => {
512
+ setTimeout(() => reject(new TimeoutError(\`Race timed out after \${timeout}ms\`)), timeout);
513
+ });
514
+
515
+ return Promise.race([...promises, timeoutPromise]);
516
+ }
517
+
518
+ /**
519
+ * Deadline-based timeout
520
+ */
521
+ export class Deadline {
522
+ private readonly deadline: number;
523
+
524
+ constructor(timeout: number) {
525
+ this.deadline = Date.now() + timeout;
526
+ }
527
+
528
+ get remaining(): number {
529
+ return Math.max(0, this.deadline - Date.now());
530
+ }
531
+
532
+ get exceeded(): boolean {
533
+ return Date.now() > this.deadline;
534
+ }
535
+
536
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
537
+ if (this.exceeded) {
538
+ throw new TimeoutError('Deadline already exceeded');
539
+ }
540
+ return withTimeout(fn, this.remaining);
541
+ }
542
+ }
543
+ `;
544
+ }
545
+ function generateFallbackDecorator() {
546
+ return `/**
547
+ * Fallback Pattern Implementation
548
+ * Provides graceful degradation when primary operations fail
549
+ */
550
+
551
+ import { Logger } from '@nestjs/common';
552
+
553
+ export interface FallbackOptions<T> {
554
+ fallback: T | (() => T) | (() => Promise<T>);
555
+ onFallback?: (error: Error) => void;
556
+ shouldFallback?: (error: Error) => boolean;
557
+ }
558
+
559
+ export class FallbackHandler<T> {
560
+ private readonly logger = new Logger(FallbackHandler.name);
561
+
562
+ constructor(private readonly options: FallbackOptions<T>) {}
563
+
564
+ async execute(fn: () => Promise<T>): Promise<T> {
565
+ try {
566
+ return await fn();
567
+ } catch (error) {
568
+ if (this.options.shouldFallback && !this.options.shouldFallback(error as Error)) {
569
+ throw error;
570
+ }
571
+
572
+ this.logger.warn(\`Operation failed, using fallback: \${(error as Error).message}\`);
573
+ this.options.onFallback?.(error as Error);
574
+
575
+ return this.resolveFallback();
576
+ }
577
+ }
578
+
579
+ private async resolveFallback(): Promise<T> {
580
+ const { fallback } = this.options;
581
+
582
+ if (typeof fallback === 'function') {
583
+ return (fallback as () => T | Promise<T>)();
584
+ }
585
+
586
+ return fallback;
587
+ }
588
+ }
589
+
590
+ /**
591
+ * Fallback decorator factory
592
+ */
593
+ export function Fallback<T>(
594
+ fallbackValue: T | (() => T) | (() => Promise<T>),
595
+ options?: Omit<FallbackOptions<T>, 'fallback'>,
596
+ ): MethodDecorator {
597
+ return function (
598
+ target: any,
599
+ propertyKey: string | symbol,
600
+ descriptor: PropertyDescriptor,
601
+ ) {
602
+ const originalMethod = descriptor.value;
603
+ const handler = new FallbackHandler({ fallback: fallbackValue, ...options });
604
+
605
+ descriptor.value = async function (...args: any[]) {
606
+ return handler.execute(() => originalMethod.apply(this, args));
607
+ };
608
+
609
+ return descriptor;
610
+ };
611
+ }
612
+
613
+ /**
614
+ * Execute with fallback
615
+ */
616
+ export function withFallback<T>(
617
+ fn: () => Promise<T>,
618
+ fallback: T | (() => T) | (() => Promise<T>),
619
+ ): Promise<T> {
620
+ return new FallbackHandler({ fallback }).execute(fn);
621
+ }
622
+
623
+ /**
624
+ * Cache-based fallback
625
+ */
626
+ export class CachedFallback<T> {
627
+ private cache: T | null = null;
628
+ private cacheTime: number = 0;
629
+
630
+ constructor(private readonly ttl: number = 60000) {}
631
+
632
+ async execute(fn: () => Promise<T>): Promise<T> {
633
+ try {
634
+ const result = await fn();
635
+ this.cache = result;
636
+ this.cacheTime = Date.now();
637
+ return result;
638
+ } catch (error) {
639
+ if (this.cache !== null && Date.now() - this.cacheTime < this.ttl) {
640
+ return this.cache;
641
+ }
642
+ throw error;
643
+ }
644
+ }
645
+
646
+ invalidate(): void {
647
+ this.cache = null;
648
+ this.cacheTime = 0;
649
+ }
650
+ }
651
+
652
+ /**
653
+ * Multi-tier fallback
654
+ */
655
+ export class FallbackChain<T> {
656
+ private readonly handlers: Array<() => Promise<T>> = [];
657
+
658
+ addHandler(handler: () => Promise<T>): this {
659
+ this.handlers.push(handler);
660
+ return this;
661
+ }
662
+
663
+ async execute(): Promise<T> {
664
+ let lastError: Error | null = null;
665
+
666
+ for (const handler of this.handlers) {
667
+ try {
668
+ return await handler();
669
+ } catch (error) {
670
+ lastError = error as Error;
671
+ }
672
+ }
673
+
674
+ throw new FallbackExhaustedError('All fallback handlers failed', lastError!);
675
+ }
676
+ }
677
+
678
+ export class FallbackExhaustedError extends Error {
679
+ constructor(message: string, public readonly lastError: Error) {
680
+ super(message);
681
+ this.name = 'FallbackExhaustedError';
682
+ }
683
+ }
684
+ `;
685
+ }
686
+ function generateBulkhead() {
687
+ return `/**
688
+ * Bulkhead Pattern Implementation
689
+ * Isolates failures by limiting concurrent operations
690
+ */
691
+
692
+ import { Logger } from '@nestjs/common';
693
+
694
+ export interface BulkheadOptions {
695
+ name: string;
696
+ maxConcurrent: number;
697
+ maxQueue?: number;
698
+ timeout?: number;
699
+ onReject?: () => void;
700
+ }
701
+
702
+ export class Bulkhead {
703
+ private readonly logger = new Logger(Bulkhead.name);
704
+ private activeCount = 0;
705
+ private readonly queue: Array<{
706
+ resolve: (value: any) => void;
707
+ reject: (error: Error) => void;
708
+ fn: () => Promise<any>;
709
+ timer?: NodeJS.Timeout;
710
+ }> = [];
711
+
712
+ constructor(private readonly options: BulkheadOptions) {}
713
+
714
+ get availableSlots(): number {
715
+ return this.options.maxConcurrent - this.activeCount;
716
+ }
717
+
718
+ get queueSize(): number {
719
+ return this.queue.length;
720
+ }
721
+
722
+ get isFull(): boolean {
723
+ return this.activeCount >= this.options.maxConcurrent;
724
+ }
725
+
726
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
727
+ if (this.activeCount < this.options.maxConcurrent) {
728
+ return this.executeNow(fn);
729
+ }
730
+
731
+ if (this.options.maxQueue && this.queue.length >= this.options.maxQueue) {
732
+ this.options.onReject?.();
733
+ throw new BulkheadRejectError(
734
+ \`Bulkhead \${this.options.name} is full (active: \${this.activeCount}, queued: \${this.queue.length})\`,
735
+ );
736
+ }
737
+
738
+ return this.enqueue(fn);
739
+ }
740
+
741
+ private async executeNow<T>(fn: () => Promise<T>): Promise<T> {
742
+ this.activeCount++;
743
+
744
+ try {
745
+ return await fn();
746
+ } finally {
747
+ this.activeCount--;
748
+ this.processQueue();
749
+ }
750
+ }
751
+
752
+ private enqueue<T>(fn: () => Promise<T>): Promise<T> {
753
+ return new Promise((resolve, reject) => {
754
+ const item: any = { resolve, reject, fn };
755
+
756
+ if (this.options.timeout) {
757
+ item.timer = setTimeout(() => {
758
+ const index = this.queue.indexOf(item);
759
+ if (index !== -1) {
760
+ this.queue.splice(index, 1);
761
+ reject(new BulkheadTimeoutError(\`Bulkhead \${this.options.name} queue timeout\`));
762
+ }
763
+ }, this.options.timeout);
764
+ }
765
+
766
+ this.queue.push(item);
767
+ });
768
+ }
769
+
770
+ private processQueue(): void {
771
+ if (this.queue.length === 0 || this.activeCount >= this.options.maxConcurrent) {
772
+ return;
773
+ }
774
+
775
+ const item = this.queue.shift()!;
776
+ if (item.timer) {
777
+ clearTimeout(item.timer);
778
+ }
779
+
780
+ this.executeNow(item.fn)
781
+ .then(item.resolve)
782
+ .catch(item.reject);
783
+ }
784
+
785
+ getStats(): BulkheadStats {
786
+ return {
787
+ name: this.options.name,
788
+ activeCount: this.activeCount,
789
+ queueSize: this.queue.length,
790
+ maxConcurrent: this.options.maxConcurrent,
791
+ maxQueue: this.options.maxQueue || 0,
792
+ availableSlots: this.availableSlots,
793
+ };
794
+ }
795
+ }
796
+
797
+ export interface BulkheadStats {
798
+ name: string;
799
+ activeCount: number;
800
+ queueSize: number;
801
+ maxConcurrent: number;
802
+ maxQueue: number;
803
+ availableSlots: number;
804
+ }
805
+
806
+ export class BulkheadRejectError extends Error {
807
+ constructor(message: string) {
808
+ super(message);
809
+ this.name = 'BulkheadRejectError';
810
+ }
811
+ }
812
+
813
+ export class BulkheadTimeoutError extends Error {
814
+ constructor(message: string) {
815
+ super(message);
816
+ this.name = 'BulkheadTimeoutError';
817
+ }
818
+ }
819
+
820
+ /**
821
+ * Bulkhead Registry
822
+ */
823
+ export class BulkheadRegistry {
824
+ private readonly bulkheads = new Map<string, Bulkhead>();
825
+
826
+ getOrCreate(options: BulkheadOptions): Bulkhead {
827
+ let bulkhead = this.bulkheads.get(options.name);
828
+ if (!bulkhead) {
829
+ bulkhead = new Bulkhead(options);
830
+ this.bulkheads.set(options.name, bulkhead);
831
+ }
832
+ return bulkhead;
833
+ }
834
+
835
+ get(name: string): Bulkhead | undefined {
836
+ return this.bulkheads.get(name);
837
+ }
838
+
839
+ getAllStats(): BulkheadStats[] {
840
+ return Array.from(this.bulkheads.values()).map(b => b.getStats());
841
+ }
842
+ }
843
+
844
+ /**
845
+ * Bulkhead decorator factory
846
+ */
847
+ export function BulkheadLimit(options: BulkheadOptions): MethodDecorator {
848
+ const bulkhead = new Bulkhead(options);
849
+
850
+ return function (
851
+ target: any,
852
+ propertyKey: string | symbol,
853
+ descriptor: PropertyDescriptor,
854
+ ) {
855
+ const originalMethod = descriptor.value;
856
+
857
+ descriptor.value = async function (...args: any[]) {
858
+ return bulkhead.execute(() => originalMethod.apply(this, args));
859
+ };
860
+
861
+ return descriptor;
862
+ };
863
+ }
864
+ `;
865
+ }
866
+ function generateResilienceModule() {
867
+ return `import { Module, Global, DynamicModule } from '@nestjs/common';
868
+ import { CircuitBreakerRegistry } from './circuit-breaker';
869
+ import { BulkheadRegistry } from './bulkhead';
870
+
871
+ export interface ResilienceModuleOptions {
872
+ circuitBreaker?: {
873
+ defaultFailureThreshold?: number;
874
+ defaultSuccessThreshold?: number;
875
+ defaultTimeout?: number;
876
+ };
877
+ bulkhead?: {
878
+ defaultMaxConcurrent?: number;
879
+ defaultMaxQueue?: number;
880
+ };
881
+ }
882
+
883
+ @Global()
884
+ @Module({})
885
+ export class ResilienceModule {
886
+ static forRoot(options: ResilienceModuleOptions = {}): DynamicModule {
887
+ return {
888
+ module: ResilienceModule,
889
+ providers: [
890
+ {
891
+ provide: 'RESILIENCE_OPTIONS',
892
+ useValue: options,
893
+ },
894
+ {
895
+ provide: CircuitBreakerRegistry,
896
+ useFactory: () => new CircuitBreakerRegistry(),
897
+ },
898
+ {
899
+ provide: BulkheadRegistry,
900
+ useFactory: () => new BulkheadRegistry(),
901
+ },
902
+ ],
903
+ exports: [CircuitBreakerRegistry, BulkheadRegistry],
904
+ };
905
+ }
906
+ }
907
+ `;
908
+ }
909
+ function generateResilienceDecorators() {
910
+ return `/**
911
+ * Combined Resilience Decorators
912
+ * Apply multiple resilience patterns at once
913
+ */
914
+
915
+ import { CircuitBreaker, CircuitBreakerOptions } from './circuit-breaker';
916
+ import { RetryStrategy, RetryOptions } from './retry.strategy';
917
+ import { TimeoutHandler, TimeoutOptions } from './timeout.handler';
918
+ import { FallbackHandler, FallbackOptions } from './fallback.decorator';
919
+ import { Bulkhead, BulkheadOptions } from './bulkhead';
920
+
921
+ export interface ResilientOptions {
922
+ circuitBreaker?: Partial<CircuitBreakerOptions>;
923
+ retry?: Partial<RetryOptions>;
924
+ timeout?: number;
925
+ fallback?: any;
926
+ bulkhead?: Partial<BulkheadOptions>;
927
+ }
928
+
929
+ /**
930
+ * Combined resilience decorator
931
+ * Applies circuit breaker, retry, timeout, fallback, and bulkhead
932
+ */
933
+ export function Resilient(options: ResilientOptions): MethodDecorator {
934
+ return function (
935
+ target: any,
936
+ propertyKey: string | symbol,
937
+ descriptor: PropertyDescriptor,
938
+ ) {
939
+ const originalMethod = descriptor.value;
940
+ const methodName = String(propertyKey);
941
+
942
+ // Create handlers
943
+ const circuitBreaker = options.circuitBreaker
944
+ ? new CircuitBreaker({
945
+ name: \`\${target.constructor.name}.\${methodName}\`,
946
+ failureThreshold: options.circuitBreaker.failureThreshold || 5,
947
+ successThreshold: options.circuitBreaker.successThreshold || 2,
948
+ timeout: options.circuitBreaker.timeout || 30000,
949
+ ...options.circuitBreaker,
950
+ })
951
+ : null;
952
+
953
+ const retry = options.retry ? new RetryStrategy(options.retry) : null;
954
+ const timeout = options.timeout ? new TimeoutHandler() : null;
955
+ const fallback = options.fallback !== undefined
956
+ ? new FallbackHandler({ fallback: options.fallback })
957
+ : null;
958
+ const bulkhead = options.bulkhead
959
+ ? new Bulkhead({
960
+ name: \`\${target.constructor.name}.\${methodName}\`,
961
+ maxConcurrent: options.bulkhead.maxConcurrent || 10,
962
+ ...options.bulkhead,
963
+ })
964
+ : null;
965
+
966
+ descriptor.value = async function (...args: any[]) {
967
+ let fn = () => originalMethod.apply(this, args);
968
+
969
+ // Apply patterns in order: bulkhead -> circuit breaker -> retry -> timeout -> fallback
970
+ if (bulkhead) {
971
+ const innerFn = fn;
972
+ fn = () => bulkhead.execute(innerFn);
973
+ }
974
+
975
+ if (circuitBreaker) {
976
+ const innerFn = fn;
977
+ fn = () => circuitBreaker.execute(innerFn);
978
+ }
979
+
980
+ if (retry) {
981
+ const innerFn = fn;
982
+ fn = () => retry.execute(innerFn);
983
+ }
984
+
985
+ if (timeout) {
986
+ const innerFn = fn;
987
+ fn = () => timeout.execute(innerFn, { timeout: options.timeout! });
988
+ }
989
+
990
+ if (fallback) {
991
+ const innerFn = fn;
992
+ fn = () => fallback.execute(innerFn);
993
+ }
994
+
995
+ return fn();
996
+ };
997
+
998
+ return descriptor;
999
+ };
1000
+ }
1001
+
1002
+ /**
1003
+ * Apply circuit breaker to a class method
1004
+ */
1005
+ export function WithCircuitBreaker(options: Partial<CircuitBreakerOptions> = {}): MethodDecorator {
1006
+ return function (
1007
+ target: any,
1008
+ propertyKey: string | symbol,
1009
+ descriptor: PropertyDescriptor,
1010
+ ) {
1011
+ const originalMethod = descriptor.value;
1012
+ const breaker = new CircuitBreaker({
1013
+ name: \`\${target.constructor.name}.\${String(propertyKey)}\`,
1014
+ failureThreshold: 5,
1015
+ successThreshold: 2,
1016
+ timeout: 30000,
1017
+ ...options,
1018
+ });
1019
+
1020
+ descriptor.value = async function (...args: any[]) {
1021
+ return breaker.execute(() => originalMethod.apply(this, args));
1022
+ };
1023
+
1024
+ return descriptor;
1025
+ };
1026
+ }
1027
+ `;
1028
+ }
1029
+ //# sourceMappingURL=resilience-patterns.js.map