specweave 0.18.1 → 0.20.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 (384) hide show
  1. package/CLAUDE.md +229 -1817
  2. package/README.md +68 -0
  3. package/bin/specweave.js +62 -6
  4. package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
  5. package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +3 -0
  6. package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
  7. package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts +21 -0
  8. package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts.map +1 -0
  9. package/dist/plugins/specweave/lib/hooks/update-ac-status.js +162 -0
  10. package/dist/plugins/specweave/lib/hooks/update-ac-status.js.map +1 -0
  11. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
  12. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
  13. package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
  14. package/dist/plugins/specweave-github/lib/completion-calculator.d.ts +112 -0
  15. package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -0
  16. package/dist/plugins/specweave-github/lib/completion-calculator.js +301 -0
  17. package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -0
  18. package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +3 -3
  19. package/dist/plugins/specweave-github/lib/duplicate-detector.js +3 -3
  20. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +7 -0
  21. package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -1
  22. package/dist/plugins/specweave-github/lib/epic-content-builder.js +42 -0
  23. package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -1
  24. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +14 -0
  25. package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
  26. package/dist/plugins/specweave-github/lib/github-client-v2.js +51 -0
  27. package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
  28. package/dist/plugins/specweave-github/lib/github-epic-sync.js +1 -1
  29. package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -1
  30. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +87 -0
  31. package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -0
  32. package/dist/plugins/specweave-github/lib/github-feature-sync.js +412 -0
  33. package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -0
  34. package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
  35. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +64 -13
  36. package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
  37. package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts +78 -0
  38. package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts.map +1 -0
  39. package/dist/plugins/specweave-github/lib/progress-comment-builder.js +237 -0
  40. package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -0
  41. package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts +97 -0
  42. package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts.map +1 -0
  43. package/dist/plugins/specweave-github/lib/user-story-content-builder.js +301 -0
  44. package/dist/plugins/specweave-github/lib/user-story-content-builder.js.map +1 -0
  45. package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts +83 -0
  46. package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -0
  47. package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +386 -0
  48. package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -0
  49. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +8 -6
  50. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -1
  51. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +78 -117
  52. package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -1
  53. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts +57 -0
  54. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts.map +1 -0
  55. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +248 -0
  56. package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js.map +1 -0
  57. package/dist/plugins/specweave-kafka/lib/cli/types.d.ts +82 -0
  58. package/dist/plugins/specweave-kafka/lib/cli/types.d.ts.map +1 -0
  59. package/dist/plugins/specweave-kafka/lib/cli/types.js +13 -0
  60. package/dist/plugins/specweave-kafka/lib/cli/types.js.map +1 -0
  61. package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts +49 -0
  62. package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts.map +1 -0
  63. package/dist/plugins/specweave-kafka/lib/mcp/detector.js +316 -0
  64. package/dist/plugins/specweave-kafka/lib/mcp/detector.js.map +1 -0
  65. package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts +70 -0
  66. package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts.map +1 -0
  67. package/dist/plugins/specweave-kafka/lib/mcp/types.js +23 -0
  68. package/dist/plugins/specweave-kafka/lib/mcp/types.js.map +1 -0
  69. package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts +85 -0
  70. package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts.map +1 -0
  71. package/dist/plugins/specweave-kafka/lib/utils/partitioning.js +281 -0
  72. package/dist/plugins/specweave-kafka/lib/utils/partitioning.js.map +1 -0
  73. package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts +75 -0
  74. package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts.map +1 -0
  75. package/dist/plugins/specweave-kafka/lib/utils/sizing.js +238 -0
  76. package/dist/plugins/specweave-kafka/lib/utils/sizing.js.map +1 -0
  77. package/dist/src/cli/commands/import-docs.js +4 -4
  78. package/dist/src/cli/commands/import-docs.js.map +1 -1
  79. package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
  80. package/dist/src/cli/commands/init-multiproject.js +17 -18
  81. package/dist/src/cli/commands/init-multiproject.js.map +1 -1
  82. package/dist/src/cli/commands/migrate-to-multiproject.d.ts.map +1 -1
  83. package/dist/src/cli/commands/migrate-to-multiproject.js +8 -4
  84. package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
  85. package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
  86. package/dist/src/cli/commands/switch-project.js +9 -26
  87. package/dist/src/cli/commands/switch-project.js.map +1 -1
  88. package/dist/src/cli/commands/sync-spec-content.js +3 -0
  89. package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
  90. package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
  91. package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
  92. package/dist/src/core/deduplication/command-deduplicator.js +254 -0
  93. package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
  94. package/dist/src/core/increment/active-increment-manager.d.ts +42 -15
  95. package/dist/src/core/increment/active-increment-manager.d.ts.map +1 -1
  96. package/dist/src/core/increment/active-increment-manager.js +113 -46
  97. package/dist/src/core/increment/active-increment-manager.js.map +1 -1
  98. package/dist/src/core/increment/conflict-resolver.d.ts +40 -0
  99. package/dist/src/core/increment/conflict-resolver.d.ts.map +1 -0
  100. package/dist/src/core/increment/conflict-resolver.js +219 -0
  101. package/dist/src/core/increment/conflict-resolver.js.map +1 -0
  102. package/dist/src/core/increment/discipline-checker.d.ts.map +1 -1
  103. package/dist/src/core/increment/discipline-checker.js +7 -1
  104. package/dist/src/core/increment/discipline-checker.js.map +1 -1
  105. package/dist/src/core/increment/duplicate-detector.d.ts +52 -0
  106. package/dist/src/core/increment/duplicate-detector.d.ts.map +1 -0
  107. package/dist/src/core/increment/duplicate-detector.js +276 -0
  108. package/dist/src/core/increment/duplicate-detector.js.map +1 -0
  109. package/dist/src/core/increment/increment-archiver.d.ts +90 -0
  110. package/dist/src/core/increment/increment-archiver.d.ts.map +1 -0
  111. package/dist/src/core/increment/increment-archiver.js +368 -0
  112. package/dist/src/core/increment/increment-archiver.js.map +1 -0
  113. package/dist/src/core/increment/increment-reopener.d.ts +165 -0
  114. package/dist/src/core/increment/increment-reopener.d.ts.map +1 -0
  115. package/dist/src/core/increment/increment-reopener.js +390 -0
  116. package/dist/src/core/increment/increment-reopener.js.map +1 -0
  117. package/dist/src/core/increment/metadata-manager.d.ts +26 -1
  118. package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
  119. package/dist/src/core/increment/metadata-manager.js +143 -5
  120. package/dist/src/core/increment/metadata-manager.js.map +1 -1
  121. package/dist/src/core/increment/recent-work-scanner.d.ts +121 -0
  122. package/dist/src/core/increment/recent-work-scanner.d.ts.map +1 -0
  123. package/dist/src/core/increment/recent-work-scanner.js +303 -0
  124. package/dist/src/core/increment/recent-work-scanner.js.map +1 -0
  125. package/dist/src/core/increment/types.d.ts +1 -0
  126. package/dist/src/core/increment/types.d.ts.map +1 -1
  127. package/dist/src/core/increment-utils.d.ts +112 -0
  128. package/dist/src/core/increment-utils.d.ts.map +1 -0
  129. package/dist/src/core/increment-utils.js +210 -0
  130. package/dist/src/core/increment-utils.js.map +1 -0
  131. package/dist/src/core/living-docs/ac-project-specific-generator.d.ts +65 -0
  132. package/dist/src/core/living-docs/ac-project-specific-generator.d.ts.map +1 -0
  133. package/dist/src/core/living-docs/ac-project-specific-generator.js +175 -0
  134. package/dist/src/core/living-docs/ac-project-specific-generator.js.map +1 -0
  135. package/dist/src/core/living-docs/feature-archiver.d.ts +130 -0
  136. package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -0
  137. package/dist/src/core/living-docs/feature-archiver.js +549 -0
  138. package/dist/src/core/living-docs/feature-archiver.js.map +1 -0
  139. package/dist/src/core/living-docs/feature-id-manager.d.ts +81 -0
  140. package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -0
  141. package/dist/src/core/living-docs/feature-id-manager.js +339 -0
  142. package/dist/src/core/living-docs/feature-id-manager.js.map +1 -0
  143. package/dist/src/core/living-docs/hierarchy-mapper.d.ts +144 -83
  144. package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
  145. package/dist/src/core/living-docs/hierarchy-mapper.js +488 -270
  146. package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
  147. package/dist/src/core/living-docs/index.d.ts +6 -0
  148. package/dist/src/core/living-docs/index.d.ts.map +1 -1
  149. package/dist/src/core/living-docs/index.js +6 -0
  150. package/dist/src/core/living-docs/index.js.map +1 -1
  151. package/dist/src/core/living-docs/project-detector.d.ts +6 -0
  152. package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
  153. package/dist/src/core/living-docs/project-detector.js +35 -1
  154. package/dist/src/core/living-docs/project-detector.js.map +1 -1
  155. package/dist/src/core/living-docs/spec-distributor.d.ts +100 -26
  156. package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -1
  157. package/dist/src/core/living-docs/spec-distributor.js +1275 -258
  158. package/dist/src/core/living-docs/spec-distributor.js.map +1 -1
  159. package/dist/src/core/living-docs/task-project-specific-generator.d.ts +109 -0
  160. package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -0
  161. package/dist/src/core/living-docs/task-project-specific-generator.js +221 -0
  162. package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -0
  163. package/dist/src/core/living-docs/types.d.ts +143 -0
  164. package/dist/src/core/living-docs/types.d.ts.map +1 -1
  165. package/dist/src/core/project-manager.d.ts +2 -17
  166. package/dist/src/core/project-manager.d.ts.map +1 -1
  167. package/dist/src/core/project-manager.js +68 -48
  168. package/dist/src/core/project-manager.js.map +1 -1
  169. package/dist/src/core/spec-content-sync.d.ts +1 -1
  170. package/dist/src/core/spec-content-sync.d.ts.map +1 -1
  171. package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -1
  172. package/dist/src/core/sync/enhanced-content-builder.js +2 -1
  173. package/dist/src/core/sync/enhanced-content-builder.js.map +1 -1
  174. package/dist/src/core/sync/performance-optimizer.d.ts +153 -0
  175. package/dist/src/core/sync/performance-optimizer.d.ts.map +1 -0
  176. package/dist/src/core/sync/performance-optimizer.js +220 -0
  177. package/dist/src/core/sync/performance-optimizer.js.map +1 -0
  178. package/dist/src/core/sync/retry-handler.d.ts +98 -0
  179. package/dist/src/core/sync/retry-handler.d.ts.map +1 -0
  180. package/dist/src/core/sync/retry-handler.js +196 -0
  181. package/dist/src/core/sync/retry-handler.js.map +1 -0
  182. package/dist/src/core/types/config.d.ts +94 -0
  183. package/dist/src/core/types/config.d.ts.map +1 -1
  184. package/dist/src/core/types/config.js +16 -0
  185. package/dist/src/core/types/config.js.map +1 -1
  186. package/dist/src/core/types/increment-metadata.d.ts +6 -0
  187. package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
  188. package/dist/src/core/types/increment-metadata.js +10 -1
  189. package/dist/src/core/types/increment-metadata.js.map +1 -1
  190. package/dist/src/integrations/jira/jira-incremental-mapper.d.ts.map +1 -1
  191. package/dist/src/integrations/jira/jira-incremental-mapper.js +4 -8
  192. package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
  193. package/dist/src/integrations/jira/jira-mapper.d.ts.map +1 -1
  194. package/dist/src/integrations/jira/jira-mapper.js +4 -8
  195. package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
  196. package/package.json +1 -1
  197. package/plugins/specweave/COMMANDS.md +13 -4
  198. package/plugins/specweave/commands/specweave-abandon.md +22 -20
  199. package/plugins/specweave/commands/specweave-archive-features.md +121 -0
  200. package/plugins/specweave/commands/specweave-archive-increments.md +82 -0
  201. package/plugins/specweave/commands/specweave-archive.md +363 -0
  202. package/plugins/specweave/commands/specweave-backlog.md +211 -0
  203. package/plugins/specweave/commands/specweave-fix-duplicates.md +517 -0
  204. package/plugins/specweave/commands/specweave-increment.md +4 -3
  205. package/plugins/specweave/commands/specweave-progress.md +176 -27
  206. package/plugins/specweave/commands/specweave-reopen.md +391 -0
  207. package/plugins/specweave/commands/specweave-restore-feature.md +90 -0
  208. package/plugins/specweave/commands/specweave-restore.md +309 -0
  209. package/plugins/specweave/commands/specweave-resume.md +51 -23
  210. package/plugins/specweave/commands/specweave-status.md +41 -7
  211. package/plugins/specweave/commands/specweave-sync-specs.md +425 -0
  212. package/plugins/specweave/hooks/hooks.json +4 -0
  213. package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
  214. package/plugins/specweave/hooks/post-task-completion.sh +39 -0
  215. package/plugins/specweave/hooks/pre-command-deduplication.sh +83 -0
  216. package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
  217. package/plugins/specweave/lib/hooks/sync-living-docs.js +2 -0
  218. package/plugins/specweave/lib/hooks/sync-living-docs.ts +4 -0
  219. package/plugins/specweave/lib/hooks/update-ac-status.js +102 -0
  220. package/plugins/specweave/lib/hooks/update-ac-status.ts +192 -0
  221. package/plugins/specweave/skills/archive-increments/SKILL.md +198 -0
  222. package/plugins/specweave/skills/increment-planner/scripts/feature-utils.js +14 -0
  223. package/plugins/specweave/skills/smart-reopen-detector/SKILL.md +244 -0
  224. package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
  225. package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
  226. package/plugins/specweave-confluent/.claude-plugin/plugin.json +23 -0
  227. package/plugins/specweave-confluent/README.md +375 -0
  228. package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +306 -0
  229. package/plugins/specweave-confluent/skills/confluent-kafka-connect/SKILL.md +453 -0
  230. package/plugins/specweave-confluent/skills/confluent-ksqldb/SKILL.md +470 -0
  231. package/plugins/specweave-confluent/skills/confluent-schema-registry/SKILL.md +316 -0
  232. package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +2 -2
  233. package/plugins/specweave-github/agents/user-story-updater/AGENT.md +148 -0
  234. package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +1 -1
  235. package/plugins/specweave-github/commands/specweave-github-update-user-story.md +156 -0
  236. package/plugins/specweave-github/hooks/post-task-completion.sh +10 -9
  237. package/plugins/specweave-github/lib/completion-calculator.js +262 -0
  238. package/plugins/specweave-github/lib/completion-calculator.ts +434 -0
  239. package/plugins/specweave-github/lib/duplicate-detector.js +3 -3
  240. package/plugins/specweave-github/lib/duplicate-detector.ts +4 -4
  241. package/plugins/specweave-github/lib/epic-content-builder.js +38 -0
  242. package/plugins/specweave-github/lib/epic-content-builder.ts +59 -0
  243. package/plugins/specweave-github/lib/github-client-v2.js +49 -0
  244. package/plugins/specweave-github/lib/github-client-v2.ts +59 -0
  245. package/plugins/specweave-github/lib/github-epic-sync.ts +1 -1
  246. package/plugins/specweave-github/lib/github-feature-sync.js +381 -0
  247. package/plugins/specweave-github/lib/github-feature-sync.ts +568 -0
  248. package/plugins/specweave-github/lib/github-spec-content-sync.js +40 -10
  249. package/plugins/specweave-github/lib/github-spec-content-sync.ts +82 -14
  250. package/plugins/specweave-github/lib/progress-comment-builder.js +229 -0
  251. package/plugins/specweave-github/lib/progress-comment-builder.ts +324 -0
  252. package/plugins/specweave-github/lib/user-story-content-builder.js +299 -0
  253. package/plugins/specweave-github/lib/user-story-content-builder.ts +413 -0
  254. package/plugins/specweave-github/lib/user-story-issue-builder.js +344 -0
  255. package/plugins/specweave-github/lib/user-story-issue-builder.ts +543 -0
  256. package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +189 -0
  257. package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
  258. package/plugins/specweave-jira/lib/{enhanced-jira-sync.ts.disabled → enhanced-jira-sync.ts} +26 -52
  259. package/plugins/specweave-kafka/.claude-plugin/plugin.json +26 -0
  260. package/plugins/specweave-kafka/IMPLEMENTATION-COMPLETE.md +483 -0
  261. package/plugins/specweave-kafka/README.md +242 -0
  262. package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +235 -0
  263. package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +209 -0
  264. package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +266 -0
  265. package/plugins/specweave-kafka/commands/deploy.md +99 -0
  266. package/plugins/specweave-kafka/commands/dev-env.md +176 -0
  267. package/plugins/specweave-kafka/commands/mcp-configure.md +101 -0
  268. package/plugins/specweave-kafka/commands/monitor-setup.md +96 -0
  269. package/plugins/specweave-kafka/docker/kafka-local/docker-compose.yml +187 -0
  270. package/plugins/specweave-kafka/docker/redpanda/docker-compose.yml +199 -0
  271. package/plugins/specweave-kafka/docker/templates/consumer-nodejs.js +225 -0
  272. package/plugins/specweave-kafka/docker/templates/consumer-python.py +220 -0
  273. package/plugins/specweave-kafka/docker/templates/producer-nodejs.js +168 -0
  274. package/plugins/specweave-kafka/docker/templates/producer-python.py +167 -0
  275. package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.js +438 -0
  276. package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.ts +541 -0
  277. package/plugins/specweave-kafka/lib/adapters/platform-adapter.js +47 -0
  278. package/plugins/specweave-kafka/lib/adapters/platform-adapter.ts +343 -0
  279. package/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +258 -0
  280. package/plugins/specweave-kafka/lib/cli/kcat-wrapper.ts +298 -0
  281. package/plugins/specweave-kafka/lib/cli/types.js +10 -0
  282. package/plugins/specweave-kafka/lib/cli/types.ts +92 -0
  283. package/plugins/specweave-kafka/lib/connectors/connector-catalog.js +305 -0
  284. package/plugins/specweave-kafka/lib/connectors/connector-catalog.ts +528 -0
  285. package/plugins/specweave-kafka/lib/documentation/diagram-generator.js +114 -0
  286. package/plugins/specweave-kafka/lib/documentation/diagram-generator.ts +195 -0
  287. package/plugins/specweave-kafka/lib/documentation/exporter.js +210 -0
  288. package/plugins/specweave-kafka/lib/documentation/exporter.ts +338 -0
  289. package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.js +60 -0
  290. package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.ts +130 -0
  291. package/plugins/specweave-kafka/lib/documentation/topology-generator.js +143 -0
  292. package/plugins/specweave-kafka/lib/documentation/topology-generator.ts +290 -0
  293. package/plugins/specweave-kafka/lib/mcp/detector.js +298 -0
  294. package/plugins/specweave-kafka/lib/mcp/detector.ts +352 -0
  295. package/plugins/specweave-kafka/lib/mcp/types.js +21 -0
  296. package/plugins/specweave-kafka/lib/mcp/types.ts +77 -0
  297. package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.js +193 -0
  298. package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.ts +362 -0
  299. package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.js +188 -0
  300. package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.ts +359 -0
  301. package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.js +195 -0
  302. package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.ts +380 -0
  303. package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.js +209 -0
  304. package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.ts +358 -0
  305. package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.js +354 -0
  306. package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.ts +563 -0
  307. package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.js +259 -0
  308. package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.ts +516 -0
  309. package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.js +233 -0
  310. package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.ts +423 -0
  311. package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.js +266 -0
  312. package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.ts +445 -0
  313. package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.js +312 -0
  314. package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.ts +561 -0
  315. package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.js +289 -0
  316. package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.ts +607 -0
  317. package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.js +264 -0
  318. package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.ts +498 -0
  319. package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.js +263 -0
  320. package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.ts +549 -0
  321. package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.js +205 -0
  322. package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.ts +399 -0
  323. package/plugins/specweave-kafka/lib/performance/performance-optimizer.js +249 -0
  324. package/plugins/specweave-kafka/lib/performance/performance-optimizer.ts +427 -0
  325. package/plugins/specweave-kafka/lib/security/kafka-security.js +252 -0
  326. package/plugins/specweave-kafka/lib/security/kafka-security.ts +494 -0
  327. package/plugins/specweave-kafka/lib/utils/capacity-planner.js +203 -0
  328. package/plugins/specweave-kafka/lib/utils/capacity-planner.ts +469 -0
  329. package/plugins/specweave-kafka/lib/utils/config-validator.js +419 -0
  330. package/plugins/specweave-kafka/lib/utils/config-validator.ts +564 -0
  331. package/plugins/specweave-kafka/lib/utils/partitioning.js +329 -0
  332. package/plugins/specweave-kafka/lib/utils/partitioning.ts +473 -0
  333. package/plugins/specweave-kafka/lib/utils/sizing.js +221 -0
  334. package/plugins/specweave-kafka/lib/utils/sizing.ts +374 -0
  335. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-broker-metrics.json +628 -0
  336. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-cluster-overview.json +564 -0
  337. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-consumer-lag.json +509 -0
  338. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-jvm-metrics.json +674 -0
  339. package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-topic-metrics.json +578 -0
  340. package/plugins/specweave-kafka/monitoring/grafana/provisioning/dashboards/kafka.yml +17 -0
  341. package/plugins/specweave-kafka/monitoring/grafana/provisioning/datasources/prometheus.yml +17 -0
  342. package/plugins/specweave-kafka/monitoring/prometheus/kafka-alerts.yml +415 -0
  343. package/plugins/specweave-kafka/monitoring/prometheus/kafka-jmx-exporter.yml +256 -0
  344. package/plugins/specweave-kafka/package.json +41 -0
  345. package/plugins/specweave-kafka/skills/kafka-architecture/SKILL.md +647 -0
  346. package/plugins/specweave-kafka/skills/kafka-cli-tools/SKILL.md +433 -0
  347. package/plugins/specweave-kafka/skills/kafka-iac-deployment/SKILL.md +449 -0
  348. package/plugins/specweave-kafka/skills/kafka-kubernetes/SKILL.md +667 -0
  349. package/plugins/specweave-kafka/skills/kafka-mcp-integration/SKILL.md +273 -0
  350. package/plugins/specweave-kafka/skills/kafka-observability/SKILL.md +576 -0
  351. package/plugins/specweave-kafka/templates/config/broker-production.properties +254 -0
  352. package/plugins/specweave-kafka/templates/config/consumer-low-latency.properties +112 -0
  353. package/plugins/specweave-kafka/templates/config/producer-high-throughput.properties +120 -0
  354. package/plugins/specweave-kafka/templates/migration/mirrormaker2-config.properties +234 -0
  355. package/plugins/specweave-kafka/templates/monitoring/grafana/multi-cluster-dashboard.json +686 -0
  356. package/plugins/specweave-kafka/terraform/apache-kafka/main.tf +347 -0
  357. package/plugins/specweave-kafka/terraform/apache-kafka/outputs.tf +107 -0
  358. package/plugins/specweave-kafka/terraform/apache-kafka/templates/kafka-broker-init.sh.tpl +216 -0
  359. package/plugins/specweave-kafka/terraform/apache-kafka/variables.tf +156 -0
  360. package/plugins/specweave-kafka/terraform/aws-msk/main.tf +362 -0
  361. package/plugins/specweave-kafka/terraform/aws-msk/outputs.tf +93 -0
  362. package/plugins/specweave-kafka/terraform/aws-msk/templates/server.properties.tpl +32 -0
  363. package/plugins/specweave-kafka/terraform/aws-msk/variables.tf +235 -0
  364. package/plugins/specweave-kafka/terraform/azure-event-hubs/main.tf +281 -0
  365. package/plugins/specweave-kafka/terraform/azure-event-hubs/outputs.tf +118 -0
  366. package/plugins/specweave-kafka/terraform/azure-event-hubs/variables.tf +148 -0
  367. package/plugins/specweave-kafka/tsconfig.json +21 -0
  368. package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +23 -0
  369. package/plugins/specweave-kafka-streams/README.md +310 -0
  370. package/plugins/specweave-kafka-streams/skills/kafka-streams-topology/SKILL.md +539 -0
  371. package/plugins/specweave-n8n/.claude-plugin/plugin.json +22 -0
  372. package/plugins/specweave-n8n/README.md +354 -0
  373. package/plugins/specweave-n8n/skills/n8n-kafka-workflows/SKILL.md +504 -0
  374. package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
  375. package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
  376. package/src/templates/AGENTS.md.template +601 -7
  377. package/src/templates/CLAUDE.md.template +188 -88
  378. package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +0 -255
  379. package/plugins/specweave-github/commands/specweave-github-sync-epic.md +0 -248
  380. package/plugins/specweave-github/commands/specweave-github-sync-from.md +0 -147
  381. package/plugins/specweave-github/commands/specweave-github-sync-spec.md +0 -208
  382. package/plugins/specweave-github/commands/specweave-github-sync-tasks.md +0 -530
  383. package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +0 -267
  384. package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +0 -240
