hookarmor 1.0.2 → 1.1.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/src/worker.js CHANGED
@@ -1,62 +1,72 @@
1
- const EventEmitter = require('events');
2
-
3
- class RetryWorker extends EventEmitter {
4
- constructor(storage, dispatcher, options = {}) {
5
- super();
6
- this.storage = storage;
7
- this.dispatcher = dispatcher;
8
- this.intervalMs = options.intervalMs || 5000;
9
- this.timer = null;
10
- this.isProcessing = false;
11
- }
12
-
13
- start() {
14
- if (this.timer) return;
15
- this.timer = setInterval(() => this.tick(), this.intervalMs);
16
- // Unref timer so it doesn't hold Node process open during graceful shutdown or tests if desired
17
- if (this.timer.unref) this.timer.unref();
18
- this.emit('started');
19
- }
20
-
21
- stop() {
22
- if (this.timer) {
23
- clearInterval(this.timer);
24
- this.timer = null;
25
- this.emit('stopped');
26
- }
27
- }
28
-
29
- async tick() {
30
- if (this.isProcessing) return;
31
- this.isProcessing = true;
32
-
33
- try {
34
- const eventsDue = this.storage.getEventsDueForRetry(25);
35
- if (!eventsDue || eventsDue.length === 0) {
36
- this.isProcessing = false;
37
- return;
38
- }
39
-
40
- for (const event of eventsDue) {
41
- const claimed = this.storage.claimEventForRetry(event.id);
42
- if (!claimed) continue; // Another process or manual replay already claimed it
43
-
44
- const endpoint = this.storage.getEndpoint(event.endpoint_id);
45
- if (!endpoint) continue;
46
-
47
- this.emit('retry:claimed', { eventId: event.id, endpointId: endpoint.id });
48
-
49
- // Dispatch asynchronously without blocking next iterations
50
- this.dispatcher.dispatch(event, endpoint).catch((err) => {
51
- this.emit('retry:error', { eventId: event.id, error: err.message });
52
- });
53
- }
54
- } catch (err) {
55
- this.emit('error', err);
56
- } finally {
57
- this.isProcessing = false;
58
- }
59
- }
60
- }
61
-
62
- module.exports = RetryWorker;
1
+ const EventEmitter = require('events');
2
+
3
+ class RetryWorker extends EventEmitter {
4
+ constructor(storage, dispatcher, options = {}) {
5
+ super();
6
+ this.storage = storage;
7
+ this.dispatcher = dispatcher;
8
+ this.intervalMs = options.intervalMs || 5000;
9
+ this.retentionDays = options.retentionDays || 0;
10
+ this.pruneIntervalMs = options.pruneIntervalMs || 60 * 60 * 1000;
11
+ this.lastPruneAt = 0;
12
+ this.timer = null;
13
+ this.isProcessing = false;
14
+ }
15
+
16
+ start() {
17
+ if (this.timer) return;
18
+ this.timer = setInterval(() => this.tick(), this.intervalMs);
19
+ // Unref timer so it doesn't hold Node process open during graceful shutdown or tests if desired
20
+ if (this.timer.unref) this.timer.unref();
21
+ this.emit('started');
22
+ }
23
+
24
+ stop() {
25
+ if (this.timer) {
26
+ clearInterval(this.timer);
27
+ this.timer = null;
28
+ this.emit('stopped');
29
+ }
30
+ }
31
+
32
+ prune() {
33
+ if (!this.retentionDays || Date.now() - this.lastPruneAt < this.pruneIntervalMs) return;
34
+ this.lastPruneAt = Date.now();
35
+ const removed = this.storage.pruneEvents(this.retentionDays);
36
+ if (removed > 0) this.emit('pruned', { removed });
37
+ }
38
+
39
+ async tick() {
40
+ if (this.isProcessing) return;
41
+ this.isProcessing = true;
42
+
43
+ try {
44
+ this.prune();
45
+
46
+ const eventsDue = this.storage.getEventsDueForRetry(25);
47
+ if (!eventsDue || eventsDue.length === 0) return;
48
+
49
+ for (const event of eventsDue) {
50
+ const claimed = this.storage.claimEventForRetry(event.id);
51
+ if (!claimed) continue; // Another process or manual replay already claimed it
52
+
53
+ const endpoint = this.storage.getEndpoint(event.endpoint_id);
54
+ if (!endpoint) continue;
55
+
56
+ this.emit('retry:claimed', { eventId: event.id, endpointId: endpoint.id });
57
+
58
+ // Dispatch asynchronously without blocking next iterations
59
+ this.dispatcher.dispatch(event, endpoint, { replay: true }).catch((err) => {
60
+ this.emit('retry:error', { eventId: event.id, error: err.message });
61
+ });
62
+ }
63
+ } catch (err) {
64
+ if (this.listenerCount('error') > 0) this.emit('error', err);
65
+ else console.error('[RetryWorker] Tick failed:', err.message);
66
+ } finally {
67
+ this.isProcessing = false;
68
+ }
69
+ }
70
+ }
71
+
72
+ module.exports = RetryWorker;