@push.rocks/smartmta 5.1.2 → 5.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/changelog.md +14 -0
  2. package/dist_ts/00_commitinfo_data.d.ts +8 -0
  3. package/dist_ts/00_commitinfo_data.js +9 -0
  4. package/dist_ts/index.d.ts +3 -0
  5. package/dist_ts/index.js +4 -0
  6. package/dist_ts/logger.d.ts +17 -0
  7. package/dist_ts/logger.js +76 -0
  8. package/dist_ts/mail/core/classes.bouncemanager.d.ts +185 -0
  9. package/dist_ts/mail/core/classes.bouncemanager.js +569 -0
  10. package/dist_ts/mail/core/classes.email.d.ts +291 -0
  11. package/dist_ts/mail/core/classes.email.js +802 -0
  12. package/dist_ts/mail/core/classes.emailvalidator.d.ts +61 -0
  13. package/dist_ts/mail/core/classes.emailvalidator.js +184 -0
  14. package/dist_ts/mail/core/classes.templatemanager.d.ts +95 -0
  15. package/dist_ts/mail/core/classes.templatemanager.js +240 -0
  16. package/dist_ts/mail/core/index.d.ts +4 -0
  17. package/dist_ts/mail/core/index.js +6 -0
  18. package/dist_ts/mail/delivery/classes.delivery.queue.d.ts +163 -0
  19. package/dist_ts/mail/delivery/classes.delivery.queue.js +488 -0
  20. package/dist_ts/mail/delivery/classes.delivery.system.d.ts +160 -0
  21. package/dist_ts/mail/delivery/classes.delivery.system.js +630 -0
  22. package/dist_ts/mail/delivery/classes.unified.rate.limiter.d.ts +200 -0
  23. package/dist_ts/mail/delivery/classes.unified.rate.limiter.js +820 -0
  24. package/dist_ts/mail/delivery/index.d.ts +4 -0
  25. package/dist_ts/mail/delivery/index.js +6 -0
  26. package/dist_ts/mail/delivery/interfaces.d.ts +140 -0
  27. package/dist_ts/mail/delivery/interfaces.js +17 -0
  28. package/dist_ts/mail/index.d.ts +7 -0
  29. package/dist_ts/mail/index.js +12 -0
  30. package/dist_ts/mail/routing/classes.dkim.manager.d.ts +25 -0
  31. package/dist_ts/mail/routing/classes.dkim.manager.js +127 -0
  32. package/dist_ts/mail/routing/classes.dns.manager.d.ts +79 -0
  33. package/dist_ts/mail/routing/classes.dns.manager.js +415 -0
  34. package/dist_ts/mail/routing/classes.domain.registry.d.ts +54 -0
  35. package/dist_ts/mail/routing/classes.domain.registry.js +119 -0
  36. package/dist_ts/mail/routing/classes.email.action.executor.d.ts +33 -0
  37. package/dist_ts/mail/routing/classes.email.action.executor.js +137 -0
  38. package/dist_ts/mail/routing/classes.email.router.d.ts +171 -0
  39. package/dist_ts/mail/routing/classes.email.router.js +494 -0
  40. package/dist_ts/mail/routing/classes.unified.email.server.d.ts +241 -0
  41. package/dist_ts/mail/routing/classes.unified.email.server.js +935 -0
  42. package/dist_ts/mail/routing/index.d.ts +7 -0
  43. package/dist_ts/mail/routing/index.js +9 -0
  44. package/dist_ts/mail/routing/interfaces.d.ts +187 -0
  45. package/dist_ts/mail/routing/interfaces.js +2 -0
  46. package/dist_ts/mail/security/classes.dkimcreator.d.ts +72 -0
  47. package/dist_ts/mail/security/classes.dkimcreator.js +360 -0
  48. package/dist_ts/mail/security/classes.spfverifier.d.ts +62 -0
  49. package/dist_ts/mail/security/classes.spfverifier.js +87 -0
  50. package/dist_ts/mail/security/index.d.ts +2 -0
  51. package/dist_ts/mail/security/index.js +4 -0
  52. package/dist_ts/paths.d.ts +14 -0
  53. package/dist_ts/paths.js +39 -0
  54. package/dist_ts/plugins.d.ts +24 -0
  55. package/dist_ts/plugins.js +28 -0
  56. package/dist_ts/security/classes.contentscanner.d.ts +130 -0
  57. package/dist_ts/security/classes.contentscanner.js +338 -0
  58. package/dist_ts/security/classes.ipreputationchecker.d.ts +73 -0
  59. package/dist_ts/security/classes.ipreputationchecker.js +263 -0
  60. package/dist_ts/security/classes.rustsecuritybridge.d.ts +398 -0
  61. package/dist_ts/security/classes.rustsecuritybridge.js +484 -0
  62. package/dist_ts/security/classes.securitylogger.d.ts +140 -0
  63. package/dist_ts/security/classes.securitylogger.js +235 -0
  64. package/dist_ts/security/index.d.ts +4 -0
  65. package/dist_ts/security/index.js +5 -0
  66. package/package.json +6 -1
  67. package/readme.md +52 -9
  68. package/ts/00_commitinfo_data.ts +8 -0
  69. package/ts/index.ts +3 -0
  70. package/ts/logger.ts +91 -0
  71. package/ts/mail/core/classes.bouncemanager.ts +731 -0
  72. package/ts/mail/core/classes.email.ts +942 -0
  73. package/ts/mail/core/classes.emailvalidator.ts +239 -0
  74. package/ts/mail/core/classes.templatemanager.ts +320 -0
  75. package/ts/mail/core/index.ts +5 -0
  76. package/ts/mail/delivery/classes.delivery.queue.ts +645 -0
  77. package/ts/mail/delivery/classes.delivery.system.ts +816 -0
  78. package/ts/mail/delivery/classes.unified.rate.limiter.ts +1053 -0
  79. package/ts/mail/delivery/index.ts +5 -0
  80. package/ts/mail/delivery/interfaces.ts +167 -0
  81. package/ts/mail/index.ts +17 -0
  82. package/ts/mail/routing/classes.dkim.manager.ts +157 -0
  83. package/ts/mail/routing/classes.dns.manager.ts +573 -0
  84. package/ts/mail/routing/classes.domain.registry.ts +139 -0
  85. package/ts/mail/routing/classes.email.action.executor.ts +175 -0
  86. package/ts/mail/routing/classes.email.router.ts +575 -0
  87. package/ts/mail/routing/classes.unified.email.server.ts +1207 -0
  88. package/ts/mail/routing/index.ts +9 -0
  89. package/ts/mail/routing/interfaces.ts +202 -0
  90. package/ts/mail/security/classes.dkimcreator.ts +447 -0
  91. package/ts/mail/security/classes.spfverifier.ts +126 -0
  92. package/ts/mail/security/index.ts +3 -0
  93. package/ts/paths.ts +48 -0
  94. package/ts/plugins.ts +53 -0
  95. package/ts/security/classes.contentscanner.ts +400 -0
  96. package/ts/security/classes.ipreputationchecker.ts +315 -0
  97. package/ts/security/classes.rustsecuritybridge.ts +943 -0
  98. package/ts/security/classes.securitylogger.ts +299 -0
  99. package/ts/security/index.ts +40 -0
