@bernierllc/email-domain-verification 1.0.3 → 1.0.6

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,42 @@
1
+ # Change Log
2
+
3
+ All notable changes to this project will be documented in this file.
4
+ See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
+
6
+ ## 1.0.6 (2025-12-25)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * **ci:** add lerna version step before publish ([3d20300](https://github.com/bernierllc/tools/commit/3d203002143bf353fffafe4f8a78a99009567347))
12
+ * **ci:** fix release workflow + convert internal deps to workspace:* ([01c078b](https://github.com/bernierllc/tools/commit/01c078b49d6025f7eef750f79207a1c71c8d85dc))
13
+ * update neverhub-adapter deps to workspace:* and publish 0.1.2 ([f0e3d04](https://github.com/bernierllc/tools/commit/f0e3d04d8d4f094e3bb899ddf81e93243d16e2c2))
14
+
15
+
16
+
17
+
18
+
19
+ ## 1.0.5 (2025-12-25)
20
+
21
+
22
+ ### Bug Fixes
23
+
24
+ * **ci:** fix release workflow + convert internal deps to workspace:* ([01c078b](https://github.com/bernierllc/tools/commit/01c078b49d6025f7eef750f79207a1c71c8d85dc))
25
+ * update neverhub-adapter deps to workspace:* and publish 0.1.2 ([f0e3d04](https://github.com/bernierllc/tools/commit/f0e3d04d8d4f094e3bb899ddf81e93243d16e2c2))
26
+
27
+
28
+
29
+
30
+
1
31
  # @bernierllc/email-domain-verification
2
32
 
33
+ ## 1.0.4
34
+
35
+ ### Patch Changes
36
+
37
+ - Updated dependencies [[`73c1d1e`](https://github.com/bernierllc/tools/commit/73c1d1e46114cf2ccfd5b74b83743361247f4502)]:
38
+ - @bernierllc/logger@1.1.0
39
+
3
40
  ## 1.0.3
4
41
 
5
42
  ### Patch Changes
@@ -0,0 +1,566 @@
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
+ import { EmailDomainVerificationService } from '../src/EmailDomainVerificationService';
10
+ import {
11
+ DomainVerificationError,
12
+ DNSPropagationError,
13
+ ProviderAPIError,
14
+ InvalidDomainError,
15
+ successResult,
16
+ errorResult,
17
+ } from '../src/errors';
18
+
19
+ // Create mock logger directly
20
+ const createMockLogger = () => ({
21
+ debug: jest.fn(),
22
+ info: jest.fn(),
23
+ warn: jest.fn(),
24
+ error: jest.fn(),
25
+ fatal: jest.fn()
26
+ });
27
+
28
+ // Mock external dependencies
29
+ jest.mock('dns2', () => ({
30
+ Resolver: jest.fn().mockImplementation(() => ({
31
+ resolve: jest.fn().mockResolvedValue({
32
+ answers: [
33
+ { type: 'TXT', data: 'v=spf1 include:sendgrid.net -all' },
34
+ { ns: '8.8.8.8' }
35
+ ]
36
+ })
37
+ }))
38
+ }));
39
+
40
+ jest.mock('axios', () => ({
41
+ create: jest.fn(() => ({
42
+ post: jest.fn().mockResolvedValue({
43
+ data: {
44
+ id: 'domain-123',
45
+ dns: [],
46
+ domain: {
47
+ name: 'example.com',
48
+ state: 'active'
49
+ },
50
+ receiving_dns_records: [],
51
+ sending_dns_records: []
52
+ }
53
+ }),
54
+ get: jest.fn().mockResolvedValue({
55
+ data: [{
56
+ domain: 'example.com',
57
+ id: 'domain-123'
58
+ }]
59
+ })
60
+ }))
61
+ }));
62
+
63
+ jest.mock('cron', () => ({
64
+ CronJob: jest.fn().mockImplementation((_schedule: string, callback: () => void) => {
65
+ const job = {
66
+ start: jest.fn(),
67
+ stop: jest.fn(),
68
+ callback
69
+ };
70
+ return job;
71
+ })
72
+ }));
73
+
74
+ describe('Coverage Gap Tests - EmailDomainVerificationService', () => {
75
+ let service: EmailDomainVerificationService;
76
+ let logger: ReturnType<typeof createMockLogger>;
77
+
78
+ beforeEach(() => {
79
+ logger = createMockLogger();
80
+ service = new EmailDomainVerificationService({ logger: logger as any });
81
+ });
82
+
83
+ afterEach(async () => {
84
+ await service.shutdown();
85
+ jest.clearAllMocks();
86
+ });
87
+
88
+ describe('Initialize method', () => {
89
+ it('should initialize the service', async () => {
90
+ await service.initialize();
91
+ expect(logger.info).toHaveBeenCalledWith(
92
+ 'Email Domain Verification Service initialized',
93
+ expect.any(Object)
94
+ );
95
+ });
96
+ });
97
+
98
+ describe('SPF Record Generation - provider variations', () => {
99
+ it('should generate SPF record for mailgun provider', async () => {
100
+ const result = await service.setupDomain({
101
+ domain: 'example.com',
102
+ provider: 'mailgun',
103
+ providerCredentials: { apiKey: 'test-key' },
104
+ dkim: { enabled: false },
105
+ spf: { enabled: true, strict: true },
106
+ dmarc: { enabled: false }
107
+ });
108
+
109
+ expect(result.success).toBe(true);
110
+ const spfRecord = result.dnsRecords.find(r => r.type === 'SPF');
111
+ expect(spfRecord?.value).toContain('include:mailgun.org');
112
+ });
113
+
114
+ it('should generate SPF record for SES provider', async () => {
115
+ const result = await service.setupDomain({
116
+ domain: 'example.com',
117
+ provider: 'ses',
118
+ providerCredentials: { apiKey: 'test-key' },
119
+ dkim: { enabled: false },
120
+ spf: { enabled: true, strict: true },
121
+ dmarc: { enabled: false }
122
+ });
123
+
124
+ expect(result.success).toBe(true);
125
+ const spfRecord = result.dnsRecords.find(r => r.type === 'SPF');
126
+ expect(spfRecord?.value).toContain('include:amazonses.com');
127
+ });
128
+
129
+ it('should generate SPF with custom includes', async () => {
130
+ const result = await service.setupDomain({
131
+ domain: 'example.com',
132
+ provider: 'sendgrid',
133
+ providerCredentials: { apiKey: 'test-key' },
134
+ dkim: { enabled: false },
135
+ spf: {
136
+ enabled: true,
137
+ strict: true,
138
+ includes: ['include:custom.com']
139
+ },
140
+ dmarc: { enabled: false }
141
+ });
142
+
143
+ expect(result.success).toBe(true);
144
+ const spfRecord = result.dnsRecords.find(r => r.type === 'SPF');
145
+ expect(spfRecord?.value).toContain('include:custom.com');
146
+ });
147
+
148
+ it('should generate SPF with IPv4 addresses', async () => {
149
+ const result = await service.setupDomain({
150
+ domain: 'example.com',
151
+ provider: 'sendgrid',
152
+ providerCredentials: { apiKey: 'test-key' },
153
+ dkim: { enabled: false },
154
+ spf: {
155
+ enabled: true,
156
+ strict: true,
157
+ ipv4Addresses: ['192.168.1.1', '10.0.0.1']
158
+ },
159
+ dmarc: { enabled: false }
160
+ });
161
+
162
+ expect(result.success).toBe(true);
163
+ const spfRecord = result.dnsRecords.find(r => r.type === 'SPF');
164
+ expect(spfRecord?.value).toContain('ip4:192.168.1.1');
165
+ expect(spfRecord?.value).toContain('ip4:10.0.0.1');
166
+ });
167
+
168
+ it('should generate SPF with IPv6 addresses', async () => {
169
+ const result = await service.setupDomain({
170
+ domain: 'example.com',
171
+ provider: 'sendgrid',
172
+ providerCredentials: { apiKey: 'test-key' },
173
+ dkim: { enabled: false },
174
+ spf: {
175
+ enabled: true,
176
+ strict: true,
177
+ ipv6Addresses: ['2001:db8::1']
178
+ },
179
+ dmarc: { enabled: false }
180
+ });
181
+
182
+ expect(result.success).toBe(true);
183
+ const spfRecord = result.dnsRecords.find(r => r.type === 'SPF');
184
+ expect(spfRecord?.value).toContain('ip6:2001:db8::1');
185
+ });
186
+
187
+ it('should generate SPF with non-strict mode (~all)', async () => {
188
+ const result = await service.setupDomain({
189
+ domain: 'example.com',
190
+ provider: 'sendgrid',
191
+ providerCredentials: { apiKey: 'test-key' },
192
+ dkim: { enabled: false },
193
+ spf: { enabled: true, strict: false },
194
+ dmarc: { enabled: false }
195
+ });
196
+
197
+ expect(result.success).toBe(true);
198
+ const spfRecord = result.dnsRecords.find(r => r.type === 'SPF');
199
+ expect(spfRecord?.value).toContain('~all');
200
+ });
201
+ });
202
+
203
+ describe('DMARC Record Generation - variations', () => {
204
+ it('should generate DMARC with aggregateEmail', async () => {
205
+ const result = await service.setupDomain({
206
+ domain: 'example.com',
207
+ provider: 'sendgrid',
208
+ providerCredentials: { apiKey: 'test-key' },
209
+ dkim: { enabled: false },
210
+ spf: { enabled: false },
211
+ dmarc: {
212
+ enabled: true,
213
+ policy: 'quarantine',
214
+ aggregateEmail: 'forensic@example.com'
215
+ }
216
+ });
217
+
218
+ expect(result.success).toBe(true);
219
+ const dmarcRecord = result.dnsRecords.find(r => r.type === 'DMARC');
220
+ expect(dmarcRecord?.value).toContain('ruf=mailto:forensic@example.com');
221
+ });
222
+
223
+ it('should generate DMARC with reportEmail', async () => {
224
+ const result = await service.setupDomain({
225
+ domain: 'example.com',
226
+ provider: 'sendgrid',
227
+ providerCredentials: { apiKey: 'test-key' },
228
+ dkim: { enabled: false },
229
+ spf: { enabled: false },
230
+ dmarc: {
231
+ enabled: true,
232
+ policy: 'reject',
233
+ reportEmail: 'dmarc@example.com'
234
+ }
235
+ });
236
+
237
+ expect(result.success).toBe(true);
238
+ const dmarcRecord = result.dnsRecords.find(r => r.type === 'DMARC');
239
+ expect(dmarcRecord?.value).toContain('rua=mailto:dmarc@example.com');
240
+ });
241
+
242
+ it('should generate DMARC with all options', async () => {
243
+ const result = await service.setupDomain({
244
+ domain: 'example.com',
245
+ provider: 'sendgrid',
246
+ providerCredentials: { apiKey: 'test-key' },
247
+ dkim: { enabled: false },
248
+ spf: { enabled: false },
249
+ dmarc: {
250
+ enabled: true,
251
+ policy: 'reject',
252
+ percentage: 50,
253
+ subdomain: 'quarantine',
254
+ reportEmail: 'dmarc@example.com',
255
+ aggregateEmail: 'forensic@example.com'
256
+ }
257
+ });
258
+
259
+ expect(result.success).toBe(true);
260
+ const dmarcRecord = result.dnsRecords.find(r => r.type === 'DMARC');
261
+ expect(dmarcRecord?.value).toContain('p=reject');
262
+ expect(dmarcRecord?.value).toContain('pct=50');
263
+ expect(dmarcRecord?.value).toContain('sp=quarantine');
264
+ expect(dmarcRecord?.value).toContain('rua=');
265
+ expect(dmarcRecord?.value).toContain('ruf=');
266
+ });
267
+ });
268
+
269
+ describe('Provider-specific records', () => {
270
+ it('should generate records for postmark provider (default case)', async () => {
271
+ const result = await service.setupDomain({
272
+ domain: 'example.com',
273
+ provider: 'postmark' as any,
274
+ providerCredentials: { apiKey: 'test-key' },
275
+ dkim: { enabled: false },
276
+ spf: { enabled: true, strict: true },
277
+ dmarc: { enabled: false }
278
+ });
279
+
280
+ expect(result.success).toBe(true);
281
+ });
282
+ });
283
+
284
+ describe('Error Handling', () => {
285
+ it('should handle verifyDomain error catch block', async () => {
286
+ // Create a service that will throw during verification
287
+ const errorService = new EmailDomainVerificationService({ logger: logger as any });
288
+
289
+ // Mock the verifyWithProvider to throw
290
+ (errorService as any).verifyWithProvider = jest.fn().mockRejectedValue(
291
+ new Error('Provider API failed')
292
+ );
293
+
294
+ const result = await errorService.verifyDomain('example.com', 'sendgrid', { apiKey: 'key' });
295
+
296
+ expect(result.success).toBe(false);
297
+ expect(result.errors).toContain('Provider API failed');
298
+ expect(logger.error).toHaveBeenCalled();
299
+
300
+ await errorService.shutdown();
301
+ });
302
+
303
+ it('should handle verifyDomain with non-Error exception', async () => {
304
+ const errorService = new EmailDomainVerificationService({ logger: logger as any });
305
+
306
+ // Mock to throw non-Error
307
+ (errorService as any).verifyWithProvider = jest.fn().mockRejectedValue('String error');
308
+
309
+ const result = await errorService.verifyDomain('example.com', 'sendgrid', { apiKey: 'key' });
310
+
311
+ expect(result.success).toBe(false);
312
+ expect(result.errors).toContain('Unknown error');
313
+
314
+ await errorService.shutdown();
315
+ });
316
+
317
+ it('should handle setupDomain with invalid domain', async () => {
318
+ const result = await service.setupDomain({
319
+ domain: '', // Invalid empty domain
320
+ provider: 'sendgrid',
321
+ providerCredentials: { apiKey: 'test-key' },
322
+ dkim: { enabled: false },
323
+ spf: { enabled: true },
324
+ dmarc: { enabled: false }
325
+ });
326
+
327
+ expect(result.success).toBe(false);
328
+ expect(result.errors).toBeDefined();
329
+ });
330
+ });
331
+
332
+ describe('DNS Propagation', () => {
333
+ it('should check propagation for DNS records', async () => {
334
+ const records = [
335
+ {
336
+ type: 'SPF' as const,
337
+ host: 'example.com',
338
+ value: 'v=spf1 include:sendgrid.net -all',
339
+ ttl: 3600,
340
+ provider: 'sendgrid' as const,
341
+ required: true,
342
+ instructions: 'Add this TXT record to your domain'
343
+ }
344
+ ];
345
+
346
+ const results = await service.checkPropagation('example.com', records);
347
+
348
+ expect(Array.isArray(results)).toBe(true);
349
+ expect(results.length).toBe(1);
350
+ });
351
+ });
352
+
353
+ describe('Domain Health', () => {
354
+ it('should get domain health status', async () => {
355
+ const health = await service.getDomainHealth('example.com');
356
+
357
+ expect(health.domain).toBe('example.com');
358
+ expect(health).toHaveProperty('healthy');
359
+ expect(health).toHaveProperty('lastChecked');
360
+ });
361
+ });
362
+
363
+ describe('Verification History', () => {
364
+ it('should return empty history for new domain', () => {
365
+ const history = service.getVerificationHistory('newdomain.com');
366
+ expect(Array.isArray(history)).toBe(true);
367
+ });
368
+
369
+ it('should return history after domain setup', async () => {
370
+ await service.setupDomain({
371
+ domain: 'test-history.com',
372
+ provider: 'sendgrid',
373
+ providerCredentials: { apiKey: 'test-key' },
374
+ dkim: { enabled: false },
375
+ spf: { enabled: true },
376
+ dmarc: { enabled: false }
377
+ });
378
+
379
+ const history = service.getVerificationHistory('test-history.com');
380
+ expect(history.length).toBeGreaterThan(0);
381
+ });
382
+
383
+ it('should respect limit parameter', async () => {
384
+ await service.setupDomain({
385
+ domain: 'test-limit.com',
386
+ provider: 'sendgrid',
387
+ providerCredentials: { apiKey: 'test-key' },
388
+ dkim: { enabled: false },
389
+ spf: { enabled: true },
390
+ dmarc: { enabled: false }
391
+ });
392
+
393
+ const history = service.getVerificationHistory('test-limit.com', 1);
394
+ expect(history.length).toBeLessThanOrEqual(1);
395
+ });
396
+ });
397
+
398
+ describe('Health Monitoring', () => {
399
+ it('should start and stop health monitoring', async () => {
400
+ // First verify a domain to start monitoring
401
+ const verifyService = new EmailDomainVerificationService({ logger: logger as any });
402
+
403
+ // Mock successful verification
404
+ (verifyService as any).verifyWithProvider = jest.fn().mockResolvedValue({
405
+ verified: true,
406
+ message: 'Domain verified'
407
+ });
408
+
409
+ await verifyService.verifyDomain('test.com', 'sendgrid', { apiKey: 'key' });
410
+
411
+ // Stop monitoring explicitly
412
+ verifyService.stopHealthMonitoring('test.com');
413
+
414
+ expect(logger.info).toHaveBeenCalledWith('Health monitoring stopped', { domain: 'test.com' });
415
+
416
+ await verifyService.shutdown();
417
+ });
418
+
419
+ it('should handle stopping monitoring for non-existent domain', () => {
420
+ // Should not throw when stopping non-existent domain
421
+ expect(() => service.stopHealthMonitoring('nonexistent.com')).not.toThrow();
422
+ });
423
+ });
424
+
425
+ describe('Domain Verification', () => {
426
+ it('should verify domain with sendgrid provider', async () => {
427
+ const result = await service.verifyDomain('example.com', 'sendgrid', { apiKey: 'test-key' });
428
+
429
+ // The mock returns valid: undefined which maps to not verified
430
+ expect(result).toHaveProperty('success');
431
+ expect(result).toHaveProperty('domain', 'example.com');
432
+ });
433
+
434
+ it('should verify domain with mailgun provider', async () => {
435
+ const result = await service.verifyDomain('example.com', 'mailgun', { apiKey: 'test-key' });
436
+
437
+ expect(result).toHaveProperty('success');
438
+ expect(result).toHaveProperty('domain', 'example.com');
439
+ });
440
+
441
+ it('should verify domain with SES provider', async () => {
442
+ const result = await service.verifyDomain('example.com', 'ses', { apiKey: 'test-key' });
443
+
444
+ expect(result.success).toBe(false); // SES returns not implemented
445
+ expect(result.domain).toBe('example.com');
446
+ });
447
+
448
+ it('should handle verified domain status correctly', async () => {
449
+ const verifyService = new EmailDomainVerificationService({ logger: logger as any });
450
+
451
+ // Mock successful verification
452
+ (verifyService as any).verifyWithProvider = jest.fn().mockResolvedValue({
453
+ verified: true,
454
+ message: 'Domain verified successfully'
455
+ });
456
+
457
+ const result = await verifyService.verifyDomain('verified.com', 'sendgrid', { apiKey: 'key' });
458
+
459
+ expect(result.success).toBe(true);
460
+ expect(result.status).toBe('verified');
461
+
462
+ await verifyService.shutdown();
463
+ });
464
+
465
+ it('should handle failed domain verification', async () => {
466
+ const verifyService = new EmailDomainVerificationService({ logger: logger as any });
467
+
468
+ // Mock failed verification
469
+ (verifyService as any).verifyWithProvider = jest.fn().mockResolvedValue({
470
+ verified: false,
471
+ message: 'DNS records not found'
472
+ });
473
+
474
+ const result = await verifyService.verifyDomain('failed.com', 'sendgrid', { apiKey: 'key' });
475
+
476
+ expect(result.success).toBe(false);
477
+ expect(result.status).toBe('failed');
478
+ expect(result.errors).toBeDefined();
479
+
480
+ await verifyService.shutdown();
481
+ });
482
+
483
+ it('should handle unsupported provider in verification', async () => {
484
+ const verifyService = new EmailDomainVerificationService({ logger: logger as any });
485
+
486
+ const result = await verifyService.verifyDomain('example.com', 'unknown' as any, { apiKey: 'key' });
487
+
488
+ expect(result.success).toBe(false);
489
+
490
+ await verifyService.shutdown();
491
+ });
492
+ });
493
+ });
494
+
495
+ describe('Error Classes', () => {
496
+ describe('DomainVerificationError', () => {
497
+ it('should create error with all properties', () => {
498
+ const error = new DomainVerificationError('Test error', 'TEST_CODE', true, { key: 'value' });
499
+
500
+ expect(error.message).toBe('Test error');
501
+ expect(error.code).toBe('TEST_CODE');
502
+ expect(error.retryable).toBe(true);
503
+ expect(error.details).toEqual({ key: 'value' });
504
+ expect(error.name).toBe('DomainVerificationError');
505
+ });
506
+
507
+ it('should default retryable to false', () => {
508
+ const error = new DomainVerificationError('Test error', 'TEST_CODE');
509
+ expect(error.retryable).toBe(false);
510
+ });
511
+ });
512
+
513
+ describe('DNSPropagationError', () => {
514
+ it('should create with correct code and retryable flag', () => {
515
+ const error = new DNSPropagationError('DNS not propagated', { domain: 'test.com' });
516
+
517
+ expect(error.code).toBe('DNS_PROPAGATION_ERROR');
518
+ expect(error.retryable).toBe(true);
519
+ expect(error.name).toBe('DNSPropagationError');
520
+ expect(error.details).toEqual({ domain: 'test.com' });
521
+ });
522
+ });
523
+
524
+ describe('ProviderAPIError', () => {
525
+ it('should create with correct code and retryable flag', () => {
526
+ const error = new ProviderAPIError('API failed', { status: 500 });
527
+
528
+ expect(error.code).toBe('PROVIDER_API_ERROR');
529
+ expect(error.retryable).toBe(true);
530
+ expect(error.name).toBe('ProviderAPIError');
531
+ });
532
+ });
533
+
534
+ describe('InvalidDomainError', () => {
535
+ it('should create with correct code and non-retryable flag', () => {
536
+ const error = new InvalidDomainError('Invalid domain format');
537
+
538
+ expect(error.code).toBe('INVALID_DOMAIN');
539
+ expect(error.retryable).toBe(false);
540
+ expect(error.name).toBe('InvalidDomainError');
541
+ });
542
+ });
543
+
544
+ describe('Result Helpers', () => {
545
+ it('successResult should create success result', () => {
546
+ const result = successResult({ domain: 'test.com' });
547
+
548
+ expect(result.success).toBe(true);
549
+ expect(result.data).toEqual({ domain: 'test.com' });
550
+ });
551
+
552
+ it('errorResult should create error result from DomainVerificationError', () => {
553
+ const error = new DomainVerificationError('Test error', 'TEST', true, { key: 'value' });
554
+ const result = errorResult(error);
555
+
556
+ expect(result.success).toBe(false);
557
+ expect(result.error).toEqual({
558
+ code: 'TEST',
559
+ message: 'Test error',
560
+ retryable: true,
561
+ details: { key: 'value' }
562
+ });
563
+ });
564
+ });
565
+ });
566
+
package/jest.config.cjs CHANGED
@@ -17,10 +17,10 @@ module.exports = {
17
17
  ],
18
18
  coverageThreshold: {
19
19
  global: {
20
- branches: 50, // TODO: Increase to 85% - current: 50.92%
21
- functions: 74, // TODO: Increase to 85% - current: 74.07%
22
- lines: 72, // TODO: Increase to 85% - current: 72.9%
23
- statements: 72 // TODO: Increase to 85% - current: 72.37%
20
+ branches: 65,
21
+ functions: 85,
22
+ lines: 85,
23
+ statements: 85
24
24
  }
25
25
  },
26
26
  testMatch: [
package/package.json CHANGED
@@ -1,9 +1,18 @@
1
1
  {
2
2
  "name": "@bernierllc/email-domain-verification",
3
- "version": "1.0.3",
3
+ "version": "1.0.6",
4
4
  "description": "Programmatic domain setup and DNS management for production email sending with multi-provider support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "tsc",
9
+ "test": "jest --watch",
10
+ "test:run": "jest",
11
+ "test:coverage": "jest --coverage",
12
+ "lint": "eslint src --ext .ts,.tsx",
13
+ "clean": "rm -rf dist",
14
+ "prepublishOnly": "npm run clean && npm run build"
15
+ },
7
16
  "keywords": [
8
17
  "email",
9
18
  "domain",
@@ -21,29 +30,29 @@
21
30
  "author": "Bernier LLC",
22
31
  "license": "SEE LICENSE IN LICENSE",
23
32
  "dependencies": {
24
- "dns2": "^2.1.0",
25
- "dns-packet": "^5.6.1",
33
+ "@bernierllc/crypto-utils": "1.0.6",
34
+ "@bernierllc/email-sender-manager": "1.2.0",
35
+ "@bernierllc/logger": "1.3.0",
36
+ "@bernierllc/neverhub-adapter": "0.1.4",
26
37
  "axios": "^1.6.0",
27
38
  "cron": "^3.1.6",
28
- "@bernierllc/email-sender-manager": "1.0.2",
29
- "@bernierllc/crypto-utils": "1.0.4",
30
- "@bernierllc/logger": "1.0.4",
31
- "@bernierllc/neverhub-adapter": "0.1.2"
39
+ "dns-packet": "^5.6.1",
40
+ "dns2": "^2.1.0"
32
41
  },
33
42
  "devDependencies": {
34
- "@types/jest": "^29.0.0",
35
- "@types/node": "^20.0.0",
36
43
  "@types/cron": "^2.0.1",
37
44
  "@types/dns2": "^2.0.0",
45
+ "@types/jest": "^29.0.0",
46
+ "@types/node": "^20.0.0",
47
+ "@types/react": "^18.0.0",
38
48
  "@typescript-eslint/eslint-plugin": "^6.0.0",
39
49
  "@typescript-eslint/parser": "^6.0.0",
40
50
  "eslint": "^8.0.0",
41
51
  "jest": "^29.0.0",
42
- "ts-jest": "^29.0.0",
43
- "typescript": "^5.0.0",
44
52
  "react": "^18.0.0",
45
53
  "react-dom": "^18.0.0",
46
- "@types/react": "^18.0.0"
54
+ "ts-jest": "^29.0.0",
55
+ "typescript": "^5.0.0"
47
56
  },
48
57
  "peerDependencies": {
49
58
  "react": ">=18.0.0",
@@ -62,12 +71,5 @@
62
71
  "category": "service",
63
72
  "priority": "critical"
64
73
  },
65
- "scripts": {
66
- "build": "tsc",
67
- "test": "jest --watch",
68
- "test:run": "jest",
69
- "test:coverage": "jest --coverage",
70
- "lint": "eslint src --ext .ts,.tsx",
71
- "clean": "rm -rf dist"
72
- }
73
- }
74
+ "gitHead": "af7e74b3715d56d3a193e1bb6743b337c2b0df6d"
75
+ }