@bernierllc/sender-identity-verification 1.3.1 → 1.4.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.
@@ -6,34 +6,97 @@ This file is licensed to the client under a limited-use license.
6
6
  The client may use and modify this code *only within the scope of the project it was delivered for*.
7
7
  Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
8
8
  */
9
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ var desc = Object.getOwnPropertyDescriptor(m, k);
12
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
13
+ desc = { enumerable: true, get: function() { return m[k]; } };
14
+ }
15
+ Object.defineProperty(o, k2, desc);
16
+ }) : (function(o, m, k, k2) {
17
+ if (k2 === undefined) k2 = k;
18
+ o[k2] = m[k];
19
+ }));
20
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
21
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
22
+ }) : function(o, v) {
23
+ o["default"] = v;
24
+ });
25
+ var __importStar = (this && this.__importStar) || (function () {
26
+ var ownKeys = function(o) {
27
+ ownKeys = Object.getOwnPropertyNames || function (o) {
28
+ var ar = [];
29
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
30
+ return ar;
31
+ };
32
+ return ownKeys(o);
33
+ };
34
+ return function (mod) {
35
+ if (mod && mod.__esModule) return mod;
36
+ var result = {};
37
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
38
+ __setModuleDefault(result, mod);
39
+ return result;
40
+ };
41
+ })();
9
42
  Object.defineProperty(exports, "__esModule", { value: true });
10
43
  exports.SenderIdentityVerification = void 0;
11
44
  const types_js_1 = require("./types.js");
12
- const SendGridProvider_js_1 = require("./providers/SendGridProvider.js");
13
- const MailgunProvider_js_1 = require("./providers/MailgunProvider.js");
14
- const SESProvider_js_1 = require("./providers/SESProvider.js");
15
- const SMTPProvider_js_1 = require("./providers/SMTPProvider.js");
16
45
  const verification_email_js_1 = require("./templates/verification-email.js");
17
46
  const domain_extractor_js_1 = require("./utils/domain-extractor.js");
47
+ const manual_instructions_js_1 = require("./manual-instructions.js");
48
+ const email_sender_manager_1 = require("@bernierllc/email-sender-manager");
18
49
  /**
19
- * Sender identity verification service
50
+ * Sender identity verification service.
51
+ *
52
+ * When configured with an `EmailSenderManager` (via config.senderManager) and
53
+ * provider plugins (via config.providerPlugins or registerProviderPlugin()),
54
+ * all persistence and provider API calls are delegated. Without those, the
55
+ * service falls back to the legacy in-memory store.
20
56
  */
