@hed-hog/core 0.0.295 → 0.0.296

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (144) hide show
  1. package/dist/auth/auth.controller.d.ts +4 -4
  2. package/dist/auth/auth.service.d.ts +4 -4
  3. package/dist/challenge/challenge.service.d.ts +2 -2
  4. package/dist/core.module.d.ts.map +1 -1
  5. package/dist/core.module.js +4 -1
  6. package/dist/core.module.js.map +1 -1
  7. package/dist/dashboard/dashboard-core/dashboard-core.controller.d.ts +1 -1
  8. package/dist/dashboard/dashboard-core/dashboard-core.service.d.ts +2 -2
  9. package/dist/index.d.ts +13 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +14 -0
  12. package/dist/index.js.map +1 -1
  13. package/dist/integration/index.d.ts +4 -0
  14. package/dist/integration/index.d.ts.map +1 -0
  15. package/dist/integration/index.js +20 -0
  16. package/dist/integration/index.js.map +1 -0
  17. package/dist/integration/integration-api.validation.d.ts +2 -0
  18. package/dist/integration/integration-api.validation.d.ts.map +1 -0
  19. package/dist/integration/integration-api.validation.js +126 -0
  20. package/dist/integration/integration-api.validation.js.map +1 -0
  21. package/dist/integration/integration.module.d.ts +3 -0
  22. package/dist/integration/integration.module.d.ts.map +1 -0
  23. package/dist/integration/integration.module.js +54 -0
  24. package/dist/integration/integration.module.js.map +1 -0
  25. package/dist/integration/services/domain-event.publisher.d.ts +31 -0
  26. package/dist/integration/services/domain-event.publisher.d.ts.map +1 -0
  27. package/dist/integration/services/domain-event.publisher.js +79 -0
  28. package/dist/integration/services/domain-event.publisher.js.map +1 -0
  29. package/dist/integration/services/event-subscriber.registry.d.ts +37 -0
  30. package/dist/integration/services/event-subscriber.registry.d.ts.map +1 -0
  31. package/dist/integration/services/event-subscriber.registry.js +86 -0
  32. package/dist/integration/services/event-subscriber.registry.js.map +1 -0
  33. package/dist/integration/services/inbox.service.d.ts +55 -0
  34. package/dist/integration/services/inbox.service.d.ts.map +1 -0
  35. package/dist/integration/services/inbox.service.js +173 -0
  36. package/dist/integration/services/inbox.service.js.map +1 -0
  37. package/dist/integration/services/index.d.ts +12 -0
  38. package/dist/integration/services/index.d.ts.map +1 -0
  39. package/dist/integration/services/index.js +28 -0
  40. package/dist/integration/services/index.js.map +1 -0
  41. package/dist/integration/services/integration-developer-api.service.d.ts +30 -0
  42. package/dist/integration/services/integration-developer-api.service.d.ts.map +1 -0
  43. package/dist/integration/services/integration-developer-api.service.js +55 -0
  44. package/dist/integration/services/integration-developer-api.service.js.map +1 -0
  45. package/dist/integration/services/integration-link.service.d.ts +52 -0
  46. package/dist/integration/services/integration-link.service.d.ts.map +1 -0
  47. package/dist/integration/services/integration-link.service.js +128 -0
  48. package/dist/integration/services/integration-link.service.js.map +1 -0
  49. package/dist/integration/services/integration-settings.service.d.ts +23 -0
  50. package/dist/integration/services/integration-settings.service.d.ts.map +1 -0
  51. package/dist/integration/services/integration-settings.service.js +81 -0
  52. package/dist/integration/services/integration-settings.service.js.map +1 -0
  53. package/dist/integration/services/outbox-polling.coordinator.d.ts +45 -0
  54. package/dist/integration/services/outbox-polling.coordinator.d.ts.map +1 -0
  55. package/dist/integration/services/outbox-polling.coordinator.js +143 -0
  56. package/dist/integration/services/outbox-polling.coordinator.js.map +1 -0
  57. package/dist/integration/services/outbox.notifier.d.ts +30 -0
  58. package/dist/integration/services/outbox.notifier.d.ts.map +1 -0
  59. package/dist/integration/services/outbox.notifier.js +57 -0
  60. package/dist/integration/services/outbox.notifier.js.map +1 -0
  61. package/dist/integration/services/outbox.processor.d.ts +42 -0
  62. package/dist/integration/services/outbox.processor.d.ts.map +1 -0
  63. package/dist/integration/services/outbox.processor.job.d.ts +43 -0
  64. package/dist/integration/services/outbox.processor.job.d.ts.map +1 -0
  65. package/dist/integration/services/outbox.processor.job.js +100 -0
  66. package/dist/integration/services/outbox.processor.job.js.map +1 -0
  67. package/dist/integration/services/outbox.processor.js +208 -0
  68. package/dist/integration/services/outbox.processor.js.map +1 -0
  69. package/dist/integration/services/outbox.service.d.ts +53 -0
  70. package/dist/integration/services/outbox.service.d.ts.map +1 -0
  71. package/dist/integration/services/outbox.service.js +149 -0
  72. package/dist/integration/services/outbox.service.js.map +1 -0
  73. package/dist/integration/types/event.types.d.ts +88 -0
  74. package/dist/integration/types/event.types.d.ts.map +1 -0
  75. package/dist/integration/types/event.types.js +35 -0
  76. package/dist/integration/types/event.types.js.map +1 -0
  77. package/dist/integration/types/index.d.ts +3 -0
  78. package/dist/integration/types/index.d.ts.map +1 -0
  79. package/dist/integration/types/index.js +19 -0
  80. package/dist/integration/types/index.js.map +1 -0
  81. package/dist/integration/types/subscriber.types.d.ts +31 -0
  82. package/dist/integration/types/subscriber.types.d.ts.map +1 -0
  83. package/dist/integration/types/subscriber.types.js +3 -0
  84. package/dist/integration/types/subscriber.types.js.map +1 -0
  85. package/dist/oauth/oauth.controller.js.map +1 -1
  86. package/dist/oauth/oauth.service.d.ts +3 -3
  87. package/dist/oauth/oauth.service.d.ts.map +1 -1
  88. package/dist/oauth/oauth.service.js.map +1 -1
  89. package/dist/profile/profile.controller.d.ts +3 -3
  90. package/dist/profile/profile.service.d.ts +3 -3
  91. package/dist/setting/setting.controller.d.ts +12 -8
  92. package/dist/setting/setting.controller.d.ts.map +1 -1
  93. package/dist/setting/setting.service.d.ts +12 -8
  94. package/dist/setting/setting.service.d.ts.map +1 -1
  95. package/dist/setting/setting.service.js +21 -1
  96. package/dist/setting/setting.service.js.map +1 -1
  97. package/dist/user/user.controller.d.ts +4 -4
  98. package/dist/user/user.service.d.ts +9 -9
  99. package/hedhog/data/dashboard_component_role.yaml +223 -223
  100. package/hedhog/data/dashboard_role.yaml +18 -18
  101. package/hedhog/data/route.yaml +2 -0
  102. package/hedhog/data/setting_group.yaml +955 -470
  103. package/hedhog/data/setting_subgroup.yaml +303 -0
  104. package/hedhog/frontend/app/configurations/[slug]/components/setting-field.tsx.ejs +44 -18
  105. package/hedhog/frontend/app/configurations/[slug]/page.tsx.ejs +134 -27
  106. package/hedhog/frontend/app/configurations/layout.tsx.ejs +84 -23
  107. package/hedhog/frontend/app/dashboard/components/widgets/permissions-chart.tsx.ejs +62 -62
  108. package/hedhog/frontend/app/dashboard/page.tsx.ejs +29 -29
  109. package/hedhog/frontend/app/preferences/page.tsx.ejs +2 -5
  110. package/hedhog/table/inbox_event.yaml +40 -0
  111. package/hedhog/table/integration_link.yaml +33 -0
  112. package/hedhog/table/outbox_event.yaml +45 -0
  113. package/hedhog/table/setting.yaml +7 -0
  114. package/hedhog/table/setting_subgroup.yaml +19 -0
  115. package/package.json +8 -8
  116. package/src/ai/ai.service.ts +3 -3
  117. package/src/auth/auth.controller.ts +11 -11
  118. package/src/auth/auth.service.ts +8 -8
  119. package/src/core.module.ts +4 -1
  120. package/src/index.ts +15 -0
  121. package/src/integration/README.md +397 -0
  122. package/src/integration/USAGE_EXAMPLE.md +279 -0
  123. package/src/integration/index.ts +4 -0
  124. package/src/integration/integration-api.validation.ts +154 -0
  125. package/src/integration/integration.module.ts +53 -0
  126. package/src/integration/services/domain-event.publisher.ts +136 -0
  127. package/src/integration/services/event-subscriber.registry.ts +89 -0
  128. package/src/integration/services/inbox.service.ts +218 -0
  129. package/src/integration/services/index.ts +12 -0
  130. package/src/integration/services/integration-developer-api.service.ts +96 -0
  131. package/src/integration/services/integration-link.service.ts +154 -0
  132. package/src/integration/services/integration-settings.service.ts +128 -0
  133. package/src/integration/services/outbox-polling.coordinator.ts +146 -0
  134. package/src/integration/services/outbox.notifier.ts +48 -0
  135. package/src/integration/services/outbox.processor.job.ts +97 -0
  136. package/src/integration/services/outbox.processor.ts +266 -0
  137. package/src/integration/services/outbox.service.ts +209 -0
  138. package/src/integration/types/event.types.ts +93 -0
  139. package/src/integration/types/index.ts +3 -0
  140. package/src/integration/types/subscriber.types.ts +37 -0
  141. package/src/oauth/oauth.controller.ts +17 -17
  142. package/src/oauth/oauth.service.ts +20 -20
  143. package/src/setting/setting.service.ts +27 -2
  144. package/src/task/task.service.ts +5 -5
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var OutboxProcessorJob_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.OutboxProcessorJob = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const integration_settings_service_1 = require("./integration-settings.service");
16
+ const outbox_polling_coordinator_1 = require("./outbox-polling.coordinator");
17
+ const outbox_notifier_1 = require("./outbox.notifier");
18
+ const outbox_processor_1 = require("./outbox.processor");
19
+ /**
20
+ * Hybrid background job for outbox processing
21
+ * Combines startup recovery, immediate notification reactions, and periodic polling
22
+ */
23
+ let OutboxProcessorJob = OutboxProcessorJob_1 = class OutboxProcessorJob {
24
+ constructor(processor, notifier, coordinator, integrationSettingsService) {
25
+ this.processor = processor;
26
+ this.notifier = notifier;
27
+ this.coordinator = coordinator;
28
+ this.integrationSettingsService = integrationSettingsService;
29
+ this.logger = new common_1.Logger(OutboxProcessorJob_1.name);
30
+ }
31
+ /**
32
+ * Called when module initializes
33
+ * 1. Drain pending events from startup
34
+ * 2. Start hybrid processing loop
35
+ */
36
+ async onModuleInit() {
37
+ const settings = await this.integrationSettingsService.getRuntimeSettings();
38
+ const isEnabled = settings.outboxEnabled;
39
+ if (!isEnabled) {
40
+ this.logger.debug('Outbox integration disabled in settings');
41
+ return;
42
+ }
43
+ // Step 1: Startup drain
44
+ const startupDrainEnabled = settings.startupDrainEnabled;
45
+ if (startupDrainEnabled) {
46
+ this.logger.log('Starting outbox startup drain...');
47
+ try {
48
+ const processed = await this.processor.startupDrain();
49
+ this.logger.log(`Outbox startup drain completed: ${processed} events`);
50
+ }
51
+ catch (error) {
52
+ this.logger.error('Outbox startup drain failed:', error);
53
+ }
54
+ }
55
+ // Step 2: Start hybrid processing loop
56
+ const processorEnabled = settings.outboxProcessorEnabled;
57
+ if (processorEnabled) {
58
+ this.logger.log('Starting outbox hybrid processing loop');
59
+ try {
60
+ await this.coordinator.startProcessingLoop();
61
+ }
62
+ catch (error) {
63
+ this.logger.error('Failed to start outbox processing loop:', error);
64
+ }
65
+ }
66
+ else {
67
+ this.logger.debug('Outbox processor disabled in settings');
68
+ }
69
+ }
70
+ /**
71
+ * Called when module is destroyed
72
+ * Gracefully stop the processing loop
73
+ */
74
+ async onModuleDestroy() {
75
+ this.logger.log('Shutting down outbox processing');
76
+ try {
77
+ await this.coordinator.stopProcessingLoop();
78
+ }
79
+ catch (error) {
80
+ this.logger.error('Error stopping outbox processing loop:', error);
81
+ }
82
+ }
83
+ /**
84
+ * Get current processing statistics
85
+ * Useful for health checks and monitoring
86
+ */
87
+ async getStats() {
88
+ const stats = await this.processor.getStats();
89
+ return Object.assign(Object.assign({}, stats), { pollingLoopRunning: this.coordinator.isRunning() });
90
+ }
91
+ };
92
+ exports.OutboxProcessorJob = OutboxProcessorJob;
93
+ exports.OutboxProcessorJob = OutboxProcessorJob = OutboxProcessorJob_1 = __decorate([
94
+ (0, common_1.Injectable)(),
95
+ __metadata("design:paramtypes", [outbox_processor_1.OutboxProcessor,
96
+ outbox_notifier_1.OutboxNotifier,
97
+ outbox_polling_coordinator_1.OutboxPollingCoordinator,
98
+ integration_settings_service_1.IntegrationSettingsService])
99
+ ], OutboxProcessorJob);
100
+ //# sourceMappingURL=outbox.processor.job.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.processor.job.js","sourceRoot":"","sources":["../../../src/integration/services/outbox.processor.job.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAKwB;AACxB,iFAA4E;AAC5E,6EAAwE;AACxE,uDAAmD;AACnD,yDAAqD;AAErD;;;GAGG;AAEI,IAAM,kBAAkB,0BAAxB,MAAM,kBAAkB;IAG7B,YACmB,SAA0B,EAC1B,QAAwB,EACxB,WAAqC,EACrC,0BAAsD;QAHtD,cAAS,GAAT,SAAS,CAAiB;QAC1B,aAAQ,GAAR,QAAQ,CAAgB;QACxB,gBAAW,GAAX,WAAW,CAA0B;QACrC,+BAA0B,GAA1B,0BAA0B,CAA4B;QANxD,WAAM,GAAG,IAAI,eAAM,CAAC,oBAAkB,CAAC,IAAI,CAAC,CAAC;IAO3D,CAAC;IAEJ;;;;OAIG;IACH,KAAK,CAAC,YAAY;QAChB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,0BAA0B,CAAC,kBAAkB,EAAE,CAAC;QAC5E,MAAM,SAAS,GAAG,QAAQ,CAAC,aAAa,CAAC;QACzC,IAAI,CAAC,SAAS,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;YAC7D,OAAO;QACT,CAAC;QAED,wBAAwB;QACxB,MAAM,mBAAmB,GAAG,QAAQ,CAAC,mBAAmB,CAAC;QAEzD,IAAI,mBAAmB,EAAE,CAAC;YACxB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAC;YACpD,IAAI,CAAC;gBACH,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,YAAY,EAAE,CAAC;gBACtD,IAAI,CAAC,MAAM,CAAC,GAAG,CACb,mCAAmC,SAAS,SAAS,CACtD,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,EAAE,KAAK,CAAC,CAAC;YAC3D,CAAC;QACH,CAAC;QAED,uCAAuC;QACvC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,sBAAsB,CAAC;QAEzD,IAAI,gBAAgB,EAAE,CAAC;YACrB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,wCAAwC,CAAC,CAAC;YAC1D,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,WAAW,CAAC,mBAAmB,EAAE,CAAC;YAC/C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yCAAyC,EAAE,KAAK,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uCAAuC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,eAAe;QACnB,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,iCAAiC,CAAC,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,kBAAkB,EAAE,CAAC;QAC9C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,wCAAwC,EACxC,KAAK,CACN,CAAC;QACJ,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,QAAQ;QACZ,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,SAAS,CAAC,QAAQ,EAAE,CAAC;QAC9C,uCACK,KAAK,KACR,kBAAkB,EAAE,IAAI,CAAC,WAAW,CAAC,SAAS,EAAE,IAChD;IACJ,CAAC;CACF,CAAA;AAhFY,gDAAkB;6BAAlB,kBAAkB;IAD9B,IAAA,mBAAU,GAAE;qCAKmB,kCAAe;QAChB,gCAAc;QACX,qDAAwB;QACT,yDAA0B;GAP9D,kBAAkB,CAgF9B"}
@@ -0,0 +1,208 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var OutboxProcessor_1;
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.OutboxProcessor = void 0;
14
+ const common_1 = require("@nestjs/common");
15
+ const types_1 = require("../types");
16
+ const event_subscriber_registry_1 = require("./event-subscriber.registry");
17
+ const inbox_service_1 = require("./inbox.service");
18
+ const integration_link_service_1 = require("./integration-link.service");
19
+ const integration_settings_service_1 = require("./integration-settings.service");
20
+ const outbox_service_1 = require("./outbox.service");
21
+ let OutboxProcessor = OutboxProcessor_1 = class OutboxProcessor {
22
+ constructor(outboxService, inboxService, linkService, registry, integrationSettingsService) {
23
+ this.outboxService = outboxService;
24
+ this.inboxService = inboxService;
25
+ this.linkService = linkService;
26
+ this.registry = registry;
27
+ this.integrationSettingsService = integrationSettingsService;
28
+ this.logger = new common_1.Logger(OutboxProcessor_1.name);
29
+ }
30
+ /**
31
+ * Process a batch of pending events
32
+ * Called in a loop or by scheduled job
33
+ */
34
+ async processBatch(options) {
35
+ var _a;
36
+ const settings = await this.integrationSettingsService.getRuntimeSettings();
37
+ const batchSize = (_a = options === null || options === void 0 ? void 0 : options.batchSizeOverride) !== null && _a !== void 0 ? _a : settings.batchSize;
38
+ const leaseMs = settings.processingLeaseMs;
39
+ // Find pending events
40
+ const events = await this.outboxService.findPending(batchSize, leaseMs);
41
+ if (events.length === 0) {
42
+ return 0;
43
+ }
44
+ this.logger.debug(`Processing ${events.length} outbox events`);
45
+ let processedCount = 0;
46
+ for (const event of events) {
47
+ try {
48
+ // Mark as processing with lease
49
+ await this.outboxService.markProcessing(event.id, leaseMs);
50
+ // Get handlers for this event
51
+ const handlers = this.registry.getHandlers(event.eventName);
52
+ if (handlers.length === 0) {
53
+ this.logger.warn(`No handlers registered for event: ${event.eventName}`);
54
+ // Mark as processed even though no handlers
55
+ await this.outboxService.updateStatus(event.id, types_1.OutboxEventStatus.PROCESSED, {
56
+ processedAt: new Date(),
57
+ });
58
+ processedCount++;
59
+ continue;
60
+ }
61
+ // Execute each handler
62
+ let anyFailed = false;
63
+ for (const handlerDef of handlers) {
64
+ const inboxEvent = await this.inboxService.getOrCreate(event.id, handlerDef.consumerName);
65
+ // Skip if already processed
66
+ if (inboxEvent.status === types_1.InboxEventStatus.PROCESSED) {
67
+ this.logger.debug(`Event ${event.id} already processed by ${handlerDef.consumerName}`);
68
+ continue;
69
+ }
70
+ await this.inboxService.markProcessing(inboxEvent.id);
71
+ try {
72
+ // Execute handler
73
+ await handlerDef.handler({
74
+ eventName: event.eventName,
75
+ sourceModule: event.sourceModule,
76
+ aggregateType: event.aggregateType,
77
+ aggregateId: event.aggregateId,
78
+ payload: event.payload,
79
+ timestamp: event.createdAt,
80
+ }, {
81
+ logger: this.logger,
82
+ inboxService: this.inboxService,
83
+ linkService: this.linkService,
84
+ });
85
+ // Mark as processed
86
+ await this.inboxService.markProcessed(inboxEvent.id);
87
+ this.logger.debug(`Handler ${handlerDef.consumerName} processed event ${event.id}`);
88
+ }
89
+ catch (error) {
90
+ anyFailed = true;
91
+ this.logger.error(`Handler ${handlerDef.consumerName} failed for event ${event.id}:`, error);
92
+ await this.inboxService.markFailed(inboxEvent.id, error instanceof Error ? error.message : String(error));
93
+ }
94
+ }
95
+ // Update outbox event status based on handler results
96
+ if (!anyFailed) {
97
+ await this.outboxService.updateStatus(event.id, types_1.OutboxEventStatus.PROCESSED, {
98
+ processedAt: new Date(),
99
+ });
100
+ processedCount++;
101
+ }
102
+ else {
103
+ // At least one handler failed, retry logic applies
104
+ await this.handleEventFailure(event.id);
105
+ }
106
+ }
107
+ catch (error) {
108
+ this.logger.error(`Error processing event ${event.id}:`, error);
109
+ await this.handleEventFailure(event.id);
110
+ }
111
+ }
112
+ return processedCount;
113
+ }
114
+ /**
115
+ * Handle event that failed to process
116
+ */
117
+ async handleEventFailure(eventId) {
118
+ const settings = await this.integrationSettingsService.getRuntimeSettings();
119
+ const maxAttempts = settings.maxAttempts;
120
+ const retryBaseDelayMs = settings.retryBaseDelayMs;
121
+ const deadLetterEnabled = settings.deadLetterEnabled;
122
+ const event = await this.outboxService.getById(eventId);
123
+ if (!event) {
124
+ return;
125
+ }
126
+ const nextAttemptCount = event.attemptCount + 1;
127
+ if (nextAttemptCount >= maxAttempts) {
128
+ if (deadLetterEnabled) {
129
+ this.logger.warn(`Event ${eventId} moved to dead letter after ${maxAttempts} attempts`);
130
+ await this.outboxService.updateStatus(eventId, types_1.OutboxEventStatus.DEAD_LETTER, {
131
+ attemptCount: nextAttemptCount,
132
+ });
133
+ }
134
+ else {
135
+ this.logger.warn(`Event ${eventId} failed after ${maxAttempts} attempts (dead letter disabled)`);
136
+ await this.outboxService.updateStatus(eventId, types_1.OutboxEventStatus.FAILED, {
137
+ attemptCount: nextAttemptCount,
138
+ });
139
+ }
140
+ }
141
+ else {
142
+ // Calculate exponential backoff
143
+ const delayMs = retryBaseDelayMs * Math.pow(2, event.attemptCount);
144
+ const availableAt = new Date(Date.now() + delayMs);
145
+ this.logger.debug(`Retrying event ${eventId} in ${delayMs}ms (attempt ${nextAttemptCount}/${maxAttempts})`);
146
+ await this.outboxService.updateStatus(eventId, types_1.OutboxEventStatus.PENDING, {
147
+ attemptCount: nextAttemptCount,
148
+ availableAt,
149
+ });
150
+ }
151
+ }
152
+ /**
153
+ * Process all pending events at startup (drain)
154
+ */
155
+ async startupDrain() {
156
+ const settings = await this.integrationSettingsService.getRuntimeSettings();
157
+ const drainBatchSize = settings.startupDrainBatchSize;
158
+ this.logger.log(`Starting outbox drain with batch size: ${drainBatchSize}`);
159
+ let totalProcessed = 0;
160
+ // Keep processing until no more events
161
+ let processed = 1;
162
+ while (processed > 0) {
163
+ processed = await this.processBatch({
164
+ batchSizeOverride: drainBatchSize,
165
+ });
166
+ totalProcessed += processed;
167
+ // Small delay to prevent overwhelming the system
168
+ if (processed > 0) {
169
+ await new Promise((resolve) => setTimeout(resolve, 100));
170
+ }
171
+ }
172
+ this.logger.log(`Startup drain completed. Processed ${totalProcessed} events`);
173
+ return totalProcessed;
174
+ }
175
+ /**
176
+ * Get processor statistics
177
+ */
178
+ async getStats() {
179
+ const pendingCount = await this.outboxService.countByStatus(types_1.OutboxEventStatus.PENDING);
180
+ const processingCount = await this.outboxService.countByStatus(types_1.OutboxEventStatus.PROCESSING);
181
+ const processedCount = await this.outboxService.countByStatus(types_1.OutboxEventStatus.PROCESSED);
182
+ const failedCount = await this.outboxService.countByStatus(types_1.OutboxEventStatus.FAILED);
183
+ const deadLetterCount = await this.outboxService.countByStatus(types_1.OutboxEventStatus.DEAD_LETTER);
184
+ return {
185
+ pending: pendingCount,
186
+ processing: processingCount,
187
+ processed: processedCount,
188
+ failed: failedCount,
189
+ dead_letter: deadLetterCount,
190
+ total: pendingCount +
191
+ processingCount +
192
+ processedCount +
193
+ failedCount +
194
+ deadLetterCount,
195
+ handlerCount: this.registry.getHandlerCount(),
196
+ };
197
+ }
198
+ };
199
+ exports.OutboxProcessor = OutboxProcessor;
200
+ exports.OutboxProcessor = OutboxProcessor = OutboxProcessor_1 = __decorate([
201
+ (0, common_1.Injectable)(),
202
+ __metadata("design:paramtypes", [outbox_service_1.OutboxService,
203
+ inbox_service_1.InboxService,
204
+ integration_link_service_1.IntegrationLinkService,
205
+ event_subscriber_registry_1.EventSubscriberRegistry,
206
+ integration_settings_service_1.IntegrationSettingsService])
207
+ ], OutboxProcessor);
208
+ //# sourceMappingURL=outbox.processor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.processor.js","sourceRoot":"","sources":["../../../src/integration/services/outbox.processor.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import { PrismaService } from '@hed-hog/api-prisma';
2
+ import { OutboxEvent, OutboxEventStatus } from '../types';
3
+ export interface CreateOutboxEventDto {
4
+ eventName: string;
5
+ sourceModule: string;
6
+ aggregateType: string;
7
+ aggregateId: string;
8
+ payload: Record<string, any>;
9
+ }
10
+ export interface OutboxEventPersistenceClient {
11
+ outbox_event: PrismaService['outbox_event'];
12
+ }
13
+ export declare class OutboxService {
14
+ private readonly prisma;
15
+ constructor(prisma: PrismaService);
16
+ private toDomainEvent;
17
+ private toDatabaseId;
18
+ private toDatabaseUpdate;
19
+ /**
20
+ * Write a new event to the outbox
21
+ */
22
+ createEvent(dto: CreateOutboxEventDto, persistenceClient?: OutboxEventPersistenceClient): Promise<OutboxEvent>;
23
+ /**
24
+ * Find pending events available for processing, ordered by creation time
25
+ */
26
+ findPending(limit: number, leaseMs: number): Promise<OutboxEvent[]>;
27
+ /**
28
+ * Update event status and related fields
29
+ */
30
+ updateStatus(eventId: string | number, status: OutboxEventStatus, partialUpdate?: {
31
+ attemptCount?: number;
32
+ lastError?: string | null;
33
+ availableAt?: Date;
34
+ processedAt?: Date | null;
35
+ }): Promise<OutboxEvent>;
36
+ /**
37
+ * Mark event as processing and set lease time
38
+ */
39
+ markProcessing(eventId: string | number, leaseMs: number): Promise<OutboxEvent>;
40
+ /**
41
+ * Increment attempt count and optionally update error
42
+ */
43
+ incrementAttempt(eventId: string | number, error?: string): Promise<OutboxEvent>;
44
+ /**
45
+ * Get event by ID
46
+ */
47
+ getById(eventId: string | number): Promise<OutboxEvent | null>;
48
+ /**
49
+ * Count events by status
50
+ */
51
+ countByStatus(status: OutboxEventStatus): Promise<number>;
52
+ }
53
+ //# sourceMappingURL=outbox.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.service.d.ts","sourceRoot":"","sources":["../../../src/integration/services/outbox.service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEpD,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAE1D,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAC9B;AAED,MAAM,WAAW,4BAA4B;IAC3C,YAAY,EAAE,aAAa,CAAC,cAAc,CAAC,CAAC;CAC7C;AAkBD,qBACa,aAAa;IACZ,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,aAAa;IAElD,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,gBAAgB;IAoBxB;;OAEG;IACG,WAAW,CACf,GAAG,EAAE,oBAAoB,EACzB,iBAAiB,CAAC,EAAE,4BAA4B,GAC/C,OAAO,CAAC,WAAW,CAAC;IAmBvB;;OAEG;IACG,WAAW,CACf,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,EAAE,CAAC;IAqBzB;;OAEG;IACG,YAAY,CAChB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,MAAM,EAAE,iBAAiB,EACzB,aAAa,CAAC,EAAE;QACd,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QAC1B,WAAW,CAAC,EAAE,IAAI,CAAC;QACnB,WAAW,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;KAC3B,GACA,OAAO,CAAC,WAAW,CAAC;IAYvB;;OAEG;IACG,cAAc,CAClB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,WAAW,CAAC;IAOvB;;OAEG;IACG,gBAAgB,CACpB,OAAO,EAAE,MAAM,GAAG,MAAM,EACxB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,CAAC;IAgBvB;;OAEG;IACG,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAQpE;;OAEG;IACG,aAAa,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;CAKhE"}
@@ -0,0 +1,149 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.OutboxService = void 0;
13
+ const api_prisma_1 = require("@hed-hog/api-prisma");
14
+ const common_1 = require("@nestjs/common");
15
+ const types_1 = require("../types");
16
+ let OutboxService = class OutboxService {
17
+ constructor(prisma) {
18
+ this.prisma = prisma;
19
+ }
20
+ toDomainEvent(record) {
21
+ return {
22
+ id: String(record.id),
23
+ eventName: record.event_name,
24
+ sourceModule: record.source_module,
25
+ aggregateType: record.aggregate_type,
26
+ aggregateId: record.aggregate_id,
27
+ payload: record.payload,
28
+ status: record.status,
29
+ attemptCount: record.attempt_count,
30
+ lastError: record.last_error,
31
+ availableAt: record.available_at,
32
+ processedAt: record.processed_at,
33
+ createdAt: record.created_at,
34
+ updatedAt: record.updated_at,
35
+ };
36
+ }
37
+ toDatabaseId(eventId) {
38
+ return typeof eventId === 'number' ? eventId : Number(eventId);
39
+ }
40
+ toDatabaseUpdate(partialUpdate) {
41
+ if (!partialUpdate) {
42
+ return undefined;
43
+ }
44
+ return {
45
+ attempt_count: partialUpdate.attemptCount,
46
+ last_error: partialUpdate.lastError,
47
+ available_at: partialUpdate.availableAt,
48
+ processed_at: partialUpdate.processedAt,
49
+ };
50
+ }
51
+ /**
52
+ * Write a new event to the outbox
53
+ */
54
+ async createEvent(dto, persistenceClient) {
55
+ const client = persistenceClient !== null && persistenceClient !== void 0 ? persistenceClient : this.prisma;
56
+ const record = await client.outbox_event.create({
57
+ data: {
58
+ event_name: dto.eventName,
59
+ source_module: dto.sourceModule,
60
+ aggregate_type: dto.aggregateType,
61
+ aggregate_id: dto.aggregateId,
62
+ payload: dto.payload,
63
+ status: types_1.OutboxEventStatus.PENDING,
64
+ available_at: new Date(),
65
+ attempt_count: 0,
66
+ },
67
+ });
68
+ return this.toDomainEvent(record);
69
+ }
70
+ /**
71
+ * Find pending events available for processing, ordered by creation time
72
+ */
73
+ async findPending(limit, leaseMs) {
74
+ const now = new Date();
75
+ const leaseThreshold = new Date(now.getTime() - leaseMs);
76
+ const records = await this.prisma.outbox_event.findMany({
77
+ where: {
78
+ OR: [
79
+ { status: types_1.OutboxEventStatus.PENDING },
80
+ {
81
+ status: types_1.OutboxEventStatus.PROCESSING,
82
+ available_at: { lte: leaseThreshold },
83
+ },
84
+ ],
85
+ },
86
+ orderBy: { created_at: 'asc' },
87
+ take: limit,
88
+ });
89
+ return records.map((record) => this.toDomainEvent(record));
90
+ }
91
+ /**
92
+ * Update event status and related fields
93
+ */
94
+ async updateStatus(eventId, status, partialUpdate) {
95
+ const record = await this.prisma.outbox_event.update({
96
+ where: { id: this.toDatabaseId(eventId) },
97
+ data: Object.assign({ status }, this.toDatabaseUpdate(partialUpdate)),
98
+ });
99
+ return this.toDomainEvent(record);
100
+ }
101
+ /**
102
+ * Mark event as processing and set lease time
103
+ */
104
+ async markProcessing(eventId, leaseMs) {
105
+ const leaseUntil = new Date(Date.now() + leaseMs);
106
+ return this.updateStatus(eventId, types_1.OutboxEventStatus.PROCESSING, {
107
+ availableAt: leaseUntil,
108
+ });
109
+ }
110
+ /**
111
+ * Increment attempt count and optionally update error
112
+ */
113
+ async incrementAttempt(eventId, error) {
114
+ const record = await this.prisma.outbox_event.findUnique({
115
+ where: { id: this.toDatabaseId(eventId) },
116
+ });
117
+ if (!record) {
118
+ throw new Error(`Event ${eventId} not found`);
119
+ }
120
+ const event = this.toDomainEvent(record);
121
+ return this.updateStatus(eventId, event.status, {
122
+ attemptCount: event.attemptCount + 1,
123
+ lastError: error || null,
124
+ });
125
+ }
126
+ /**
127
+ * Get event by ID
128
+ */
129
+ async getById(eventId) {
130
+ const record = await this.prisma.outbox_event.findUnique({
131
+ where: { id: this.toDatabaseId(eventId) },
132
+ });
133
+ return record ? this.toDomainEvent(record) : null;
134
+ }
135
+ /**
136
+ * Count events by status
137
+ */
138
+ async countByStatus(status) {
139
+ return this.prisma.outbox_event.count({
140
+ where: { status },
141
+ });
142
+ }
143
+ };
144
+ exports.OutboxService = OutboxService;
145
+ exports.OutboxService = OutboxService = __decorate([
146
+ (0, common_1.Injectable)(),
147
+ __metadata("design:paramtypes", [api_prisma_1.PrismaService])
148
+ ], OutboxService);
149
+ //# sourceMappingURL=outbox.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"outbox.service.js","sourceRoot":"","sources":["../../../src/integration/services/outbox.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Event status throughout the integration lifecycle
3
+ */
4
+ export declare enum OutboxEventStatus {
5
+ PENDING = "pending",
6
+ PROCESSING = "processing",
7
+ PROCESSED = "processed",
8
+ FAILED = "failed",
9
+ DEAD_LETTER = "dead_letter"
10
+ }
11
+ /**
12
+ * Inbox event processing status per consumer
13
+ */
14
+ export declare enum InboxEventStatus {
15
+ RECEIVED = "received",
16
+ PROCESSING = "processing",
17
+ PROCESSED = "processed",
18
+ FAILED = "failed",
19
+ SKIPPED = "skipped"
20
+ }
21
+ /**
22
+ * Type of link between entities from different modules
23
+ */
24
+ export declare enum LinkType {
25
+ REFERENCE = "reference",
26
+ CASCADE = "cascade",
27
+ AGGREGATE_ROOT = "aggregate_root"
28
+ }
29
+ /**
30
+ * Domain event interface that is published and processed
31
+ */
32
+ export interface DomainEvent {
33
+ eventName: string;
34
+ sourceModule: string;
35
+ aggregateType: string;
36
+ aggregateId: string;
37
+ payload: Record<string, any>;
38
+ timestamp: Date;
39
+ }
40
+ /**
41
+ * Outbox event record from database
42
+ */
43
+ export interface OutboxEvent {
44
+ id: string;
45
+ eventName: string;
46
+ sourceModule: string;
47
+ aggregateType: string;
48
+ aggregateId: string;
49
+ payload: Record<string, any>;
50
+ status: OutboxEventStatus;
51
+ attemptCount: number;
52
+ lastError: string | null;
53
+ availableAt: Date;
54
+ processedAt: Date | null;
55
+ createdAt: Date;
56
+ updatedAt: Date;
57
+ }
58
+ /**
59
+ * Inbox event record from database for idempotency tracking
60
+ */
61
+ export interface InboxEvent {
62
+ id: string;
63
+ outboxEventId: string;
64
+ consumerName: string;
65
+ status: InboxEventStatus;
66
+ attemptCount: number;
67
+ lastError: string | null;
68
+ processedAt: Date | null;
69
+ createdAt: Date;
70
+ updatedAt: Date;
71
+ }
72
+ /**
73
+ * Integration link between entities across modules
74
+ */
75
+ export interface IntegrationLink {
76
+ id: string;
77
+ sourceModule: string;
78
+ sourceEntityType: string;
79
+ sourceEntityId: string;
80
+ targetModule: string;
81
+ targetEntityType: string;
82
+ targetEntityId: string;
83
+ linkType: LinkType;
84
+ metadata: Record<string, any> | null;
85
+ createdAt: Date;
86
+ updatedAt: Date;
87
+ }
88
+ //# sourceMappingURL=event.types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"event.types.d.ts","sourceRoot":"","sources":["../../../src/integration/types/event.types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,oBAAY,iBAAiB;IAC3B,OAAO,YAAY;IACnB,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,WAAW,gBAAgB;CAC5B;AAED;;GAEG;AACH,oBAAY,gBAAgB;IAC1B,QAAQ,aAAa;IACrB,UAAU,eAAe;IACzB,SAAS,cAAc;IACvB,MAAM,WAAW;IACjB,OAAO,YAAY;CACpB;AAED;;GAEG;AACH,oBAAY,QAAQ;IAClB,SAAS,cAAc;IACvB,OAAO,YAAY;IACnB,cAAc,mBAAmB;CAClC;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;IAC7B,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,IAAI,CAAC;IAClB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,gBAAgB,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IACzB,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE,QAAQ,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC;IACrC,SAAS,EAAE,IAAI,CAAC;IAChB,SAAS,EAAE,IAAI,CAAC;CACjB"}