tlc-claude-code 1.2.29 → 1.4.0

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 (182) hide show
  1. package/dashboard/dist/components/AuditPane.d.ts +30 -0
  2. package/dashboard/dist/components/AuditPane.js +127 -0
  3. package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
  4. package/dashboard/dist/components/AuditPane.test.js +339 -0
  5. package/dashboard/dist/components/CompliancePane.d.ts +39 -0
  6. package/dashboard/dist/components/CompliancePane.js +96 -0
  7. package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
  8. package/dashboard/dist/components/CompliancePane.test.js +183 -0
  9. package/dashboard/dist/components/SSOPane.d.ts +36 -0
  10. package/dashboard/dist/components/SSOPane.js +71 -0
  11. package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
  12. package/dashboard/dist/components/SSOPane.test.js +155 -0
  13. package/dashboard/dist/components/UsagePane.d.ts +13 -0
  14. package/dashboard/dist/components/UsagePane.js +51 -0
  15. package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
  16. package/dashboard/dist/components/UsagePane.test.js +142 -0
  17. package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
  18. package/dashboard/dist/components/WorkspaceDocsPane.js +130 -0
  19. package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
  20. package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
  21. package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
  22. package/dashboard/dist/components/WorkspacePane.js +17 -0
  23. package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
  24. package/dashboard/dist/components/WorkspacePane.test.js +84 -0
  25. package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
  26. package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
  27. package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
  28. package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
  29. package/package.json +1 -1
  30. package/server/lib/access-control-doc.js +541 -0
  31. package/server/lib/access-control-doc.test.js +672 -0
  32. package/server/lib/adr-generator.js +423 -0
  33. package/server/lib/adr-generator.test.js +586 -0
  34. package/server/lib/agent-progress-monitor.js +223 -0
  35. package/server/lib/agent-progress-monitor.test.js +202 -0
  36. package/server/lib/architecture-command.js +450 -0
  37. package/server/lib/architecture-command.test.js +754 -0
  38. package/server/lib/ast-analyzer.js +324 -0
  39. package/server/lib/ast-analyzer.test.js +437 -0
  40. package/server/lib/audit-attribution.js +191 -0
  41. package/server/lib/audit-attribution.test.js +359 -0
  42. package/server/lib/audit-classifier.js +202 -0
  43. package/server/lib/audit-classifier.test.js +209 -0
  44. package/server/lib/audit-command.js +275 -0
  45. package/server/lib/audit-command.test.js +325 -0
  46. package/server/lib/audit-exporter.js +380 -0
  47. package/server/lib/audit-exporter.test.js +464 -0
  48. package/server/lib/audit-logger.js +236 -0
  49. package/server/lib/audit-logger.test.js +364 -0
  50. package/server/lib/audit-query.js +257 -0
  51. package/server/lib/audit-query.test.js +352 -0
  52. package/server/lib/audit-storage.js +269 -0
  53. package/server/lib/audit-storage.test.js +272 -0
  54. package/server/lib/auth-system.test.js +4 -1
  55. package/server/lib/boundary-detector.js +427 -0
  56. package/server/lib/boundary-detector.test.js +320 -0
  57. package/server/lib/budget-alerts.js +138 -0
  58. package/server/lib/budget-alerts.test.js +235 -0
  59. package/server/lib/bulk-repo-init.js +342 -0
  60. package/server/lib/bulk-repo-init.test.js +388 -0
  61. package/server/lib/candidates-tracker.js +210 -0
  62. package/server/lib/candidates-tracker.test.js +300 -0
  63. package/server/lib/checkpoint-manager.js +251 -0
  64. package/server/lib/checkpoint-manager.test.js +474 -0
  65. package/server/lib/circular-detector.js +337 -0
  66. package/server/lib/circular-detector.test.js +353 -0
  67. package/server/lib/cohesion-analyzer.js +310 -0
  68. package/server/lib/cohesion-analyzer.test.js +447 -0
  69. package/server/lib/compliance-checklist.js +866 -0
  70. package/server/lib/compliance-checklist.test.js +476 -0
  71. package/server/lib/compliance-command.js +616 -0
  72. package/server/lib/compliance-command.test.js +551 -0
  73. package/server/lib/compliance-reporter.js +692 -0
  74. package/server/lib/compliance-reporter.test.js +707 -0
  75. package/server/lib/contract-testing.js +625 -0
  76. package/server/lib/contract-testing.test.js +342 -0
  77. package/server/lib/conversion-planner.js +469 -0
  78. package/server/lib/conversion-planner.test.js +361 -0
  79. package/server/lib/convert-command.js +351 -0
  80. package/server/lib/convert-command.test.js +608 -0
  81. package/server/lib/coupling-calculator.js +189 -0
  82. package/server/lib/coupling-calculator.test.js +509 -0
  83. package/server/lib/data-flow-doc.js +665 -0
  84. package/server/lib/data-flow-doc.test.js +659 -0
  85. package/server/lib/dependency-graph.js +367 -0
  86. package/server/lib/dependency-graph.test.js +516 -0
  87. package/server/lib/duplication-detector.js +349 -0
  88. package/server/lib/duplication-detector.test.js +401 -0
  89. package/server/lib/ephemeral-storage.js +249 -0
  90. package/server/lib/ephemeral-storage.test.js +254 -0
  91. package/server/lib/evidence-collector.js +627 -0
  92. package/server/lib/evidence-collector.test.js +901 -0
  93. package/server/lib/example-service.js +616 -0
  94. package/server/lib/example-service.test.js +397 -0
  95. package/server/lib/flow-diagram-generator.js +474 -0
  96. package/server/lib/flow-diagram-generator.test.js +446 -0
  97. package/server/lib/idp-manager.js +626 -0
  98. package/server/lib/idp-manager.test.js +587 -0
  99. package/server/lib/impact-scorer.js +184 -0
  100. package/server/lib/impact-scorer.test.js +211 -0
  101. package/server/lib/memory-exclusion.js +326 -0
  102. package/server/lib/memory-exclusion.test.js +241 -0
  103. package/server/lib/mermaid-generator.js +358 -0
  104. package/server/lib/mermaid-generator.test.js +301 -0
  105. package/server/lib/messaging-patterns.js +750 -0
  106. package/server/lib/messaging-patterns.test.js +213 -0
  107. package/server/lib/mfa-handler.js +452 -0
  108. package/server/lib/mfa-handler.test.js +490 -0
  109. package/server/lib/microservice-template.js +386 -0
  110. package/server/lib/microservice-template.test.js +325 -0
  111. package/server/lib/new-project-microservice.js +450 -0
  112. package/server/lib/new-project-microservice.test.js +600 -0
  113. package/server/lib/oauth-flow.js +375 -0
  114. package/server/lib/oauth-flow.test.js +487 -0
  115. package/server/lib/oauth-registry.js +190 -0
  116. package/server/lib/oauth-registry.test.js +306 -0
  117. package/server/lib/readme-generator.js +490 -0
  118. package/server/lib/readme-generator.test.js +493 -0
  119. package/server/lib/refactor-command.js +326 -0
  120. package/server/lib/refactor-command.test.js +528 -0
  121. package/server/lib/refactor-executor.js +254 -0
  122. package/server/lib/refactor-executor.test.js +305 -0
  123. package/server/lib/refactor-observer.js +292 -0
  124. package/server/lib/refactor-observer.test.js +422 -0
  125. package/server/lib/refactor-progress.js +193 -0
  126. package/server/lib/refactor-progress.test.js +251 -0
  127. package/server/lib/refactor-reporter.js +237 -0
  128. package/server/lib/refactor-reporter.test.js +247 -0
  129. package/server/lib/repo-dependency-tracker.js +261 -0
  130. package/server/lib/repo-dependency-tracker.test.js +350 -0
  131. package/server/lib/retention-policy.js +281 -0
  132. package/server/lib/retention-policy.test.js +486 -0
  133. package/server/lib/role-mapper.js +236 -0
  134. package/server/lib/role-mapper.test.js +395 -0
  135. package/server/lib/saml-provider.js +765 -0
  136. package/server/lib/saml-provider.test.js +643 -0
  137. package/server/lib/security-policy-generator.js +682 -0
  138. package/server/lib/security-policy-generator.test.js +544 -0
  139. package/server/lib/semantic-analyzer.js +198 -0
  140. package/server/lib/semantic-analyzer.test.js +474 -0
  141. package/server/lib/sensitive-detector.js +112 -0
  142. package/server/lib/sensitive-detector.test.js +209 -0
  143. package/server/lib/service-interaction-diagram.js +700 -0
  144. package/server/lib/service-interaction-diagram.test.js +638 -0
  145. package/server/lib/service-scaffold.js +486 -0
  146. package/server/lib/service-scaffold.test.js +373 -0
  147. package/server/lib/service-summary.js +553 -0
  148. package/server/lib/service-summary.test.js +619 -0
  149. package/server/lib/session-purge.js +460 -0
  150. package/server/lib/session-purge.test.js +312 -0
  151. package/server/lib/shared-kernel.js +578 -0
  152. package/server/lib/shared-kernel.test.js +255 -0
  153. package/server/lib/sso-command.js +544 -0
  154. package/server/lib/sso-command.test.js +552 -0
  155. package/server/lib/sso-session.js +492 -0
  156. package/server/lib/sso-session.test.js +670 -0
  157. package/server/lib/traefik-config.js +282 -0
  158. package/server/lib/traefik-config.test.js +312 -0
  159. package/server/lib/usage-command.js +218 -0
  160. package/server/lib/usage-command.test.js +391 -0
  161. package/server/lib/usage-formatter.js +192 -0
  162. package/server/lib/usage-formatter.test.js +267 -0
  163. package/server/lib/usage-history.js +122 -0
  164. package/server/lib/usage-history.test.js +206 -0
  165. package/server/lib/workspace-command.js +249 -0
  166. package/server/lib/workspace-command.test.js +264 -0
  167. package/server/lib/workspace-config.js +270 -0
  168. package/server/lib/workspace-config.test.js +312 -0
  169. package/server/lib/workspace-docs-command.js +547 -0
  170. package/server/lib/workspace-docs-command.test.js +692 -0
  171. package/server/lib/workspace-memory.js +451 -0
  172. package/server/lib/workspace-memory.test.js +403 -0
  173. package/server/lib/workspace-scanner.js +452 -0
  174. package/server/lib/workspace-scanner.test.js +677 -0
  175. package/server/lib/workspace-test-runner.js +315 -0
  176. package/server/lib/workspace-test-runner.test.js +294 -0
  177. package/server/lib/zero-retention-command.js +439 -0
  178. package/server/lib/zero-retention-command.test.js +448 -0
  179. package/server/lib/zero-retention.js +322 -0
  180. package/server/lib/zero-retention.test.js +258 -0
  181. package/server/package-lock.json +14 -0
  182. package/server/package.json +1 -0
