specweave 0.28.68 → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +3 -2
- package/README.md +19 -2
- package/dist/src/cli/commands/discrepancies.d.ts +89 -0
- package/dist/src/cli/commands/discrepancies.d.ts.map +1 -0
- package/dist/src/cli/commands/discrepancies.js +385 -0
- package/dist/src/cli/commands/discrepancies.js.map +1 -0
- package/dist/src/cli/commands/notifications.d.ts +70 -0
- package/dist/src/cli/commands/notifications.d.ts.map +1 -0
- package/dist/src/cli/commands/notifications.js +236 -0
- package/dist/src/cli/commands/notifications.js.map +1 -0
- package/dist/src/cli/commands/sync-logs.d.ts +54 -0
- package/dist/src/cli/commands/sync-logs.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-logs.js +240 -0
- package/dist/src/cli/commands/sync-logs.js.map +1 -0
- package/dist/src/cli/commands/sync-monitor.d.ts +42 -0
- package/dist/src/cli/commands/sync-monitor.d.ts.map +1 -0
- package/dist/src/cli/commands/sync-monitor.js +191 -0
- package/dist/src/cli/commands/sync-monitor.js.map +1 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.d.ts +45 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.d.ts.map +1 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.js +431 -0
- package/dist/src/cli/helpers/init/brownfield-analysis.js.map +1 -0
- package/dist/src/cli/helpers/init/index.d.ts +1 -0
- package/dist/src/cli/helpers/init/index.d.ts.map +1 -1
- package/dist/src/cli/helpers/init/index.js +2 -0
- package/dist/src/cli/helpers/init/index.js.map +1 -1
- package/dist/src/cli/workers/brownfield-worker.d.ts +66 -0
- package/dist/src/cli/workers/brownfield-worker.d.ts.map +1 -0
- package/dist/src/cli/workers/brownfield-worker.js +417 -0
- package/dist/src/cli/workers/brownfield-worker.js.map +1 -0
- package/dist/src/core/background/brownfield-launcher.d.ts +86 -0
- package/dist/src/core/background/brownfield-launcher.d.ts.map +1 -0
- package/dist/src/core/background/brownfield-launcher.js +295 -0
- package/dist/src/core/background/brownfield-launcher.js.map +1 -0
- package/dist/src/core/background/index.d.ts +2 -0
- package/dist/src/core/background/index.d.ts.map +1 -1
- package/dist/src/core/background/index.js +2 -0
- package/dist/src/core/background/index.js.map +1 -1
- package/dist/src/core/background/types.d.ts +23 -2
- package/dist/src/core/background/types.d.ts.map +1 -1
- package/dist/src/core/config/index.d.ts +1 -0
- package/dist/src/core/config/index.d.ts.map +1 -1
- package/dist/src/core/config/index.js +1 -0
- package/dist/src/core/config/index.js.map +1 -1
- package/dist/src/core/config/types.d.ts +6 -0
- package/dist/src/core/config/types.d.ts.map +1 -1
- package/dist/src/core/config/types.js.map +1 -1
- package/dist/src/core/dashboard/dashboard-data.d.ts +156 -0
- package/dist/src/core/dashboard/dashboard-data.d.ts.map +1 -0
- package/dist/src/core/dashboard/dashboard-data.js +191 -0
- package/dist/src/core/dashboard/dashboard-data.js.map +1 -0
- package/dist/src/core/dashboard/index.d.ts +9 -0
- package/dist/src/core/dashboard/index.d.ts.map +1 -0
- package/dist/src/core/dashboard/index.js +9 -0
- package/dist/src/core/dashboard/index.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts +77 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js +286 -0
- package/dist/src/core/discrepancy/analyzers/api-route-analyzer.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/index.d.ts +8 -0
- package/dist/src/core/discrepancy/analyzers/index.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/index.js +8 -0
- package/dist/src/core/discrepancy/analyzers/index.js.map +1 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts +96 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.d.ts.map +1 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js +247 -0
- package/dist/src/core/discrepancy/analyzers/typescript-analyzer.js.map +1 -0
- package/dist/src/core/discrepancy/brownfield-manager.d.ts +88 -0
- package/dist/src/core/discrepancy/brownfield-manager.d.ts.map +1 -0
- package/dist/src/core/discrepancy/brownfield-manager.js +520 -0
- package/dist/src/core/discrepancy/brownfield-manager.js.map +1 -0
- package/dist/src/core/discrepancy/brownfield-types.d.ts +174 -0
- package/dist/src/core/discrepancy/brownfield-types.d.ts.map +1 -0
- package/dist/src/core/discrepancy/brownfield-types.js +11 -0
- package/dist/src/core/discrepancy/brownfield-types.js.map +1 -0
- package/dist/src/core/discrepancy/detector.d.ts +92 -0
- package/dist/src/core/discrepancy/detector.d.ts.map +1 -0
- package/dist/src/core/discrepancy/detector.js +346 -0
- package/dist/src/core/discrepancy/detector.js.map +1 -0
- package/dist/src/core/discrepancy/increment-generator.d.ts +51 -0
- package/dist/src/core/discrepancy/increment-generator.d.ts.map +1 -0
- package/dist/src/core/discrepancy/increment-generator.js +234 -0
- package/dist/src/core/discrepancy/increment-generator.js.map +1 -0
- package/dist/src/core/discrepancy/index.d.ts +18 -0
- package/dist/src/core/discrepancy/index.d.ts.map +1 -0
- package/dist/src/core/discrepancy/index.js +24 -0
- package/dist/src/core/discrepancy/index.js.map +1 -0
- package/dist/src/core/discrepancy/severity-classifier.d.ts +81 -0
- package/dist/src/core/discrepancy/severity-classifier.d.ts.map +1 -0
- package/dist/src/core/discrepancy/severity-classifier.js +289 -0
- package/dist/src/core/discrepancy/severity-classifier.js.map +1 -0
- package/dist/src/core/discrepancy/spec-parser.d.ts +74 -0
- package/dist/src/core/discrepancy/spec-parser.d.ts.map +1 -0
- package/dist/src/core/discrepancy/spec-parser.js +213 -0
- package/dist/src/core/discrepancy/spec-parser.js.map +1 -0
- package/dist/src/core/discrepancy/update-recommender.d.ts +77 -0
- package/dist/src/core/discrepancy/update-recommender.d.ts.map +1 -0
- package/dist/src/core/discrepancy/update-recommender.js +323 -0
- package/dist/src/core/discrepancy/update-recommender.js.map +1 -0
- package/dist/src/core/living-docs/living-docs-sync.d.ts +13 -16
- package/dist/src/core/living-docs/living-docs-sync.d.ts.map +1 -1
- package/dist/src/core/living-docs/living-docs-sync.js +31 -112
- package/dist/src/core/living-docs/living-docs-sync.js.map +1 -1
- package/dist/src/core/logs/index.d.ts +10 -0
- package/dist/src/core/logs/index.d.ts.map +1 -0
- package/dist/src/core/logs/index.js +10 -0
- package/dist/src/core/logs/index.js.map +1 -0
- package/dist/src/core/logs/log-aggregator.d.ts +130 -0
- package/dist/src/core/logs/log-aggregator.d.ts.map +1 -0
- package/dist/src/core/logs/log-aggregator.js +206 -0
- package/dist/src/core/logs/log-aggregator.js.map +1 -0
- package/dist/src/core/logs/log-exporter.d.ts +81 -0
- package/dist/src/core/logs/log-exporter.d.ts.map +1 -0
- package/dist/src/core/logs/log-exporter.js +141 -0
- package/dist/src/core/logs/log-exporter.js.map +1 -0
- package/dist/src/core/notifications/command-integration.d.ts +82 -0
- package/dist/src/core/notifications/command-integration.d.ts.map +1 -0
- package/dist/src/core/notifications/command-integration.js +80 -0
- package/dist/src/core/notifications/command-integration.js.map +1 -0
- package/dist/src/core/notifications/index.d.ts +12 -0
- package/dist/src/core/notifications/index.d.ts.map +1 -0
- package/dist/src/core/notifications/index.js +12 -0
- package/dist/src/core/notifications/index.js.map +1 -0
- package/dist/src/core/notifications/notification-display.d.ts +70 -0
- package/dist/src/core/notifications/notification-display.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-display.js +177 -0
- package/dist/src/core/notifications/notification-display.js.map +1 -0
- package/dist/src/core/notifications/notification-manager.d.ts +126 -0
- package/dist/src/core/notifications/notification-manager.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-manager.js +287 -0
- package/dist/src/core/notifications/notification-manager.js.map +1 -0
- package/dist/src/core/notifications/notification-types.d.ts +159 -0
- package/dist/src/core/notifications/notification-types.d.ts.map +1 -0
- package/dist/src/core/notifications/notification-types.js +93 -0
- package/dist/src/core/notifications/notification-types.js.map +1 -0
- package/dist/src/core/scheduler/index.d.ts +11 -0
- package/dist/src/core/scheduler/index.d.ts.map +1 -0
- package/dist/src/core/scheduler/index.js +11 -0
- package/dist/src/core/scheduler/index.js.map +1 -0
- package/dist/src/core/scheduler/job-scheduler.d.ts +179 -0
- package/dist/src/core/scheduler/job-scheduler.d.ts.map +1 -0
- package/dist/src/core/scheduler/job-scheduler.js +282 -0
- package/dist/src/core/scheduler/job-scheduler.js.map +1 -0
- package/dist/src/core/scheduler/schedule-persistence.d.ts +83 -0
- package/dist/src/core/scheduler/schedule-persistence.d.ts.map +1 -0
- package/dist/src/core/scheduler/schedule-persistence.js +180 -0
- package/dist/src/core/scheduler/schedule-persistence.js.map +1 -0
- package/dist/src/core/scheduler/scheduled-job.d.ts +188 -0
- package/dist/src/core/scheduler/scheduled-job.d.ts.map +1 -0
- package/dist/src/core/scheduler/scheduled-job.js +182 -0
- package/dist/src/core/scheduler/scheduled-job.js.map +1 -0
- package/dist/src/core/sync/permission-enforcer.d.ts +206 -0
- package/dist/src/core/sync/permission-enforcer.d.ts.map +1 -0
- package/dist/src/core/sync/permission-enforcer.js +268 -0
- package/dist/src/core/sync/permission-enforcer.js.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts +217 -0
- package/dist/src/core/sync/sync-audit-logger.d.ts.map +1 -0
- package/dist/src/core/sync/sync-audit-logger.js +327 -0
- package/dist/src/core/sync/sync-audit-logger.js.map +1 -0
- package/dist/src/core/sync/sync-interceptor.d.ts +190 -0
- package/dist/src/core/sync/sync-interceptor.d.ts.map +1 -0
- package/dist/src/core/sync/sync-interceptor.js +224 -0
- package/dist/src/core/sync/sync-interceptor.js.map +1 -0
- package/dist/src/core/types/increment-metadata.d.ts +5 -2
- package/dist/src/core/types/increment-metadata.d.ts.map +1 -1
- package/dist/src/core/types/sync-config.d.ts +267 -0
- package/dist/src/core/types/sync-config.d.ts.map +1 -0
- package/dist/src/core/types/sync-config.js +304 -0
- package/dist/src/core/types/sync-config.js.map +1 -0
- package/dist/src/hooks/index.d.ts +11 -0
- package/dist/src/hooks/index.d.ts.map +1 -0
- package/dist/src/hooks/index.js +11 -0
- package/dist/src/hooks/index.js.map +1 -0
- package/dist/src/hooks/platform.d.ts +125 -0
- package/dist/src/hooks/platform.d.ts.map +1 -0
- package/dist/src/hooks/platform.js +325 -0
- package/dist/src/hooks/platform.js.map +1 -0
- package/dist/src/hooks/processor.d.ts +20 -0
- package/dist/src/hooks/processor.d.ts.map +1 -0
- package/dist/src/hooks/processor.js +317 -0
- package/dist/src/hooks/processor.js.map +1 -0
- package/dist/src/hooks/scheduler-startup.d.ts +19 -0
- package/dist/src/hooks/scheduler-startup.d.ts.map +1 -0
- package/dist/src/hooks/scheduler-startup.js +92 -0
- package/dist/src/hooks/scheduler-startup.js.map +1 -0
- package/dist/src/hooks/session-start.d.ts +16 -0
- package/dist/src/hooks/session-start.d.ts.map +1 -0
- package/dist/src/hooks/session-start.js +92 -0
- package/dist/src/hooks/session-start.js.map +1 -0
- package/dist/src/importers/duplicate-detector.d.ts +13 -2
- package/dist/src/importers/duplicate-detector.d.ts.map +1 -1
- package/dist/src/importers/duplicate-detector.js +21 -2
- package/dist/src/importers/duplicate-detector.js.map +1 -1
- package/dist/src/importers/item-converter.d.ts +41 -2
- package/dist/src/importers/item-converter.d.ts.map +1 -1
- package/dist/src/importers/item-converter.js +225 -38
- package/dist/src/importers/item-converter.js.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.d.ts +7 -0
- package/dist/src/living-docs/fs-id-allocator.d.ts.map +1 -1
- package/dist/src/living-docs/fs-id-allocator.js +30 -4
- package/dist/src/living-docs/fs-id-allocator.js.map +1 -1
- package/dist/src/sync/ado-sync-wrapper.d.ts +137 -0
- package/dist/src/sync/ado-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/ado-sync-wrapper.js +148 -0
- package/dist/src/sync/ado-sync-wrapper.js.map +1 -0
- package/dist/src/sync/github-sync-wrapper.d.ts +195 -0
- package/dist/src/sync/github-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/github-sync-wrapper.js +220 -0
- package/dist/src/sync/github-sync-wrapper.js.map +1 -0
- package/dist/src/sync/jira-sync-wrapper.d.ts +155 -0
- package/dist/src/sync/jira-sync-wrapper.d.ts.map +1 -0
- package/dist/src/sync/jira-sync-wrapper.js +175 -0
- package/dist/src/sync/jira-sync-wrapper.js.map +1 -0
- package/dist/src/utils/feature-id-derivation.d.ts +58 -0
- package/dist/src/utils/feature-id-derivation.d.ts.map +1 -0
- package/dist/src/utils/feature-id-derivation.js +77 -0
- package/dist/src/utils/feature-id-derivation.js.map +1 -0
- package/package.json +3 -1
- package/plugins/specweave/commands/specweave-discrepancies.md +141 -0
- package/plugins/specweave/commands/specweave-discrepancy-to-increment.md +160 -0
- package/plugins/specweave/commands/specweave-jobs.md +45 -2
- package/plugins/specweave/commands/specweave-notifications.md +92 -0
- package/plugins/specweave/commands/specweave-sync-logs.md +131 -0
- package/plugins/specweave/commands/specweave-sync-monitor.md +57 -0
- package/plugins/specweave/hooks/hooks.json +3 -3
- package/plugins/specweave/hooks/lib/scheduler-startup.sh +72 -0
- package/plugins/specweave/hooks/universal/dispatcher.mjs +246 -0
- package/plugins/specweave/hooks/universal/session-start.cmd +16 -0
- package/plugins/specweave/hooks/universal/session-start.ps1 +16 -0
- package/plugins/specweave/hooks/v2/dispatchers/session-start.sh +14 -5
- package/plugins/specweave/lib/vendor/core/types/increment-metadata.d.ts +5 -2
- package/plugins/specweave/skills/discrepancy-viewer.md +154 -0
- package/plugins/specweave-github/hooks/.specweave/logs/hooks-debug.log +46 -0
- package/plugins/specweave-release/hooks/.specweave/logs/dora-tracking.log +69 -0
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts +0 -26
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js +0 -249
- package/dist/plugins/specweave-github/lib/enhanced-github-sync.js.map +0 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts +0 -28
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.d.ts.map +0 -1
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -156
- package/dist/plugins/specweave-jira/lib/enhanced-jira-sync.js.map +0 -1
- package/dist/src/core/sync/bidirectional-engine.d.ts +0 -119
- package/dist/src/core/sync/bidirectional-engine.d.ts.map +0 -1
- package/dist/src/core/sync/bidirectional-engine.js +0 -359
- package/dist/src/core/sync/bidirectional-engine.js.map +0 -1
- package/dist/src/core/sync/conflict-resolver.d.ts +0 -66
- package/dist/src/core/sync/conflict-resolver.d.ts.map +0 -1
- package/dist/src/core/sync/conflict-resolver.js +0 -108
- package/dist/src/core/sync/conflict-resolver.js.map +0 -1
- package/dist/src/core/sync/enhanced-content-builder.d.ts +0 -55
- package/dist/src/core/sync/enhanced-content-builder.d.ts.map +0 -1
- package/dist/src/core/sync/enhanced-content-builder.js +0 -203
- package/dist/src/core/sync/enhanced-content-builder.js.map +0 -1
- package/dist/src/core/sync/folder-mapper.d.ts +0 -71
- package/dist/src/core/sync/folder-mapper.d.ts.map +0 -1
- package/dist/src/core/sync/folder-mapper.js +0 -203
- package/dist/src/core/sync/folder-mapper.js.map +0 -1
- package/dist/src/core/sync/label-detector.d.ts +0 -66
- package/dist/src/core/sync/label-detector.d.ts.map +0 -1
- package/dist/src/core/sync/label-detector.js +0 -224
- package/dist/src/core/sync/label-detector.js.map +0 -1
- package/dist/src/core/sync/performance-optimizer.d.ts +0 -153
- package/dist/src/core/sync/performance-optimizer.d.ts.map +0 -1
- package/dist/src/core/sync/performance-optimizer.js +0 -220
- package/dist/src/core/sync/performance-optimizer.js.map +0 -1
- package/dist/src/core/sync/profile-selector.d.ts +0 -52
- package/dist/src/core/sync/profile-selector.d.ts.map +0 -1
- package/dist/src/core/sync/profile-selector.js +0 -179
- package/dist/src/core/sync/profile-selector.js.map +0 -1
- package/dist/src/core/sync/profile-validator.d.ts +0 -52
- package/dist/src/core/sync/profile-validator.d.ts.map +0 -1
- package/dist/src/core/sync/profile-validator.js +0 -170
- package/dist/src/core/sync/profile-validator.js.map +0 -1
- package/dist/src/core/sync/rate-limiter.d.ts +0 -116
- package/dist/src/core/sync/rate-limiter.d.ts.map +0 -1
- package/dist/src/core/sync/rate-limiter.js +0 -308
- package/dist/src/core/sync/rate-limiter.js.map +0 -1
- package/dist/src/core/sync/retry-handler.d.ts +0 -98
- package/dist/src/core/sync/retry-handler.d.ts.map +0 -1
- package/dist/src/core/sync/retry-handler.js +0 -196
- package/dist/src/core/sync/retry-handler.js.map +0 -1
- package/dist/src/core/sync/retry-logic.d.ts +0 -64
- package/dist/src/core/sync/retry-logic.d.ts.map +0 -1
- package/dist/src/core/sync/retry-logic.js +0 -165
- package/dist/src/core/sync/retry-logic.js.map +0 -1
- package/dist/src/core/sync/status-cache.d.ts +0 -91
- package/dist/src/core/sync/status-cache.d.ts.map +0 -1
- package/dist/src/core/sync/status-cache.js +0 -140
- package/dist/src/core/sync/status-cache.js.map +0 -1
- package/dist/src/core/sync/status-mapper.d.ts +0 -69
- package/dist/src/core/sync/status-mapper.d.ts.map +0 -1
- package/dist/src/core/sync/status-mapper.js +0 -90
- package/dist/src/core/sync/status-mapper.js.map +0 -1
- package/dist/src/core/sync/status-sync-engine.d.ts +0 -162
- package/dist/src/core/sync/status-sync-engine.d.ts.map +0 -1
- package/dist/src/core/sync/status-sync-engine.js +0 -347
- package/dist/src/core/sync/status-sync-engine.js.map +0 -1
- package/dist/src/core/sync/sync-event-logger.d.ts +0 -113
- package/dist/src/core/sync/sync-event-logger.d.ts.map +0 -1
- package/dist/src/core/sync/sync-event-logger.js +0 -141
- package/dist/src/core/sync/sync-event-logger.js.map +0 -1
- package/dist/src/core/sync/time-range-selector.d.ts +0 -48
- package/dist/src/core/sync/time-range-selector.d.ts.map +0 -1
- package/dist/src/core/sync/time-range-selector.js +0 -224
- package/dist/src/core/sync/time-range-selector.js.map +0 -1
- package/dist/src/core/sync/types.d.ts +0 -52
- package/dist/src/core/sync/types.d.ts.map +0 -1
- package/dist/src/core/sync/types.js +0 -5
- package/dist/src/core/sync/types.js.map +0 -1
- package/dist/src/core/sync/workflow-detector.d.ts +0 -95
- package/dist/src/core/sync/workflow-detector.d.ts.map +0 -1
- package/dist/src/core/sync/workflow-detector.js +0 -175
- package/dist/src/core/sync/workflow-detector.js.map +0 -1
- package/plugins/specweave-github/lib/enhanced-github-sync.js +0 -220
- package/plugins/specweave-github/lib/enhanced-github-sync.ts +0 -322
- package/plugins/specweave-jira/lib/enhanced-jira-sync.js +0 -134
- package/plugins/specweave-jira/lib/enhanced-jira-sync.ts +0 -196
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GitHub Sync Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Wraps GitHub sync operations with the SyncInterceptor for permission checks
|
|
5
|
+
* and audit logging. This provides a consistent interface for all GitHub sync
|
|
6
|
+
* operations while ensuring they go through the permission enforcement layer.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const wrapper = new GitHubSyncWrapper({
|
|
11
|
+
* client: GitHubClientV2.fromRepo(owner, repo),
|
|
12
|
+
* interceptor,
|
|
13
|
+
* logger
|
|
14
|
+
* });
|
|
15
|
+
*
|
|
16
|
+
* // All operations now go through permission enforcement
|
|
17
|
+
* const issue = await wrapper.createUserStoryIssue(params);
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @module sync/github-sync-wrapper
|
|
21
|
+
*/
|
|
22
|
+
import { GitHubClientV2 } from '../../plugins/specweave-github/lib/github-client-v2.js';
|
|
23
|
+
import { consoleLogger } from '../utils/logger.js';
|
|
24
|
+
/**
|
|
25
|
+
* GitHubSyncWrapper - Permission-enforced GitHub sync operations
|
|
26
|
+
*
|
|
27
|
+
* All GitHub sync operations pass through the SyncInterceptor which:
|
|
28
|
+
* 1. Checks permissions before execution
|
|
29
|
+
* 2. Logs all attempts (success, denied, error)
|
|
30
|
+
* 3. Provides consistent error handling
|
|
31
|
+
*
|
|
32
|
+
* This wrapper does NOT modify the underlying GitHubClientV2 - it provides
|
|
33
|
+
* a parallel interface that can be used when permission checking is needed.
|
|
34
|
+
*/
|
|
35
|
+
export class GitHubSyncWrapper {
|
|
36
|
+
constructor(options) {
|
|
37
|
+
this.client = options.client;
|
|
38
|
+
this.interceptor = options.interceptor;
|
|
39
|
+
this.logger = options.logger ?? consoleLogger;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Create a GitHub issue for a user story
|
|
43
|
+
*
|
|
44
|
+
* @param params - Issue creation parameters
|
|
45
|
+
* @returns Wrapped operation result
|
|
46
|
+
*/
|
|
47
|
+
async createUserStoryIssue(params) {
|
|
48
|
+
const itemId = `${params.featureId}/${params.userStoryId}`;
|
|
49
|
+
const result = await this.interceptor.intercept('github', 'upsert-internal', itemId, 'internal', () => this.client.createUserStoryIssue({
|
|
50
|
+
featureId: params.featureId,
|
|
51
|
+
userStoryId: params.userStoryId,
|
|
52
|
+
title: params.title,
|
|
53
|
+
body: params.body,
|
|
54
|
+
labels: params.labels || [],
|
|
55
|
+
milestone: params.milestone ?? undefined,
|
|
56
|
+
}));
|
|
57
|
+
return this.toWrappedResult(result);
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Create a generic epic/increment issue
|
|
61
|
+
*
|
|
62
|
+
* @param title - Issue title
|
|
63
|
+
* @param body - Issue body
|
|
64
|
+
* @param milestone - Optional milestone number or string
|
|
65
|
+
* @param labels - Issue labels
|
|
66
|
+
* @returns Wrapped operation result
|
|
67
|
+
*/
|
|
68
|
+
async createEpicIssue(title, body, milestone, labels = []) {
|
|
69
|
+
// Extract item ID from title if possible
|
|
70
|
+
const match = title.match(/\[([^\]]+)\]/);
|
|
71
|
+
const itemId = match ? match[1] : 'unknown';
|
|
72
|
+
const result = await this.interceptor.intercept('github', 'upsert-internal', itemId, 'internal', () => this.client.createEpicIssue(title, body, milestone, labels));
|
|
73
|
+
return this.toWrappedResult(result);
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Update a GitHub issue body
|
|
77
|
+
*
|
|
78
|
+
* @param issueNumber - Issue number
|
|
79
|
+
* @param body - New issue body
|
|
80
|
+
* @param origin - Item origin (internal or external)
|
|
81
|
+
* @returns Wrapped operation result
|
|
82
|
+
*/
|
|
83
|
+
async updateIssueBody(issueNumber, body, origin = 'internal') {
|
|
84
|
+
const itemId = `issue-${issueNumber}`;
|
|
85
|
+
const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
|
|
86
|
+
const result = await this.interceptor.intercept('github', operation, itemId, origin, () => this.client.updateIssueBody(issueNumber, body));
|
|
87
|
+
return this.toWrappedResult(result);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Close a GitHub issue
|
|
91
|
+
*
|
|
92
|
+
* @param issueNumber - Issue number
|
|
93
|
+
* @param comment - Optional closing comment
|
|
94
|
+
* @returns Wrapped operation result
|
|
95
|
+
*/
|
|
96
|
+
async closeIssue(issueNumber, comment) {
|
|
97
|
+
const itemId = `issue-${issueNumber}`;
|
|
98
|
+
const result = await this.interceptor.interceptStatusUpdate('github', itemId, () => this.client.closeIssue(issueNumber, comment));
|
|
99
|
+
return this.toWrappedResult(result);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Reopen a GitHub issue
|
|
103
|
+
*
|
|
104
|
+
* @param issueNumber - Issue number
|
|
105
|
+
* @param comment - Optional reopening comment
|
|
106
|
+
* @returns Wrapped operation result
|
|
107
|
+
*/
|
|
108
|
+
async reopenIssue(issueNumber, comment) {
|
|
109
|
+
const itemId = `issue-${issueNumber}`;
|
|
110
|
+
const result = await this.interceptor.interceptStatusUpdate('github', itemId, () => this.client.reopenIssue(issueNumber, comment));
|
|
111
|
+
return this.toWrappedResult(result);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Add a comment to a GitHub issue
|
|
115
|
+
*
|
|
116
|
+
* @param issueNumber - Issue number
|
|
117
|
+
* @param comment - Comment body
|
|
118
|
+
* @param origin - Item origin
|
|
119
|
+
* @returns Wrapped operation result
|
|
120
|
+
*/
|
|
121
|
+
async addComment(issueNumber, comment, origin = 'internal') {
|
|
122
|
+
const itemId = `issue-${issueNumber}`;
|
|
123
|
+
const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
|
|
124
|
+
const result = await this.interceptor.intercept('github', operation, itemId, origin, () => this.client.addComment(issueNumber, comment));
|
|
125
|
+
return this.toWrappedResult(result);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Add labels to a GitHub issue
|
|
129
|
+
*
|
|
130
|
+
* @param issueNumber - Issue number
|
|
131
|
+
* @param labels - Labels to add
|
|
132
|
+
* @returns Wrapped operation result
|
|
133
|
+
*/
|
|
134
|
+
async addLabels(issueNumber, labels) {
|
|
135
|
+
const itemId = `issue-${issueNumber}`;
|
|
136
|
+
const result = await this.interceptor.interceptUpsert('github', itemId, () => this.client.addLabels(issueNumber, labels));
|
|
137
|
+
return this.toWrappedResult(result);
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Get a GitHub issue (read-only, always allowed)
|
|
141
|
+
*
|
|
142
|
+
* @param issueNumber - Issue number
|
|
143
|
+
* @returns Wrapped operation result
|
|
144
|
+
*/
|
|
145
|
+
async getIssue(issueNumber) {
|
|
146
|
+
const itemId = `issue-${issueNumber}`;
|
|
147
|
+
const result = await this.interceptor.interceptRead('github', itemId, () => this.client.getIssue(issueNumber));
|
|
148
|
+
return this.toWrappedResult(result);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Search for an issue by title (read-only, always allowed)
|
|
152
|
+
*
|
|
153
|
+
* @param title - Title to search for
|
|
154
|
+
* @returns Wrapped operation result
|
|
155
|
+
*/
|
|
156
|
+
async searchIssueByTitle(title) {
|
|
157
|
+
const result = await this.interceptor.interceptRead('github', `search:${title}`, () => this.client.searchIssueByTitle(title));
|
|
158
|
+
return this.toWrappedResult(result);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Check if an operation would be allowed (pre-flight check)
|
|
162
|
+
*
|
|
163
|
+
* @param operation - Operation type
|
|
164
|
+
* @param itemId - Item ID
|
|
165
|
+
* @returns true if operation would be allowed
|
|
166
|
+
*/
|
|
167
|
+
wouldAllow(operation, itemId) {
|
|
168
|
+
return this.interceptor.wouldAllow('github', operation, itemId);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Get the underlying client for operations that don't need interception
|
|
172
|
+
* (e.g., already-checked operations in bulk workflows)
|
|
173
|
+
*/
|
|
174
|
+
getClient() {
|
|
175
|
+
return this.client;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Convert interceptor result to wrapped result
|
|
179
|
+
*/
|
|
180
|
+
toWrappedResult(result) {
|
|
181
|
+
if (!result.allowed) {
|
|
182
|
+
return {
|
|
183
|
+
success: false,
|
|
184
|
+
result: null,
|
|
185
|
+
denialReason: result.permission.reason,
|
|
186
|
+
durationMs: result.durationMs,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
if (result.error) {
|
|
190
|
+
return {
|
|
191
|
+
success: false,
|
|
192
|
+
result: null,
|
|
193
|
+
error: result.error.message,
|
|
194
|
+
durationMs: result.durationMs,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
success: true,
|
|
199
|
+
result: result.result,
|
|
200
|
+
durationMs: result.durationMs,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Factory function to create GitHubSyncWrapper
|
|
206
|
+
*
|
|
207
|
+
* @param owner - Repository owner
|
|
208
|
+
* @param repo - Repository name
|
|
209
|
+
* @param interceptor - Sync interceptor
|
|
210
|
+
* @param logger - Optional logger
|
|
211
|
+
*/
|
|
212
|
+
export function createGitHubSyncWrapper(owner, repo, interceptor, logger) {
|
|
213
|
+
const client = GitHubClientV2.fromRepo(owner, repo);
|
|
214
|
+
return new GitHubSyncWrapper({
|
|
215
|
+
client,
|
|
216
|
+
interceptor,
|
|
217
|
+
logger,
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
//# sourceMappingURL=github-sync-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-sync-wrapper.js","sourceRoot":"","sources":["../../../src/sync/github-sync-wrapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JIRA Sync Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Wraps JIRA sync operations with the SyncInterceptor for permission checks
|
|
5
|
+
* and audit logging. This provides a consistent interface for all JIRA sync
|
|
6
|
+
* operations while ensuring they go through the permission enforcement layer.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const wrapper = new JiraSyncWrapper({
|
|
11
|
+
* client: new JiraClient({ baseUrl, credentials }),
|
|
12
|
+
* projectKey: 'PROJ',
|
|
13
|
+
* interceptor,
|
|
14
|
+
* logger
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // All operations now go through permission enforcement
|
|
18
|
+
* const issue = await wrapper.createIssue(params);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @module sync/jira-sync-wrapper
|
|
22
|
+
*/
|
|
23
|
+
import { SyncInterceptor } from '../core/sync/sync-interceptor.js';
|
|
24
|
+
import { JiraClient, JiraIssue, JiraIssueCreate, JiraIssueUpdate } from '../integrations/jira/jira-client.js';
|
|
25
|
+
import { Logger } from '../utils/logger.js';
|
|
26
|
+
/**
|
|
27
|
+
* Wrapper options
|
|
28
|
+
*/
|
|
29
|
+
export interface JiraSyncWrapperOptions {
|
|
30
|
+
/**
|
|
31
|
+
* JIRA client instance
|
|
32
|
+
*/
|
|
33
|
+
client: JiraClient;
|
|
34
|
+
/**
|
|
35
|
+
* Default project key for operations
|
|
36
|
+
*/
|
|
37
|
+
projectKey: string;
|
|
38
|
+
/**
|
|
39
|
+
* Sync interceptor for permission checking
|
|
40
|
+
*/
|
|
41
|
+
interceptor: SyncInterceptor;
|
|
42
|
+
/**
|
|
43
|
+
* Logger instance
|
|
44
|
+
*/
|
|
45
|
+
logger?: Logger;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Operation result with additional metadata
|
|
49
|
+
*/
|
|
50
|
+
export interface WrappedOperationResult<T> {
|
|
51
|
+
/**
|
|
52
|
+
* Whether the operation was allowed and succeeded
|
|
53
|
+
*/
|
|
54
|
+
success: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* The result of the operation (null if denied or failed)
|
|
57
|
+
*/
|
|
58
|
+
result: T | null;
|
|
59
|
+
/**
|
|
60
|
+
* Reason for denial (if denied)
|
|
61
|
+
*/
|
|
62
|
+
denialReason?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Error message (if failed)
|
|
65
|
+
*/
|
|
66
|
+
error?: string;
|
|
67
|
+
/**
|
|
68
|
+
* Duration of the operation in milliseconds
|
|
69
|
+
*/
|
|
70
|
+
durationMs: number;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* JiraSyncWrapper - Permission-enforced JIRA sync operations
|
|
74
|
+
*
|
|
75
|
+
* All JIRA sync operations pass through the SyncInterceptor which:
|
|
76
|
+
* 1. Checks permissions before execution
|
|
77
|
+
* 2. Logs all attempts (success, denied, error)
|
|
78
|
+
* 3. Provides consistent error handling
|
|
79
|
+
*/
|
|
80
|
+
export declare class JiraSyncWrapper {
|
|
81
|
+
private readonly client;
|
|
82
|
+
private readonly projectKey;
|
|
83
|
+
private readonly interceptor;
|
|
84
|
+
private readonly logger;
|
|
85
|
+
constructor(options: JiraSyncWrapperOptions);
|
|
86
|
+
/**
|
|
87
|
+
* Create a JIRA issue
|
|
88
|
+
*
|
|
89
|
+
* @param issue - Issue creation parameters
|
|
90
|
+
* @param origin - Item origin (internal or external)
|
|
91
|
+
* @returns Wrapped operation result
|
|
92
|
+
*/
|
|
93
|
+
createIssue(issue: JiraIssueCreate, origin?: 'internal' | 'external'): Promise<WrappedOperationResult<JiraIssue>>;
|
|
94
|
+
/**
|
|
95
|
+
* Update a JIRA issue
|
|
96
|
+
*
|
|
97
|
+
* @param update - Issue update parameters
|
|
98
|
+
* @param origin - Item origin (internal or external)
|
|
99
|
+
* @returns Wrapped operation result
|
|
100
|
+
*/
|
|
101
|
+
updateIssue(update: JiraIssueUpdate, origin?: 'internal' | 'external'): Promise<WrappedOperationResult<JiraIssue>>;
|
|
102
|
+
/**
|
|
103
|
+
* Update issue status (transition)
|
|
104
|
+
*
|
|
105
|
+
* @param issueKey - JIRA issue key
|
|
106
|
+
* @param targetStatus - Target status name
|
|
107
|
+
* @returns Wrapped operation result
|
|
108
|
+
*/
|
|
109
|
+
updateStatus(issueKey: string, targetStatus: string): Promise<WrappedOperationResult<JiraIssue>>;
|
|
110
|
+
/**
|
|
111
|
+
* Get a JIRA issue (read-only, always allowed)
|
|
112
|
+
*
|
|
113
|
+
* @param issueKey - JIRA issue key
|
|
114
|
+
* @returns Wrapped operation result
|
|
115
|
+
*/
|
|
116
|
+
getIssue(issueKey: string): Promise<WrappedOperationResult<JiraIssue>>;
|
|
117
|
+
/**
|
|
118
|
+
* Search issues with JQL filter (read-only, always allowed)
|
|
119
|
+
*
|
|
120
|
+
* @param jql - JQL query string
|
|
121
|
+
* @param maxResults - Maximum results to return
|
|
122
|
+
* @returns Wrapped operation result
|
|
123
|
+
*/
|
|
124
|
+
searchIssues(jql: string, maxResults?: number): Promise<WrappedOperationResult<JiraIssue[]>>;
|
|
125
|
+
/**
|
|
126
|
+
* Add labels to a JIRA issue
|
|
127
|
+
*
|
|
128
|
+
* @param issueKey - JIRA issue key
|
|
129
|
+
* @param labels - Labels to add
|
|
130
|
+
* @param origin - Item origin
|
|
131
|
+
* @returns Wrapped operation result
|
|
132
|
+
*/
|
|
133
|
+
addLabels(issueKey: string, labels: string[], origin?: 'internal' | 'external'): Promise<WrappedOperationResult<JiraIssue>>;
|
|
134
|
+
/**
|
|
135
|
+
* Check if an operation would be allowed (pre-flight check)
|
|
136
|
+
*
|
|
137
|
+
* @param operation - Operation type
|
|
138
|
+
* @param itemId - Item ID
|
|
139
|
+
* @returns true if operation would be allowed
|
|
140
|
+
*/
|
|
141
|
+
wouldAllow(operation: 'upsert-internal' | 'upsert-external' | 'update-status' | 'read', itemId: string): boolean;
|
|
142
|
+
/**
|
|
143
|
+
* Get the underlying client for operations that don't need interception
|
|
144
|
+
*/
|
|
145
|
+
getClient(): JiraClient;
|
|
146
|
+
/**
|
|
147
|
+
* Get the project key
|
|
148
|
+
*/
|
|
149
|
+
getProjectKey(): string;
|
|
150
|
+
/**
|
|
151
|
+
* Convert interceptor result to wrapped result
|
|
152
|
+
*/
|
|
153
|
+
private toWrappedResult;
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=jira-sync-wrapper.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira-sync-wrapper.d.ts","sourceRoot":"","sources":["../../../src/sync/jira-sync-wrapper.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,EAAE,eAAe,EAAqB,MAAM,kCAAkC,CAAC;AACtF,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,qCAAqC,CAAC;AAC9G,OAAO,EAAE,MAAM,EAAiB,MAAM,oBAAoB,CAAC;AAE3D;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC;IAEnB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,WAAW,EAAE,eAAe,CAAC;IAE7B;;OAEG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,EAAE,CAAC,GAAG,IAAI,CAAC;IAEjB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAa;IACpC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAS;IACpC,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAkB;IAC9C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;gBAEpB,OAAO,EAAE,sBAAsB;IAO3C;;;;;;OAMG;IACG,WAAW,CACf,KAAK,EAAE,eAAe,EACtB,MAAM,GAAE,UAAU,GAAG,UAAuB,GAC3C,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAe7C;;;;;;OAMG;IACG,WAAW,CACf,MAAM,EAAE,eAAe,EACvB,MAAM,GAAE,UAAU,GAAG,UAAuB,GAC3C,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAe7C;;;;;;OAMG;IACG,YAAY,CAChB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAiB7C;;;;;OAKG;IACG,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAU5E;;;;;;OAMG;IACG,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,UAAU,GAAE,MAAW,GACtB,OAAO,CAAC,sBAAsB,CAAC,SAAS,EAAE,CAAC,CAAC;IAU/C;;;;;;;OAOG;IACG,SAAS,CACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EAAE,EAChB,MAAM,GAAE,UAAU,GAAG,UAAuB,GAC3C,OAAO,CAAC,sBAAsB,CAAC,SAAS,CAAC,CAAC;IAwB7C;;;;;;OAMG;IACH,UAAU,CACR,SAAS,EAAE,iBAAiB,GAAG,iBAAiB,GAAG,eAAe,GAAG,MAAM,EAC3E,MAAM,EAAE,MAAM,GACb,OAAO;IAIV;;OAEG;IACH,SAAS,IAAI,UAAU;IAIvB;;OAEG;IACH,aAAa,IAAI,MAAM;IAIvB;;OAEG;IACH,OAAO,CAAC,eAAe;CAyBxB"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JIRA Sync Wrapper
|
|
3
|
+
*
|
|
4
|
+
* Wraps JIRA sync operations with the SyncInterceptor for permission checks
|
|
5
|
+
* and audit logging. This provides a consistent interface for all JIRA sync
|
|
6
|
+
* operations while ensuring they go through the permission enforcement layer.
|
|
7
|
+
*
|
|
8
|
+
* Usage:
|
|
9
|
+
* ```typescript
|
|
10
|
+
* const wrapper = new JiraSyncWrapper({
|
|
11
|
+
* client: new JiraClient({ baseUrl, credentials }),
|
|
12
|
+
* projectKey: 'PROJ',
|
|
13
|
+
* interceptor,
|
|
14
|
+
* logger
|
|
15
|
+
* });
|
|
16
|
+
*
|
|
17
|
+
* // All operations now go through permission enforcement
|
|
18
|
+
* const issue = await wrapper.createIssue(params);
|
|
19
|
+
* ```
|
|
20
|
+
*
|
|
21
|
+
* @module sync/jira-sync-wrapper
|
|
22
|
+
*/
|
|
23
|
+
import { consoleLogger } from '../utils/logger.js';
|
|
24
|
+
/**
|
|
25
|
+
* JiraSyncWrapper - Permission-enforced JIRA sync operations
|
|
26
|
+
*
|
|
27
|
+
* All JIRA sync operations pass through the SyncInterceptor which:
|
|
28
|
+
* 1. Checks permissions before execution
|
|
29
|
+
* 2. Logs all attempts (success, denied, error)
|
|
30
|
+
* 3. Provides consistent error handling
|
|
31
|
+
*/
|
|
32
|
+
export class JiraSyncWrapper {
|
|
33
|
+
constructor(options) {
|
|
34
|
+
this.client = options.client;
|
|
35
|
+
this.projectKey = options.projectKey;
|
|
36
|
+
this.interceptor = options.interceptor;
|
|
37
|
+
this.logger = options.logger ?? consoleLogger;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Create a JIRA issue
|
|
41
|
+
*
|
|
42
|
+
* @param issue - Issue creation parameters
|
|
43
|
+
* @param origin - Item origin (internal or external)
|
|
44
|
+
* @returns Wrapped operation result
|
|
45
|
+
*/
|
|
46
|
+
async createIssue(issue, origin = 'internal') {
|
|
47
|
+
const itemId = `${this.projectKey}:${issue.summary.substring(0, 50)}`;
|
|
48
|
+
const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
|
|
49
|
+
const result = await this.interceptor.intercept('jira', operation, itemId, origin, () => this.client.createIssue(issue, this.projectKey));
|
|
50
|
+
return this.toWrappedResult(result);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Update a JIRA issue
|
|
54
|
+
*
|
|
55
|
+
* @param update - Issue update parameters
|
|
56
|
+
* @param origin - Item origin (internal or external)
|
|
57
|
+
* @returns Wrapped operation result
|
|
58
|
+
*/
|
|
59
|
+
async updateIssue(update, origin = 'internal') {
|
|
60
|
+
const itemId = update.key;
|
|
61
|
+
const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
|
|
62
|
+
const result = await this.interceptor.intercept('jira', operation, itemId, origin, () => this.client.updateIssue(update));
|
|
63
|
+
return this.toWrappedResult(result);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Update issue status (transition)
|
|
67
|
+
*
|
|
68
|
+
* @param issueKey - JIRA issue key
|
|
69
|
+
* @param targetStatus - Target status name
|
|
70
|
+
* @returns Wrapped operation result
|
|
71
|
+
*/
|
|
72
|
+
async updateStatus(issueKey, targetStatus) {
|
|
73
|
+
const result = await this.interceptor.interceptStatusUpdate('jira', issueKey, async () => {
|
|
74
|
+
// Update status via updateIssue which handles transitions
|
|
75
|
+
const updatedIssue = await this.client.updateIssue({
|
|
76
|
+
key: issueKey,
|
|
77
|
+
status: targetStatus,
|
|
78
|
+
});
|
|
79
|
+
return updatedIssue;
|
|
80
|
+
});
|
|
81
|
+
return this.toWrappedResult(result);
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Get a JIRA issue (read-only, always allowed)
|
|
85
|
+
*
|
|
86
|
+
* @param issueKey - JIRA issue key
|
|
87
|
+
* @returns Wrapped operation result
|
|
88
|
+
*/
|
|
89
|
+
async getIssue(issueKey) {
|
|
90
|
+
const result = await this.interceptor.interceptRead('jira', issueKey, () => this.client.getIssue(issueKey));
|
|
91
|
+
return this.toWrappedResult(result);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Search issues with JQL filter (read-only, always allowed)
|
|
95
|
+
*
|
|
96
|
+
* @param jql - JQL query string
|
|
97
|
+
* @param maxResults - Maximum results to return
|
|
98
|
+
* @returns Wrapped operation result
|
|
99
|
+
*/
|
|
100
|
+
async searchIssues(jql, maxResults = 50) {
|
|
101
|
+
const result = await this.interceptor.interceptRead('jira', `jql:${jql.substring(0, 50)}`, () => this.client.searchIssues({ jql, maxResults }));
|
|
102
|
+
return this.toWrappedResult(result);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Add labels to a JIRA issue
|
|
106
|
+
*
|
|
107
|
+
* @param issueKey - JIRA issue key
|
|
108
|
+
* @param labels - Labels to add
|
|
109
|
+
* @param origin - Item origin
|
|
110
|
+
* @returns Wrapped operation result
|
|
111
|
+
*/
|
|
112
|
+
async addLabels(issueKey, labels, origin = 'internal') {
|
|
113
|
+
const operation = origin === 'external' ? 'upsert-external' : 'upsert-internal';
|
|
114
|
+
const result = await this.interceptor.intercept('jira', operation, issueKey, origin, async () => {
|
|
115
|
+
// Get current labels and merge
|
|
116
|
+
const currentIssue = await this.client.getIssue(issueKey);
|
|
117
|
+
const currentLabels = currentIssue.fields.labels || [];
|
|
118
|
+
const mergedLabels = [...new Set([...currentLabels, ...labels])];
|
|
119
|
+
return this.client.updateIssue({
|
|
120
|
+
key: issueKey,
|
|
121
|
+
labels: mergedLabels,
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
return this.toWrappedResult(result);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Check if an operation would be allowed (pre-flight check)
|
|
128
|
+
*
|
|
129
|
+
* @param operation - Operation type
|
|
130
|
+
* @param itemId - Item ID
|
|
131
|
+
* @returns true if operation would be allowed
|
|
132
|
+
*/
|
|
133
|
+
wouldAllow(operation, itemId) {
|
|
134
|
+
return this.interceptor.wouldAllow('jira', operation, itemId);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get the underlying client for operations that don't need interception
|
|
138
|
+
*/
|
|
139
|
+
getClient() {
|
|
140
|
+
return this.client;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get the project key
|
|
144
|
+
*/
|
|
145
|
+
getProjectKey() {
|
|
146
|
+
return this.projectKey;
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Convert interceptor result to wrapped result
|
|
150
|
+
*/
|
|
151
|
+
toWrappedResult(result) {
|
|
152
|
+
if (!result.allowed) {
|
|
153
|
+
return {
|
|
154
|
+
success: false,
|
|
155
|
+
result: null,
|
|
156
|
+
denialReason: result.permission.reason,
|
|
157
|
+
durationMs: result.durationMs,
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
if (result.error) {
|
|
161
|
+
return {
|
|
162
|
+
success: false,
|
|
163
|
+
result: null,
|
|
164
|
+
error: result.error.message,
|
|
165
|
+
durationMs: result.durationMs,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
success: true,
|
|
170
|
+
result: result.result,
|
|
171
|
+
durationMs: result.durationMs,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=jira-sync-wrapper.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"jira-sync-wrapper.js","sourceRoot":"","sources":["../../../src/sync/jira-sync-wrapper.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Feature ID Derivation Utility
|
|
3
|
+
*
|
|
4
|
+
* Derives feature ID from increment ID using the 1:1 mapping principle:
|
|
5
|
+
* - Each increment maps to exactly one feature folder
|
|
6
|
+
* - Feature ID = FS-{increment_number}
|
|
7
|
+
*
|
|
8
|
+
* @example
|
|
9
|
+
* deriveFeatureId('0081-ado-repo-cloning') → 'FS-081'
|
|
10
|
+
* deriveFeatureId('0100-some-feature') → 'FS-100'
|
|
11
|
+
* deriveFeatureId('1000-future-feature') → 'FS-1000'
|
|
12
|
+
*
|
|
13
|
+
* @see ADR-0140 for the decision rationale
|
|
14
|
+
*/
|
|
15
|
+
/**
|
|
16
|
+
* Derive feature ID from increment ID
|
|
17
|
+
*
|
|
18
|
+
* The feature ID is derived directly from the increment number:
|
|
19
|
+
* - Extract leading digits from increment ID
|
|
20
|
+
* - Format as FS-XXX (minimum 3 digits, more if needed)
|
|
21
|
+
*
|
|
22
|
+
* This eliminates the need to store feature_id in metadata.json,
|
|
23
|
+
* as it's 100% derivable from the increment ID.
|
|
24
|
+
*
|
|
25
|
+
* @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning")
|
|
26
|
+
* @returns Feature ID (e.g., "FS-081")
|
|
27
|
+
* @throws Error if increment ID format is invalid
|
|
28
|
+
*/
|
|
29
|
+
export declare function deriveFeatureId(incrementId: string): string;
|
|
30
|
+
/**
|
|
31
|
+
* Extract increment number from increment ID
|
|
32
|
+
*
|
|
33
|
+
* @param incrementId - Increment ID (e.g., "0081-ado-repo-cloning")
|
|
34
|
+
* @returns Increment number (e.g., 81)
|
|
35
|
+
* @throws Error if increment ID format is invalid
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractIncrementNumber(incrementId: string): number;
|
|
38
|
+
/**
|
|
39
|
+
* Validate feature ID format
|
|
40
|
+
*
|
|
41
|
+
* Valid formats:
|
|
42
|
+
* - FS-XXX (internal, 3+ digits)
|
|
43
|
+
* - FS-XXXE (external, 3+ digits with E suffix)
|
|
44
|
+
*
|
|
45
|
+
* @param featureId - Feature ID to validate
|
|
46
|
+
* @returns true if valid, false otherwise
|
|
47
|
+
*/
|
|
48
|
+
export declare function isValidFeatureId(featureId: string): boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Check if feature ID is external (imported)
|
|
51
|
+
*
|
|
52
|
+
* External features have an 'E' suffix: FS-042E
|
|
53
|
+
*
|
|
54
|
+
* @param featureId - Feature ID to check
|
|
55
|
+
* @returns true if external, false if internal
|
|
56
|
+
*/
|
|
57
|
+
export declare function isExternalFeatureId(featureId: string): boolean;
|
|
58
|
+
//# sourceMappingURL=feature-id-derivation.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"feature-id-derivation.d.ts","sourceRoot":"","sources":["../../../src/utils/feature-id-derivation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAU3D;AAED;;;;;;GAMG;AACH,wBAAgB,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAMlE;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE9D"}
|