@@ -0,0 +1,233 @@
1
+ class DeadLetterQueueHandler {
2
+ constructor(producer, consumer, config) {
3
+ this.config = {
4
+ inputTopic: config.inputTopic,
5
+ dlqTopic: config.dlqTopic,
6
+ retryTopic: config.retryTopic,
7
+ maxRetries: config.maxRetries || 3,
8
+ backoffMultiplier: config.backoffMultiplier || 2,
9
+ initialBackoffMs: config.initialBackoffMs || 1e3,
10
+ maxBackoffMs: config.maxBackoffMs || 6e4,
11
+ consumerGroupId: config.consumerGroupId
12
+ };
13
+ this.producer = producer;
14
+ this.consumer = consumer;
15
+ }
16
+ /**
17
+ * Process message with DLQ handling
18
+ *
19
+ * @param payload - Kafka message payload
20
+ * @param handler - Message processing function
21
+ */
22
+ async processWithDLQ(payload, handler) {
23
+ const { topic, partition, message } = payload;
24
+ try {
25
+ await handler(message);
26
+ await this.consumer.commitOffsets([
27
+ {
28
+ topic,
29
+ partition,
30
+ offset: (parseInt(message.offset) + 1).toString()
31
+ }
32
+ ]);
33
+ } catch (error) {
34
+ await this.handleFailure(topic, partition, message, error);
35
+ }
36
+ }
37
+ /**
38
+ * Handle message processing failure
39
+ */
40
+ async handleFailure(topic, partition, message, error) {
41
+ const retryCount = this.getRetryCount(message);
42
+ const nextRetryCount = retryCount + 1;
43
+ console.error(`Message processing failed (attempt ${nextRetryCount}/${this.config.maxRetries}):`, {
44
+ topic,
45
+ partition,
46
+ offset: message.offset,
47
+ error: error.message
48
+ });
49
+ if (nextRetryCount >= this.config.maxRetries) {
50
+ await this.sendToDLQ(topic, partition, message, error, retryCount);
51
+ } else if (this.config.retryTopic) {
52
+ await this.sendToRetry(topic, partition, message, error, nextRetryCount);
53
+ } else {
54
+ await this.sendToDLQ(topic, partition, message, error, retryCount);
55
+ }
56
+ await this.consumer.commitOffsets([
57
+ {
58
+ topic,
59
+ partition,
60
+ offset: (parseInt(message.offset) + 1).toString()
61
+ }
62
+ ]);
63
+ }
64
+ /**
65
+ * Send message to retry topic
66
+ */
67
+ async sendToRetry(originalTopic, originalPartition, message, error, retryCount) {
68
+ const backoffMs = this.calculateBackoff(retryCount);
69
+ const nextRetryTimestamp = Date.now() + backoffMs;
70
+ const headers = {
71
+ ...this.convertHeaders(message.headers),
72
+ "dlq.original.topic": originalTopic,
73
+ "dlq.original.partition": originalPartition.toString(),
74
+ "dlq.original.offset": message.offset,
75
+ "dlq.retry.count": retryCount.toString(),
76
+ "dlq.error.message": error.message,
77
+ "dlq.error.stacktrace": error.stack || "",
78
+ "dlq.failure.timestamp": Date.now().toString(),
79
+ "dlq.next.retry.timestamp": nextRetryTimestamp.toString()
80
+ };
81
+ await this.producer.send({
82
+ topic: this.config.retryTopic,
83
+ messages: [
84
+ {
85
+ key: message.key,
86
+ value: message.value,
87
+ headers,
88
+ timestamp: nextRetryTimestamp.toString()
89
+ // Delay processing
90
+ }
91
+ ]
92
+ });
93
+ console.log(`Message sent to retry topic (${this.config.retryTopic}) with ${backoffMs}ms backoff`);
94
+ }
95
+ /**
96
+ * Send message to dead letter queue
97
+ */
98
+ async sendToDLQ(originalTopic, originalPartition, message, error, retryCount) {
99
+ const headers = {
100
+ ...this.convertHeaders(message.headers),
101
+ "dlq.original.topic": originalTopic,
102
+ "dlq.original.partition": originalPartition.toString(),
103
+ "dlq.original.offset": message.offset,
104
+ "dlq.retry.count": retryCount.toString(),
105
+ "dlq.error.message": error.message,
106
+ "dlq.error.stacktrace": error.stack || "",
107
+ "dlq.failure.timestamp": Date.now().toString()
108
+ };
109
+ await this.producer.send({
110
+ topic: this.config.dlqTopic,
111
+ messages: [
112
+ {
113
+ key: message.key,
114
+ value: message.value,
115
+ headers
116
+ }
117
+ ]
118
+ });
119
+ console.error(`Message sent to DLQ (${this.config.dlqTopic}) after ${retryCount} retries`);
120
+ }
121
+ /**
122
+ * Calculate exponential backoff delay
123
+ */
124
+ calculateBackoff(retryCount) {
125
+ const backoff = this.config.initialBackoffMs * Math.pow(this.config.backoffMultiplier, retryCount - 1);
126
+ return Math.min(backoff, this.config.maxBackoffMs);
127
+ }
128
+ /**
129
+ * Get retry count from message headers
130
+ */
131
+ getRetryCount(message) {
132
+ const retryHeader = message.headers?.["dlq.retry.count"];
133
+ if (!retryHeader) return 0;
134
+ const value = Array.isArray(retryHeader) ? retryHeader[0] : retryHeader;
135
+ return parseInt(value?.toString() || "0", 10);
136
+ }
137
+ /**
138
+ * Convert Kafka headers to string map
139
+ */
140
+ convertHeaders(headers) {
141
+ if (!headers) return {};
142
+ const result = {};
143
+ for (const [key, value] of Object.entries(headers)) {
144
+ const val = Array.isArray(value) ? value[0] : value;
145
+ result[key] = val?.toString() || "";
146
+ }
147
+ return result;
148
+ }
149
+ }
150
+ class RetryTopicConsumer {
151
+ constructor(consumer, dlqHandler) {
152
+ this.consumer = consumer;
153
+ this.dlqHandler = dlqHandler;
154
+ }
155
+ /**
156
+ * Run retry consumer
157
+ *
158
+ * Checks timestamp header and delays processing if needed.
159
+ */
160
+ async run(handler) {
161
+ await this.consumer.run({
162
+ eachMessage: async (payload) => {
163
+ const { message } = payload;
164
+ const nextRetryTimestamp = this.getNextRetryTimestamp(message);
165
+ if (nextRetryTimestamp) {
166
+ const now = Date.now();
167
+ if (now < nextRetryTimestamp) {
168
+ console.log(`Skipping message (not ready for retry yet): ${nextRetryTimestamp - now}ms remaining`);
169
+ return;
170
+ }
171
+ }
172
+ await this.dlqHandler.processWithDLQ(payload, handler);
173
+ }
174
+ });
175
+ }
176
+ /**
177
+ * Get next retry timestamp from message headers
178
+ */
179
+ getNextRetryTimestamp(message) {
180
+ const header = message.headers?.["dlq.next.retry.timestamp"];
181
+ if (!header) return null;
182
+ const value = Array.isArray(header) ? header[0] : header;
183
+ return parseInt(value?.toString() || "0", 10);
184
+ }
185
+ }
186
+ class DLQMonitor {
187
+ constructor(consumer, dlqTopic) {
188
+ this.consumer = consumer;
189
+ this.dlqTopic = dlqTopic;
190
+ }
191
+ /**
192
+ * Monitor DLQ for new messages and trigger alerts
193
+ */
194
+ async monitor(onMessage) {
195
+ await this.consumer.subscribe({ topics: [this.dlqTopic], fromBeginning: false });
196
+ await this.consumer.run({
197
+ eachMessage: async ({ message }) => {
198
+ const headers = this.extractDLQHeaders(message);
199
+ await onMessage(message, headers);
200
+ }
201
+ });
202
+ }
203
+ /**
204
+ * Extract DLQ headers from message
205
+ */
206
+ extractDLQHeaders(message) {
207
+ const headers = message.headers || {};
208
+ const getHeader = (key) => {
209
+ const value = headers[key];
210
+ return (Array.isArray(value) ? value[0] : value)?.toString() || "";
211
+ };
212
+ return {
213
+ "dlq.original.topic": getHeader("dlq.original.topic"),
214
+ "dlq.original.partition": getHeader("dlq.original.partition"),
215
+ "dlq.original.offset": getHeader("dlq.original.offset"),
216
+ "dlq.retry.count": getHeader("dlq.retry.count"),
217
+ "dlq.error.message": getHeader("dlq.error.message"),
218
+ "dlq.error.stacktrace": getHeader("dlq.error.stacktrace"),
219
+ "dlq.failure.timestamp": getHeader("dlq.failure.timestamp")
220
+ };
221
+ }
222
+ }
223
+ var dead_letter_queue_default = {
224
+ DeadLetterQueueHandler,
225
+ RetryTopicConsumer,
226
+ DLQMonitor
227
+ };
228
+ export {
229
+ DLQMonitor,
230
+ DeadLetterQueueHandler,
231
+ RetryTopicConsumer,
232
+ dead_letter_queue_default as default
233
+ };
@@ -0,0 +1,423 @@
1
+ /**
2
+ * Dead Letter Queue (DLQ) Pattern for Kafka
3
+ *
4
+ * Handles poison messages and failed processing with retry logic
5
+ * and eventual routing to dead letter topics.
6
+ *
7
+ * @module dead-letter-queue
8
+ */
9
+
10
+ import { Producer, Consumer, EachMessagePayload, Message } from 'kafkajs';
11
+
12
+ /**
13
+ * DLQ Configuration
14
+ */
15
+ export interface DLQConfig {
16
+ /** Main input topic */
17
+ inputTopic: string;
18
+ /** Dead letter topic for failed messages */
19
+ dlqTopic: string;
20
+ /** Optional retry topic */
21
+ retryTopic?: string;
22
+ /** Max retry attempts before DLQ (default: 3) */
23
+ maxRetries?: number;
24
+ /** Exponential backoff multiplier (default: 2) */
25
+ backoffMultiplier?: number;
26
+ /** Initial backoff delay in ms (default: 1000) */
27
+ initialBackoffMs?: number;
28
+ /** Max backoff delay in ms (default: 60000) */
29
+ maxBackoffMs?: number;
30
+ /** Consumer group ID */
31
+ consumerGroupId: string;
32
+ }
33
+
34
+ /**
35
+ * DLQ Message Headers
36
+ */
37
+ export interface DLQHeaders {
38
+ /** Original topic */
39
+ 'dlq.original.topic': string;
40
+ /** Original partition */
41
+ 'dlq.original.partition': string;
42
+ /** Original offset */
43
+ 'dlq.original.offset': string;
44
+ /** Retry attempt count */
45
+ 'dlq.retry.count': string;
46
+ /** Error message */
47
+ 'dlq.error.message': string;
48
+ /** Error stacktrace */
49
+ 'dlq.error.stacktrace'?: string;
50
+ /** Timestamp of failure */
51
+ 'dlq.failure.timestamp': string;
52
+ /** Next retry timestamp (if applicable) */
53
+ 'dlq.next.retry.timestamp'?: string;
54
+ }
55
+
56
+ /**
57
+ * Dead Letter Queue Handler
58
+ *
59
+ * Implements retry logic with exponential backoff and DLQ routing.
60
+ */
61
+ export class DeadLetterQueueHandler {
62
+ private config: Required<Omit<DLQConfig, 'retryTopic'>> & { retryTopic?: string };
63
+ private producer: Producer;
64
+ private consumer: Consumer;
65
+
66
+ constructor(producer: Producer, consumer: Consumer, config: DLQConfig) {
67
+ this.config = {
68
+ inputTopic: config.inputTopic,
69
+ dlqTopic: config.dlqTopic,
70
+ retryTopic: config.retryTopic,
71
+ maxRetries: config.maxRetries || 3,
72
+ backoffMultiplier: config.backoffMultiplier || 2,
73
+ initialBackoffMs: config.initialBackoffMs || 1000,
74
+ maxBackoffMs: config.maxBackoffMs || 60000,
75
+ consumerGroupId: config.consumerGroupId,
76
+ };
77
+ this.producer = producer;
78
+ this.consumer = consumer;
79
+ }
80
+
81
+ /**
82
+ * Process message with DLQ handling
83
+ *
84
+ * @param payload - Kafka message payload
85
+ * @param handler - Message processing function
86
+ */
87
+ async processWithDLQ(
88
+ payload: EachMessagePayload,
89
+ handler: (message: Message) => Promise<void>
90
+ ): Promise<void> {
91
+ const { topic, partition, message } = payload;
92
+
93
+ try {
94
+ // Attempt to process message
95
+ await handler(message);
96
+
97
+ // Success: commit offset
98
+ await this.consumer.commitOffsets([
99
+ {
100
+ topic,
101
+ partition,
102
+ offset: (parseInt(message.offset) + 1).toString(),
103
+ },
104
+ ]);
105
+ } catch (error) {
106
+ // Failure: handle with retry/DLQ logic
107
+ await this.handleFailure(topic, partition, message, error as Error);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Handle message processing failure
113
+ */
114
+ private async handleFailure(
115
+ topic: string,
116
+ partition: number,
117
+ message: Message,
118
+ error: Error
119
+ ): Promise<void> {
120
+ // Get current retry count
121
+ const retryCount = this.getRetryCount(message);
122
+ const nextRetryCount = retryCount + 1;
123
+
124
+ console.error(`Message processing failed (attempt ${nextRetryCount}/${this.config.maxRetries}):`, {
125
+ topic,
126
+ partition,
127
+ offset: message.offset,
128
+ error: error.message,
129
+ });
130
+
131
+ if (nextRetryCount >= this.config.maxRetries) {
132
+ // Max retries reached → Send to DLQ
133
+ await this.sendToDLQ(topic, partition, message, error, retryCount);
134
+ } else if (this.config.retryTopic) {
135
+ // Send to retry topic with backoff
136
+ await this.sendToRetry(topic, partition, message, error, nextRetryCount);
137
+ } else {
138
+ // No retry topic → Send directly to DLQ
139
+ await this.sendToDLQ(topic, partition, message, error, retryCount);
140
+ }
141
+
142
+ // Commit offset (message handled, don't reprocess)
143
+ await this.consumer.commitOffsets([
144
+ {
145
+ topic,
146
+ partition,
147
+ offset: (parseInt(message.offset) + 1).toString(),
148
+ },
149
+ ]);
150
+ }
151
+
152
+ /**
153
+ * Send message to retry topic
154
+ */
155
+ private async sendToRetry(
156
+ originalTopic: string,
157
+ originalPartition: number,
158
+ message: Message,
159
+ error: Error,
160
+ retryCount: number
161
+ ): Promise<void> {
162
+ const backoffMs = this.calculateBackoff(retryCount);
163
+ const nextRetryTimestamp = Date.now() + backoffMs;
164
+
165
+ const headers: Record<string, string> = {
166
+ ...this.convertHeaders(message.headers),
167
+ 'dlq.original.topic': originalTopic,
168
+ 'dlq.original.partition': originalPartition.toString(),
169
+ 'dlq.original.offset': message.offset,
170
+ 'dlq.retry.count': retryCount.toString(),
171
+ 'dlq.error.message': error.message,
172
+ 'dlq.error.stacktrace': error.stack || '',
173
+ 'dlq.failure.timestamp': Date.now().toString(),
174
+ 'dlq.next.retry.timestamp': nextRetryTimestamp.toString(),
175
+ };
176
+
177
+ await this.producer.send({
178
+ topic: this.config.retryTopic!,
179
+ messages: [
180
+ {
181
+ key: message.key,
182
+ value: message.value,
183
+ headers,
184
+ timestamp: nextRetryTimestamp.toString(), // Delay processing
185
+ },
186
+ ],
187
+ });
188
+
189
+ console.log(`Message sent to retry topic (${this.config.retryTopic}) with ${backoffMs}ms backoff`);
190
+ }
191
+
192
+ /**
193
+ * Send message to dead letter queue
194
+ */
195
+ private async sendToDLQ(
196
+ originalTopic: string,
197
+ originalPartition: number,
198
+ message: Message,
199
+ error: Error,
200
+ retryCount: number
201
+ ): Promise<void> {
202
+ const headers: Record<string, string> = {
203
+ ...this.convertHeaders(message.headers),
204
+ 'dlq.original.topic': originalTopic,
205
+ 'dlq.original.partition': originalPartition.toString(),
206
+ 'dlq.original.offset': message.offset,
207
+ 'dlq.retry.count': retryCount.toString(),
208
+ 'dlq.error.message': error.message,
209
+ 'dlq.error.stacktrace': error.stack || '',
210
+ 'dlq.failure.timestamp': Date.now().toString(),
211
+ };
212
+
213
+ await this.producer.send({
214
+ topic: this.config.dlqTopic,
215
+ messages: [
216
+ {
217
+ key: message.key,
218
+ value: message.value,
219
+ headers,
220
+ },
221
+ ],
222
+ });
223
+
224
+ console.error(`Message sent to DLQ (${this.config.dlqTopic}) after ${retryCount} retries`);
225
+ }
226
+
227
+ /**
228
+ * Calculate exponential backoff delay
229
+ */
230
+ private calculateBackoff(retryCount: number): number {
231
+ const backoff = this.config.initialBackoffMs * Math.pow(this.config.backoffMultiplier, retryCount - 1);
232
+ return Math.min(backoff, this.config.maxBackoffMs);
233
+ }
234
+
235
+ /**
236
+ * Get retry count from message headers
237
+ */
238
+ private getRetryCount(message: Message): number {
239
+ const retryHeader = message.headers?.['dlq.retry.count'];
240
+ if (!retryHeader) return 0;
241
+
242
+ const value = Array.isArray(retryHeader) ? retryHeader[0] : retryHeader;
243
+ return parseInt(value?.toString() || '0', 10);
244
+ }
245
+
246
+ /**
247
+ * Convert Kafka headers to string map
248
+ */
249
+ private convertHeaders(headers?: Message['headers']): Record<string, string> {
250
+ if (!headers) return {};
251
+
252
+ const result: Record<string, string> = {};
253
+ for (const [key, value] of Object.entries(headers)) {
254
+ const val = Array.isArray(value) ? value[0] : value;
255
+ result[key] = val?.toString() || '';
256
+ }
257
+ return result;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Retry Topic Consumer
263
+ *
264
+ * Consumes from retry topic and processes messages after backoff delay.
265
+ */
266
+ export class RetryTopicConsumer {
267
+ private consumer: Consumer;
268
+ private dlqHandler: DeadLetterQueueHandler;
269
+
270
+ constructor(consumer: Consumer, dlqHandler: DeadLetterQueueHandler) {
271
+ this.consumer = consumer;
272
+ this.dlqHandler = dlqHandler;
273
+ }
274
+
275
+ /**
276
+ * Run retry consumer
277
+ *
278
+ * Checks timestamp header and delays processing if needed.
279
+ */
280
+ async run(handler: (message: Message) => Promise<void>): Promise<void> {
281
+ await this.consumer.run({
282
+ eachMessage: async (payload) => {
283
+ const { message } = payload;
284
+
285
+ // Check if message is ready for retry
286
+ const nextRetryTimestamp = this.getNextRetryTimestamp(message);
287
+ if (nextRetryTimestamp) {
288
+ const now = Date.now();
289
+ if (now < nextRetryTimestamp) {
290
+ // Too early, skip for now (will be reprocessed later)
291
+ console.log(`Skipping message (not ready for retry yet): ${nextRetryTimestamp - now}ms remaining`);
292
+ return;
293
+ }
294
+ }
295
+
296
+ // Ready for retry
297
+ await this.dlqHandler.processWithDLQ(payload, handler);
298
+ },
299
+ });
300
+ }
301
+
302
+ /**
303
+ * Get next retry timestamp from message headers
304
+ */
305
+ private getNextRetryTimestamp(message: Message): number | null {
306
+ const header = message.headers?.['dlq.next.retry.timestamp'];
307
+ if (!header) return null;
308
+
309
+ const value = Array.isArray(header) ? header[0] : header;
310
+ return parseInt(value?.toString() || '0', 10);
311
+ }
312
+ }
313
+
314
+ /**
315
+ * DLQ Monitoring and Alerting
316
+ */
317
+ export class DLQMonitor {
318
+ private consumer: Consumer;
319
+ private dlqTopic: string;
320
+
321
+ constructor(consumer: Consumer, dlqTopic: string) {
322
+ this.consumer = consumer;
323
+ this.dlqTopic = dlqTopic;
324
+ }
325
+
326
+ /**
327
+ * Monitor DLQ for new messages and trigger alerts
328
+ */
329
+ async monitor(onMessage: (message: Message, headers: Partial<DLQHeaders>) => Promise<void>): Promise<void> {
330
+ await this.consumer.subscribe({ topics: [this.dlqTopic], fromBeginning: false });
331
+
332
+ await this.consumer.run({
333
+ eachMessage: async ({ message }) => {
334
+ const headers = this.extractDLQHeaders(message);
335
+ await onMessage(message, headers);
336
+ },
337
+ });
338
+ }
339
+
340
+ /**
341
+ * Extract DLQ headers from message
342
+ */
343
+ private extractDLQHeaders(message: Message): Partial<DLQHeaders> {
344
+ const headers = message.headers || {};
345
+ const getHeader = (key: string) => {
346
+ const value = headers[key];
347
+ return (Array.isArray(value) ? value[0] : value)?.toString() || '';
348
+ };
349
+
350
+ return {
351
+ 'dlq.original.topic': getHeader('dlq.original.topic'),
352
+ 'dlq.original.partition': getHeader('dlq.original.partition'),
353
+ 'dlq.original.offset': getHeader('dlq.original.offset'),
354
+ 'dlq.retry.count': getHeader('dlq.retry.count'),
355
+ 'dlq.error.message': getHeader('dlq.error.message'),
356
+ 'dlq.error.stacktrace': getHeader('dlq.error.stacktrace'),
357
+ 'dlq.failure.timestamp': getHeader('dlq.failure.timestamp'),
358
+ };
359
+ }
360
+ }
361
+
362
+ /**
363
+ * Example Usage: DLQ Handler
364
+ *
365
+ * ```typescript
366
+ * const kafka = new Kafka({ brokers: ['localhost:9092'] });
367
+ * const producer = kafka.producer();
368
+ * const consumer = kafka.consumer({ groupId: 'order-processor' });
369
+ *
370
+ * await producer.connect();
371
+ * await consumer.connect();
372
+ * await consumer.subscribe({ topics: ['orders'] });
373
+ *
374
+ * const dlqHandler = new DeadLetterQueueHandler(producer, consumer, {
375
+ * inputTopic: 'orders',
376
+ * dlqTopic: 'orders-dlq',
377
+ * retryTopic: 'orders-retry',
378
+ * maxRetries: 3,
379
+ * consumerGroupId: 'order-processor',
380
+ * });
381
+ *
382
+ * await consumer.run({
383
+ * eachMessage: async (payload) => {
384
+ * await dlqHandler.processWithDLQ(payload, async (message) => {
385
+ * const order = JSON.parse(message.value.toString());
386
+ * await processOrder(order); // May throw error
387
+ * });
388
+ * },
389
+ * });
390
+ * ```
391
+ */
392
+
393
+ /**
394
+ * Example Usage: DLQ Monitoring
395
+ *
396
+ * ```typescript
397
+ * const dlqMonitor = new DLQMonitor(
398
+ * kafka.consumer({ groupId: 'dlq-monitor' }),
399
+ * 'orders-dlq'
400
+ * );
401
+ *
402
+ * await dlqMonitor.monitor(async (message, headers) => {
403
+ * // Alert on new DLQ message
404
+ * console.error('DLQ Message Received:', {
405
+ * originalTopic: headers['dlq.original.topic'],
406
+ * error: headers['dlq.error.message'],
407
+ * retries: headers['dlq.retry.count'],
408
+ * });
409
+ *
410
+ * // Send alert to Slack/PagerDuty
411
+ * await sendAlert({
412
+ * severity: 'HIGH',
413
+ * message: `Message failed after ${headers['dlq.retry.count']} retries`,
414
+ * });
415
+ * });
416
+ * ```
417
+ */
418
+
419
+ export default {
420
+ DeadLetterQueueHandler,
421
+ RetryTopicConsumer,
422
+ DLQMonitor,
423
+ };