specweave 0.18.0 → 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.
- package/CLAUDE.md +229 -1817
- package/README.md +68 -0
- package/bin/specweave.js +62 -6
- package/dist/locales/de/.gitkeep +0 -0
- package/dist/locales/de/cli.json +108 -0
- package/dist/locales/en/cli.json +287 -0
- package/dist/locales/en/errors.json +7 -0
- package/dist/locales/en/templates.json +6 -0
- package/dist/locales/es/.gitkeep +0 -0
- package/dist/locales/es/cli.json +41 -0
- package/dist/locales/fr/.gitkeep +0 -0
- package/dist/locales/fr/cli.json +108 -0
- package/dist/locales/ja/.gitkeep +0 -0
- package/dist/locales/ja/cli.json +108 -0
- package/dist/locales/ko/.gitkeep +0 -0
- package/dist/locales/ko/cli.json +108 -0
- package/dist/locales/pt/.gitkeep +0 -0
- package/dist/locales/pt/cli.json +108 -0
- package/dist/locales/ru/.gitkeep +0 -0
- package/dist/locales/ru/cli.json +269 -0
- package/dist/locales/zh/.gitkeep +0 -0
- package/dist/locales/zh/cli.json +108 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.d.ts.map +1 -1
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js +3 -0
- package/dist/plugins/specweave/lib/hooks/sync-living-docs.js.map +1 -1
- package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts +21 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.d.ts.map +1 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.js +162 -0
- package/dist/plugins/specweave/lib/hooks/update-ac-status.js.map +1 -0
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js +65 -6
- package/dist/plugins/specweave-ado/lib/ado-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts +25 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js +191 -0
- package/dist/plugins/specweave-ado/lib/enhanced-ado-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts +112 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.js +301 -0
- package/dist/plugins/specweave-github/lib/completion-calculator.js.map +1 -0
- package/dist/plugins/specweave-github/lib/duplicate-detector.d.ts +3 -3
- package/dist/plugins/specweave-github/lib/duplicate-detector.js +3 -3
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts +70 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js +258 -0
- package/dist/plugins/specweave-github/lib/epic-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts +14 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-client-v2.js +51 -0
- package/dist/plugins/specweave-github/lib/github-client-v2.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts +2 -2
- package/dist/plugins/specweave-github/lib/github-epic-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-epic-sync.js +20 -5
- package/dist/plugins/specweave-github/lib/github-epic-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts +87 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js +412 -0
- package/dist/plugins/specweave-github/lib/github-feature-sync.js.map +1 -0
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.d.ts.map +1 -1
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js +64 -13
- package/dist/plugins/specweave-github/lib/github-spec-content-sync.js.map +1 -1
- package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts +78 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js +237 -0
- package/dist/plugins/specweave-github/lib/progress-comment-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts +97 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.js +301 -0
- package/dist/plugins/specweave-github/lib/user-story-content-builder.js.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts +83 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.d.ts.map +1 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js +386 -0
- package/dist/plugins/specweave-github/lib/user-story-issue-builder.js.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +28 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +1 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +156 -0
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts +57 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +248 -0
- package/dist/plugins/specweave-kafka/lib/cli/kcat-wrapper.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.d.ts +82 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.js +13 -0
- package/dist/plugins/specweave-kafka/lib/cli/types.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts +49 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.js +316 -0
- package/dist/plugins/specweave-kafka/lib/mcp/detector.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts +70 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.js +23 -0
- package/dist/plugins/specweave-kafka/lib/mcp/types.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts +85 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.js +281 -0
- package/dist/plugins/specweave-kafka/lib/utils/partitioning.js.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts +75 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.d.ts.map +1 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.js +238 -0
- package/dist/plugins/specweave-kafka/lib/utils/sizing.js.map +1 -0
- package/dist/spec-parser.js +629 -0
- package/dist/src/cli/commands/import-docs.js +4 -4
- package/dist/src/cli/commands/import-docs.js.map +1 -1
- package/dist/src/cli/commands/init-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/init-multiproject.js +17 -18
- package/dist/src/cli/commands/init-multiproject.js.map +1 -1
- package/dist/src/cli/commands/init.d.ts.map +1 -1
- package/dist/src/cli/commands/init.js +107 -3
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.d.ts.map +1 -1
- package/dist/src/cli/commands/migrate-to-multiproject.js +8 -4
- package/dist/src/cli/commands/migrate-to-multiproject.js.map +1 -1
- package/dist/src/cli/commands/switch-project.d.ts.map +1 -1
- package/dist/src/cli/commands/switch-project.js +9 -26
- package/dist/src/cli/commands/switch-project.js.map +1 -1
- package/dist/src/cli/commands/sync-spec-content.js +3 -0
- package/dist/src/cli/commands/sync-spec-content.js.map +1 -1
- package/dist/src/core/deduplication/command-deduplicator.d.ts +166 -0
- package/dist/src/core/deduplication/command-deduplicator.d.ts.map +1 -0
- package/dist/src/core/deduplication/command-deduplicator.js +254 -0
- package/dist/src/core/deduplication/command-deduplicator.js.map +1 -0
- package/dist/src/core/increment/active-increment-manager.d.ts +42 -15
- package/dist/src/core/increment/active-increment-manager.d.ts.map +1 -1
- package/dist/src/core/increment/active-increment-manager.js +113 -46
- package/dist/src/core/increment/active-increment-manager.js.map +1 -1
- package/dist/src/core/increment/conflict-resolver.d.ts +40 -0
- package/dist/src/core/increment/conflict-resolver.d.ts.map +1 -0
- package/dist/src/core/increment/conflict-resolver.js +219 -0
- package/dist/src/core/increment/conflict-resolver.js.map +1 -0
- package/dist/src/core/increment/discipline-checker.d.ts.map +1 -1
- package/dist/src/core/increment/discipline-checker.js +7 -1
- package/dist/src/core/increment/discipline-checker.js.map +1 -1
- package/dist/src/core/increment/duplicate-detector.d.ts +52 -0
- package/dist/src/core/increment/duplicate-detector.d.ts.map +1 -0
- package/dist/src/core/increment/duplicate-detector.js +276 -0
- package/dist/src/core/increment/duplicate-detector.js.map +1 -0
- package/dist/src/core/increment/increment-archiver.d.ts +90 -0
- package/dist/src/core/increment/increment-archiver.d.ts.map +1 -0
- package/dist/src/core/increment/increment-archiver.js +368 -0
- package/dist/src/core/increment/increment-archiver.js.map +1 -0
- package/dist/src/core/increment/increment-reopener.d.ts +165 -0
- package/dist/src/core/increment/increment-reopener.d.ts.map +1 -0
- package/dist/src/core/increment/increment-reopener.js +390 -0
- package/dist/src/core/increment/increment-reopener.js.map +1 -0
- package/dist/src/core/increment/metadata-manager.d.ts +26 -1
- package/dist/src/core/increment/metadata-manager.d.ts.map +1 -1
- package/dist/src/core/increment/metadata-manager.js +143 -5
- package/dist/src/core/increment/metadata-manager.js.map +1 -1
- package/dist/src/core/increment/recent-work-scanner.d.ts +121 -0
- package/dist/src/core/increment/recent-work-scanner.d.ts.map +1 -0
- package/dist/src/core/increment/recent-work-scanner.js +303 -0
- package/dist/src/core/increment/recent-work-scanner.js.map +1 -0
- package/dist/src/core/increment/types.d.ts +1 -0
- package/dist/src/core/increment/types.d.ts.map +1 -1
- package/dist/src/core/increment-utils.d.ts +112 -0
- package/dist/src/core/increment-utils.d.ts.map +1 -0
- package/dist/src/core/increment-utils.js +210 -0
- package/dist/src/core/increment-utils.js.map +1 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.d.ts +65 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.js +175 -0
- package/dist/src/core/living-docs/ac-project-specific-generator.js.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts +130 -0
- package/dist/src/core/living-docs/feature-archiver.d.ts.map +1 -0
- package/dist/src/core/living-docs/feature-archiver.js +549 -0
- package/dist/src/core/living-docs/feature-archiver.js.map +1 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts +81 -0
- package/dist/src/core/living-docs/feature-id-manager.d.ts.map +1 -0
- package/dist/src/core/living-docs/feature-id-manager.js +339 -0
- package/dist/src/core/living-docs/feature-id-manager.js.map +1 -0
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts +144 -83
- package/dist/src/core/living-docs/hierarchy-mapper.d.ts.map +1 -1
- package/dist/src/core/living-docs/hierarchy-mapper.js +488 -270
- package/dist/src/core/living-docs/hierarchy-mapper.js.map +1 -1
- package/dist/src/core/living-docs/index.d.ts +6 -0
- package/dist/src/core/living-docs/index.d.ts.map +1 -1
- package/dist/src/core/living-docs/index.js +6 -0
- package/dist/src/core/living-docs/index.js.map +1 -1
- package/dist/src/core/living-docs/project-detector.d.ts +6 -0
- package/dist/src/core/living-docs/project-detector.d.ts.map +1 -1
- package/dist/src/core/living-docs/project-detector.js +35 -1
- package/dist/src/core/living-docs/project-detector.js.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.d.ts +100 -26
- package/dist/src/core/living-docs/spec-distributor.d.ts.map +1 -1
- package/dist/src/core/living-docs/spec-distributor.js +1275 -258
- package/dist/src/core/living-docs/spec-distributor.js.map +1 -1
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts +109 -0
- package/dist/src/core/living-docs/task-project-specific-generator.d.ts.map +1 -0
- package/dist/src/core/living-docs/task-project-specific-generator.js +221 -0
- package/dist/src/core/living-docs/task-project-specific-generator.js.map +1 -0
- package/dist/src/core/living-docs/types.d.ts +143 -0
- package/dist/src/core/living-docs/types.d.ts.map +1 -1
- package/dist/src/core/project-manager.d.ts +2 -17
- package/dist/src/core/project-manager.d.ts.map +1 -1
- package/dist/src/core/project-manager.js +68 -48
- package/dist/src/core/project-manager.js.map +1 -1
- package/dist/src/core/spec-content-sync.d.ts +1 -1
- package/dist/src/core/spec-content-sync.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.d.ts +32 -54
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +1 -1
- package/dist/src/core/sync/enhanced-content-builder.js +142 -138
- package/dist/src/core/sync/enhanced-content-builder.js.map +1 -1
- package/dist/src/core/sync/performance-optimizer.d.ts +153 -0
- package/dist/src/core/sync/performance-optimizer.d.ts.map +1 -0
- package/dist/src/core/sync/performance-optimizer.js +220 -0
- package/dist/src/core/sync/performance-optimizer.js.map +1 -0
- package/dist/src/core/sync/retry-handler.d.ts +98 -0
- package/dist/src/core/sync/retry-handler.d.ts.map +1 -0
- package/dist/src/core/sync/retry-handler.js +196 -0
- package/dist/src/core/sync/retry-handler.js.map +1 -0
- package/dist/src/core/sync/spec-content-sync.d.ts +88 -0
- package/dist/src/core/sync/spec-content-sync.d.ts.map +1 -0
- package/dist/src/core/sync/spec-content-sync.js +5 -0
- package/dist/src/core/sync/spec-content-sync.js.map +1 -0
- package/dist/src/core/sync/types.d.ts +52 -0
- package/dist/src/core/sync/types.d.ts.map +1 -0
- package/dist/src/core/sync/types.js +5 -0
- package/dist/src/core/sync/types.js.map +1 -0
- package/dist/src/core/types/config.d.ts +125 -0
- package/dist/src/core/types/config.d.ts.map +1 -1
- package/dist/src/core/types/config.js +25 -0
- package/dist/src/core/types/config.js.map +1 -1
- package/dist/src/core/types/increment-metadata.d.ts +10 -0
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/increment-metadata.js +10 -1
- package/dist/src/core/types/increment-metadata.js.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-incremental-mapper.js +4 -8
- package/dist/src/integrations/jira/jira-incremental-mapper.js.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.d.ts.map +1 -1
- package/dist/src/integrations/jira/jira-mapper.js +4 -8
- package/dist/src/integrations/jira/jira-mapper.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/package.json +1 -1
- package/plugins/specweave/COMMANDS.md +13 -4
- package/plugins/specweave/agents/pm/AGENT.md +159 -12
- package/plugins/specweave/commands/specweave-abandon.md +22 -20
- package/plugins/specweave/commands/specweave-archive-features.md +121 -0
- package/plugins/specweave/commands/specweave-archive-increments.md +82 -0
- package/plugins/specweave/commands/specweave-archive.md +363 -0
- package/plugins/specweave/commands/specweave-backlog.md +211 -0
- package/plugins/specweave/commands/specweave-fix-duplicates.md +517 -0
- package/plugins/specweave/commands/specweave-increment.md +4 -3
- package/plugins/specweave/commands/specweave-progress.md +176 -27
- package/plugins/specweave/commands/specweave-reopen.md +391 -0
- package/plugins/specweave/commands/specweave-restore-feature.md +90 -0
- package/plugins/specweave/commands/specweave-restore.md +309 -0
- package/plugins/specweave/commands/specweave-resume.md +51 -23
- package/plugins/specweave/commands/specweave-status.md +41 -7
- package/plugins/specweave/commands/specweave-sync-specs.md +425 -0
- package/plugins/specweave/commands/specweave.md +70 -405
- package/plugins/specweave/hooks/hooks.json +4 -0
- package/plugins/specweave/hooks/lib/sync-spec-content.sh +2 -2
- package/plugins/specweave/hooks/post-increment-planning.sh +26 -2
- package/plugins/specweave/hooks/post-task-completion.sh +39 -0
- package/plugins/specweave/hooks/pre-command-deduplication.sh +83 -0
- package/plugins/specweave/hooks/user-prompt-submit.sh +1 -1
- package/plugins/specweave/lib/hooks/sync-living-docs.js +2 -0
- package/plugins/specweave/lib/hooks/sync-living-docs.ts +4 -0
- package/plugins/specweave/lib/hooks/update-ac-status.js +102 -0
- package/plugins/specweave/lib/hooks/update-ac-status.ts +192 -0
- package/plugins/specweave/skills/archive-increments/SKILL.md +198 -0
- package/plugins/specweave/skills/increment-planner/scripts/feature-utils.js +14 -0
- package/plugins/specweave/skills/smart-reopen-detector/SKILL.md +244 -0
- package/plugins/specweave-ado/lib/ado-spec-content-sync.js +49 -5
- package/plugins/specweave-ado/lib/ado-spec-content-sync.ts +72 -6
- package/plugins/specweave-ado/lib/enhanced-ado-sync.js +170 -0
- package/plugins/specweave-confluent/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-confluent/README.md +375 -0
- package/plugins/specweave-confluent/agents/confluent-architect/AGENT.md +306 -0
- package/plugins/specweave-confluent/skills/confluent-kafka-connect/SKILL.md +453 -0
- package/plugins/specweave-confluent/skills/confluent-ksqldb/SKILL.md +470 -0
- package/plugins/specweave-confluent/skills/confluent-schema-registry/SKILL.md +316 -0
- package/plugins/specweave-github/agents/github-task-splitter/AGENT.md +2 -2
- package/plugins/specweave-github/agents/user-story-updater/AGENT.md +148 -0
- package/plugins/specweave-github/commands/specweave-github-cleanup-duplicates.md +1 -1
- package/plugins/specweave-github/commands/specweave-github-update-user-story.md +156 -0
- package/plugins/specweave-github/hooks/post-task-completion.sh +42 -9
- package/plugins/specweave-github/lib/completion-calculator.js +262 -0
- package/plugins/specweave-github/lib/completion-calculator.ts +434 -0
- package/plugins/specweave-github/lib/duplicate-detector.js +3 -3
- package/plugins/specweave-github/lib/duplicate-detector.ts +4 -4
- package/plugins/specweave-github/lib/epic-content-builder.js +265 -0
- package/plugins/specweave-github/lib/epic-content-builder.ts +376 -0
- package/plugins/specweave-github/lib/github-client-v2.js +49 -0
- package/plugins/specweave-github/lib/github-client-v2.ts +59 -0
- package/plugins/specweave-github/lib/github-epic-sync.js +23 -24
- package/plugins/specweave-github/lib/github-epic-sync.ts +30 -5
- package/plugins/specweave-github/lib/github-feature-sync.js +381 -0
- package/plugins/specweave-github/lib/github-feature-sync.ts +568 -0
- package/plugins/specweave-github/lib/github-spec-content-sync.js +40 -10
- package/plugins/specweave-github/lib/github-spec-content-sync.ts +82 -14
- package/plugins/specweave-github/lib/progress-comment-builder.js +229 -0
- package/plugins/specweave-github/lib/progress-comment-builder.ts +324 -0
- package/plugins/specweave-github/lib/user-story-content-builder.js +299 -0
- package/plugins/specweave-github/lib/user-story-content-builder.ts +413 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.js +344 -0
- package/plugins/specweave-github/lib/user-story-issue-builder.ts +543 -0
- package/plugins/specweave-github/skills/github-issue-standard/SKILL.md +189 -0
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +134 -0
- package/plugins/specweave-jira/lib/{enhanced-jira-sync.ts.disabled → enhanced-jira-sync.ts} +26 -52
- package/plugins/specweave-kafka/.claude-plugin/plugin.json +26 -0
- package/plugins/specweave-kafka/IMPLEMENTATION-COMPLETE.md +483 -0
- package/plugins/specweave-kafka/README.md +242 -0
- package/plugins/specweave-kafka/agents/kafka-architect/AGENT.md +235 -0
- package/plugins/specweave-kafka/agents/kafka-devops/AGENT.md +209 -0
- package/plugins/specweave-kafka/agents/kafka-observability/AGENT.md +266 -0
- package/plugins/specweave-kafka/commands/deploy.md +99 -0
- package/plugins/specweave-kafka/commands/dev-env.md +176 -0
- package/plugins/specweave-kafka/commands/mcp-configure.md +101 -0
- package/plugins/specweave-kafka/commands/monitor-setup.md +96 -0
- package/plugins/specweave-kafka/docker/kafka-local/docker-compose.yml +187 -0
- package/plugins/specweave-kafka/docker/redpanda/docker-compose.yml +199 -0
- package/plugins/specweave-kafka/docker/templates/consumer-nodejs.js +225 -0
- package/plugins/specweave-kafka/docker/templates/consumer-python.py +220 -0
- package/plugins/specweave-kafka/docker/templates/producer-nodejs.js +168 -0
- package/plugins/specweave-kafka/docker/templates/producer-python.py +167 -0
- package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.js +438 -0
- package/plugins/specweave-kafka/lib/adapters/apache-kafka-adapter.ts +541 -0
- package/plugins/specweave-kafka/lib/adapters/platform-adapter.js +47 -0
- package/plugins/specweave-kafka/lib/adapters/platform-adapter.ts +343 -0
- package/plugins/specweave-kafka/lib/cli/kcat-wrapper.js +258 -0
- package/plugins/specweave-kafka/lib/cli/kcat-wrapper.ts +298 -0
- package/plugins/specweave-kafka/lib/cli/types.js +10 -0
- package/plugins/specweave-kafka/lib/cli/types.ts +92 -0
- package/plugins/specweave-kafka/lib/connectors/connector-catalog.js +305 -0
- package/plugins/specweave-kafka/lib/connectors/connector-catalog.ts +528 -0
- package/plugins/specweave-kafka/lib/documentation/diagram-generator.js +114 -0
- package/plugins/specweave-kafka/lib/documentation/diagram-generator.ts +195 -0
- package/plugins/specweave-kafka/lib/documentation/exporter.js +210 -0
- package/plugins/specweave-kafka/lib/documentation/exporter.ts +338 -0
- package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.js +60 -0
- package/plugins/specweave-kafka/lib/documentation/schema-catalog-generator.ts +130 -0
- package/plugins/specweave-kafka/lib/documentation/topology-generator.js +143 -0
- package/plugins/specweave-kafka/lib/documentation/topology-generator.ts +290 -0
- package/plugins/specweave-kafka/lib/mcp/detector.js +298 -0
- package/plugins/specweave-kafka/lib/mcp/detector.ts +352 -0
- package/plugins/specweave-kafka/lib/mcp/types.js +21 -0
- package/plugins/specweave-kafka/lib/mcp/types.ts +77 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.js +193 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-config-manager.ts +362 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.js +188 -0
- package/plugins/specweave-kafka/lib/multi-cluster/cluster-switcher.ts +359 -0
- package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.js +195 -0
- package/plugins/specweave-kafka/lib/multi-cluster/health-aggregator.ts +380 -0
- package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.js +209 -0
- package/plugins/specweave-kafka/lib/observability/opentelemetry-kafka.ts +358 -0
- package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.js +354 -0
- package/plugins/specweave-kafka/lib/patterns/advanced-ksqldb-patterns.ts +563 -0
- package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.js +259 -0
- package/plugins/specweave-kafka/lib/patterns/circuit-breaker-resilience.ts +516 -0
- package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.js +233 -0
- package/plugins/specweave-kafka/lib/patterns/dead-letter-queue.ts +423 -0
- package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.js +266 -0
- package/plugins/specweave-kafka/lib/patterns/exactly-once-semantics.ts +445 -0
- package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.js +312 -0
- package/plugins/specweave-kafka/lib/patterns/flink-kafka-integration.ts +561 -0
- package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.js +289 -0
- package/plugins/specweave-kafka/lib/patterns/multi-dc-replication.ts +607 -0
- package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.js +264 -0
- package/plugins/specweave-kafka/lib/patterns/rate-limiting-backpressure.ts +498 -0
- package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.js +263 -0
- package/plugins/specweave-kafka/lib/patterns/stream-processing-optimization.ts +549 -0
- package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.js +205 -0
- package/plugins/specweave-kafka/lib/patterns/tiered-storage-compaction.ts +399 -0
- package/plugins/specweave-kafka/lib/performance/performance-optimizer.js +249 -0
- package/plugins/specweave-kafka/lib/performance/performance-optimizer.ts +427 -0
- package/plugins/specweave-kafka/lib/security/kafka-security.js +252 -0
- package/plugins/specweave-kafka/lib/security/kafka-security.ts +494 -0
- package/plugins/specweave-kafka/lib/utils/capacity-planner.js +203 -0
- package/plugins/specweave-kafka/lib/utils/capacity-planner.ts +469 -0
- package/plugins/specweave-kafka/lib/utils/config-validator.js +419 -0
- package/plugins/specweave-kafka/lib/utils/config-validator.ts +564 -0
- package/plugins/specweave-kafka/lib/utils/partitioning.js +329 -0
- package/plugins/specweave-kafka/lib/utils/partitioning.ts +473 -0
- package/plugins/specweave-kafka/lib/utils/sizing.js +221 -0
- package/plugins/specweave-kafka/lib/utils/sizing.ts +374 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-broker-metrics.json +628 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-cluster-overview.json +564 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-consumer-lag.json +509 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-jvm-metrics.json +674 -0
- package/plugins/specweave-kafka/monitoring/grafana/dashboards/kafka-topic-metrics.json +578 -0
- package/plugins/specweave-kafka/monitoring/grafana/provisioning/dashboards/kafka.yml +17 -0
- package/plugins/specweave-kafka/monitoring/grafana/provisioning/datasources/prometheus.yml +17 -0
- package/plugins/specweave-kafka/monitoring/prometheus/kafka-alerts.yml +415 -0
- package/plugins/specweave-kafka/monitoring/prometheus/kafka-jmx-exporter.yml +256 -0
- package/plugins/specweave-kafka/package.json +41 -0
- package/plugins/specweave-kafka/skills/kafka-architecture/SKILL.md +647 -0
- package/plugins/specweave-kafka/skills/kafka-cli-tools/SKILL.md +433 -0
- package/plugins/specweave-kafka/skills/kafka-iac-deployment/SKILL.md +449 -0
- package/plugins/specweave-kafka/skills/kafka-kubernetes/SKILL.md +667 -0
- package/plugins/specweave-kafka/skills/kafka-mcp-integration/SKILL.md +273 -0
- package/plugins/specweave-kafka/skills/kafka-observability/SKILL.md +576 -0
- package/plugins/specweave-kafka/templates/config/broker-production.properties +254 -0
- package/plugins/specweave-kafka/templates/config/consumer-low-latency.properties +112 -0
- package/plugins/specweave-kafka/templates/config/producer-high-throughput.properties +120 -0
- package/plugins/specweave-kafka/templates/migration/mirrormaker2-config.properties +234 -0
- package/plugins/specweave-kafka/templates/monitoring/grafana/multi-cluster-dashboard.json +686 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/main.tf +347 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/outputs.tf +107 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/templates/kafka-broker-init.sh.tpl +216 -0
- package/plugins/specweave-kafka/terraform/apache-kafka/variables.tf +156 -0
- package/plugins/specweave-kafka/terraform/aws-msk/main.tf +362 -0
- package/plugins/specweave-kafka/terraform/aws-msk/outputs.tf +93 -0
- package/plugins/specweave-kafka/terraform/aws-msk/templates/server.properties.tpl +32 -0
- package/plugins/specweave-kafka/terraform/aws-msk/variables.tf +235 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/main.tf +281 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/outputs.tf +118 -0
- package/plugins/specweave-kafka/terraform/azure-event-hubs/variables.tf +148 -0
- package/plugins/specweave-kafka/tsconfig.json +21 -0
- package/plugins/specweave-kafka-streams/.claude-plugin/plugin.json +23 -0
- package/plugins/specweave-kafka-streams/README.md +310 -0
- package/plugins/specweave-kafka-streams/skills/kafka-streams-topology/SKILL.md +539 -0
- package/plugins/specweave-n8n/.claude-plugin/plugin.json +22 -0
- package/plugins/specweave-n8n/README.md +354 -0
- package/plugins/specweave-n8n/skills/n8n-kafka-workflows/SKILL.md +504 -0
- package/plugins/specweave-release/commands/specweave-release-platform.md +1 -1
- package/plugins/specweave-release/hooks/post-task-completion.sh +2 -2
- package/src/templates/AGENTS.md.template +601 -7
- package/src/templates/CLAUDE.md.template +188 -88
- package/plugins/specweave-ado/commands/specweave-ado-sync-spec.md +0 -255
- package/plugins/specweave-github/commands/specweave-github-sync-epic.md +0 -248
- package/plugins/specweave-github/commands/specweave-github-sync-from.md +0 -147
- package/plugins/specweave-github/commands/specweave-github-sync-spec.md +0 -208
- package/plugins/specweave-github/commands/specweave-github-sync-tasks.md +0 -530
- package/plugins/specweave-jira/commands/specweave-jira-sync-epic.md +0 -267
- package/plugins/specweave-jira/commands/specweave-jira-sync-spec.md +0 -240
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry Instrumentation for Apache Kafka
|
|
3
|
+
*
|
|
4
|
+
* Provides distributed tracing and metrics for Kafka producers and consumers
|
|
5
|
+
* following OpenTelemetry semantic conventions.
|
|
6
|
+
*
|
|
7
|
+
* @module opentelemetry-kafka
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
trace,
|
|
12
|
+
context,
|
|
13
|
+
SpanKind,
|
|
14
|
+
SpanStatusCode,
|
|
15
|
+
Span,
|
|
16
|
+
Context,
|
|
17
|
+
propagation,
|
|
18
|
+
} from '@opentelemetry/api';
|
|
19
|
+
import { SemanticAttributes } from '@opentelemetry/semantic-conventions';
|
|
20
|
+
import { Producer, Consumer, Message, RecordMetadata } from 'kafkajs';
|
|
21
|
+
|
|
22
|
+
// Kafka-specific semantic attributes (following OTel conventions)
|
|
23
|
+
export const KafkaAttributes = {
|
|
24
|
+
MESSAGING_SYSTEM: 'messaging.system',
|
|
25
|
+
MESSAGING_DESTINATION: 'messaging.destination',
|
|
26
|
+
MESSAGING_DESTINATION_KIND: 'messaging.destination_kind',
|
|
27
|
+
MESSAGING_OPERATION: 'messaging.operation',
|
|
28
|
+
MESSAGING_KAFKA_MESSAGE_KEY: 'messaging.kafka.message_key',
|
|
29
|
+
MESSAGING_KAFKA_PARTITION: 'messaging.kafka.partition',
|
|
30
|
+
MESSAGING_KAFKA_OFFSET: 'messaging.kafka.offset',
|
|
31
|
+
MESSAGING_KAFKA_TOMBSTONE: 'messaging.kafka.tombstone',
|
|
32
|
+
MESSAGING_KAFKA_CLIENT_ID: 'messaging.kafka.client_id',
|
|
33
|
+
MESSAGING_KAFKA_CONSUMER_GROUP: 'messaging.kafka.consumer.group',
|
|
34
|
+
} as const;
|
|
35
|
+
|
|
36
|
+
// Trace context header keys
|
|
37
|
+
const TRACEPARENT_HEADER = 'traceparent';
|
|
38
|
+
const TRACESTATE_HEADER = 'tracestate';
|
|
39
|
+
|
|
40
|
+
export interface KafkaTracingConfig {
|
|
41
|
+
/** Tracer name (default: 'kafka') */
|
|
42
|
+
tracerName?: string;
|
|
43
|
+
/** Whether to capture message payloads in spans (default: false for security) */
|
|
44
|
+
capturePayloads?: boolean;
|
|
45
|
+
/** Whether to capture message keys in spans (default: true) */
|
|
46
|
+
captureKeys?: boolean;
|
|
47
|
+
/** Max payload size to capture in bytes (default: 1024) */
|
|
48
|
+
maxPayloadSize?: number;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* OpenTelemetry instrumentation for Kafka Producer
|
|
53
|
+
*/
|
|
54
|
+
export class KafkaProducerTracing {
|
|
55
|
+
private tracer: ReturnType<typeof trace.getTracer>;
|
|
56
|
+
private config: Required<KafkaTracingConfig>;
|
|
57
|
+
|
|
58
|
+
constructor(config: KafkaTracingConfig = {}) {
|
|
59
|
+
this.config = {
|
|
60
|
+
tracerName: config.tracerName || 'kafka-producer',
|
|
61
|
+
capturePayloads: config.capturePayloads || false,
|
|
62
|
+
captureKeys: config.captureKeys || true,
|
|
63
|
+
maxPayloadSize: config.maxPayloadSize || 1024,
|
|
64
|
+
};
|
|
65
|
+
this.tracer = trace.getTracer(this.config.tracerName);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Instrument a producer.send() call with distributed tracing
|
|
70
|
+
*/
|
|
71
|
+
async traceSend(
|
|
72
|
+
producer: Producer,
|
|
73
|
+
topic: string,
|
|
74
|
+
messages: Message[],
|
|
75
|
+
clientId?: string
|
|
76
|
+
): Promise<RecordMetadata[]> {
|
|
77
|
+
// Create producer span
|
|
78
|
+
return this.tracer.startActiveSpan(
|
|
79
|
+
`${topic} send`,
|
|
80
|
+
{
|
|
81
|
+
kind: SpanKind.PRODUCER,
|
|
82
|
+
attributes: {
|
|
83
|
+
[KafkaAttributes.MESSAGING_SYSTEM]: 'kafka',
|
|
84
|
+
[KafkaAttributes.MESSAGING_DESTINATION]: topic,
|
|
85
|
+
[KafkaAttributes.MESSAGING_DESTINATION_KIND]: 'topic',
|
|
86
|
+
[KafkaAttributes.MESSAGING_OPERATION]: 'send',
|
|
87
|
+
[KafkaAttributes.MESSAGING_KAFKA_CLIENT_ID]: clientId || 'unknown',
|
|
88
|
+
'messaging.batch.message_count': messages.length,
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
async (span: Span) => {
|
|
92
|
+
try {
|
|
93
|
+
// Inject trace context into message headers
|
|
94
|
+
const instrumentedMessages = messages.map((msg) => {
|
|
95
|
+
const headers = msg.headers || {};
|
|
96
|
+
|
|
97
|
+
// Inject trace context (W3C Trace Context propagation)
|
|
98
|
+
propagation.inject(context.active(), headers, {
|
|
99
|
+
set: (carrier, key, value) => {
|
|
100
|
+
carrier[key] = value;
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Add span attributes
|
|
105
|
+
if (this.config.captureKeys && msg.key) {
|
|
106
|
+
span.setAttribute(
|
|
107
|
+
KafkaAttributes.MESSAGING_KAFKA_MESSAGE_KEY,
|
|
108
|
+
msg.key.toString()
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (this.config.capturePayloads && msg.value) {
|
|
113
|
+
const payload = msg.value.toString();
|
|
114
|
+
const truncated = payload.length > this.config.maxPayloadSize
|
|
115
|
+
? payload.substring(0, this.config.maxPayloadSize) + '...'
|
|
116
|
+
: payload;
|
|
117
|
+
span.setAttribute('messaging.message.payload', truncated);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { ...msg, headers };
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// Send messages
|
|
124
|
+
const result = await producer.send({
|
|
125
|
+
topic,
|
|
126
|
+
messages: instrumentedMessages,
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
// Record success
|
|
130
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
131
|
+
|
|
132
|
+
// Add result attributes
|
|
133
|
+
result.forEach((metadata, index) => {
|
|
134
|
+
span.addEvent('message.sent', {
|
|
135
|
+
[KafkaAttributes.MESSAGING_KAFKA_PARTITION]: metadata.partition,
|
|
136
|
+
[KafkaAttributes.MESSAGING_KAFKA_OFFSET]: metadata.offset?.toString() || 'unknown',
|
|
137
|
+
'message.index': index,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return result;
|
|
142
|
+
} catch (error) {
|
|
143
|
+
// Record error
|
|
144
|
+
span.setStatus({
|
|
145
|
+
code: SpanStatusCode.ERROR,
|
|
146
|
+
message: error instanceof Error ? error.message : String(error),
|
|
147
|
+
});
|
|
148
|
+
span.recordException(error as Error);
|
|
149
|
+
throw error;
|
|
150
|
+
} finally {
|
|
151
|
+
span.end();
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* OpenTelemetry instrumentation for Kafka Consumer
|
|
160
|
+
*/
|
|
161
|
+
export class KafkaConsumerTracing {
|
|
162
|
+
private tracer: ReturnType<typeof trace.getTracer>;
|
|
163
|
+
private config: Required<KafkaTracingConfig>;
|
|
164
|
+
|
|
165
|
+
constructor(config: KafkaTracingConfig = {}) {
|
|
166
|
+
this.config = {
|
|
167
|
+
tracerName: config.tracerName || 'kafka-consumer',
|
|
168
|
+
capturePayloads: config.capturePayloads || false,
|
|
169
|
+
captureKeys: config.captureKeys || true,
|
|
170
|
+
maxPayloadSize: config.maxPayloadSize || 1024,
|
|
171
|
+
};
|
|
172
|
+
this.tracer = trace.getTracer(this.config.tracerName);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Instrument a message consumption with distributed tracing
|
|
177
|
+
*/
|
|
178
|
+
async traceMessage<T>(
|
|
179
|
+
topic: string,
|
|
180
|
+
partition: number,
|
|
181
|
+
message: Message,
|
|
182
|
+
consumerGroup: string,
|
|
183
|
+
handler: (ctx: Context) => Promise<T>
|
|
184
|
+
): Promise<T> {
|
|
185
|
+
// Extract trace context from message headers
|
|
186
|
+
const parentContext = propagation.extract(
|
|
187
|
+
context.active(),
|
|
188
|
+
message.headers || {},
|
|
189
|
+
{
|
|
190
|
+
get: (carrier, key) => {
|
|
191
|
+
const value = carrier[key];
|
|
192
|
+
return Array.isArray(value) ? value[0]?.toString() : value?.toString();
|
|
193
|
+
},
|
|
194
|
+
keys: (carrier) => Object.keys(carrier),
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// Create consumer span (child of producer span via trace context)
|
|
199
|
+
return this.tracer.startActiveSpan(
|
|
200
|
+
`${topic} receive`,
|
|
201
|
+
{
|
|
202
|
+
kind: SpanKind.CONSUMER,
|
|
203
|
+
attributes: {
|
|
204
|
+
[KafkaAttributes.MESSAGING_SYSTEM]: 'kafka',
|
|
205
|
+
[KafkaAttributes.MESSAGING_DESTINATION]: topic,
|
|
206
|
+
[KafkaAttributes.MESSAGING_DESTINATION_KIND]: 'topic',
|
|
207
|
+
[KafkaAttributes.MESSAGING_OPERATION]: 'receive',
|
|
208
|
+
[KafkaAttributes.MESSAGING_KAFKA_PARTITION]: partition,
|
|
209
|
+
[KafkaAttributes.MESSAGING_KAFKA_OFFSET]: message.offset,
|
|
210
|
+
[KafkaAttributes.MESSAGING_KAFKA_CONSUMER_GROUP]: consumerGroup,
|
|
211
|
+
[KafkaAttributes.MESSAGING_KAFKA_TOMBSTONE]: message.value === null,
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
parentContext,
|
|
215
|
+
async (span: Span) => {
|
|
216
|
+
try {
|
|
217
|
+
// Add message attributes
|
|
218
|
+
if (this.config.captureKeys && message.key) {
|
|
219
|
+
span.setAttribute(
|
|
220
|
+
KafkaAttributes.MESSAGING_KAFKA_MESSAGE_KEY,
|
|
221
|
+
message.key.toString()
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
if (this.config.capturePayloads && message.value) {
|
|
226
|
+
const payload = message.value.toString();
|
|
227
|
+
const truncated = payload.length > this.config.maxPayloadSize
|
|
228
|
+
? payload.substring(0, this.config.maxPayloadSize) + '...'
|
|
229
|
+
: payload;
|
|
230
|
+
span.setAttribute('messaging.message.payload', truncated);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Execute handler with active span context
|
|
234
|
+
const result = await handler(context.active());
|
|
235
|
+
|
|
236
|
+
// Record success
|
|
237
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
238
|
+
return result;
|
|
239
|
+
} catch (error) {
|
|
240
|
+
// Record error
|
|
241
|
+
span.setStatus({
|
|
242
|
+
code: SpanStatusCode.ERROR,
|
|
243
|
+
message: error instanceof Error ? error.message : String(error),
|
|
244
|
+
});
|
|
245
|
+
span.recordException(error as Error);
|
|
246
|
+
throw error;
|
|
247
|
+
} finally {
|
|
248
|
+
span.end();
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Create a processing span for custom operations within message handler
|
|
256
|
+
*/
|
|
257
|
+
createProcessingSpan<T>(
|
|
258
|
+
name: string,
|
|
259
|
+
operation: string,
|
|
260
|
+
callback: (span: Span) => Promise<T>
|
|
261
|
+
): Promise<T> {
|
|
262
|
+
return this.tracer.startActiveSpan(
|
|
263
|
+
name,
|
|
264
|
+
{
|
|
265
|
+
kind: SpanKind.INTERNAL,
|
|
266
|
+
attributes: {
|
|
267
|
+
'operation.name': operation,
|
|
268
|
+
},
|
|
269
|
+
},
|
|
270
|
+
async (span: Span) => {
|
|
271
|
+
try {
|
|
272
|
+
const result = await callback(span);
|
|
273
|
+
span.setStatus({ code: SpanStatusCode.OK });
|
|
274
|
+
return result;
|
|
275
|
+
} catch (error) {
|
|
276
|
+
span.setStatus({
|
|
277
|
+
code: SpanStatusCode.ERROR,
|
|
278
|
+
message: error instanceof Error ? error.message : String(error),
|
|
279
|
+
});
|
|
280
|
+
span.recordException(error as Error);
|
|
281
|
+
throw error;
|
|
282
|
+
} finally {
|
|
283
|
+
span.end();
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Example Usage: Producer
|
|
292
|
+
*
|
|
293
|
+
* ```typescript
|
|
294
|
+
* import { KafkaProducerTracing } from './opentelemetry-kafka';
|
|
295
|
+
*
|
|
296
|
+
* const producerTracing = new KafkaProducerTracing({
|
|
297
|
+
* tracerName: 'my-kafka-producer',
|
|
298
|
+
* capturePayloads: false, // Don't capture sensitive data
|
|
299
|
+
* captureKeys: true,
|
|
300
|
+
* });
|
|
301
|
+
*
|
|
302
|
+
* // Send with tracing
|
|
303
|
+
* await producerTracing.traceSend(
|
|
304
|
+
* producer,
|
|
305
|
+
* 'orders',
|
|
306
|
+
* [
|
|
307
|
+
* { key: 'order-123', value: JSON.stringify({ id: 123, total: 99.99 }) }
|
|
308
|
+
* ],
|
|
309
|
+
* 'order-producer'
|
|
310
|
+
* );
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Example Usage: Consumer
|
|
316
|
+
*
|
|
317
|
+
* ```typescript
|
|
318
|
+
* import { KafkaConsumerTracing } from './opentelemetry-kafka';
|
|
319
|
+
*
|
|
320
|
+
* const consumerTracing = new KafkaConsumerTracing({
|
|
321
|
+
* tracerName: 'my-kafka-consumer',
|
|
322
|
+
* capturePayloads: false,
|
|
323
|
+
* });
|
|
324
|
+
*
|
|
325
|
+
* await consumer.run({
|
|
326
|
+
* eachMessage: async ({ topic, partition, message }) => {
|
|
327
|
+
* // Automatic trace context extraction and span creation
|
|
328
|
+
* await consumerTracing.traceMessage(
|
|
329
|
+
* topic,
|
|
330
|
+
* partition,
|
|
331
|
+
* message,
|
|
332
|
+
* 'my-consumer-group',
|
|
333
|
+
* async (ctx) => {
|
|
334
|
+
* // Your message processing logic here
|
|
335
|
+
* const order = JSON.parse(message.value.toString());
|
|
336
|
+
*
|
|
337
|
+
* // Create child span for database operation
|
|
338
|
+
* await consumerTracing.createProcessingSpan(
|
|
339
|
+
* 'save-order-to-db',
|
|
340
|
+
* 'database.insert',
|
|
341
|
+
* async (span) => {
|
|
342
|
+
* span.setAttribute('db.operation', 'INSERT');
|
|
343
|
+
* span.setAttribute('db.table', 'orders');
|
|
344
|
+
* await db.insert(order);
|
|
345
|
+
* }
|
|
346
|
+
* );
|
|
347
|
+
* }
|
|
348
|
+
* );
|
|
349
|
+
* },
|
|
350
|
+
* });
|
|
351
|
+
* ```
|
|
352
|
+
*/
|
|
353
|
+
|
|
354
|
+
export default {
|
|
355
|
+
KafkaProducerTracing,
|
|
356
|
+
KafkaConsumerTracing,
|
|
357
|
+
KafkaAttributes,
|
|
358
|
+
};
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
var JoinPattern = /* @__PURE__ */ ((JoinPattern2) => {
|
|
2
|
+
JoinPattern2["STREAM_STREAM"] = "stream-stream";
|
|
3
|
+
JoinPattern2["STREAM_TABLE"] = "stream-table";
|
|
4
|
+
JoinPattern2["TABLE_TABLE"] = "table-table";
|
|
5
|
+
JoinPattern2["MULTI_WAY"] = "multi-way";
|
|
6
|
+
JoinPattern2["SELF_JOIN"] = "self-join";
|
|
7
|
+
return JoinPattern2;
|
|
8
|
+
})(JoinPattern || {});
|
|
9
|
+
var AggregationPattern = /* @__PURE__ */ ((AggregationPattern2) => {
|
|
10
|
+
AggregationPattern2["SIMPLE"] = "simple";
|
|
11
|
+
AggregationPattern2["SESSION"] = "session";
|
|
12
|
+
AggregationPattern2["HOPPING"] = "hopping";
|
|
13
|
+
AggregationPattern2["TUMBLING"] = "tumbling";
|
|
14
|
+
AggregationPattern2["CUSTOM_UDF"] = "custom-udf";
|
|
15
|
+
return AggregationPattern2;
|
|
16
|
+
})(AggregationPattern || {});
|
|
17
|
+
class KsqlDBQueryBuilder {
|
|
18
|
+
/**
|
|
19
|
+
* Generate stream-stream join query
|
|
20
|
+
*
|
|
21
|
+
* Use case: Join two event streams within a time window (e.g., clicks + purchases)
|
|
22
|
+
*/
|
|
23
|
+
static generateStreamStreamJoin(options) {
|
|
24
|
+
return `
|
|
25
|
+
-- Stream-Stream Join (time-windowed)
|
|
26
|
+
CREATE STREAM ${options.outputStream} AS
|
|
27
|
+
SELECT
|
|
28
|
+
${options.selectFields.join(",\n ")}
|
|
29
|
+
FROM ${options.leftStream} L
|
|
30
|
+
INNER JOIN ${options.rightStream} R
|
|
31
|
+
WITHIN ${options.windowSizeMinutes} MINUTES
|
|
32
|
+
ON L.${options.joinKey} = R.${options.joinKey}
|
|
33
|
+
EMIT CHANGES;
|
|
34
|
+
|
|
35
|
+
/* Example Output:
|
|
36
|
+
* Left stream: clicks (user_id, page, timestamp)
|
|
37
|
+
* Right stream: purchases (user_id, product, amount, timestamp)
|
|
38
|
+
* Join: Match clicks within 30 minutes of purchase
|
|
39
|
+
* Result: User journey (click \u2192 purchase conversion)
|
|
40
|
+
*/
|
|
41
|
+
`.trim();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Generate stream-table join query
|
|
45
|
+
*
|
|
46
|
+
* Use case: Enrich stream with reference data (e.g., user profile lookup)
|
|
47
|
+
*/
|
|
48
|
+
static generateStreamTableJoin(options) {
|
|
49
|
+
return `
|
|
50
|
+
-- Stream-Table Join (enrichment)
|
|
51
|
+
CREATE STREAM ${options.outputStream} AS
|
|
52
|
+
SELECT
|
|
53
|
+
${options.selectFields.join(",\n ")}
|
|
54
|
+
FROM ${options.stream} S
|
|
55
|
+
LEFT JOIN ${options.table} T
|
|
56
|
+
ON S.${options.joinKey} = T.${options.joinKey}
|
|
57
|
+
EMIT CHANGES;
|
|
58
|
+
|
|
59
|
+
/* Example Output:
|
|
60
|
+
* Stream: orders (order_id, user_id, amount)
|
|
61
|
+
* Table: users (user_id, name, email, tier)
|
|
62
|
+
* Result: Enriched orders with user metadata
|
|
63
|
+
* Note: Table must be keyed on join key!
|
|
64
|
+
*/
|
|
65
|
+
`.trim();
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Generate table-table join query
|
|
69
|
+
*
|
|
70
|
+
* Use case: Create materialized view of joined tables
|
|
71
|
+
*/
|
|
72
|
+
static generateTableTableJoin(options) {
|
|
73
|
+
return `
|
|
74
|
+
-- Table-Table Join (materialized view)
|
|
75
|
+
CREATE TABLE ${options.outputTable} AS
|
|
76
|
+
SELECT
|
|
77
|
+
${options.selectFields.join(",\n ")}
|
|
78
|
+
FROM ${options.leftTable} L
|
|
79
|
+
INNER JOIN ${options.rightTable} R
|
|
80
|
+
ON L.${options.joinKey} = R.${options.joinKey};
|
|
81
|
+
|
|
82
|
+
/* Example Output:
|
|
83
|
+
* Left table: customer_profiles (customer_id, name, email)
|
|
84
|
+
* Right table: customer_preferences (customer_id, category, value)
|
|
85
|
+
* Result: Unified customer view (profile + preferences)
|
|
86
|
+
* Note: Both tables must be keyed on join key!
|
|
87
|
+
*/
|
|
88
|
+
`.trim();
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Generate multi-way join query
|
|
92
|
+
*
|
|
93
|
+
* Use case: Join 3+ streams/tables (e.g., clicks + cart + purchase)
|
|
94
|
+
*/
|
|
95
|
+
static generateMultiWayJoin(options) {
|
|
96
|
+
if (options.streams.length < 3) {
|
|
97
|
+
throw new Error("Multi-way join requires at least 3 streams");
|
|
98
|
+
}
|
|
99
|
+
const baseStream = options.streams[0];
|
|
100
|
+
const joinClauses = options.streams.slice(1).map((stream, index) => {
|
|
101
|
+
const within = options.windowSizeMinutes ? `WITHIN ${options.windowSizeMinutes} MINUTES` : "";
|
|
102
|
+
return `INNER JOIN ${stream.name} ${stream.alias} ${within}
|
|
103
|
+
ON ${baseStream.alias}.${options.joinKey} = ${stream.alias}.${options.joinKey}`;
|
|
104
|
+
});
|
|
105
|
+
return `
|
|
106
|
+
-- Multi-Way Join (3+ streams)
|
|
107
|
+
CREATE STREAM ${options.outputStream} AS
|
|
108
|
+
SELECT
|
|
109
|
+
${options.selectFields.join(",\n ")}
|
|
110
|
+
FROM ${baseStream.name} ${baseStream.alias}
|
|
111
|
+
${joinClauses.join("\n")}
|
|
112
|
+
EMIT CHANGES;
|
|
113
|
+
|
|
114
|
+
/* Example Output:
|
|
115
|
+
* Stream 1: clicks (user_id, page)
|
|
116
|
+
* Stream 2: add_to_cart (user_id, product)
|
|
117
|
+
* Stream 3: purchases (user_id, order_id)
|
|
118
|
+
* Result: Full funnel (click \u2192 cart \u2192 purchase)
|
|
119
|
+
*/
|
|
120
|
+
`.trim();
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Generate session window aggregation
|
|
124
|
+
*
|
|
125
|
+
* Use case: Group events by user session (e.g., website activity)
|
|
126
|
+
*/
|
|
127
|
+
static generateSessionAggregation(options) {
|
|
128
|
+
const aggFields = options.aggregations.map((agg) => `${agg.func}(${agg.column}) AS ${agg.alias}`).join(",\n ");
|
|
129
|
+
return `
|
|
130
|
+
-- Session Window Aggregation
|
|
131
|
+
CREATE TABLE ${options.outputTable} AS
|
|
132
|
+
SELECT
|
|
133
|
+
${options.groupByKey},
|
|
134
|
+
${aggFields}
|
|
135
|
+
FROM ${options.inputStream}
|
|
136
|
+
WINDOW SESSION (${options.sessionGapMinutes} MINUTES)
|
|
137
|
+
GROUP BY ${options.groupByKey}
|
|
138
|
+
EMIT CHANGES;
|
|
139
|
+
|
|
140
|
+
/* Example Output:
|
|
141
|
+
* Input: page_views (user_id, page, timestamp)
|
|
142
|
+
* Session gap: 30 minutes of inactivity
|
|
143
|
+
* Aggregations:
|
|
144
|
+
* - COUNT(*) AS page_count
|
|
145
|
+
* - COLLECT_LIST(page) AS pages_visited
|
|
146
|
+
* - EARLIEST_BY_OFFSET(timestamp) AS session_start
|
|
147
|
+
* - LATEST_BY_OFFSET(timestamp) AS session_end
|
|
148
|
+
* Result: User sessions with duration and page count
|
|
149
|
+
*/
|
|
150
|
+
`.trim();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Generate hopping window aggregation
|
|
154
|
+
*
|
|
155
|
+
* Use case: Overlapping time windows (e.g., 5-minute windows every 1 minute)
|
|
156
|
+
*/
|
|
157
|
+
static generateHoppingAggregation(options) {
|
|
158
|
+
const aggFields = options.aggregations.map((agg) => `${agg.func}(${agg.column}) AS ${agg.alias}`).join(",\n ");
|
|
159
|
+
return `
|
|
160
|
+
-- Hopping Window Aggregation
|
|
161
|
+
CREATE TABLE ${options.outputTable} AS
|
|
162
|
+
SELECT
|
|
163
|
+
${options.groupByKey},
|
|
164
|
+
WINDOWSTART AS window_start,
|
|
165
|
+
WINDOWEND AS window_end,
|
|
166
|
+
${aggFields}
|
|
167
|
+
FROM ${options.inputStream}
|
|
168
|
+
WINDOW HOPPING (SIZE ${options.windowSizeMinutes} MINUTES, ADVANCE BY ${options.advanceByMinutes} MINUTES)
|
|
169
|
+
GROUP BY ${options.groupByKey}
|
|
170
|
+
EMIT CHANGES;
|
|
171
|
+
|
|
172
|
+
/* Example Output:
|
|
173
|
+
* Input: sensor_data (sensor_id, temperature, timestamp)
|
|
174
|
+
* Window: 5 minutes, advance 1 minute (80% overlap)
|
|
175
|
+
* Aggregations:
|
|
176
|
+
* - AVG(temperature) AS avg_temp
|
|
177
|
+
* - MAX(temperature) AS max_temp
|
|
178
|
+
* - STDDEV(temperature) AS temp_stddev
|
|
179
|
+
* Use case: Sliding window anomaly detection
|
|
180
|
+
*/
|
|
181
|
+
`.trim();
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Generate tumbling window aggregation
|
|
185
|
+
*
|
|
186
|
+
* Use case: Non-overlapping time windows (e.g., hourly totals)
|
|
187
|
+
*/
|
|
188
|
+
static generateTumblingAggregation(options) {
|
|
189
|
+
const aggFields = options.aggregations.map((agg) => `${agg.func}(${agg.column}) AS ${agg.alias}`).join(",\n ");
|
|
190
|
+
return `
|
|
191
|
+
-- Tumbling Window Aggregation
|
|
192
|
+
CREATE TABLE ${options.outputTable} AS
|
|
193
|
+
SELECT
|
|
194
|
+
${options.groupByKey},
|
|
195
|
+
WINDOWSTART AS window_start,
|
|
196
|
+
WINDOWEND AS window_end,
|
|
197
|
+
${aggFields}
|
|
198
|
+
FROM ${options.inputStream}
|
|
199
|
+
WINDOW TUMBLING (SIZE ${options.windowSizeMinutes} MINUTES)
|
|
200
|
+
GROUP BY ${options.groupByKey}
|
|
201
|
+
EMIT CHANGES;
|
|
202
|
+
|
|
203
|
+
/* Example Output:
|
|
204
|
+
* Input: transactions (merchant_id, amount, timestamp)
|
|
205
|
+
* Window: 60 minutes (non-overlapping hourly windows)
|
|
206
|
+
* Aggregations:
|
|
207
|
+
* - COUNT(*) AS transaction_count
|
|
208
|
+
* - SUM(amount) AS total_revenue
|
|
209
|
+
* - AVG(amount) AS avg_transaction_value
|
|
210
|
+
* Use case: Hourly sales reports
|
|
211
|
+
*/
|
|
212
|
+
`.trim();
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Generate custom UDF aggregation
|
|
216
|
+
*
|
|
217
|
+
* Use case: Complex business logic (e.g., weighted average, percentiles)
|
|
218
|
+
*/
|
|
219
|
+
static generateCustomUDFQuery(options) {
|
|
220
|
+
const params = options.udfParameters.join(", ");
|
|
221
|
+
const groupBy = options.groupByKey ? `GROUP BY ${options.groupByKey}` : "";
|
|
222
|
+
return `
|
|
223
|
+
-- Custom UDF Query
|
|
224
|
+
CREATE STREAM ${options.outputStream} AS
|
|
225
|
+
SELECT
|
|
226
|
+
${options.groupByKey ? `${options.groupByKey},` : ""}
|
|
227
|
+
${options.udfName}(${params}) AS result
|
|
228
|
+
FROM ${options.inputStream}
|
|
229
|
+
${groupBy}
|
|
230
|
+
EMIT CHANGES;
|
|
231
|
+
|
|
232
|
+
/* Example UDF (Java):
|
|
233
|
+
* @UdfDescription(name = "weighted_avg", description = "Calculates weighted average")
|
|
234
|
+
* public class WeightedAvgUDF {
|
|
235
|
+
* @Udf(description = "weighted_avg(value, weight)")
|
|
236
|
+
* public double weightedAvg(double value, double weight) {
|
|
237
|
+
* // Custom aggregation logic
|
|
238
|
+
* }
|
|
239
|
+
* }
|
|
240
|
+
*
|
|
241
|
+
* Usage:
|
|
242
|
+
* SELECT weighted_avg(price, quantity) AS avg_price FROM orders;
|
|
243
|
+
*/
|
|
244
|
+
`.trim();
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
class KsqlDBUDFGenerator {
|
|
248
|
+
/**
|
|
249
|
+
* Generate scalar UDF template
|
|
250
|
+
*/
|
|
251
|
+
static generateScalarUDF(options) {
|
|
252
|
+
const params = options.parameters.map((p) => `final ${p.type} ${p.name}`).join(", ");
|
|
253
|
+
return `
|
|
254
|
+
package com.example.ksqldb.udf;
|
|
255
|
+
|
|
256
|
+
import io.confluent.ksql.function.udf.Udf;
|
|
257
|
+
import io.confluent.ksql.function.udf.UdfDescription;
|
|
258
|
+
|
|
259
|
+
@UdfDescription(
|
|
260
|
+
name = "${options.udfName}",
|
|
261
|
+
description = "${options.description}"
|
|
262
|
+
)
|
|
263
|
+
public class ${this.toPascalCase(options.udfName)}UDF {
|
|
264
|
+
|
|
265
|
+
@Udf(description = "${options.description}")
|
|
266
|
+
public ${options.returnType} ${options.udfName}(${params}) {
|
|
267
|
+
// TODO: Implement UDF logic
|
|
268
|
+
return null;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* Deployment:
|
|
273
|
+
* 1. Build JAR: mvn clean package
|
|
274
|
+
* 2. Copy to ksqlDB extensions: /usr/share/java/ksqldb-server/ext/
|
|
275
|
+
* 3. Restart ksqlDB server
|
|
276
|
+
* 4. Test: SHOW FUNCTIONS;
|
|
277
|
+
*/
|
|
278
|
+
`.trim();
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Generate UDAF (User-Defined Aggregate Function) template
|
|
282
|
+
*/
|
|
283
|
+
static generateUDAF(options) {
|
|
284
|
+
return `
|
|
285
|
+
package com.example.ksqldb.udaf;
|
|
286
|
+
|
|
287
|
+
import io.confluent.ksql.function.udaf.Udaf;
|
|
288
|
+
import io.confluent.ksql.function.udaf.UdafDescription;
|
|
289
|
+
import io.confluent.ksql.function.udaf.UdafFactory;
|
|
290
|
+
|
|
291
|
+
@UdafDescription(
|
|
292
|
+
name = "${options.udafName}",
|
|
293
|
+
description = "${options.description}"
|
|
294
|
+
)
|
|
295
|
+
public class ${this.toPascalCase(options.udafName)}UDAF {
|
|
296
|
+
|
|
297
|
+
@UdafFactory(description = "${options.description}")
|
|
298
|
+
public static Udaf<${options.inputType}, ${options.aggregateType}, ${options.returnType}> create${this.toPascalCase(options.udafName)}() {
|
|
299
|
+
return new Udaf<${options.inputType}, ${options.aggregateType}, ${options.returnType}>() {
|
|
300
|
+
|
|
301
|
+
@Override
|
|
302
|
+
public ${options.aggregateType} initialize() {
|
|
303
|
+
// Initialize aggregate state
|
|
304
|
+
return null; // TODO
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
@Override
|
|
308
|
+
public ${options.aggregateType} aggregate(${options.inputType} newValue, ${options.aggregateType} aggregate) {
|
|
309
|
+
// Add new value to aggregate
|
|
310
|
+
return aggregate; // TODO
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
@Override
|
|
314
|
+
public ${options.aggregateType} merge(${options.aggregateType} agg1, ${options.aggregateType} agg2) {
|
|
315
|
+
// Merge two aggregates (for parallel processing)
|
|
316
|
+
return agg1; // TODO
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
@Override
|
|
320
|
+
public ${options.returnType} map(${options.aggregateType} aggregate) {
|
|
321
|
+
// Convert aggregate to return type
|
|
322
|
+
return null; // TODO
|
|
323
|
+
}
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/* Example Usage (ksqlDB):
|
|
329
|
+
* CREATE TABLE aggregated AS
|
|
330
|
+
* SELECT
|
|
331
|
+
* key,
|
|
332
|
+
* ${options.udafName}(value) AS result
|
|
333
|
+
* FROM input_stream
|
|
334
|
+
* GROUP BY key;
|
|
335
|
+
*/
|
|
336
|
+
`.trim();
|
|
337
|
+
}
|
|
338
|
+
static toPascalCase(str) {
|
|
339
|
+
return str.split("_").map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()).join("");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
var advanced_ksqldb_patterns_default = {
|
|
343
|
+
KsqlDBQueryBuilder,
|
|
344
|
+
KsqlDBUDFGenerator,
|
|
345
|
+
JoinPattern,
|
|
346
|
+
AggregationPattern
|
|
347
|
+
};
|
|
348
|
+
export {
|
|
349
|
+
AggregationPattern,
|
|
350
|
+
JoinPattern,
|
|
351
|
+
KsqlDBQueryBuilder,
|
|
352
|
+
KsqlDBUDFGenerator,
|
|
353
|
+
advanced_ksqldb_patterns_default as default
|
|
354
|
+
};
|