21
57
  class SenderIdentityVerification {
22
58
  constructor(config) {
23
59
  this.config = config;
60
+ // Legacy in-memory store — used only when no senderManager is configured
24
61
  this.senders = new Map();
62
+ // Provider plugin registry (keyed by providerId)
63
+ this.providerPlugins = new Map();
25
64
  // Initialize domain verification from config if provided
26
65
  if (config.domainVerificationConfig?.instance) {
27
66
  this.domainVerification = config.domainVerificationConfig.instance;
28
67
  }
68
+ // Wire core sender manager if provided
69
+ if (config.senderManager) {
70
+ this.senderManager = config.senderManager;
71
+ }
72
+ // Register initial provider plugins
73
+ if (config.providerPlugins) {
74
+ for (const plugin of config.providerPlugins) {
75
+ this.providerPlugins.set(plugin.providerId, plugin);
76
+ }
77
+ }
78
+ }
79
+ /**
80
+ * Register a provider plugin at runtime.
81
+ */
82
+ registerProviderPlugin(plugin) {
83
+ this.providerPlugins.set(plugin.providerId, plugin);
84
+ this.logger?.info('Provider plugin registered', { providerId: plugin.providerId, name: plugin.name });
85
+ }
86
+ /**
87
+ * Get a registered provider plugin by providerId.
88
+ */
89
+ getProviderPlugin(providerId) {
90
+ return this.providerPlugins.get(providerId);
29
91
  }
30
92
  /**
31
93
  * Initialize service and register with NeverHub
32
94
  */
33
95
  async initialize() {
34
- // Initialize dependencies (would import from actual packages)
35
- // For now, we'll use stubs since we're missing dependencies
36
- await this.loadSendersFromDatabase();
96
+ // Load from database if no senderManager (legacy path)
97
+ if (!this.senderManager) {
98
+ await this.loadSendersFromDatabase();
99
+ }
37
100
  this.logger?.info('Sender identity verification service initialized');
38
101
  }
39
102
  /**
@@ -50,7 +113,7 @@ class SenderIdentityVerification {
50
113
  }
51
114
  // 2. Extract domain
52
115
  const domain = (0, domain_extractor_js_1.extractDomain)(input.email);
53
- // 3. Check if domain is verified (stub for missing dependency)
116
+ // 3. Check if domain is verified
54
117
  if (this.domainVerification) {
55
118
  const domainStatus = await this.domainVerification.getDomainStatus(domain);
56
119
  if (!domainStatus.isVerified) {
@@ -89,14 +152,51 @@ class SenderIdentityVerification {
89
152
  if (input.isDefault) {
90
153
  await this.unsetOtherDefaults(input.provider);
91
154
  }
92
- // 6. Persist to database
93
- await this.saveSenderToDatabase(sender);
94
- this.senders.set(sender.id, sender);
95
- // 7. Send verification email (unless skipped)
96
- if (!input.skipVerification) {
155
+ // 6. Persist — delegate to senderManager if available, else legacy
156
+ if (this.senderManager) {
157
+ const coreRequest = this.toCoreSenderRequest(input, sender);
158
+ const created = await this.senderManager.createSender(coreRequest);
159
+ sender.id = created.id;
160
+ sender.providerSenderId = created.providerSenderId;
161
+ }
162
+ else {
163
+ await this.saveSenderToDatabase(sender);
164
+ this.senders.set(sender.id, sender);
165
+ }
166
+ // 7. Delegate to provider plugin for API-level creation
167
+ const plugin = this.providerPlugins.get(input.provider);
168
+ if (plugin && (0, email_sender_manager_1.isManagedSenderProvider)(plugin) && !input.skipVerification) {
169
+ const providerResult = await plugin.createSender({
170
+ fromEmail: input.email,
171
+ fromName: input.name,
172
+ replyToEmail: input.replyToEmail,
173
+ replyToName: input.replyToName,
174
+ providerFields: input.providerFields,
175
+ });
176
+ if (providerResult.success) {
177
+ sender.providerSenderId = providerResult.providerSenderId;
178
+ sender.providerMetadata = providerResult.metadata;
179
+ // Persist the provider sender ID
180
+ if (this.senderManager) {
181
+ await this.senderManager.updateSender(sender.id, {
182
+ providerSenderId: providerResult.providerSenderId,
183
+ providerMetadata: providerResult.metadata,
184
+ lastModifiedBy: input.createdBy || 'system',
185
+ });
186
+ }
187
+ }
188
+ else {
189
+ this.logger?.warn('Provider creation failed, sender saved locally', {
190
+ senderId: sender.id,
191
+ error: providerResult.error?.message,
192
+ });
193
+ }
194
+ }
195
+ // 8. Send verification email (unless skipped or provider handles it)
196
+ if (!input.skipVerification && !plugin) {
97
197
  await this.sendVerificationEmail(sender);
98
198
  }
99
- // 8. Publish event
199
+ // 9. Publish event
100
200
  if (this.neverhub) {
101
201
  await this.neverhub.publishEvent({
102
202
  type: 'sender-identity.created',
@@ -216,7 +316,7 @@ class SenderIdentityVerification {
216
316
  errors: ['Account locked']
217
317
  };
218
318
  }
219
- // 5. Verify with provider
319
+ // 5. Verify with provider — use plugin system first, fall back to legacy
220
320
  const providerResult = await this.verifyWithProvider(sender);
221
321
  if (!providerResult.success) {
222
322
  sender.status = types_js_1.SenderStatus.FAILED;
@@ -241,6 +341,17 @@ class SenderIdentityVerification {
241
341
  sender.validationErrors = undefined;
242
342
  sender.updatedAt = new Date();
243
343
  await this.saveSenderToDatabase(sender);
344
+ // Update core layer if available
345
+ if (this.senderManager) {
346
+ await this.senderManager.updateSender(sender.id, {
347
+ isVerified: true,
348
+ verificationStatus: 'verified',
349
+ lastVerifiedAt: sender.verifiedAt,
350
+ providerSenderId: sender.providerSenderId,
351
+ providerMetadata: sender.providerMetadata,
352
+ lastModifiedBy: 'system',
353
+ });
354
+ }
244
355
  // 7. Publish event
245
356
  if (this.neverhub) {
246
357
  await this.neverhub.publishEvent({
@@ -274,37 +385,90 @@ class SenderIdentityVerification {
274
385
  }
275
386
  }
276
387
  /**
277
- * Verify sender with email provider
388
+ * Verify sender with email provider.
389
+ *
390
+ * Uses the plugin registry first. If no plugin is registered for the
391
+ * provider, falls back to the legacy provider classes.
278
392
  */
279
393
  async verifyWithProvider(sender) {
394
+ // Try plugin-based verification
395
+ const plugin = this.providerPlugins.get(sender.provider);
396
+ if (plugin) {
397
+ if ((0, email_sender_manager_1.isVerifiableSenderProvider)(plugin) && sender.providerSenderId) {
398
+ const result = await plugin.checkVerificationStatus(sender.providerSenderId);
399
+ if (result.status === 'verified') {
400
+ return {
401
+ success: true,
402
+ data: {
403
+ providerId: result.providerSenderId,
404
+ metadata: { verificationType: result.verificationType },
405
+ },
406
+ };
407
+ }
408
+ return {
409
+ success: false,
410
+ error: result.error?.message || `Verification status: ${result.status}`,
411
+ };
412
+ }
413
+ // Plugin exists but is not verifiable — validate only
414
+ const validation = await plugin.validateSender(sender.email);
415
+ if (validation.isVerified) {
416
+ return {
417
+ success: true,
418
+ data: { metadata: validation.metadata },
419
+ };
420
+ }
421
+ return { success: true, data: {} };
422
+ }
423
+ // Manual verification providers
424
+ if ((0, manual_instructions_js_1.isManualVerificationProvider)(sender.provider)) {
425
+ const instructions = (0, manual_instructions_js_1.getManualInstructions)(sender.provider);
426
+ return {
427
+ success: true,
428
+ data: {
429
+ metadata: instructions ? { manualInstructions: instructions } : {},
430
+ },
431
+ };
432
+ }
433
+ // Legacy fallback — for backward compatibility when no plugins are registered
434
+ return this.legacyVerifyWithProvider(sender);
435
+ }
436
+ /**
437
+ * Legacy provider verification using inline provider classes.
438
+ * Preserved for backward compatibility when no plugins are configured.
439
+ */
440
+ async legacyVerifyWithProvider(sender) {
441
+ // Dynamic imports to avoid breaking if legacy providers are removed later
280
442
  switch (sender.provider) {
281
443
  case types_js_1.EmailProvider.SENDGRID:
282
444
  if (this.config.sendgridApiKey) {
283
- const provider = new SendGridProvider_js_1.SendGridProvider(this.config.sendgridApiKey);
445
+ const { SendGridProvider } = await Promise.resolve().then(() => __importStar(require('./providers/SendGridProvider.js')));
446
+ const provider = new SendGridProvider(this.config.sendgridApiKey);
284
447
  return provider.verifySender(sender);
285
448
  }
286
449
  return { success: false, error: 'SendGrid API key not configured' };
287
450
  case types_js_1.EmailProvider.MAILGUN:
288
451
  if (this.config.mailgunApiKey) {
289
- const provider = new MailgunProvider_js_1.MailgunProvider(this.config.mailgunApiKey);
452
+ const { MailgunProvider } = await Promise.resolve().then(() => __importStar(require('./providers/MailgunProvider.js')));
453
+ const provider = new MailgunProvider(this.config.mailgunApiKey);
290
454
  return provider.verifySender(sender);
291
455
  }
292
- return { success: true, data: {} }; // Mailgun doesn't require verification
456
+ return { success: true, data: {} };
293
457
  case types_js_1.EmailProvider.SES:
294
458
  if (this.config.sesAccessKey && this.config.sesSecretKey && this.config.sesRegion) {
295
- const provider = new SESProvider_js_1.SESProvider(this.config.sesAccessKey, this.config.sesSecretKey, this.config.sesRegion);
459
+ const { SESProvider } = await Promise.resolve().then(() => __importStar(require('./providers/SESProvider.js')));
460
+ const provider = new SESProvider(this.config.sesAccessKey, this.config.sesSecretKey, this.config.sesRegion);
296
461
  return provider.verifySender(sender);
297
462
  }
298
463
  return { success: false, error: 'SES credentials not configured' };
299
464
  case types_js_1.EmailProvider.SMTP: {
300
- const provider = new SMTPProvider_js_1.SMTPProvider();
465
+ const { SMTPProvider } = await Promise.resolve().then(() => __importStar(require('./providers/SMTPProvider.js')));
466
+ const provider = new SMTPProvider();
301
467
  return provider.verifySender(sender);
302
468
  }
303
469
  case types_js_1.EmailProvider.POSTMARK:
304
470
  case types_js_1.EmailProvider.MANDRILL:
305
471
  case types_js_1.EmailProvider.SMTP2GO:
306
- // These providers handle sender verification through their own dashboards.
307
- // Return success to allow the sender to be registered locally.
308
472
  return { success: true, data: {} };
309
473
  default:
310
474
  return {
@@ -317,6 +481,13 @@ class SenderIdentityVerification {
317
481
  * Get sender by ID
318
482
  */
319
483
  async getSender(senderId) {
484
+ if (this.senderManager) {
485
+ const config = await this.senderManager.getSender(senderId);
486
+ if (!config) {
487
+ return { success: false, error: `Sender not found: ${senderId}` };
488
+ }
489
+ return { success: true, data: this.coreConfigToIdentity(config) };
490
+ }
320
491
  const sender = this.senders.get(senderId);
321
492
  if (!sender) {
322
493
  return {
@@ -334,6 +505,21 @@ class SenderIdentityVerification {
334
505
  */
335
506
  async listSenders(options = {}) {
336
507
  try {
508
+ if (this.senderManager) {
509
+ const result = await this.senderManager.listSenders({
510
+ provider: options.provider,
511
+ isActive: options.isActive,
512
+ domain: options.domain,
513
+ offset: options.offset,
514
+ limit: options.limit,
515
+ });
516
+ const identities = result.items.map((c) => this.coreConfigToIdentity(c));
517
+ // Apply status filter locally (core layer doesn't have SenderStatus enum)
518
+ const filtered = options.status
519
+ ? identities.filter((s) => s.status === options.status)
520
+ : identities;
521
+ return { success: true, data: filtered };
522
+ }
337
523
  let senders = Array.from(this.senders.values());
338
524
  // Filter by provider
339
525
  if (options.provider) {
@@ -374,13 +560,12 @@ class SenderIdentityVerification {
374
560
  */
375
561
  async updateSender(senderId, input) {
376
562
  try {
377
- const sender = this.senders.get(senderId);
378
- if (!sender) {
379
- return {
380
- success: false,
381
- error: `Sender not found: ${senderId}`
382
- };
563
+ // Fetch from appropriate store
564
+ const getResult = await this.getSender(senderId);
565
+ if (!getResult.success || !getResult.data) {
566
+ return { success: false, error: `Sender not found: ${senderId}` };
383
567
  }
568
+ const sender = getResult.data;
384
569
  // Update fields
385
570
  if (input.name !== undefined)
386
571
  sender.name = input.name;
@@ -404,7 +589,32 @@ class SenderIdentityVerification {
404
589
  sender.verifiedAt = undefined;
405
590
  sender.lastValidated = undefined;
406
591
  }
407
- await this.saveSenderToDatabase(sender);
592
+ // Persist
593
+ if (this.senderManager) {
594
+ await this.senderManager.updateSender(senderId, {
595
+ name: input.name,
596
+ fromName: input.name,
597
+ replyToEmail: input.replyToEmail,
598
+ replyToName: input.replyToName,
599
+ isDefault: input.isDefault,
600
+ isActive: input.isActive,
601
+ lastModifiedBy: input.lastModifiedBy || 'system',
602
+ });
603
+ }
604
+ else {
605
+ await this.saveSenderToDatabase(sender);
606
+ }
607
+ // Delegate to provider plugin if available
608
+ const plugin = this.providerPlugins.get(sender.provider);
609
+ if (plugin && (0, email_sender_manager_1.isManagedSenderProvider)(plugin) && sender.providerSenderId) {
610
+ await plugin.updateSender({
611
+ providerSenderId: sender.providerSenderId,
612
+ fromName: input.name,
613
+ replyToEmail: input.replyToEmail,
614
+ replyToName: input.replyToName,
615
+ providerFields: input.providerFields,
616
+ });
617
+ }
408
618
  // Publish event
409
619
  if (this.neverhub) {
410
620
  await this.neverhub.publishEvent({
@@ -431,19 +641,27 @@ class SenderIdentityVerification {
431
641
  */
432
642
  async deleteSender(senderId) {
433
643
  try {
434
- const sender = this.senders.get(senderId);
435
- if (!sender) {
436
- return {
437
- success: false,
438
- error: `Sender not found: ${senderId}`
439
- };
644
+ const getResult = await this.getSender(senderId);
645
+ if (!getResult.success || !getResult.data) {
646
+ return { success: false, error: `Sender not found: ${senderId}` };
647
+ }
648
+ const sender = getResult.data;
649
+ // Delete from provider if plugin supports it
650
+ const plugin = this.providerPlugins.get(sender.provider);
651
+ if (plugin && (0, email_sender_manager_1.isManagedSenderProvider)(plugin) && sender.providerSenderId) {
652
+ await plugin.deleteSender(sender.providerSenderId);
653
+ }
654
+ // Persist deletion
655
+ if (this.senderManager) {
656
+ await this.senderManager.deleteSender(senderId);
657
+ }
658
+ else {
659
+ sender.deletedAt = new Date();
660
+ sender.isActive = false;
661
+ sender.updatedAt = new Date();
662
+ await this.saveSenderToDatabase(sender);
663
+ this.senders.delete(senderId);
440
664
  }
441
- // Soft delete
442
- sender.deletedAt = new Date();
443
- sender.isActive = false;
444
- sender.updatedAt = new Date();
445
- await this.saveSenderToDatabase(sender);
446
- this.senders.delete(senderId);
447
665
  // Publish event
448
666
  if (this.neverhub) {
449
667
  await this.neverhub.publishEvent({
@@ -467,13 +685,11 @@ class SenderIdentityVerification {
467
685
  */
468
686
  async checkCompliance(senderId) {
469
687
  try {
470
- const sender = this.senders.get(senderId);
471
- if (!sender) {
472
- return {
473
- success: false,
474
- error: `Sender not found: ${senderId}`
475
- };
688
+ const getResult = await this.getSender(senderId);
689
+ if (!getResult.success || !getResult.data) {
690
+ return { success: false, error: `Sender not found: ${senderId}` };
476
691
  }
692
+ const sender = getResult.data;
477
693
  const errors = [];
478
694
  const warnings = [];
479
695
  // 1. Check email format
@@ -481,7 +697,7 @@ class SenderIdentityVerification {
481
697
  if (!emailFormat) {
482
698
  errors.push('Invalid email format');
483
699
  }
484
- // 2. Check domain verification (stub)
700
+ // 2. Check domain verification
485
701
  let domainVerified = true;
486
702
  let spfValid = true;
487
703
  let dkimValid = true;
@@ -500,12 +716,17 @@ class SenderIdentityVerification {
500
716
  errors.push('DKIM record not configured or invalid');
501
717
  }
502
718
  }
503
- // 3. Provider-specific checks
504
- if (sender.provider === types_js_1.EmailProvider.SENDGRID) {
505
- if (!sender.providerSenderId) {
506
- warnings.push('SendGrid sender ID not set - may need re-verification');
719
+ // 3. Provider-specific checks via plugin
720
+ const plugin = this.providerPlugins.get(sender.provider);
721
+ if (plugin && (0, email_sender_manager_1.isVerifiableSenderProvider)(plugin) && sender.providerSenderId) {
722
+ const verificationResult = await plugin.checkVerificationStatus(sender.providerSenderId);
723
+ if (verificationResult.status !== 'verified') {
724
+ warnings.push(`Provider verification status: ${verificationResult.status}`);
507
725
  }
508
726
  }
727
+ else if (sender.provider === types_js_1.EmailProvider.SENDGRID && !sender.providerSenderId) {
728
+ warnings.push('SendGrid sender ID not set - may need re-verification');
729
+ }
509
730
  const isCompliant = errors.length === 0;
510
731
  const result = {
511
732
  isCompliant,
@@ -539,6 +760,18 @@ class SenderIdentityVerification {
539
760
  * Get default sender for provider
540
761
  */
541
762
  async getDefaultSender(provider) {
763
+ if (this.senderManager) {
764
+ const result = await this.senderManager.listSenders({
765
+ provider,
766
+ isActive: true,
767
+ isVerified: true,
768
+ });
769
+ const defaultSender = result.items.find((s) => s.isDefault);
770
+ if (!defaultSender) {
771
+ return { success: false, error: `No default sender found for provider ${provider}` };
772
+ }
773
+ return { success: true, data: this.coreConfigToIdentity(defaultSender) };
774
+ }
542
775
  const senders = Array.from(this.senders.values());
543
776
  const defaultSender = senders.find(s => s.provider === provider &&
544
777
  s.isDefault &&
@@ -560,13 +793,11 @@ class SenderIdentityVerification {
560
793
  */
561
794
  async resendVerification(senderId) {
562
795
  try {
563
- const sender = this.senders.get(senderId);
564
- if (!sender) {
565
- return {
566
- success: false,
567
- error: `Sender not found: ${senderId}`
568
- };
796
+ const getResult = await this.getSender(senderId);
797
+ if (!getResult.success || !getResult.data) {
798
+ return { success: false, error: `Sender not found: ${senderId}` };
569
799
  }
800
+ const sender = getResult.data;
570
801
  if (sender.status === types_js_1.SenderStatus.VERIFIED) {
571
802
  return {
572
803
  success: false,
@@ -589,6 +820,14 @@ class SenderIdentityVerification {
589
820
  };
590
821
  }
591
822
  }
823
+ // If provider supports verification, use it
824
+ const plugin = this.providerPlugins.get(sender.provider);
825
+ if (plugin && (0, email_sender_manager_1.isVerifiableSenderProvider)(plugin) && sender.providerSenderId) {
826
+ const result = await plugin.initiateVerification(sender.providerSenderId);
827
+ if (result.success) {
828
+ return { success: true };
829
+ }
830
+ }
592
831
  return this.sendVerificationEmail(sender);
593
832
  }
594
833
  catch (error) {
@@ -599,6 +838,35 @@ class SenderIdentityVerification {
599
838
  };
600
839
  }
601
840
  }
841
+ /**
842
+ * Check verification status for a sender using the provider plugin.
843
+ */
844
+ async checkVerificationStatus(senderId) {
845
+ const getResult = await this.getSender(senderId);
846
+ if (!getResult.success || !getResult.data) {
847
+ return { success: false, error: `Sender not found: ${senderId}` };
848
+ }
849
+ const sender = getResult.data;
850
+ const plugin = this.providerPlugins.get(sender.provider);
851
+ if (!plugin || !(0, email_sender_manager_1.isVerifiableSenderProvider)(plugin) || !sender.providerSenderId) {
852
+ return {
853
+ success: true,
854
+ data: {
855
+ status: sender.status,
856
+ verificationType: 'manual',
857
+ },
858
+ };
859
+ }
860
+ const result = await plugin.checkVerificationStatus(sender.providerSenderId);
861
+ return {
862
+ success: result.success,
863
+ data: {
864
+ status: result.status,
865
+ verificationType: result.verificationType,
866
+ dnsRecords: result.dnsRecords,
867
+ },
868
+ };
869
+ }
602
870
  // ============================================
603
871
  // Private Helper Methods
604
872
  // ============================================
@@ -622,12 +890,31 @@ class SenderIdentityVerification {
622
890
  return emailRegex.test(email);
623
891
  }
624
892
  async findSenderByEmail(email) {
893
+ if (this.senderManager) {
894
+ const result = await this.senderManager.listSenders({ limit: 1000 });
895
+ return result.items
896
+ .map((c) => this.coreConfigToIdentity(c))
897
+ .find((s) => s.email === email && !s.deletedAt);
898
+ }
625
899
  return Array.from(this.senders.values()).find(s => s.email === email && !s.deletedAt);
626
900
  }
627
901
  async findSenderByToken(token) {
902
+ // Token lookup is always in-memory (tokens live in the service layer)
628
903
  return Array.from(this.senders.values()).find(s => s.verificationToken === token);
629
904
  }
630
905
  async unsetOtherDefaults(provider) {
906
+ if (this.senderManager) {
907
+ const result = await this.senderManager.listSenders({ provider });
908
+ for (const sender of result.items) {
909
+ if (sender.isDefault) {
910
+ await this.senderManager.updateSender(sender.id, {
911
+ isDefault: false,
912
+ lastModifiedBy: 'system',
913
+ });
914
+ }
915
+ }
916
+ return;
917
+ }
631
918
  const senders = Array.from(this.senders.values())
632
919
  .filter(s => s.provider === provider && s.isDefault);
633
920
  for (const sender of senders) {
@@ -637,13 +924,57 @@ class SenderIdentityVerification {
637
924
  }
638
925
  }
639
926
  async loadSendersFromDatabase() {
640
- // TODO: Implement database loading
927
+ // TODO: Implement database loading for legacy path
641
928
  // For now, no-op
642
929
  }
643
930
  async saveSenderToDatabase(sender) {
644
- // TODO: Implement database persistence
931
+ // TODO: Implement database persistence for legacy path
645
932
  // For now, just update in-memory map
646
933
  this.senders.set(sender.id, sender);
647
934
  }
935
+ // ============================================
936
+ // Mapping helpers between service and core types
937
+ // ============================================
938
+ toCoreSenderRequest(input, sender) {
939
+ return {
940
+ name: input.name,
941
+ fromEmail: input.email,
942
+ fromName: input.name,
943
+ replyToEmail: input.replyToEmail,
944
+ replyToName: input.replyToName,
945
+ provider: input.provider,
946
+ allowedDomains: [sender.domain],
947
+ createdBy: input.createdBy || 'system',
948
+ };
949
+ }
950
+ coreConfigToIdentity(config) {
951
+ return {
952
+ id: config.id,
953
+ email: config.fromEmail,
954
+ name: config.fromName,
955
+ replyToEmail: config.replyToEmail,
956
+ replyToName: config.replyToName,
957
+ domain: config.domain,
958
+ provider: config.provider,
959
+ status: this.mapVerificationStatus(config.verificationStatus),
960
+ isDefault: config.isDefault,
961
+ isActive: config.isActive,
962
+ verificationAttempts: 0,
963
+ verifiedAt: config.lastVerifiedAt,
964
+ providerSenderId: config.providerSenderId,
965
+ providerMetadata: config.providerMetadata,
966
+ createdAt: config.createdAt,
967
+ updatedAt: config.updatedAt,
968
+ };
969
+ }
970
+ mapVerificationStatus(status) {
971
+ switch (status) {
972
+ case 'verified': return types_js_1.SenderStatus.VERIFIED;
973
+ case 'failed': return types_js_1.SenderStatus.FAILED;
974
+ case 'expired': return types_js_1.SenderStatus.EXPIRED;
975
+ case 'pending':
976
+ default: return types_js_1.SenderStatus.PENDING;
977
+ }
978
+ }
648
979
  }
649
980
  exports.SenderIdentityVerification = SenderIdentityVerification;
package/dist/config.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { IEmailDomainVerification } from './types.js';
2
+ import type { EmailSenderManager, SenderProviderPlugin } from '@bernierllc/email-sender-manager';
2
3
  /**
3
4
  * Configuration for sender identity verification service
4
5
  */
@@ -10,6 +11,8 @@ export interface SenderIdentityConfig {
10
11
  domainVerificationConfig: {
11
12
  instance?: IEmailDomainVerification;
12
13
  };
14
+ senderManager?: EmailSenderManager;
15
+ providerPlugins?: SenderProviderPlugin[];
13
16
  retryConfig?: {
14
17
  maxRetries?: number;
15
18
  initialDelayMs?: number;
package/dist/config.js CHANGED
@@ -24,6 +24,8 @@ function loadConfigFromEnv(overrides = {}) {
24
24
  databaseUrl: process.env.DATABASE_URL || overrides.databaseUrl,
25
25
  emailSenderConfig: overrides.emailSenderConfig || {},
26
26
  domainVerificationConfig: overrides.domainVerificationConfig || {},
27
+ senderManager: overrides.senderManager,
28
+ providerPlugins: overrides.providerPlugins,
27
29
  retryConfig: overrides.retryConfig
28
30
  };
29
31
  // Log configuration (without sensitive data)