@bernierllc/email-domain-verification 1.0.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 (36) hide show
  1. package/.eslintrc.cjs +29 -0
  2. package/README.md +397 -0
  3. package/__mocks__/@bernierllc/crypto-utils.ts +15 -0
  4. package/__mocks__/@bernierllc/logger.ts +42 -0
  5. package/__mocks__/@bernierllc/neverhub-adapter.ts +17 -0
  6. package/__tests__/EmailDomainVerificationService.test.ts +582 -0
  7. package/coverage/clover.xml +263 -0
  8. package/coverage/coverage-final.json +3 -0
  9. package/coverage/lcov-report/EmailDomainVerificationService.ts.html +3061 -0
  10. package/coverage/lcov-report/base.css +224 -0
  11. package/coverage/lcov-report/block-navigation.js +87 -0
  12. package/coverage/lcov-report/errors.ts.html +322 -0
  13. package/coverage/lcov-report/favicon.png +0 -0
  14. package/coverage/lcov-report/index.html +131 -0
  15. package/coverage/lcov-report/prettify.css +1 -0
  16. package/coverage/lcov-report/prettify.js +2 -0
  17. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  18. package/coverage/lcov-report/sorter.js +210 -0
  19. package/coverage/lcov.info +485 -0
  20. package/dist/EmailDomainVerificationService.d.ts +179 -0
  21. package/dist/EmailDomainVerificationService.js +822 -0
  22. package/dist/errors.d.ts +39 -0
  23. package/dist/errors.js +66 -0
  24. package/dist/index.d.ts +3 -0
  25. package/dist/index.js +28 -0
  26. package/dist/types.d.ts +209 -0
  27. package/dist/types.js +9 -0
  28. package/jest.config.cjs +44 -0
  29. package/jest.setup.js +19 -0
  30. package/package.json +72 -0
  31. package/src/EmailDomainVerificationService.ts +992 -0
  32. package/src/dns2.d.ts +29 -0
  33. package/src/errors.ts +79 -0
  34. package/src/index.ts +11 -0
  35. package/src/types.ts +281 -0
  36. package/tsconfig.json +30 -0
