@bernierllc/email-sender-manager 1.0.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.
Files changed (55) hide show
  1. package/LICENSE +5 -0
  2. package/README.md +466 -0
  3. package/dist/__tests__/manager.test.d.ts +2 -0
  4. package/dist/__tests__/manager.test.d.ts.map +1 -0
  5. package/dist/__tests__/manager.test.js +344 -0
  6. package/dist/__tests__/manager.test.js.map +1 -0
  7. package/dist/__tests__/mocks/mock-database.d.ts +15 -0
  8. package/dist/__tests__/mocks/mock-database.d.ts.map +1 -0
  9. package/dist/__tests__/mocks/mock-database.js +219 -0
  10. package/dist/__tests__/mocks/mock-database.js.map +1 -0
  11. package/dist/__tests__/selection/selector.test.d.ts +2 -0
  12. package/dist/__tests__/selection/selector.test.d.ts.map +1 -0
  13. package/dist/__tests__/selection/selector.test.js +351 -0
  14. package/dist/__tests__/selection/selector.test.js.map +1 -0
  15. package/dist/__tests__/utils/domain-utils.test.d.ts +2 -0
  16. package/dist/__tests__/utils/domain-utils.test.d.ts.map +1 -0
  17. package/dist/__tests__/utils/domain-utils.test.js +91 -0
  18. package/dist/__tests__/utils/domain-utils.test.js.map +1 -0
  19. package/dist/__tests__/utils/validation-utils.test.d.ts +2 -0
  20. package/dist/__tests__/utils/validation-utils.test.d.ts.map +1 -0
  21. package/dist/__tests__/utils/validation-utils.test.js +117 -0
  22. package/dist/__tests__/utils/validation-utils.test.js.map +1 -0
  23. package/dist/database/bootstrap.d.ts +35 -0
  24. package/dist/database/bootstrap.d.ts.map +1 -0
  25. package/dist/database/bootstrap.js +183 -0
  26. package/dist/database/bootstrap.js.map +1 -0
  27. package/dist/database/schema.d.ts +13 -0
  28. package/dist/database/schema.d.ts.map +1 -0
  29. package/dist/database/schema.js +72 -0
  30. package/dist/database/schema.js.map +1 -0
  31. package/dist/index.d.ts +8 -0
  32. package/dist/index.d.ts.map +1 -0
  33. package/dist/index.js +18 -0
  34. package/dist/index.js.map +1 -0
  35. package/dist/manager.d.ts +85 -0
  36. package/dist/manager.d.ts.map +1 -0
  37. package/dist/manager.js +469 -0
  38. package/dist/manager.js.map +1 -0
  39. package/dist/selection/selector.d.ts +27 -0
  40. package/dist/selection/selector.d.ts.map +1 -0
  41. package/dist/selection/selector.js +107 -0
  42. package/dist/selection/selector.js.map +1 -0
  43. package/dist/types.d.ts +257 -0
  44. package/dist/types.d.ts.map +1 -0
  45. package/dist/types.js +9 -0
  46. package/dist/types.js.map +1 -0
  47. package/dist/utils/domain-utils.d.ts +21 -0
  48. package/dist/utils/domain-utils.d.ts.map +1 -0
  49. package/dist/utils/domain-utils.js +65 -0
  50. package/dist/utils/domain-utils.js.map +1 -0
  51. package/dist/utils/validation-utils.d.ts +23 -0
  52. package/dist/utils/validation-utils.d.ts.map +1 -0
  53. package/dist/utils/validation-utils.js +148 -0
  54. package/dist/utils/validation-utils.js.map +1 -0
  55. package/package.json +49 -0