@@ -0,0 +1,750 @@
1
+ /**
2
+ * Messaging Patterns Module
3
+ * Inter-service communication setup generator
4
+ */
5
+
6
+ /**
7
+ * Generate UUID v4
8
+ * @returns {string} UUID string
9
+ */
10
+ function generateUuid() {
11
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
12
+ const r = (Math.random() * 16) | 0;
13
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
14
+ return v.toString(16);
15
+ });
16
+ }
17
+
18
+ /**
19
+ * MessagingPatterns class for generating inter-service communication setup
20
+ */
21
+ class MessagingPatterns {
22
+ /**
23
+ * Create a MessagingPatterns instance
24
+ * @param {Object} options - Configuration options
25
+ */
26
+ constructor(options = {}) {
27
+ this.options = options;
28
+ }
29
+
30
+ /**
31
+ * Generate complete messaging setup
32
+ * @param {Object} config - Configuration
33
+ * @param {Array<string>} config.events - Event names
34
+ * @param {string} config.broker - Broker type (e.g., 'redis')
35
+ * @returns {Object} Generated files
36
+ */
37
+ generate(config) {
38
+ const { events = [], broker = 'redis' } = config;
39
+
40
+ const files = [];
41
+
42
+ // Event bus config
43
+ files.push({
44
+ path: 'messaging/event-bus.js',
45
+ content: this.generateEventBusConfig(broker),
46
+ });
47
+
48
+ // Publisher utility
49
+ files.push({
50
+ path: 'messaging/publisher.js',
51
+ content: this.generatePublisher(),
52
+ });
53
+
54
+ // Subscriber utility
55
+ files.push({
56
+ path: 'messaging/subscriber.js',
57
+ content: this.generateSubscriber(),
58
+ });
59
+
60
+ // Dead letter config
61
+ files.push({
62
+ path: 'messaging/dead-letter.js',
63
+ content: this.generateDeadLetterConfig(),
64
+ });
65
+
66
+ // Event catalog
67
+ files.push({
68
+ path: 'messaging/event-catalog.js',
69
+ content: this.generateEventCatalog(events),
70
+ });
71
+
72
+ // Index file
73
+ files.push({
74
+ path: 'messaging/index.js',
75
+ content: this._generateIndex(),
76
+ });
77
+
78
+ return { files };
79
+ }
80
+
81
+ /**
82
+ * Generate event bus configuration
83
+ * @param {string} broker - Broker type
84
+ * @returns {string} Event bus configuration code
85
+ */
86
+ generateEventBusConfig(broker) {
87
+ return `/**
88
+ * Event Bus Configuration
89
+ * Broker: ${broker}
90
+ */
91
+
92
+ const Redis = require('ioredis');
93
+
94
+ // Connection configuration
95
+ const config = {
96
+ broker: '${broker}',
97
+ connectionUrl: process.env.REDIS_URL || 'redis://localhost:6379',
98
+ options: {
99
+ // Retry configuration
100
+ retry: {
101
+ maxAttempts: 10,
102
+ initialDelay: 100,
103
+ maxDelay: 30000,
104
+ factor: 2,
105
+ },
106
+ // Connection options
107
+ lazyConnect: true,
108
+ enableReadyCheck: true,
109
+ maxRetriesPerRequest: 3,
110
+ },
111
+ // Channel naming convention
112
+ channel: {
113
+ prefix: 'events',
114
+ separator: ':',
115
+ },
116
+ };
117
+
118
+ // Create Redis client with retry logic
119
+ function createClient() {
120
+ const client = new Redis(config.connectionUrl, {
121
+ retryStrategy: (times) => {
122
+ if (times > config.options.retry.maxAttempts) {
123
+ return null; // Stop retrying
124
+ }
125
+ const delay = Math.min(
126
+ config.options.retry.initialDelay * Math.pow(config.options.retry.factor, times - 1),
127
+ config.options.retry.maxDelay
128
+ );
129
+ return delay;
130
+ },
131
+ lazyConnect: config.options.lazyConnect,
132
+ enableReadyCheck: config.options.enableReadyCheck,
133
+ maxRetriesPerRequest: config.options.maxRetriesPerRequest,
134
+ });
135
+
136
+ client.on('error', (err) => {
137
+ console.error('[EventBus] Redis connection error:', err.message);
138
+ });
139
+
140
+ client.on('connect', () => {
141
+ console.log('[EventBus] Connected to Redis');
142
+ });
143
+
144
+ client.on('reconnecting', () => {
145
+ console.log('[EventBus] Reconnecting to Redis...');
146
+ });
147
+
148
+ return client;
149
+ }
150
+
151
+ // Get channel name for an event
152
+ function getChannelName(eventName) {
153
+ return \`\${config.channel.prefix}\${config.channel.separator}\${eventName}\`;
154
+ }
155
+
156
+ module.exports = {
157
+ config,
158
+ createClient,
159
+ getChannelName,
160
+ };
161
+ `;
162
+ }
163
+
164
+ /**
165
+ * Generate publisher utility code
166
+ * @returns {string} Publisher utility code
167
+ */
168
+ generatePublisher() {
169
+ return `/**
170
+ * Message Publisher Utility
171
+ */
172
+
173
+ const { createClient, getChannelName } = require('./event-bus');
174
+ const { v4: uuidv4 } = require('uuid');
175
+
176
+ let publisherClient = null;
177
+
178
+ /**
179
+ * Get or create publisher client
180
+ * @returns {Object} Redis client for publishing
181
+ */
182
+ function getPublisherClient() {
183
+ if (!publisherClient) {
184
+ publisherClient = createClient();
185
+ }
186
+ return publisherClient;
187
+ }
188
+
189
+ /**
190
+ * Publish an event to the message broker
191
+ * @param {string} eventName - Name of the event
192
+ * @param {Object} payload - Event payload
193
+ * @param {Object} options - Additional options
194
+ * @returns {Promise<void>}
195
+ */
196
+ async function publish(eventName, payload, options = {}) {
197
+ const client = getPublisherClient();
198
+
199
+ // Add metadata to message
200
+ const message = {
201
+ id: options.id || uuidv4(),
202
+ timestamp: options.timestamp || new Date().toISOString(),
203
+ source: options.source || process.env.SERVICE_NAME || 'unknown',
204
+ type: eventName,
205
+ payload,
206
+ metadata: options.metadata || {},
207
+ };
208
+
209
+ try {
210
+ const channel = getChannelName(eventName);
211
+ const serialized = JSON.stringify(message);
212
+
213
+ await client.publish(channel, serialized);
214
+
215
+ console.log(\`[Publisher] Published \${eventName} to \${channel}\`);
216
+
217
+ return { success: true, messageId: message.id };
218
+ } catch (error) {
219
+ console.error(\`[Publisher] Failed to publish \${eventName}:\`, error.message);
220
+ throw error;
221
+ }
222
+ }
223
+
224
+ /**
225
+ * Publish multiple events in batch
226
+ * @param {Array<{eventName: string, payload: Object}>} events - Events to publish
227
+ * @returns {Promise<Array>}
228
+ */
229
+ async function publishBatch(events) {
230
+ const results = [];
231
+ for (const event of events) {
232
+ try {
233
+ const result = await publish(event.eventName, event.payload, event.options);
234
+ results.push({ ...result, eventName: event.eventName });
235
+ } catch (error) {
236
+ results.push({ success: false, eventName: event.eventName, error: error.message });
237
+ }
238
+ }
239
+ return results;
240
+ }
241
+
242
+ /**
243
+ * Close publisher connection
244
+ * @returns {Promise<void>}
245
+ */
246
+ async function closePublisher() {
247
+ if (publisherClient) {
248
+ await publisherClient.quit();
249
+ publisherClient = null;
250
+ }
251
+ }
252
+
253
+ module.exports = {
254
+ publish,
255
+ publishBatch,
256
+ closePublisher,
257
+ getPublisherClient,
258
+ };
259
+ `;
260
+ }
261
+
262
+ /**
263
+ * Generate subscriber utility code
264
+ * @returns {string} Subscriber utility code
265
+ */
266
+ generateSubscriber() {
267
+ return `/**
268
+ * Message Subscriber Utility
269
+ */
270
+
271
+ const { createClient, getChannelName } = require('./event-bus');
272
+ const { handleDeadLetter } = require('./dead-letter');
273
+
274
+ let subscriberClient = null;
275
+ const handlers = new Map();
276
+
277
+ /**
278
+ * Get or create subscriber client
279
+ * @returns {Object} Redis client for subscribing
280
+ */
281
+ function getSubscriberClient() {
282
+ if (!subscriberClient) {
283
+ subscriberClient = createClient();
284
+ setupMessageHandler();
285
+ }
286
+ return subscriberClient;
287
+ }
288
+
289
+ /**
290
+ * Setup message handler for incoming messages
291
+ */
292
+ function setupMessageHandler() {
293
+ subscriberClient.on('message', async (channel, rawMessage) => {
294
+ try {
295
+ // Deserialize message
296
+ const message = JSON.parse(rawMessage);
297
+
298
+ const eventHandlers = handlers.get(channel);
299
+ if (eventHandlers && eventHandlers.length > 0) {
300
+ for (const handler of eventHandlers) {
301
+ try {
302
+ await handler(message);
303
+ } catch (handlerError) {
304
+ console.error(\`[Subscriber] Handler error on \${channel}:\`, handlerError.message);
305
+ await handleDeadLetter(message, handlerError);
306
+ }
307
+ }
308
+ }
309
+ } catch (parseError) {
310
+ console.error('[Subscriber] Failed to parse message:', parseError.message);
311
+ }
312
+ });
313
+ }
314
+
315
+ /**
316
+ * Subscribe to an event
317
+ * @param {string} eventName - Name of the event
318
+ * @param {Function} handler - Handler function
319
+ * @returns {Promise<void>}
320
+ */
321
+ async function subscribe(eventName, handler) {
322
+ const client = getSubscriberClient();
323
+ const channel = getChannelName(eventName);
324
+
325
+ // Register handler
326
+ if (!handlers.has(channel)) {
327
+ handlers.set(channel, []);
328
+ }
329
+ handlers.get(channel).push(handler);
330
+
331
+ // Subscribe to channel
332
+ await client.subscribe(channel);
333
+
334
+ console.log(\`[Subscriber] Subscribed to \${eventName} on \${channel}\`);
335
+ }
336
+
337
+ /**
338
+ * Unsubscribe from an event
339
+ * @param {string} eventName - Name of the event
340
+ * @returns {Promise<void>}
341
+ */
342
+ async function unsubscribe(eventName) {
343
+ const client = getSubscriberClient();
344
+ const channel = getChannelName(eventName);
345
+
346
+ await client.unsubscribe(channel);
347
+ handlers.delete(channel);
348
+
349
+ console.log(\`[Subscriber] Unsubscribed from \${eventName}\`);
350
+ }
351
+
352
+ /**
353
+ * Close subscriber connection
354
+ * @returns {Promise<void>}
355
+ */
356
+ async function closeSubscriber() {
357
+ if (subscriberClient) {
358
+ await subscriberClient.quit();
359
+ subscriberClient = null;
360
+ handlers.clear();
361
+ }
362
+ }
363
+
364
+ module.exports = {
365
+ subscribe,
366
+ unsubscribe,
367
+ closeSubscriber,
368
+ getSubscriberClient,
369
+ };
370
+ `;
371
+ }
372
+
373
+ /**
374
+ * Generate dead letter queue configuration
375
+ * @returns {string} Dead letter queue configuration code
376
+ */
377
+ generateDeadLetterConfig() {
378
+ return `/**
379
+ * Dead Letter Queue Configuration
380
+ * Handles failed message processing
381
+ */
382
+
383
+ const { createClient, config } = require('./event-bus');
384
+
385
+ let deadLetterClient = null;
386
+
387
+ const DEAD_LETTER_PREFIX = 'dead:letter:queue';
388
+ const MAX_RETRY_COUNT = 3;
389
+
390
+ /**
391
+ * Get or create dead letter client
392
+ * @returns {Object} Redis client for dead letter queue
393
+ */
394
+ function getDeadLetterClient() {
395
+ if (!deadLetterClient) {
396
+ deadLetterClient = createClient();
397
+ }
398
+ return deadLetterClient;
399
+ }
400
+
401
+ /**
402
+ * Handle failed message by storing in dead letter queue
403
+ * @param {Object} message - Original message
404
+ * @param {Error} error - Error that occurred
405
+ * @returns {Promise<void>}
406
+ */
407
+ async function handleDeadLetter(message, error) {
408
+ const client = getDeadLetterClient();
409
+
410
+ // Get current retry count
411
+ const retryCount = (message.metadata?.retryCount || 0) + 1;
412
+
413
+ const deadLetterEntry = {
414
+ originalMessage: message,
415
+ error: {
416
+ message: error.message,
417
+ stack: error.stack,
418
+ },
419
+ failedAt: new Date().toISOString(),
420
+ retryCount: retryCount,
421
+ maxRetries: MAX_RETRY_COUNT,
422
+ canRetry: retryCount < MAX_RETRY_COUNT,
423
+ };
424
+
425
+ const key = \`\${DEAD_LETTER_PREFIX}:\${message.type}:\${message.id}\`;
426
+
427
+ await client.set(key, JSON.stringify(deadLetterEntry));
428
+ await client.lpush(\`\${DEAD_LETTER_PREFIX}:list\`, key);
429
+
430
+ console.log(\`[DeadLetter] Stored failed message \${message.id} (retry count: \${retryCount})\`);
431
+ }
432
+
433
+ /**
434
+ * Get all messages in dead letter queue
435
+ * @param {Object} options - Query options
436
+ * @returns {Promise<Array>}
437
+ */
438
+ async function getDeadLetterMessages(options = {}) {
439
+ const client = getDeadLetterClient();
440
+ const { limit = 100, offset = 0 } = options;
441
+
442
+ const keys = await client.lrange(\`\${DEAD_LETTER_PREFIX}:list\`, offset, offset + limit - 1);
443
+
444
+ const messages = [];
445
+ for (const key of keys) {
446
+ const data = await client.get(key);
447
+ if (data) {
448
+ messages.push(JSON.parse(data));
449
+ }
450
+ }
451
+
452
+ return messages;
453
+ }
454
+
455
+ /**
456
+ * Retry a dead letter message
457
+ * @param {string} messageId - Message ID to retry
458
+ * @param {string} eventType - Event type
459
+ * @returns {Promise<Object>}
460
+ */
461
+ async function retryDeadLetter(messageId, eventType) {
462
+ const client = getDeadLetterClient();
463
+ const key = \`\${DEAD_LETTER_PREFIX}:\${eventType}:\${messageId}\`;
464
+
465
+ const data = await client.get(key);
466
+ if (!data) {
467
+ throw new Error(\`Dead letter message not found: \${messageId}\`);
468
+ }
469
+
470
+ const entry = JSON.parse(data);
471
+
472
+ if (!entry.canRetry) {
473
+ throw new Error(\`Message \${messageId} has exceeded max retry count\`);
474
+ }
475
+
476
+ // Update retry count in original message
477
+ const messageToRetry = {
478
+ ...entry.originalMessage,
479
+ metadata: {
480
+ ...entry.originalMessage.metadata,
481
+ retryCount: entry.retryCount,
482
+ retriedAt: new Date().toISOString(),
483
+ },
484
+ };
485
+
486
+ // Remove from dead letter queue
487
+ await client.del(key);
488
+ await client.lrem(\`\${DEAD_LETTER_PREFIX}:list\`, 1, key);
489
+
490
+ return messageToRetry;
491
+ }
492
+
493
+ /**
494
+ * Purge dead letter queue
495
+ * @param {Object} options - Purge options
496
+ * @returns {Promise<number>} Number of messages purged
497
+ */
498
+ async function purgeDeadLetter(options = {}) {
499
+ const client = getDeadLetterClient();
500
+ const { olderThan } = options;
501
+
502
+ const keys = await client.lrange(\`\${DEAD_LETTER_PREFIX}:list\`, 0, -1);
503
+
504
+ let purged = 0;
505
+ for (const key of keys) {
506
+ const data = await client.get(key);
507
+ if (data) {
508
+ const entry = JSON.parse(data);
509
+ const shouldPurge = !olderThan || new Date(entry.failedAt) < olderThan;
510
+
511
+ if (shouldPurge) {
512
+ await client.del(key);
513
+ await client.lrem(\`\${DEAD_LETTER_PREFIX}:list\`, 1, key);
514
+ purged++;
515
+ }
516
+ }
517
+ }
518
+
519
+ return purged;
520
+ }
521
+
522
+ /**
523
+ * Close dead letter client
524
+ * @returns {Promise<void>}
525
+ */
526
+ async function closeDeadLetterClient() {
527
+ if (deadLetterClient) {
528
+ await deadLetterClient.quit();
529
+ deadLetterClient = null;
530
+ }
531
+ }
532
+
533
+ module.exports = {
534
+ handleDeadLetter,
535
+ getDeadLetterMessages,
536
+ retryDeadLetter,
537
+ purgeDeadLetter,
538
+ closeDeadLetterClient,
539
+ MAX_RETRY_COUNT,
540
+ DEAD_LETTER_PREFIX,
541
+ };
542
+ `;
543
+ }
544
+
545
+ /**
546
+ * Generate event catalog
547
+ * @param {Array<string>} events - Event names
548
+ * @returns {string} Event catalog code
549
+ */
550
+ generateEventCatalog(events) {
551
+ const eventEntries = events
552
+ .map((event) => {
553
+ const schemaExample = this._generateEventSchema(event);
554
+ return ` ${event}: {
555
+ name: '${event}',
556
+ description: '${this._generateEventDescription(event)}',
557
+ schema: ${JSON.stringify(schemaExample.schema, null, 6).split('\n').join('\n ')},
558
+ example: ${JSON.stringify(schemaExample.example, null, 6).split('\n').join('\n ')},
559
+ }`;
560
+ })
561
+ .join(',\n');
562
+
563
+ return `/**
564
+ * Event Catalog
565
+ * Lists all event types with schemas and examples
566
+ */
567
+
568
+ const catalog = {
569
+ ${eventEntries}
570
+ };
571
+
572
+ /**
573
+ * Get all event types
574
+ * @returns {Array<string>}
575
+ */
576
+ function getEventTypes() {
577
+ return Object.keys(catalog);
578
+ }
579
+
580
+ /**
581
+ * Get event schema
582
+ * @param {string} eventName - Event name
583
+ * @returns {Object|null}
584
+ */
585
+ function getEventSchema(eventName) {
586
+ const event = catalog[eventName];
587
+ return event ? event.schema : null;
588
+ }
589
+
590
+ /**
591
+ * Get event example
592
+ * @param {string} eventName - Event name
593
+ * @returns {Object|null}
594
+ */
595
+ function getEventExample(eventName) {
596
+ const event = catalog[eventName];
597
+ return event ? event.example : null;
598
+ }
599
+
600
+ /**
601
+ * Validate event payload against schema
602
+ * @param {string} eventName - Event name
603
+ * @param {Object} payload - Payload to validate
604
+ * @returns {Object} Validation result
605
+ */
606
+ function validateEventPayload(eventName, payload) {
607
+ const schema = getEventSchema(eventName);
608
+ if (!schema) {
609
+ return { valid: false, errors: ['Unknown event type'] };
610
+ }
611
+
612
+ const errors = [];
613
+ const required = schema.required || [];
614
+
615
+ for (const field of required) {
616
+ if (payload[field] === undefined) {
617
+ errors.push(\`Missing required field: \${field}\`);
618
+ }
619
+ }
620
+
621
+ return {
622
+ valid: errors.length === 0,
623
+ errors,
624
+ };
625
+ }
626
+
627
+ module.exports = {
628
+ catalog,
629
+ getEventTypes,
630
+ getEventSchema,
631
+ getEventExample,
632
+ validateEventPayload,
633
+ };
634
+ `;
635
+ }
636
+
637
+ /**
638
+ * Generate event schema based on event name
639
+ * @private
640
+ */
641
+ _generateEventSchema(eventName) {
642
+ // Parse event name to determine schema
643
+ const parts = eventName.match(/([A-Z][a-z]+)/g) || [eventName];
644
+ const entity = parts[0].toLowerCase();
645
+ const action = parts.slice(1).join('').toLowerCase() || 'action';
646
+
647
+ return {
648
+ schema: {
649
+ type: 'object',
650
+ required: ['id', 'timestamp', 'type', 'payload'],
651
+ properties: {
652
+ id: { type: 'string', format: 'uuid' },
653
+ timestamp: { type: 'string', format: 'date-time' },
654
+ type: { type: 'string', const: eventName },
655
+ source: { type: 'string' },
656
+ payload: {
657
+ type: 'object',
658
+ properties: {
659
+ [`${entity}Id`]: { type: 'string' },
660
+ data: { type: 'object' },
661
+ },
662
+ },
663
+ metadata: { type: 'object' },
664
+ },
665
+ },
666
+ example: {
667
+ id: 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d',
668
+ timestamp: '2024-01-15T10:30:00.000Z',
669
+ type: eventName,
670
+ source: 'service-name',
671
+ payload: {
672
+ [`${entity}Id`]: 'entity-123',
673
+ data: {},
674
+ },
675
+ metadata: {},
676
+ },
677
+ };
678
+ }
679
+
680
+ /**
681
+ * Generate event description based on name
682
+ * @private
683
+ */
684
+ _generateEventDescription(eventName) {
685
+ const parts = eventName.match(/([A-Z][a-z]+)/g) || [eventName];
686
+ if (parts.length >= 2) {
687
+ return `Emitted when a ${parts[0].toLowerCase()} is ${parts.slice(1).join(' ').toLowerCase()}`;
688
+ }
689
+ return `Event: ${eventName}`;
690
+ }
691
+
692
+ /**
693
+ * Generate index file
694
+ * @private
695
+ */
696
+ _generateIndex() {
697
+ return `/**
698
+ * Messaging Module Index
699
+ */
700
+
701
+ const eventBus = require('./event-bus');
702
+ const publisher = require('./publisher');
703
+ const subscriber = require('./subscriber');
704
+ const deadLetter = require('./dead-letter');
705
+ const eventCatalog = require('./event-catalog');
706
+
707
+ module.exports = {
708
+ // Event Bus
709
+ ...eventBus,
710
+
711
+ // Publisher
712
+ publish: publisher.publish,
713
+ publishBatch: publisher.publishBatch,
714
+ closePublisher: publisher.closePublisher,
715
+
716
+ // Subscriber
717
+ subscribe: subscriber.subscribe,
718
+ unsubscribe: subscriber.unsubscribe,
719
+ closeSubscriber: subscriber.closeSubscriber,
720
+
721
+ // Dead Letter
722
+ handleDeadLetter: deadLetter.handleDeadLetter,
723
+ getDeadLetterMessages: deadLetter.getDeadLetterMessages,
724
+ retryDeadLetter: deadLetter.retryDeadLetter,
725
+ purgeDeadLetter: deadLetter.purgeDeadLetter,
726
+
727
+ // Event Catalog
728
+ catalog: eventCatalog.catalog,
729
+ getEventTypes: eventCatalog.getEventTypes,
730
+ getEventSchema: eventCatalog.getEventSchema,
731
+ getEventExample: eventCatalog.getEventExample,
732
+ validateEventPayload: eventCatalog.validateEventPayload,
733
+ };
734
+ `;
735
+ }
736
+ }
737
+
738
+ /**
739
+ * Create messaging patterns instance
740
+ * @param {Object} options - Options
741
+ * @returns {MessagingPatterns} Instance
742
+ */
743
+ function createMessagingPatterns(options = {}) {
744
+ return new MessagingPatterns(options);
745
+ }
746
+
747
+ module.exports = {
748
+ MessagingPatterns,
749
+ createMessagingPatterns,
750
+ };