@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/src/dns2.d.ts 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
+ declare module 'dns2' {
10
+ export interface ResolverOptions {
11
+ nameServers?: string[];
12
+ timeout?: number;
13
+ }
14
+
15
+ export interface DNSAnswer {
16
+ type: string;
17
+ data: string;
18
+ ns?: string;
19
+ }
20
+
21
+ export interface DNSResponse {
22
+ answers: DNSAnswer[];
23
+ }
24
+
25
+ export class Resolver {
26
+ constructor(options?: ResolverOptions);
27
+ resolve(hostname: string, recordType: string): Promise<DNSResponse>;
28
+ }
29
+ }
package/src/errors.ts ADDED
@@ -0,0 +1,79 @@
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
+ /**
10
+ * Domain verification error types
11
+ */
12
+ export class DomainVerificationError extends Error {
13
+ constructor(
14
+ message: string,
15
+ public code: string,
16
+ public retryable = false,
17
+ public details?: unknown
18
+ ) {
19
+ super(message);
20
+ this.name = 'DomainVerificationError';
21
+ }
22
+ }
23
+
24
+ export class DNSPropagationError extends DomainVerificationError {
25
+ constructor(message: string, details?: unknown) {
26
+ super(message, 'DNS_PROPAGATION_ERROR', true, details);
27
+ this.name = 'DNSPropagationError';
28
+ }
29
+ }
30
+
31
+ export class ProviderAPIError extends DomainVerificationError {
32
+ constructor(message: string, details?: unknown) {
33
+ super(message, 'PROVIDER_API_ERROR', true, details);
34
+ this.name = 'ProviderAPIError';
35
+ }
36
+ }
37
+
38
+ export class InvalidDomainError extends DomainVerificationError {
39
+ constructor(message: string, details?: unknown) {
40
+ super(message, 'INVALID_DOMAIN', false, details);
41
+ this.name = 'InvalidDomainError';
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Structured result pattern for all service operations
47
+ */
48
+ export interface ServiceResult<T = unknown> {
49
+ success: boolean;
50
+ data?: T;
51
+ error?: {
52
+ code: string;
53
+ message: string;
54
+ retryable: boolean;
55
+ details?: unknown;
56
+ };
57
+ }
58
+
59
+ /**
60
+ * Helper to create success result
61
+ */
62
+ export function successResult<T>(data: T): ServiceResult<T> {
63
+ return { success: true, data };
64
+ }
65
+
66
+ /**
67
+ * Helper to create error result
68
+ */
69
+ export function errorResult(error: DomainVerificationError): ServiceResult {
70
+ return {
71
+ success: false,
72
+ error: {
73
+ code: error.code,
74
+ message: error.message,
75
+ retryable: error.retryable,
76
+ details: error.details
77
+ }
78
+ };
79
+ }
package/src/index.ts ADDED
@@ -0,0 +1,11 @@
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 { EmailDomainVerificationService } from './EmailDomainVerificationService';
10
+ export * from './types';
11
+ export * from './errors';
package/src/types.ts ADDED
@@ -0,0 +1,281 @@
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
+ /**
10
+ * Email provider types supported for domain verification
11
+ */
12
+ export type EmailProvider = 'sendgrid' | 'mailgun' | 'ses' | 'custom';
13
+
14
+ /**
15
+ * DNS record types for email authentication
16
+ */
17
+ export type DNSRecordType = 'SPF' | 'DKIM' | 'DMARC' | 'MX' | 'TXT' | 'CNAME';
18
+
19
+ /**
20
+ * Domain verification status
21
+ */
22
+ export type VerificationStatus =
23
+ | 'pending' // DNS records generated, awaiting propagation
24
+ | 'propagating' // DNS records detected, propagating across nameservers
25
+ | 'verified' // Domain fully verified by provider
26
+ | 'failed' // Verification failed
27
+ | 'expired' // Verification expired, needs re-verification
28
+ | 'suspended'; // Domain suspended by provider
29
+
30
+ /**
31
+ * DNS record configuration for email authentication
32
+ */
33
+ export interface DNSRecord {
34
+ /** Record type (SPF, DKIM, DMARC, etc.) */
35
+ type: DNSRecordType;
36
+
37
+ /** Hostname for the record */
38
+ host: string;
39
+
40
+ /** Record value/content */
41
+ value: string;
42
+
43
+ /** TTL in seconds */
44
+ ttl: number;
45
+
46
+ /** Priority (for MX records) */
47
+ priority?: number;
48
+
49
+ /** Provider that requires this record */
50
+ provider: EmailProvider;
51
+
52
+ /** Whether this record is required or optional */
53
+ required: boolean;
54
+
55
+ /** Human-readable instructions */
56
+ instructions: string;
57
+ }
58
+
59
+ /**
60
+ * DKIM configuration and keypair
61
+ */
62
+ export interface DKIMConfig {
63
+ /** DKIM selector (e.g., 'default', 'k1', 's1') */
64
+ selector: string;
65
+
66
+ /** DKIM private key (PEM format) */
67
+ privateKey: string;
68
+
69
+ /** DKIM public key (PEM format) */
70
+ publicKey: string;
71
+
72
+ /** Key size in bits (1024, 2048) */
73
+ keySize: 1024 | 2048;
74
+
75
+ /** DNS record for DKIM public key */
76
+ dnsRecord: DNSRecord;
77
+
78
+ /** Provider-specific DKIM configuration */
79
+ providerConfig?: Record<string, unknown>;
80
+ }
81
+
82
+ /**
83
+ * Domain verification configuration
84
+ */
85
+ export interface DomainConfig {
86
+ /** Domain name (e.g., 'example.com') */
87
+ domain: string;
88
+
89
+ /** Email provider to use */
90
+ provider: EmailProvider;
91
+
92
+ /** Provider API credentials */
93
+ providerCredentials: {
94
+ apiKey?: string;
95
+ apiSecret?: string;
96
+ region?: string; // For AWS SES
97
+ smtpPassword?: string; // For Mailgun
98
+ [key: string]: unknown;
99
+ };
100
+
101
+ /** DKIM configuration */
102
+ dkim: {
103
+ enabled: boolean;
104
+ selector?: string;
105
+ keySize?: 1024 | 2048;
106
+ };
107
+
108
+ /** SPF configuration */
109
+ spf: {
110
+ enabled: boolean;
111
+ includes?: string[]; // Additional includes
112
+ ipv4Addresses?: string[];
113
+ ipv6Addresses?: string[];
114
+ strict?: boolean; // Use -all vs ~all
115
+ };
116
+
117
+ /** DMARC configuration */
118
+ dmarc: {
119
+ enabled: boolean;
120
+ policy?: 'none' | 'quarantine' | 'reject';
121
+ percentage?: number;
122
+ reportEmail?: string;
123
+ aggregateEmail?: string;
124
+ subdomain?: 'none' | 'quarantine' | 'reject';
125
+ };
126
+
127
+ /** MX records configuration */
128
+ mx?: {
129
+ enabled: boolean;
130
+ records?: Array<{ priority: number; host: string }>;
131
+ };
132
+
133
+ /** Verification callback URL */
134
+ callbackUrl?: string;
135
+
136
+ /** Custom metadata */
137
+ metadata?: Record<string, unknown>;
138
+ }
139
+
140
+ /**
141
+ * Domain verification result
142
+ */
143
+ export interface DomainVerificationResult {
144
+ /** Verification success status */
145
+ success: boolean;
146
+
147
+ /** Domain name */
148
+ domain: string;
149
+
150
+ /** Current verification status */
151
+ status: VerificationStatus;
152
+
153
+ /** DNS records to configure */
154
+ dnsRecords: DNSRecord[];
155
+
156
+ /** DKIM configuration (if enabled) */
157
+ dkimConfig?: DKIMConfig;
158
+
159
+ /** Verification errors (if any) */
160
+ errors?: string[];
161
+
162
+ /** Provider-specific verification data */
163
+ providerData?: Record<string, unknown>;
164
+
165
+ /** Estimated propagation time (seconds) */
166
+ estimatedPropagationTime?: number;
167
+
168
+ /** Next steps for user */
169
+ nextSteps?: string[];
170
+ }
171
+
172
+ /**
173
+ * DNS propagation check result
174
+ */
175
+ export interface DNSPropagationResult {
176
+ /** Domain name */
177
+ domain: string;
178
+
179
+ /** DNS record being checked */
180
+ record: DNSRecord;
181
+
182
+ /** Whether record is propagated */
183
+ propagated: boolean;
184
+
185
+ /** Nameservers checked */
186
+ nameservers: string[];
187
+
188
+ /** Propagation percentage (0-100) */
189
+ propagationPercentage: number;
190
+
191
+ /** Actual DNS value found */
192
+ actualValue?: string;
193
+
194
+ /** Expected DNS value */
195
+ expectedValue: string;
196
+
197
+ /** Values match */
198
+ valuesMatch: boolean;
199
+
200
+ /** Errors encountered */
201
+ errors?: string[];
202
+ }
203
+
204
+ /**
205
+ * Domain health status
206
+ */
207
+ export interface DomainHealthStatus {
208
+ /** Domain name */
209
+ domain: string;
210
+
211
+ /** Overall health status */
212
+ healthy: boolean;
213
+
214
+ /** Verification status */
215
+ verificationStatus: VerificationStatus;
216
+
217
+ /** Last verification check timestamp */
218
+ lastChecked: Date;
219
+
220
+ /** Individual record health */
221
+ records: Array<{
222
+ type: DNSRecordType;
223
+ healthy: boolean;
224
+ lastChecked: Date;
225
+ error?: string;
226
+ }>;
227
+
228
+ /** Provider-specific health data */
229
+ providerHealth?: {
230
+ connected: boolean;
231
+ lastApiCall: Date;
232
+ error?: string;
233
+ };
234
+
235
+ /** Warnings */
236
+ warnings?: string[];
237
+
238
+ /** Recommendations */
239
+ recommendations?: string[];
240
+ }
241
+
242
+ /**
243
+ * Verification history entry
244
+ */
245
+ export interface VerificationHistoryEntry {
246
+ /** Entry ID */
247
+ id: string;
248
+
249
+ /** Domain name */
250
+ domain: string;
251
+
252
+ /** Timestamp */
253
+ timestamp: Date;
254
+
255
+ /** Event type */
256
+ eventType: 'initiated' | 'dns-updated' | 'propagated' | 'verified' | 'failed' | 'expired';
257
+
258
+ /** Event details */
259
+ details: string;
260
+
261
+ /** Previous status */
262
+ previousStatus?: VerificationStatus;
263
+
264
+ /** New status */
265
+ newStatus?: VerificationStatus;
266
+
267
+ /** User who triggered event */
268
+ triggeredBy?: string;
269
+
270
+ /** Additional metadata */
271
+ metadata?: Record<string, unknown>;
272
+ }
273
+
274
+ /**
275
+ * Result type for domain verification operations
276
+ */
277
+ export interface DomainVerificationServiceResult<T = unknown> {
278
+ success: boolean;
279
+ data?: T;
280
+ error?: string;
281
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "declaration": true,
7
+ "outDir": "./dist",
8
+ "rootDir": "./src",
9
+ "strict": true,
10
+ "esModuleInterop": true,
11
+ "skipLibCheck": true,
12
+ "forceConsistentCasingInFileNames": true,
13
+ "resolveJsonModule": true,
14
+ "moduleResolution": "node",
15
+ "noImplicitAny": true,
16
+ "strictNullChecks": true,
17
+ "strictFunctionTypes": true,
18
+ "strictBindCallApply": true,
19
+ "strictPropertyInitialization": true,
20
+ "noImplicitThis": true,
21
+ "alwaysStrict": true,
22
+ "noUnusedLocals": true,
23
+ "noUnusedParameters": true,
24
+ "noImplicitReturns": true,
25
+ "noFallthroughCasesInSwitch": true,
26
+ "jsx": "react"
27
+ },
28
+ "include": ["src/**/*"],
29
+ "exclude": ["node_modules", "dist", "__tests__"]
30
+ }