@@ -0,0 +1,163 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { type EmailProcessingMode } from './interfaces.js';
3
+ import type { IEmailRoute } from '../routing/interfaces.js';
4
+ /**
5
+ * Queue item status
6
+ */
7
+ export type QueueItemStatus = 'pending' | 'processing' | 'delivered' | 'failed' | 'deferred';
8
+ /**
9
+ * Queue item interface
10
+ */
11
+ export interface IQueueItem {
12
+ id: string;
13
+ processingMode: EmailProcessingMode;
14
+ processingResult: any;
15
+ route: IEmailRoute;
16
+ status: QueueItemStatus;
17
+ attempts: number;
18
+ nextAttempt: Date;
19
+ lastError?: string;
20
+ createdAt: Date;
21
+ updatedAt: Date;
22
+ deliveredAt?: Date;
23
+ }
24
+ /**
25
+ * Queue options interface
26
+ */
27
+ export interface IQueueOptions {
28
+ storageType?: 'memory' | 'disk';
29
+ persistentPath?: string;
30
+ checkInterval?: number;
31
+ maxQueueSize?: number;
32
+ maxPerDestination?: number;
33
+ maxRetries?: number;
34
+ baseRetryDelay?: number;
35
+ maxRetryDelay?: number;
36
+ }
37
+ /**
38
+ * Queue statistics interface
39
+ */
40
+ export interface IQueueStats {
41
+ queueSize: number;
42
+ status: {
43
+ pending: number;
44
+ processing: number;
45
+ delivered: number;
46
+ failed: number;
47
+ deferred: number;
48
+ };
49
+ modes: {
50
+ forward: number;
51
+ mta: number;
52
+ process: number;
53
+ };
54
+ oldestItem?: Date;
55
+ newestItem?: Date;
56
+ averageAttempts: number;
57
+ totalProcessed: number;
58
+ processingActive: boolean;
59
+ }
60
+ /**
61
+ * A unified queue for all email modes
62
+ */
63
+ export declare class UnifiedDeliveryQueue extends EventEmitter {
64
+ private options;
65
+ private queue;
66
+ private checkTimer?;
67
+ private stats;
68
+ private processing;
69
+ private totalProcessed;
70
+ /**
71
+ * Create a new unified delivery queue
72
+ * @param options Queue options
73
+ */
74
+ constructor(options: IQueueOptions);
75
+ /**
76
+ * Initialize the queue
77
+ */
78
+ initialize(): Promise<void>;
79
+ /**
80
+ * Start queue processing
81
+ */
82
+ private startProcessing;
83
+ /**
84
+ * Stop queue processing
85
+ */
86
+ private stopProcessing;
87
+ /**
88
+ * Check for items that need to be processed
89
+ */
90
+ private processQueue;
91
+ /**
92
+ * Add an item to the queue
93
+ * @param processingResult Processing result to queue
94
+ * @param mode Processing mode
95
+ * @param route Email route
96
+ */
97
+ enqueue(processingResult: any, mode: EmailProcessingMode, route: IEmailRoute): Promise<string>;
98
+ /**
99
+ * Get an item from the queue
100
+ * @param id Item ID
101
+ */
102
+ getItem(id: string): IQueueItem | undefined;
103
+ /**
104
+ * Mark an item as being processed
105
+ * @param id Item ID
106
+ */
107
+ markProcessing(id: string): Promise<boolean>;
108
+ /**
109
+ * Mark an item as delivered
110
+ * @param id Item ID
111
+ */
112
+ markDelivered(id: string): Promise<boolean>;
113
+ /**
114
+ * Mark an item as failed
115
+ * @param id Item ID
116
+ * @param error Error message
117
+ */
118
+ markFailed(id: string, error: string): Promise<boolean>;
119
+ /**
120
+ * Remove an item from the queue
121
+ * @param id Item ID
122
+ */
123
+ removeItem(id: string): Promise<boolean>;
124
+ /**
125
+ * Persist an item to disk
126
+ * @param item Item to persist
127
+ */
128
+ private persistItem;
129
+ /**
130
+ * Remove an item from disk
131
+ * @param id Item ID
132
+ */
133
+ private removeItemFromDisk;
134
+ /**
135
+ * Load queue items from disk
136
+ */
137
+ private loadFromDisk;
138
+ /**
139
+ * Update queue statistics
140
+ */
141
+ private updateStats;
142
+ /**
143
+ * Get queue statistics
144
+ */
145
+ getStats(): IQueueStats;
146
+ /**
147
+ * Pause queue processing
148
+ */
149
+ pause(): void;
150
+ /**
151
+ * Resume queue processing
152
+ */
153
+ resume(): void;
154
+ /**
155
+ * Clean up old delivered and failed items
156
+ * @param maxAge Maximum age in milliseconds (default: 7 days)
157
+ */
158
+ cleanupOldItems(maxAge?: number): Promise<number>;
159
+ /**
160
+ * Shutdown the queue
161
+ */
162
+ shutdown(): Promise<void>;
163
+ }
@@ -0,0 +1,488 @@
1
+ import * as plugins from '../../plugins.js';
2
+ import { EventEmitter } from 'node:events';
3
+ import * as fs from 'node:fs';
4
+ import * as path from 'node:path';
5
+ import { logger } from '../../logger.js';
6
+ import {} from './interfaces.js';
7
+ /**
8
+ * A unified queue for all email modes
9
+ */
10
+ export class UnifiedDeliveryQueue extends EventEmitter {
11
+ options;
12
+ queue = new Map();
13
+ checkTimer;
14
+ stats;
15
+ processing = false;
16
+ totalProcessed = 0;
17
+ /**
18
+ * Create a new unified delivery queue
19
+ * @param options Queue options
20
+ */
21
+ constructor(options) {
22
+ super();
23
+ // Set default options
24
+ this.options = {
25
+ storageType: options.storageType || 'memory',
26
+ persistentPath: options.persistentPath || path.join(process.cwd(), 'email-queue'),
27
+ checkInterval: options.checkInterval || 30000, // 30 seconds
28
+ maxQueueSize: options.maxQueueSize || 10000,
29
+ maxPerDestination: options.maxPerDestination || 100,
30
+ maxRetries: options.maxRetries || 5,
31
+ baseRetryDelay: options.baseRetryDelay || 60000, // 1 minute
32
+ maxRetryDelay: options.maxRetryDelay || 3600000 // 1 hour
33
+ };
34
+ // Initialize statistics
35
+ this.stats = {
36
+ queueSize: 0,
37
+ status: {
38
+ pending: 0,
39
+ processing: 0,
40
+ delivered: 0,
41
+ failed: 0,
42
+ deferred: 0
43
+ },
44
+ modes: {
45
+ forward: 0,
46
+ mta: 0,
47
+ process: 0
48
+ },
49
+ averageAttempts: 0,
50
+ totalProcessed: 0,
51
+ processingActive: false
52
+ };
53
+ }
54
+ /**
55
+ * Initialize the queue
56
+ */
57
+ async initialize() {
58
+ logger.log('info', 'Initializing UnifiedDeliveryQueue');
59
+ try {
60
+ // Create persistent storage directory if using disk storage
61
+ if (this.options.storageType === 'disk') {
62
+ if (!fs.existsSync(this.options.persistentPath)) {
63
+ fs.mkdirSync(this.options.persistentPath, { recursive: true });
64
+ }
65
+ // Load existing items from disk
66
+ await this.loadFromDisk();
67
+ }
68
+ // Start the queue processing timer
69
+ this.startProcessing();
70
+ // Emit initialized event
71
+ this.emit('initialized');
72
+ logger.log('info', 'UnifiedDeliveryQueue initialized successfully');
73
+ }
74
+ catch (error) {
75
+ logger.log('error', `Failed to initialize queue: ${error.message}`);
76
+ throw error;
77
+ }
78
+ }
79
+ /**
80
+ * Start queue processing
81
+ */
82
+ startProcessing() {
83
+ if (this.checkTimer) {
84
+ clearInterval(this.checkTimer);
85
+ }
86
+ this.checkTimer = setInterval(() => this.processQueue(), this.options.checkInterval);
87
+ this.processing = true;
88
+ this.stats.processingActive = true;
89
+ this.emit('processingStarted');
90
+ logger.log('info', 'Queue processing started');
91
+ }
92
+ /**
93
+ * Stop queue processing
94
+ */
95
+ stopProcessing() {
96
+ if (this.checkTimer) {
97
+ clearInterval(this.checkTimer);
98
+ this.checkTimer = undefined;
99
+ }
100
+ this.processing = false;
101
+ this.stats.processingActive = false;
102
+ this.emit('processingStopped');
103
+ logger.log('info', 'Queue processing stopped');
104
+ }
105
+ /**
106
+ * Check for items that need to be processed
107
+ */
108
+ async processQueue() {
109
+ try {
110
+ const now = new Date();
111
+ let readyItems = [];
112
+ // Find items ready for processing
113
+ for (const item of this.queue.values()) {
114
+ if (item.status === 'pending' || (item.status === 'deferred' && item.nextAttempt <= now)) {
115
+ readyItems.push(item);
116
+ }
117
+ }
118
+ if (readyItems.length === 0) {
119
+ return;
120
+ }
121
+ // Sort by oldest first
122
+ readyItems.sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
123
+ // Emit event for ready items
124
+ this.emit('itemsReady', readyItems);
125
+ logger.log('info', `Found ${readyItems.length} items ready for processing`);
126
+ // Update statistics
127
+ this.updateStats();
128
+ }
129
+ catch (error) {
130
+ logger.log('error', `Error processing queue: ${error.message}`);
131
+ this.emit('error', error);
132
+ }
133
+ }
134
+ /**
135
+ * Add an item to the queue
136
+ * @param processingResult Processing result to queue
137
+ * @param mode Processing mode
138
+ * @param route Email route
139
+ */
140
+ async enqueue(processingResult, mode, route) {
141
+ // Check if queue is full
142
+ if (this.queue.size >= this.options.maxQueueSize) {
143
+ throw new Error('Queue is full');
144
+ }
145
+ // Generate a unique ID
146
+ const id = `${Date.now()}-${Math.random().toString(36).substring(2, 15)}`;
147
+ // Create queue item
148
+ const item = {
149
+ id,
150
+ processingMode: mode,
151
+ processingResult,
152
+ route,
153
+ status: 'pending',
154
+ attempts: 0,
155
+ nextAttempt: new Date(),
156
+ createdAt: new Date(),
157
+ updatedAt: new Date()
158
+ };
159
+ // Add to queue
160
+ this.queue.set(id, item);
161
+ // Persist to disk if using disk storage
162
+ if (this.options.storageType === 'disk') {
163
+ await this.persistItem(item);
164
+ }
165
+ // Update statistics
166
+ this.updateStats();
167
+ // Emit event
168
+ this.emit('itemEnqueued', item);
169
+ logger.log('info', `Item enqueued with ID ${id}, mode: ${mode}`);
170
+ return id;
171
+ }
172
+ /**
173
+ * Get an item from the queue
174
+ * @param id Item ID
175
+ */
176
+ getItem(id) {
177
+ return this.queue.get(id);
178
+ }
179
+ /**
180
+ * Mark an item as being processed
181
+ * @param id Item ID
182
+ */
183
+ async markProcessing(id) {
184
+ const item = this.queue.get(id);
185
+ if (!item) {
186
+ return false;
187
+ }
188
+ // Update status
189
+ item.status = 'processing';
190
+ item.attempts++;
191
+ item.updatedAt = new Date();
192
+ // Persist changes if using disk storage
193
+ if (this.options.storageType === 'disk') {
194
+ await this.persistItem(item);
195
+ }
196
+ // Update statistics
197
+ this.updateStats();
198
+ // Emit event
199
+ this.emit('itemProcessing', item);
200
+ logger.log('info', `Item ${id} marked as processing, attempt ${item.attempts}`);
201
+ return true;
202
+ }
203
+ /**
204
+ * Mark an item as delivered
205
+ * @param id Item ID
206
+ */
207
+ async markDelivered(id) {
208
+ const item = this.queue.get(id);
209
+ if (!item) {
210
+ return false;
211
+ }
212
+ // Update status
213
+ item.status = 'delivered';
214
+ item.updatedAt = new Date();
215
+ item.deliveredAt = new Date();
216
+ // Persist changes if using disk storage
217
+ if (this.options.storageType === 'disk') {
218
+ await this.persistItem(item);
219
+ }
220
+ // Update statistics
221
+ this.totalProcessed++;
222
+ this.updateStats();
223
+ // Emit event
224
+ this.emit('itemDelivered', item);
225
+ logger.log('info', `Item ${id} marked as delivered after ${item.attempts} attempts`);
226
+ return true;
227
+ }
228
+ /**
229
+ * Mark an item as failed
230
+ * @param id Item ID
231
+ * @param error Error message
232
+ */
233
+ async markFailed(id, error) {
234
+ const item = this.queue.get(id);
235
+ if (!item) {
236
+ return false;
237
+ }
238
+ // Determine if we should retry
239
+ if (item.attempts < this.options.maxRetries) {
240
+ // Calculate next retry time with exponential backoff
241
+ const delay = Math.min(this.options.baseRetryDelay * Math.pow(2, item.attempts - 1), this.options.maxRetryDelay);
242
+ // Update status
243
+ item.status = 'deferred';
244
+ item.lastError = error;
245
+ item.nextAttempt = new Date(Date.now() + delay);
246
+ item.updatedAt = new Date();
247
+ // Persist changes if using disk storage
248
+ if (this.options.storageType === 'disk') {
249
+ await this.persistItem(item);
250
+ }
251
+ // Emit event
252
+ this.emit('itemDeferred', item);
253
+ logger.log('info', `Item ${id} deferred for ${delay}ms, attempt ${item.attempts}, error: ${error}`);
254
+ }
255
+ else {
256
+ // Mark as permanently failed
257
+ item.status = 'failed';
258
+ item.lastError = error;
259
+ item.updatedAt = new Date();
260
+ // Persist changes if using disk storage
261
+ if (this.options.storageType === 'disk') {
262
+ await this.persistItem(item);
263
+ }
264
+ // Update statistics
265
+ this.totalProcessed++;
266
+ // Emit event
267
+ this.emit('itemFailed', item);
268
+ logger.log('warn', `Item ${id} permanently failed after ${item.attempts} attempts, error: ${error}`);
269
+ }
270
+ // Update statistics
271
+ this.updateStats();
272
+ return true;
273
+ }
274
+ /**
275
+ * Remove an item from the queue
276
+ * @param id Item ID
277
+ */
278
+ async removeItem(id) {
279
+ const item = this.queue.get(id);
280
+ if (!item) {
281
+ return false;
282
+ }
283
+ // Remove from queue
284
+ this.queue.delete(id);
285
+ // Remove from disk if using disk storage
286
+ if (this.options.storageType === 'disk') {
287
+ await this.removeItemFromDisk(id);
288
+ }
289
+ // Update statistics
290
+ this.updateStats();
291
+ // Emit event
292
+ this.emit('itemRemoved', item);
293
+ logger.log('info', `Item ${id} removed from queue`);
294
+ return true;
295
+ }
296
+ /**
297
+ * Persist an item to disk
298
+ * @param item Item to persist
299
+ */
300
+ async persistItem(item) {
301
+ try {
302
+ const filePath = path.join(this.options.persistentPath, `${item.id}.json`);
303
+ await fs.promises.writeFile(filePath, JSON.stringify(item, null, 2), 'utf8');
304
+ }
305
+ catch (error) {
306
+ logger.log('error', `Failed to persist item ${item.id}: ${error.message}`);
307
+ this.emit('error', error);
308
+ }
309
+ }
310
+ /**
311
+ * Remove an item from disk
312
+ * @param id Item ID
313
+ */
314
+ async removeItemFromDisk(id) {
315
+ try {
316
+ const filePath = path.join(this.options.persistentPath, `${id}.json`);
317
+ if (fs.existsSync(filePath)) {
318
+ await fs.promises.unlink(filePath);
319
+ }
320
+ }
321
+ catch (error) {
322
+ logger.log('error', `Failed to remove item ${id} from disk: ${error.message}`);
323
+ this.emit('error', error);
324
+ }
325
+ }
326
+ /**
327
+ * Load queue items from disk
328
+ */
329
+ async loadFromDisk() {
330
+ try {
331
+ // Check if directory exists
332
+ if (!fs.existsSync(this.options.persistentPath)) {
333
+ return;
334
+ }
335
+ // Get all JSON files
336
+ const files = fs.readdirSync(this.options.persistentPath).filter(file => file.endsWith('.json'));
337
+ // Load each file
338
+ for (const file of files) {
339
+ try {
340
+ const filePath = path.join(this.options.persistentPath, file);
341
+ const data = await fs.promises.readFile(filePath, 'utf8');
342
+ const item = JSON.parse(data);
343
+ // Convert date strings to Date objects
344
+ item.createdAt = new Date(item.createdAt);
345
+ item.updatedAt = new Date(item.updatedAt);
346
+ item.nextAttempt = new Date(item.nextAttempt);
347
+ if (item.deliveredAt) {
348
+ item.deliveredAt = new Date(item.deliveredAt);
349
+ }
350
+ // Add to queue
351
+ this.queue.set(item.id, item);
352
+ }
353
+ catch (error) {
354
+ logger.log('error', `Failed to load item from ${file}: ${error.message}`);
355
+ }
356
+ }
357
+ // Update statistics
358
+ this.updateStats();
359
+ logger.log('info', `Loaded ${this.queue.size} items from disk`);
360
+ }
361
+ catch (error) {
362
+ logger.log('error', `Failed to load items from disk: ${error.message}`);
363
+ throw error;
364
+ }
365
+ }
366
+ /**
367
+ * Update queue statistics
368
+ */
369
+ updateStats() {
370
+ // Reset counters
371
+ this.stats.queueSize = this.queue.size;
372
+ this.stats.status = {
373
+ pending: 0,
374
+ processing: 0,
375
+ delivered: 0,
376
+ failed: 0,
377
+ deferred: 0
378
+ };
379
+ this.stats.modes = {
380
+ forward: 0,
381
+ mta: 0,
382
+ process: 0
383
+ };
384
+ let totalAttempts = 0;
385
+ let oldestTime = Date.now();
386
+ let newestTime = 0;
387
+ // Count by status and mode
388
+ for (const item of this.queue.values()) {
389
+ // Count by status
390
+ this.stats.status[item.status]++;
391
+ // Count by mode
392
+ this.stats.modes[item.processingMode]++;
393
+ // Track total attempts
394
+ totalAttempts += item.attempts;
395
+ // Track oldest and newest
396
+ const itemTime = item.createdAt.getTime();
397
+ if (itemTime < oldestTime) {
398
+ oldestTime = itemTime;
399
+ }
400
+ if (itemTime > newestTime) {
401
+ newestTime = itemTime;
402
+ }
403
+ }
404
+ // Calculate average attempts
405
+ this.stats.averageAttempts = this.queue.size > 0 ? totalAttempts / this.queue.size : 0;
406
+ // Set oldest and newest
407
+ this.stats.oldestItem = this.queue.size > 0 ? new Date(oldestTime) : undefined;
408
+ this.stats.newestItem = this.queue.size > 0 ? new Date(newestTime) : undefined;
409
+ // Set total processed
410
+ this.stats.totalProcessed = this.totalProcessed;
411
+ // Set processing active
412
+ this.stats.processingActive = this.processing;
413
+ // Emit statistics event
414
+ this.emit('statsUpdated', this.stats);
415
+ }
416
+ /**
417
+ * Get queue statistics
418
+ */
419
+ getStats() {
420
+ return { ...this.stats };
421
+ }
422
+ /**
423
+ * Pause queue processing
424
+ */
425
+ pause() {
426
+ if (this.processing) {
427
+ this.stopProcessing();
428
+ logger.log('info', 'Queue processing paused');
429
+ }
430
+ }
431
+ /**
432
+ * Resume queue processing
433
+ */
434
+ resume() {
435
+ if (!this.processing) {
436
+ this.startProcessing();
437
+ logger.log('info', 'Queue processing resumed');
438
+ }
439
+ }
440
+ /**
441
+ * Clean up old delivered and failed items
442
+ * @param maxAge Maximum age in milliseconds (default: 7 days)
443
+ */
444
+ async cleanupOldItems(maxAge = 7 * 24 * 60 * 60 * 1000) {
445
+ const cutoff = new Date(Date.now() - maxAge);
446
+ let removedCount = 0;
447
+ // Find old items
448
+ for (const item of this.queue.values()) {
449
+ if (['delivered', 'failed'].includes(item.status) && item.updatedAt < cutoff) {
450
+ // Remove item
451
+ await this.removeItem(item.id);
452
+ removedCount++;
453
+ }
454
+ }
455
+ logger.log('info', `Cleaned up ${removedCount} old items`);
456
+ return removedCount;
457
+ }
458
+ /**
459
+ * Shutdown the queue
460
+ */
461
+ async shutdown() {
462
+ logger.log('info', 'Shutting down UnifiedDeliveryQueue');
463
+ // Stop processing
464
+ this.stopProcessing();
465
+ // Clear the check timer to prevent memory leaks
466
+ if (this.checkTimer) {
467
+ clearInterval(this.checkTimer);
468
+ this.checkTimer = undefined;
469
+ }
470
+ // If using disk storage, make sure all items are persisted
471
+ if (this.options.storageType === 'disk') {
472
+ const pendingWrites = [];
473
+ for (const item of this.queue.values()) {
474
+ pendingWrites.push(this.persistItem(item));
475
+ }
476
+ // Wait for all writes to complete
477
+ await Promise.all(pendingWrites);
478
+ }
479
+ // Clear the queue (memory only)
480
+ this.queue.clear();
481
+ // Update statistics
482
+ this.updateStats();
483
+ // Emit shutdown event
484
+ this.emit('shutdown');
485
+ logger.log('info', 'UnifiedDeliveryQueue shut down successfully');
486
+ }
487
+ }
488
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5kZWxpdmVyeS5xdWV1ZS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3RzL21haWwvZGVsaXZlcnkvY2xhc3Nlcy5kZWxpdmVyeS5xdWV1ZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGtCQUFrQixDQUFDO0FBQzVDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDM0MsT0FBTyxLQUFLLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFDOUIsT0FBTyxLQUFLLElBQUksTUFBTSxXQUFXLENBQUM7QUFDbEMsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ3pDLE9BQU8sRUFBNEIsTUFBTSxpQkFBaUIsQ0FBQztBQW9FM0Q7O0dBRUc7QUFDSCxNQUFNLE9BQU8sb0JBQXFCLFNBQVEsWUFBWTtJQUM1QyxPQUFPLENBQTBCO0lBQ2pDLEtBQUssR0FBNEIsSUFBSSxHQUFHLEVBQUUsQ0FBQztJQUMzQyxVQUFVLENBQWtCO0lBQzVCLEtBQUssQ0FBYztJQUNuQixVQUFVLEdBQVksS0FBSyxDQUFDO0lBQzVCLGNBQWMsR0FBVyxDQUFDLENBQUM7SUFFbkM7OztPQUdHO0lBQ0gsWUFBWSxPQUFzQjtRQUNoQyxLQUFLLEVBQUUsQ0FBQztRQUVSLHNCQUFzQjtRQUN0QixJQUFJLENBQUMsT0FBTyxHQUFHO1lBQ2IsV0FBVyxFQUFFLE9BQU8sQ0FBQyxXQUFXLElBQUksUUFBUTtZQUM1QyxjQUFjLEVBQUUsT0FBTyxDQUFDLGNBQWMsSUFBSSxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxhQUFhLENBQUM7WUFDakYsYUFBYSxFQUFFLE9BQU8sQ0FBQyxhQUFhLElBQUksS0FBSyxFQUFFLGFBQWE7WUFDNUQsWUFBWSxFQUFFLE9BQU8sQ0FBQyxZQUFZLElBQUksS0FBSztZQUMzQyxpQkFBaUIsRUFBRSxPQUFPLENB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