package/.eslintrc.cjs ADDED
@@ -0,0 +1,29 @@
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
+
9
+ module.exports = {
10
+ parser: '@typescript-eslint/parser',
11
+ extends: [
12
+ 'eslint:recommended',
13
+ 'plugin:@typescript-eslint/recommended',
14
+ ],
15
+ parserOptions: {
16
+ ecmaVersion: 2020,
17
+ sourceType: 'module',
18
+ },
19
+ rules: {
20
+ '@typescript-eslint/no-explicit-any': 'warn',
21
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
22
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
23
+ },
24
+ env: {
25
+ node: true,
26
+ es6: true,
27
+ jest: true,
28
+ },
29
+ };
package/README.md ADDED
@@ -0,0 +1,397 @@
1
+ # @bernierllc/email-domain-verification
2
+
3
+ Programmatic domain setup and DNS management for production email sending with multi-provider support.
4
+
5
+ ## Overview
6
+
7
+ The Email Domain Verification package provides comprehensive programmatic domain setup and DNS record management for production email sending. It supports multiple email service providers (SendGrid, Mailgun, AWS SES), manages SPF/DKIM/DMARC records, monitors domain health, and provides NeverAdmin UI integration for domain management workflows.
8
+
9
+ ## Features
10
+
11
+ - **Multi-Provider Support**: SendGrid, Mailgun, AWS SES
12
+ - **DNS Record Generation**: SPF, DKIM, DMARC, MX records
13
+ - **DKIM Key Management**: Automatic keypair generation and DNS configuration
14
+ - **DNS Propagation Monitoring**: Track DNS propagation across multiple nameservers
15
+ - **Health Monitoring**: Automated health checks with cron scheduling
16
+ - **Verification History**: Complete audit trail of domain verification events
17
+ - **NeverHub Integration**: Event publishing and service discovery
18
+ - **NeverAdmin UI**: Admin panels for domain management
19
+
20
+ ## Installation
21
+
22
+ ```bash
23
+ npm install @bernierllc/email-domain-verification
24
+ ```
25
+
26
+ ## Dependencies
27
+
28
+ This package requires the following @bernierllc packages:
29
+
30
+ - `@bernierllc/email-sender-manager` - Email provider integration patterns
31
+ - `@bernierllc/crypto-utils` - DKIM keypair generation
32
+ - `@bernierllc/logger` - Structured logging
33
+ - `@bernierllc/neverhub-adapter` - Service discovery and event bus
34
+
35
+ ## Usage
36
+
37
+ ### Basic Setup
38
+
39
+ ```typescript
40
+ import { EmailDomainVerificationService } from '@bernierllc/email-domain-verification';
41
+ import { Logger } from '@bernierllc/logger';
42
+
43
+ // Initialize service
44
+ const logger = new Logger({ context: 'email-verification' });
45
+ const service = new EmailDomainVerificationService({ logger });
46
+ await service.initialize();
47
+
48
+ // Setup domain verification
49
+ const result = await service.setupDomain({
50
+ domain: 'example.com',
51
+ provider: 'sendgrid',
52
+ providerCredentials: {
53
+ apiKey: process.env.SENDGRID_API_KEY!
54
+ },
55
+ dkim: {
56
+ enabled: true,
57
+ selector: 'default',
58
+ keySize: 2048
59
+ },
60
+ spf: {
61
+ enabled: true,
62
+ strict: true
63
+ },
64
+ dmarc: {
65
+ enabled: true,
66
+ policy: 'quarantine',
67
+ reportEmail: 'dmarc@example.com'
68
+ }
69
+ });
70
+
71
+ if (result.success) {
72
+ console.log('DNS records to configure:');
73
+ result.dnsRecords.forEach(record => {
74
+ console.log(`${record.type}: ${record.host} -> ${record.value}`);
75
+ });
76
+
77
+ if (result.dkimConfig) {
78
+ console.log('DKIM Private Key (store securely):', result.dkimConfig.privateKey);
79
+ }
80
+ }
81
+ ```
82
+
83
+ ### Check DNS Propagation
84
+
85
+ ```typescript
86
+ // Check if DNS records have propagated
87
+ const propagationResults = await service.checkPropagation(
88
+ 'example.com',
89
+ result.dnsRecords
90
+ );
91
+
92
+ propagationResults.forEach(result => {
93
+ console.log(`${result.record.type}: ${result.propagationPercentage}% propagated`);
94
+ if (result.propagated && result.valuesMatch) {
95
+ console.log('✓ Record fully propagated and values match');
96
+ }
97
+ });
98
+ ```
99
+
100
+ ### Verify Domain with Provider
101
+
102
+ ```typescript
103
+ // Verify domain with email provider
104
+ const verificationResult = await service.verifyDomain(
105
+ 'example.com',
106
+ 'sendgrid',
107
+ { apiKey: process.env.SENDGRID_API_KEY! }
108
+ );
109
+
110
+ if (verificationResult.success) {
111
+ console.log('Domain verified successfully!');
112
+ // Health monitoring starts automatically
113
+ }
114
+ ```
115
+
116
+ ### Monitor Domain Health
117
+
118
+ ```typescript
119
+ // Get current domain health
120
+ const health = await service.getDomainHealth('example.com');
121
+
122
+ console.log(`Domain: ${health.domain}`);
123
+ console.log(`Status: ${health.verificationStatus}`);
124
+ console.log(`Healthy: ${health.healthy}`);
125
+
126
+ if (health.warnings && health.warnings.length > 0) {
127
+ console.log('Warnings:', health.warnings);
128
+ }
129
+ ```
130
+
131
+ ### Verification History
132
+
133
+ ```typescript
134
+ // Get verification history
135
+ const history = service.getVerificationHistory('example.com', 10);
136
+
137
+ history.forEach(entry => {
138
+ console.log(`[${entry.timestamp}] ${entry.eventType}: ${entry.details}`);
139
+ });
140
+ ```
141
+
142
+ ## Provider-Specific Examples
143
+
144
+ ### SendGrid
145
+
146
+ ```typescript
147
+ const result = await service.setupDomain({
148
+ domain: 'example.com',
149
+ provider: 'sendgrid',
150
+ providerCredentials: {
151
+ apiKey: process.env.SENDGRID_API_KEY!
152
+ },
153
+ dkim: { enabled: true, keySize: 2048 },
154
+ spf: { enabled: true, strict: true },
155
+ dmarc: {
156
+ enabled: true,
157
+ policy: 'quarantine',
158
+ reportEmail: 'dmarc@example.com'
159
+ }
160
+ });
161
+ ```
162
+
163
+ ### Mailgun
164
+
165
+ ```typescript
166
+ const result = await service.setupDomain({
167
+ domain: 'example.com',
168
+ provider: 'mailgun',
169
+ providerCredentials: {
170
+ apiKey: process.env.MAILGUN_API_KEY!,
171
+ smtpPassword: process.env.MAILGUN_SMTP_PASSWORD!
172
+ },
173
+ dkim: { enabled: true, keySize: 2048 },
174
+ spf: { enabled: true },
175
+ dmarc: { enabled: true, policy: 'none' }
176
+ });
177
+ ```
178
+
179
+ ### AWS SES
180
+
181
+ ```typescript
182
+ const result = await service.setupDomain({
183
+ domain: 'example.com',
184
+ provider: 'ses',
185
+ providerCredentials: {
186
+ region: 'us-east-1',
187
+ accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
188
+ secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!
189
+ },
190
+ dkim: { enabled: true, keySize: 2048 },
191
+ spf: { enabled: true },
192
+ dmarc: { enabled: true, policy: 'quarantine' }
193
+ });
194
+ ```
195
+
196
+ ## Configuration
197
+
198
+ ### Environment Variables
199
+
200
+ ```bash
201
+ # Email Domain Verification Configuration
202
+ EMAIL_DOMAIN_VERIFICATION_HEALTH_CHECK_INTERVAL="0 */6 * * *"
203
+ EMAIL_DOMAIN_VERIFICATION_DNS_TIMEOUT=30000
204
+ EMAIL_DOMAIN_VERIFICATION_PROPAGATION_THRESHOLD=80
205
+
206
+ # Provider API Keys (optional - can be passed per-domain)
207
+ SENDGRID_API_KEY=""
208
+ MAILGUN_API_KEY=""
209
+ AWS_SES_REGION=""
210
+ AWS_SES_ACCESS_KEY_ID=""
211
+ AWS_SES_SECRET_ACCESS_KEY=""
212
+
213
+ # NeverHub Configuration (optional)
214
+ NEVERHUB_API_URL=""
215
+ NEVERHUB_API_KEY=""
216
+ ```
217
+
218
+ ### Constructor Options
219
+
220
+ ```typescript
221
+ const service = new EmailDomainVerificationService({
222
+ logger: customLogger,
223
+ neverhub: customNeverHub,
224
+ healthCheckInterval: '0 */6 * * *' // Cron expression
225
+ });
226
+ ```
227
+
228
+ ## API Reference
229
+
230
+ ### EmailDomainVerificationService
231
+
232
+ #### `setupDomain(config: DomainConfig): Promise<DomainVerificationResult>`
233
+
234
+ Setup domain verification for an email provider. Generates DNS records, DKIM keypairs, and registers with the provider.
235
+
236
+ **Parameters:**
237
+ - `config.domain` - Domain name (e.g., 'example.com')
238
+ - `config.provider` - Email provider ('sendgrid', 'mailgun', 'ses')
239
+ - `config.providerCredentials` - Provider API credentials
240
+ - `config.dkim` - DKIM configuration
241
+ - `config.spf` - SPF configuration
242
+ - `config.dmarc` - DMARC configuration
243
+
244
+ **Returns:** Domain verification result with DNS records to configure
245
+
246
+ #### `checkPropagation(domain: string, records: DNSRecord[]): Promise<DNSPropagationResult[]>`
247
+
248
+ Check DNS propagation status for domain records across multiple nameservers.
249
+
250
+ **Parameters:**
251
+ - `domain` - Domain name
252
+ - `records` - DNS records to check
253
+
254
+ **Returns:** Propagation results for each record
255
+
256
+ #### `verifyDomain(domain: string, provider: EmailProvider, credentials: object): Promise<DomainVerificationResult>`
257
+
258
+ Verify domain with email provider. Starts health monitoring if verification succeeds.
259
+
260
+ **Parameters:**
261
+ - `domain` - Domain name
262
+ - `provider` - Email provider
263
+ - `credentials` - Provider API credentials
264
+
265
+ **Returns:** Verification result
266
+
267
+ #### `getDomainHealth(domain: string): Promise<DomainHealthStatus>`
268
+
269
+ Get current domain health status.
270
+
271
+ **Parameters:**
272
+ - `domain` - Domain name
273
+
274
+ **Returns:** Health status with record details
275
+
276
+ #### `getVerificationHistory(domain: string, limit?: number): VerificationHistoryEntry[]`
277
+
278
+ Get verification history for domain.
279
+
280
+ **Parameters:**
281
+ - `domain` - Domain name
282
+ - `limit` - Maximum number of entries (default: 50)
283
+
284
+ **Returns:** History entries
285
+
286
+ #### `stopHealthMonitoring(domain: string): void`
287
+
288
+ Stop automated health monitoring for domain.
289
+
290
+ **Parameters:**
291
+ - `domain` - Domain name
292
+
293
+ #### `shutdown(): Promise<void>`
294
+
295
+ Cleanup resources and stop all health monitoring jobs.
296
+
297
+ ## Integration Status
298
+
299
+ - **Logger**: integrated - All verification events, DNS propagation checks, and health monitoring logged
300
+ - **Docs-Suite**: ready - TypeDoc format, complete API reference
301
+ - **NeverHub**: required - Event publishing for domain verification lifecycle
302
+ - **NeverAdmin**: required - Admin UI components for domain management
303
+
304
+ ## NeverHub Events
305
+
306
+ **Events Published:**
307
+ - `domain.verification.initiated` - Domain verification setup started
308
+ - `domain.propagation.checked` - DNS propagation check completed
309
+ - `domain.verification.completed` - Verification with provider completed
310
+ - `domain.health.unhealthy` - Health check detected issues
311
+
312
+ **Events Subscribed:**
313
+ - `email.domain.required` - Email service requests domain verification
314
+ - `monitoring.health.check` - System-wide health check triggered
315
+
316
+ ## TypeScript Support
317
+
318
+ This package is written in TypeScript and includes complete type definitions.
319
+
320
+ ```typescript
321
+ import {
322
+ EmailDomainVerificationService,
323
+ DomainConfig,
324
+ DomainVerificationResult,
325
+ DNSRecord,
326
+ DKIMConfig,
327
+ VerificationStatus
328
+ } from '@bernierllc/email-domain-verification';
329
+ ```
330
+
331
+ ## Error Handling
332
+
333
+ The package provides structured error types:
334
+
335
+ ```typescript
336
+ import {
337
+ DomainVerificationError,
338
+ DNSPropagationError,
339
+ ProviderAPIError,
340
+ InvalidDomainError
341
+ } from '@bernierllc/email-domain-verification';
342
+
343
+ try {
344
+ await service.setupDomain(config);
345
+ } catch (error) {
346
+ if (error instanceof InvalidDomainError) {
347
+ console.error('Invalid domain:', error.message);
348
+ } else if (error instanceof DNSPropagationError) {
349
+ console.error('DNS propagation failed:', error.message);
350
+ // Retryable error
351
+ }
352
+ }
353
+ ```
354
+
355
+ ## Testing
356
+
357
+ ```bash
358
+ # Run tests
359
+ npm test
360
+
361
+ # Run tests once
362
+ npm run test:run
363
+
364
+ # Run with coverage
365
+ npm run test:coverage
366
+ ```
367
+
368
+ ## Building
369
+
370
+ ```bash
371
+ # Build TypeScript to JavaScript
372
+ npm run build
373
+
374
+ # Clean dist directory
375
+ npm run clean
376
+ ```
377
+
378
+ ## Linting
379
+
380
+ ```bash
381
+ # Lint source code
382
+ npm run lint
383
+ ```
384
+
385
+ ## License
386
+
387
+ Copyright (c) 2025 Bernier LLC. All rights reserved.
388
+
389
+ This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
390
+
391
+ ## Related Packages
392
+
393
+ - [@bernierllc/email-sender-manager](../email-sender-manager) - Email sending orchestration
394
+ - [@bernierllc/crypto-utils](../../core/crypto-utils) - Cryptographic utilities
395
+ - [@bernierllc/logger](../../core/logger) - Structured logging
396
+ - [@bernierllc/neverhub-adapter](../../core/neverhub-adapter) - Service discovery
397
+ - [@bernierllc/email-suite](../../suite/email-suite) - Complete email management system
@@ -0,0 +1,15 @@
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
+
9
+ export async function generateUUIDKey(): Promise<{ key: string }> {
10
+ return { key: `test-uuid-${Date.now()}-${Math.random().toString(36).substr(2, 9)}` };
11
+ }
12
+
13
+ export function generateSecureToken(length = 32): string {
14
+ return Array(length).fill(0).map(() => Math.random().toString(36).charAt(2)).join('');
15
+ }
@@ -0,0 +1,42 @@
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
+
9
+ export enum LogLevel {
10
+ DEBUG = 0,
11
+ INFO = 1,
12
+ WARN = 2,
13
+ ERROR = 3,
14
+ FATAL = 4,
15
+ SILENT = 5
16
+ }
17
+
18
+ export interface LoggerOptions {
19
+ level?: LogLevel;
20
+ transports?: unknown[];
21
+ context?: Record<string, unknown>;
22
+ enableCorrelation?: boolean;
23
+ sanitize?: boolean;
24
+ sanitizeFields?: string[];
25
+ onError?: (error: Error, transport: string) => void;
26
+ }
27
+
28
+ export class Logger {
29
+ constructor(_options: LoggerOptions) {}
30
+
31
+ debug(_message: string, _metadata?: Record<string, unknown>): void {}
32
+ info(_message: string, _metadata?: Record<string, unknown>): void {}
33
+ warn(_message: string, _metadata?: Record<string, unknown>): void {}
34
+ error(_message: string, _metadata?: Record<string, unknown>): void {}
35
+ fatal(_message: string, _metadata?: Record<string, unknown>): void {}
36
+ }
37
+
38
+ export class ConsoleTransport {
39
+ constructor(_options: { level?: LogLevel } = {}) {}
40
+ }
41
+
42
+ export const createLogger = (options: LoggerOptions = { level: LogLevel.SILENT }) => new Logger(options);
@@ -0,0 +1,17 @@
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
+
9
+ export class NeverHubAdapter {
10
+ static async detect(): Promise<boolean> {
11
+ return false;
12
+ }
13
+
14
+ async register(_config: Record<string, unknown>): Promise<void> {}
15
+ async publishEvent(_event: Record<string, unknown>): Promise<void> {}
16
+ async subscribe(_eventType: string, _handler: (event: unknown) => void): Promise<void> {}
17
+ }