@@ -0,0 +1,469 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ import { SenderBootstrap } from './database/bootstrap.js';
9
+ import { SenderSelector } from './selection/selector.js';
10
+ import { extractDomain } from './utils/domain-utils.js';
11
+ import { validateCreateSenderRequest, validateSenderConfiguration, } from './utils/validation-utils.js';
12
+ export class EmailSenderManager {
13
+ database;
14
+ providers = new Map();
15
+ bootstrap;
16
+ selector;
17
+ bootstrapConfig;
18
+ // @ts-expect-error - Reserved for future validation features
19
+ validationConfig;
20
+ selectionConfig;
21
+ systemUser;
22
+ constructor(config) {
23
+ this.database = config.database;
24
+ this.systemUser = config.systemUser ?? 'system';
25
+ // Initialize configurations with defaults
26
+ this.bootstrapConfig = {
27
+ enabled: false,
28
+ createTables: false,
29
+ seedDefaultSenders: false,
30
+ defaultSenders: [],
31
+ environment: 'production',
32
+ ...config.bootstrap,
33
+ };
34
+ this.validationConfig = {
35
+ requireVerification: false,
36
+ allowUnverifiedSenders: true,
37
+ validateDomainOwnership: false,
38
+ ...config.validation,
39
+ };
40
+ this.selectionConfig = {
41
+ strategy: 'priority',
42
+ fallbackToDefault: true,
43
+ domainMatchingRules: [],
44
+ ...config.selection,
45
+ };
46
+ // Initialize bootstrap system
47
+ this.bootstrap = new SenderBootstrap(this.database, this.bootstrapConfig);
48
+ // Initialize selector
49
+ this.selector = new SenderSelector(this.selectionConfig);
50
+ // Register providers
51
+ if (config.providers) {
52
+ for (const provider of config.providers) {
53
+ this.providers.set(provider.providerId, provider);
54
+ }
55
+ }
56
+ }
57
+ /**
58
+ * Bootstrap database and seed default senders
59
+ */
60
+ async bootstrapDatabase() {
61
+ if (!this.bootstrapConfig.enabled) {
62
+ return {
63
+ success: false,
64
+ tablesCreated: false,
65
+ seedersRun: 0,
66
+ message: 'Bootstrap is not enabled in configuration',
67
+ };
68
+ }
69
+ return await this.bootstrap.bootstrap();
70
+ }
71
+ /**
72
+ * Create a new sender configuration
73
+ */
74
+ async createSender(request) {
75
+ // Validate request
76
+ const validation = validateCreateSenderRequest(request);
77
+ if (validation.errors.length > 0) {
78
+ throw new Error(`Validation failed: ${validation.errors.map((e) => e.message).join(', ')}`);
79
+ }
80
+ // Validate domain configuration
81
+ const domainValidation = validateSenderConfiguration(request.fromEmail, request.allowedDomains);
82
+ if (!domainValidation.valid) {
83
+ throw new Error(`Domain validation failed: ${domainValidation.errors.map((e) => e.message).join(', ')}`);
84
+ }
85
+ const id = this.generateId();
86
+ const domain = extractDomain(request.fromEmail);
87
+ const now = new Date();
88
+ const sender = {
89
+ id,
90
+ name: request.name,
91
+ ...(request.description !== undefined && { description: request.description }),
92
+ fromEmail: request.fromEmail,
93
+ fromName: request.fromName,
94
+ ...(request.replyToEmail !== undefined && { replyToEmail: request.replyToEmail }),
95
+ ...(request.replyToName !== undefined && { replyToName: request.replyToName }),
96
+ provider: request.provider,
97
+ ...(request.providerSenderId !== undefined && { providerSenderId: request.providerSenderId }),
98
+ ...(request.providerMetadata !== undefined && { providerMetadata: request.providerMetadata }),
99
+ isVerified: false,
100
+ verificationStatus: 'pending',
101
+ isDefault: false,
102
+ isActive: true,
103
+ priority: request.priority ?? 100,
104
+ ...(request.allowedDomains !== undefined && { allowedDomains: request.allowedDomains }),
105
+ domain,
106
+ createdAt: now,
107
+ updatedAt: now,
108
+ createdBy: request.createdBy,
109
+ lastModifiedBy: request.createdBy,
110
+ };
111
+ await this.database.execute(`INSERT INTO email_senders (
112
+ id, name, description, from_email, from_name, reply_to_email, reply_to_name,
113
+ provider, provider_sender_id, provider_metadata,
114
+ is_verified, verification_status, is_default, is_active, priority,
115
+ allowed_domains, domain, created_at, updated_at, created_by, last_modified_by
116
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
117
+ sender.id,
118
+ sender.name,
119
+ sender.description ?? null,
120
+ sender.fromEmail,
121
+ sender.fromName,
122
+ sender.replyToEmail ?? null,
123
+ sender.replyToName ?? null,
124
+ sender.provider,
125
+ sender.providerSenderId ?? null,
126
+ sender.providerMetadata ? JSON.stringify(sender.providerMetadata) : null,
127
+ sender.isVerified,
128
+ sender.verificationStatus,
129
+ sender.isDefault,
130
+ sender.isActive,
131
+ sender.priority,
132
+ sender.allowedDomains ? JSON.stringify(sender.allowedDomains) : null,
133
+ sender.domain,
134
+ sender.createdAt,
135
+ sender.updatedAt,
136
+ sender.createdBy,
137
+ sender.lastModifiedBy,
138
+ ]);
139
+ return sender;
140
+ }
141
+ /**
142
+ * Update an existing sender
143
+ */
144
+ async updateSender(id, updates) {
145
+ const existing = await this.getSender(id);
146
+ if (!existing) {
147
+ throw new Error(`Sender not found: ${id}`);
148
+ }
149
+ const now = new Date();
150
+ const updatedSender = {
151
+ ...existing,
152
+ ...updates,
153
+ updatedAt: now,
154
+ };
155
+ await this.database.execute(`UPDATE email_senders SET
156
+ name = ?, description = ?, from_name = ?, reply_to_email = ?, reply_to_name = ?,
157
+ provider_sender_id = ?, provider_metadata = ?,
158
+ is_verified = ?, verification_status = ?, last_verified_at = ?, verification_error = ?,
159
+ is_default = ?, is_active = ?, priority = ?, allowed_domains = ?,
160
+ updated_at = ?, last_modified_by = ?
161
+ WHERE id = ?`, [
162
+ updatedSender.name,
163
+ updatedSender.description ?? null,
164
+ updatedSender.fromName,
165
+ updatedSender.replyToEmail ?? null,
166
+ updatedSender.replyToName ?? null,
167
+ updatedSender.providerSenderId ?? null,
168
+ updatedSender.providerMetadata ? JSON.stringify(updatedSender.providerMetadata) : null,
169
+ updatedSender.isVerified,
170
+ updatedSender.verificationStatus,
171
+ updatedSender.lastVerifiedAt ?? null,
172
+ updatedSender.verificationError ?? null,
173
+ updatedSender.isDefault,
174
+ updatedSender.isActive,
175
+ updatedSender.priority,
176
+ updatedSender.allowedDomains ? JSON.stringify(updatedSender.allowedDomains) : null,
177
+ updatedSender.updatedAt,
178
+ updates.lastModifiedBy,
179
+ id,
180
+ ]);
181
+ return updatedSender;
182
+ }
183
+ /**
184
+ * Delete a sender
185
+ */
186
+ async deleteSender(id) {
187
+ await this.database.execute('DELETE FROM email_senders WHERE id = ?', [id]);
188
+ }
189
+ /**
190
+ * Get a single sender by ID
191
+ */
192
+ async getSender(id) {
193
+ const row = await this.database.queryOne('SELECT * FROM email_senders WHERE id = ?', [id]);
194
+ return row ? this.mapRowToSender(row) : null;
195
+ }
196
+ /**
197
+ * List senders with pagination and filtering
198
+ */
199
+ async listSenders(options = {}) {
200
+ const { provider, isActive, isVerified, domain, offset = 0, limit = 50, orderBy = 'priority', orderDirection = 'asc', } = options;
201
+ const conditions = [];
202
+ const params = [];
203
+ if (provider) {
204
+ conditions.push('provider = ?');
205
+ params.push(provider);
206
+ }
207
+ if (isActive !== undefined) {
208
+ conditions.push('is_active = ?');
209
+ params.push(isActive);
210
+ }
211
+ if (isVerified !== undefined) {
212
+ conditions.push('is_verified = ?');
213
+ params.push(isVerified);
214
+ }
215
+ if (domain) {
216
+ conditions.push('domain = ?');
217
+ params.push(domain);
218
+ }
219
+ const whereClause = conditions.length > 0 ? `WHERE ${conditions.join(' AND ')}` : '';
220
+ const orderClause = `ORDER BY ${orderBy} ${orderDirection.toUpperCase()}`;
221
+ // Get total count
222
+ const countResult = await this.database.queryOne(`SELECT COUNT(*) as count FROM email_senders ${whereClause}`, params);
223
+ const total = countResult?.count ?? 0;
224
+ // Get paginated results
225
+ const rows = await this.database.query(`SELECT * FROM email_senders ${whereClause} ${orderClause} LIMIT ? OFFSET ?`, [...params, limit, offset]);
226
+ const items = rows.map((row) => this.mapRowToSender(row));
227
+ return {
228
+ items,
229
+ total,
230
+ offset,
231
+ limit,
232
+ hasMore: offset + items.length < total,
233
+ };
234
+ }
235
+ /**
236
+ * Validate a sender configuration
237
+ */
238
+ async validateSender(id) {
239
+ const sender = await this.getSender(id);
240
+ if (!sender) {
241
+ return {
242
+ isValid: false,
243
+ errors: [{ field: 'id', message: 'Sender not found', code: 'NOT_FOUND' }],
244
+ warnings: [],
245
+ };
246
+ }
247
+ const validation = validateSenderConfiguration(sender.fromEmail, sender.allowedDomains);
248
+ return {
249
+ isValid: validation.valid,
250
+ errors: validation.errors,
251
+ warnings: [],
252
+ };
253
+ }
254
+ /**
255
+ * Verify sender with provider
256
+ */
257
+ async verifySender(id, providerId) {
258
+ const sender = await this.getSender(id);
259
+ if (!sender) {
260
+ return {
261
+ success: false,
262
+ isVerified: false,
263
+ verificationStatus: 'not_found',
264
+ error: 'Sender not found',
265
+ };
266
+ }
267
+ const provId = providerId ?? sender.provider;
268
+ const provider = this.providers.get(provId);
269
+ if (!provider) {
270
+ return {
271
+ success: false,
272
+ isVerified: false,
273
+ verificationStatus: 'provider_not_found',
274
+ error: `Provider not found: ${provId}`,
275
+ };
276
+ }
277
+ try {
278
+ const validation = await provider.validateSender(sender.fromEmail);
279
+ await this.updateSender(id, {
280
+ isVerified: validation.isVerified,
281
+ verificationStatus: validation.verificationStatus,
282
+ lastVerifiedAt: validation.lastChecked,
283
+ lastModifiedBy: this.systemUser,
284
+ });
285
+ return {
286
+ success: true,
287
+ isVerified: validation.isVerified,
288
+ verificationStatus: validation.verificationStatus,
289
+ message: validation.isVerified ? 'Sender verified successfully' : 'Sender verification failed',
290
+ };
291
+ }
292
+ catch (error) {
293
+ return {
294
+ success: false,
295
+ isVerified: false,
296
+ verificationStatus: 'error',
297
+ error: error instanceof Error ? error.message : 'Unknown error',
298
+ };
299
+ }
300
+ }
301
+ /**
302
+ * Refresh verification status from provider
303
+ */
304
+ async refreshVerificationStatus(id) {
305
+ const verification = await this.verifySender(id);
306
+ if (!verification.success) {
307
+ throw new Error(`Failed to refresh verification: ${verification.error}`);
308
+ }
309
+ const sender = await this.getSender(id);
310
+ if (!sender) {
311
+ throw new Error(`Sender not found: ${id}`);
312
+ }
313
+ return sender;
314
+ }
315
+ /**
316
+ * Select best sender for an email address
317
+ */
318
+ async selectBestSender(fromEmail, options = {}) {
319
+ const domain = extractDomain(fromEmail);
320
+ // Get all active senders
321
+ const result = await this.listSenders({
322
+ isActive: true,
323
+ limit: 1000,
324
+ });
325
+ let candidates = result.items;
326
+ // Apply filters
327
+ candidates = this.selector.filterCandidates(candidates, options);
328
+ if (candidates.length === 0) {
329
+ return this.selectionConfig.fallbackToDefault ? await this.getDefaultSender() : null;
330
+ }
331
+ return this.selector.selectBestSender(candidates, domain, options);
332
+ }
333
+ /**
334
+ * Get the default sender
335
+ */
336
+ async getDefaultSender(provider) {
337
+ const conditions = ['is_default = TRUE', 'is_active = TRUE'];
338
+ const params = [];
339
+ if (provider) {
340
+ conditions.push('provider = ?');
341
+ params.push(provider);
342
+ }
343
+ const row = await this.database.queryOne(`SELECT * FROM email_senders WHERE ${conditions.join(' AND ')} LIMIT 1`, params);
344
+ return row ? this.mapRowToSender(row) : null;
345
+ }
346
+ /**
347
+ * Get senders by domain
348
+ */
349
+ async getSendersByDomain(domain) {
350
+ const result = await this.listSenders({ domain, limit: 1000 });
351
+ return result.items;
352
+ }
353
+ /**
354
+ * Register a provider plugin
355
+ */
356
+ async registerProvider(provider) {
357
+ this.providers.set(provider.providerId, provider);
358
+ // Sync existing senders with this provider
359
+ const result = await this.listSenders({ provider: provider.providerId, limit: 1000 });
360
+ for (const sender of result.items) {
361
+ await this.refreshVerificationStatus(sender.id);
362
+ }
363
+ }
364
+ /**
365
+ * Get provider senders
366
+ */
367
+ async getProviderSenders(providerId) {
368
+ const provider = this.providers.get(providerId);
369
+ if (!provider) {
370
+ throw new Error(`Provider not found: ${providerId}`);
371
+ }
372
+ return await provider.getVerifiedSenders();
373
+ }
374
+ /**
375
+ * Sync with all providers
376
+ */
377
+ async syncWithProviders() {
378
+ const results = [];
379
+ for (const [providerId, provider] of this.providers) {
380
+ const senders = await this.listSenders({ provider: providerId, limit: 1000 });
381
+ for (const sender of senders.items) {
382
+ try {
383
+ const syncResult = await provider.syncSender(sender);
384
+ results.push(syncResult);
385
+ }
386
+ catch (error) {
387
+ results.push({
388
+ success: false,
389
+ senderId: sender.id,
390
+ synced: false,
391
+ error: error instanceof Error ? error.message : 'Unknown error',
392
+ });
393
+ }
394
+ }
395
+ }
396
+ return results;
397
+ }
398
+ /**
399
+ * Clean up inactive senders
400
+ */
401
+ async cleanupInactiveSenders() {
402
+ const result = await this.database.query('SELECT id FROM email_senders WHERE is_active = FALSE');
403
+ let deletedCount = 0;
404
+ for (const row of result) {
405
+ await this.deleteSender(row.id);
406
+ deletedCount++;
407
+ }
408
+ return {
409
+ success: true,
410
+ deletedCount,
411
+ message: `Cleaned up ${deletedCount} inactive sender(s)`,
412
+ };
413
+ }
414
+ /**
415
+ * Map database row to SenderConfiguration
416
+ */
417
+ mapRowToSender(row) {
418
+ const sender = {
419
+ id: row.id,
420
+ name: row.name,
421
+ fromEmail: row.from_email,
422
+ fromName: row.from_name,
423
+ provider: row.provider,
424
+ isVerified: Boolean(row.is_verified),
425
+ verificationStatus: row.verification_status,
426
+ isDefault: Boolean(row.is_default),
427
+ isActive: Boolean(row.is_active),
428
+ priority: row.priority,
429
+ domain: row.domain,
430
+ createdAt: new Date(row.created_at),
431
+ updatedAt: new Date(row.updated_at),
432
+ createdBy: row.created_by,
433
+ lastModifiedBy: row.last_modified_by,
434
+ };
435
+ // Add optional properties only if they exist
436
+ if (row.description !== null && row.description !== undefined) {
437
+ sender.description = row.description;
438
+ }
439
+ if (row.reply_to_email !== null && row.reply_to_email !== undefined) {
440
+ sender.replyToEmail = row.reply_to_email;
441
+ }
442
+ if (row.reply_to_name !== null && row.reply_to_name !== undefined) {
443
+ sender.replyToName = row.reply_to_name;
444
+ }
445
+ if (row.provider_sender_id !== null && row.provider_sender_id !== undefined) {
446
+ sender.providerSenderId = row.provider_sender_id;
447
+ }
448
+ if (row.provider_metadata !== null && row.provider_metadata !== undefined) {
449
+ sender.providerMetadata = JSON.parse(row.provider_metadata);
450
+ }
451
+ if (row.last_verified_at !== null && row.last_verified_at !== undefined) {
452
+ sender.lastVerifiedAt = new Date(row.last_verified_at);
453
+ }
454
+ if (row.verification_error !== null && row.verification_error !== undefined) {
455
+ sender.verificationError = row.verification_error;
456
+ }
457
+ if (row.allowed_domains !== null && row.allowed_domains !== undefined) {
458
+ sender.allowedDomains = JSON.parse(row.allowed_domains);
459
+ }
460
+ return sender;
461
+ }
462
+ /**
463
+ * Generate unique sender ID
464
+ */
465
+ generateId() {
466
+ return `sender_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
467
+ }
468
+ }
469
+ //# sourceMappingURL=manager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manager.js","sourceRoot":"","sources":["../src/manager.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ import type { SenderConfiguration, SelectionOptions, SelectionConfig } from '../types.js';
2
+ export declare class SenderSelector {
3
+ private config;
4
+ private roundRobinCounters;
5
+ constructor(config: SelectionConfig);
6
+ /**
7
+ * Select best sender from candidates
8
+ */
9
+ selectBestSender(candidates: SenderConfiguration[], domain: string, options?: SelectionOptions): SenderConfiguration | null;
10
+ /**
11
+ * Select sender by domain matching
12
+ */
13
+ private selectByDomainMatch;
14
+ /**
15
+ * Select sender by priority
16
+ */
17
+ private selectByPriority;
18
+ /**
19
+ * Select sender using round-robin
20
+ */
21
+ private selectRoundRobin;
22
+ /**
23
+ * Filter candidates by selection options
24
+ */
25
+ filterCandidates(candidates: SenderConfiguration[], options: SelectionOptions): SenderConfiguration[];
26
+ }
27
+ //# sourceMappingURL=selector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.d.ts","sourceRoot":"","sources":["../../src/selection/selector.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAG1F,qBAAa,cAAc;IAGb,OAAO,CAAC,MAAM;IAF1B,OAAO,CAAC,kBAAkB,CAAkC;gBAExC,MAAM,EAAE,eAAe;IAE3C;;OAEG;IACH,gBAAgB,CACd,UAAU,EAAE,mBAAmB,EAAE,EACjC,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,gBAAqB,GAC7B,mBAAmB,GAAG,IAAI;IAmB7B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA+B3B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAsBxB;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAUxB;;OAEG;IACH,gBAAgB,CACd,UAAU,EAAE,mBAAmB,EAAE,EACjC,OAAO,EAAE,gBAAgB,GACxB,mBAAmB,EAAE;CAoBzB"}
@@ -0,0 +1,107 @@
1
+ /*
2
+ Copyright (c) 2025 Bernier LLC
3
+
4
+ This file is licensed to the client under a limited-use license.
5
+ The client may use and modify this code *only within the scope of the project it was delivered for*.
6
+ Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
7
+ */
8
+ import { isDomainMatch } from '../utils/domain-utils.js';
9
+ export class SenderSelector {
10
+ config;
11
+ roundRobinCounters = new Map();
12
+ constructor(config) {
13
+ this.config = config;
14
+ }
15
+ /**
16
+ * Select best sender from candidates
17
+ */
18
+ selectBestSender(candidates, domain, options = {}) {
19
+ if (candidates.length === 0) {
20
+ return null;
21
+ }
22
+ const strategy = options.strategy || this.config.strategy;
23
+ switch (strategy) {
24
+ case 'domain_match':
25
+ return this.selectByDomainMatch(candidates, domain);
26
+ case 'priority':
27
+ return this.selectByPriority(candidates);
28
+ case 'round_robin':
29
+ return this.selectRoundRobin(candidates, domain);
30
+ default:
31
+ return candidates[0];
32
+ }
33
+ }
34
+ /**
35
+ * Select sender by domain matching
36
+ */
37
+ selectByDomainMatch(candidates, domain) {
38
+ // 1. Exact domain match
39
+ const exactMatches = candidates.filter((sender) => sender.domain === domain);
40
+ if (exactMatches.length > 0) {
41
+ return this.selectByPriority(exactMatches);
42
+ }
43
+ // 2. Parent/subdomain match
44
+ const relatedMatches = candidates.filter((sender) => domain.endsWith(`.${sender.domain}`) || sender.domain.endsWith(`.${domain}`));
45
+ if (relatedMatches.length > 0) {
46
+ return this.selectByPriority(relatedMatches);
47
+ }
48
+ // 3. Allowed domains match
49
+ const allowedMatches = candidates.filter((sender) => sender.allowedDomains?.some((allowed) => isDomainMatch(domain, allowed)));
50
+ if (allowedMatches.length > 0) {
51
+ return this.selectByPriority(allowedMatches);
52
+ }
53
+ // 4. Fallback to priority
54
+ return this.selectByPriority(candidates);
55
+ }
56
+ /**
57
+ * Select sender by priority
58
+ */
59
+ selectByPriority(candidates) {
60
+ return candidates.sort((a, b) => {
61
+ // Lower priority number = higher priority
62
+ if (a.priority !== b.priority) {
63
+ return a.priority - b.priority;
64
+ }
65
+ // Verified senders preferred
66
+ if (a.isVerified !== b.isVerified) {
67
+ return b.isVerified ? 1 : -1;
68
+ }
69
+ // More recently verified preferred
70
+ if (a.lastVerifiedAt && b.lastVerifiedAt) {
71
+ return b.lastVerifiedAt.getTime() - a.lastVerifiedAt.getTime();
72
+ }
73
+ // Fallback to creation order
74
+ return a.createdAt.getTime() - b.createdAt.getTime();
75
+ })[0];
76
+ }
77
+ /**
78
+ * Select sender using round-robin
79
+ */
80
+ selectRoundRobin(candidates, domain) {
81
+ const key = `rr_${domain}`;
82
+ const currentIndex = this.roundRobinCounters.get(key) ?? 0;
83
+ const nextIndex = (currentIndex + 1) % candidates.length;
84
+ this.roundRobinCounters.set(key, nextIndex);
85
+ return candidates[currentIndex];
86
+ }
87
+ /**
88
+ * Filter candidates by selection options
89
+ */
90
+ filterCandidates(candidates, options) {
91
+ let filtered = [...candidates];
92
+ // Filter by verification status
93
+ if (!options.allowUnverified) {
94
+ filtered = filtered.filter((sender) => sender.isVerified);
95
+ }
96
+ // Filter by provider
97
+ if (options.provider) {
98
+ filtered = filtered.filter((sender) => sender.provider === options.provider);
99
+ }
100
+ // Exclude specific senders
101
+ if (options.excludeIds && options.excludeIds.length > 0) {
102
+ filtered = filtered.filter((sender) => !options.excludeIds.includes(sender.id));
103
+ }
104
+ return filtered;
105
+ }
106
+ }
107
+ //# sourceMappingURL=selector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"selector.js","sourceRoot":"","sources":["../../src/selection/selector.ts"],"names":[],"mappings":"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"}