@bernierllc/sender-identity-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.
- package/.env.test +8 -0
- package/.eslintrc.js +30 -0
- package/README.md +376 -0
- package/__tests__/SenderIdentityVerification.test.ts +461 -0
- package/__tests__/__mocks__/fetch-mock.ts +156 -0
- package/__tests__/additional-coverage.test.ts +129 -0
- package/__tests__/additional-error-coverage.test.ts +483 -0
- package/__tests__/branch-coverage.test.ts +509 -0
- package/__tests__/config.test.ts +119 -0
- package/__tests__/error-handling.test.ts +321 -0
- package/__tests__/final-branch-coverage.test.ts +372 -0
- package/__tests__/integration.real-api.test.ts +295 -0
- package/__tests__/providers.test.ts +331 -0
- package/__tests__/service-coverage.test.ts +412 -0
- package/dist/SenderIdentityVerification.d.ts +72 -0
- package/dist/SenderIdentityVerification.js +643 -0
- package/dist/config.d.ts +31 -0
- package/dist/config.js +38 -0
- package/dist/errors.d.ts +27 -0
- package/dist/errors.js +61 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +21 -0
- package/dist/providers/MailgunProvider.d.ts +13 -0
- package/dist/providers/MailgunProvider.js +35 -0
- package/dist/providers/SESProvider.d.ts +12 -0
- package/dist/providers/SESProvider.js +47 -0
- package/dist/providers/SMTPProvider.d.ts +12 -0
- package/dist/providers/SMTPProvider.js +30 -0
- package/dist/providers/SendGridProvider.d.ts +19 -0
- package/dist/providers/SendGridProvider.js +98 -0
- package/dist/templates/verification-email.d.ts +9 -0
- package/dist/templates/verification-email.js +67 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.js +33 -0
- package/dist/utils/domain-extractor.d.ts +4 -0
- package/dist/utils/domain-extractor.js +20 -0
- package/jest.config.cjs +33 -0
- package/package.json +60 -0
- package/src/SenderIdentityVerification.ts +796 -0
- package/src/config.ts +81 -0
- package/src/errors.ts +64 -0
- package/src/global.d.ts +24 -0
- package/src/index.ts +24 -0
- package/src/providers/MailgunProvider.ts +35 -0
- package/src/providers/SESProvider.ts +51 -0
- package/src/providers/SMTPProvider.ts +29 -0
- package/src/providers/SendGridProvider.ts +108 -0
- package/src/templates/verification-email.ts +67 -0
- package/src/types.ts +163 -0
- package/src/utils/domain-extractor.ts +18 -0
- package/tsconfig.json +22 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.loadConfigFromEnv = loadConfigFromEnv;
|
|
11
|
+
/**
|
|
12
|
+
* Load configuration from environment variables
|
|
13
|
+
*/
|
|
14
|
+
function loadConfigFromEnv(overrides = {}) {
|
|
15
|
+
const config = {
|
|
16
|
+
verificationBaseUrl: process.env.SENDER_VERIFICATION_BASE_URL || overrides.verificationBaseUrl || '',
|
|
17
|
+
verificationFromEmail: process.env.SENDER_VERIFICATION_FROM_EMAIL || overrides.verificationFromEmail || 'noreply@example.com',
|
|
18
|
+
verificationFromName: process.env.SENDER_VERIFICATION_FROM_NAME || overrides.verificationFromName || 'Email Verification',
|
|
19
|
+
sendgridApiKey: process.env.SENDGRID_API_KEY || overrides.sendgridApiKey,
|
|
20
|
+
mailgunApiKey: process.env.MAILGUN_API_KEY || overrides.mailgunApiKey,
|
|
21
|
+
sesAccessKey: process.env.AWS_SES_ACCESS_KEY || overrides.sesAccessKey,
|
|
22
|
+
sesSecretKey: process.env.AWS_SES_SECRET_KEY || overrides.sesSecretKey,
|
|
23
|
+
sesRegion: process.env.AWS_SES_REGION || overrides.sesRegion || 'us-east-1',
|
|
24
|
+
databaseUrl: process.env.DATABASE_URL || overrides.databaseUrl,
|
|
25
|
+
emailSenderConfig: overrides.emailSenderConfig || {},
|
|
26
|
+
domainVerificationConfig: overrides.domainVerificationConfig || {},
|
|
27
|
+
retryConfig: overrides.retryConfig
|
|
28
|
+
};
|
|
29
|
+
// Log configuration (without sensitive data)
|
|
30
|
+
console.log('⚙️ Sender Identity Verification Configuration:');
|
|
31
|
+
console.log(` └── Verification Base URL: ${config.verificationBaseUrl || '(not set)'}`);
|
|
32
|
+
console.log(` └── From Email: ${config.verificationFromEmail}`);
|
|
33
|
+
console.log(` └── SendGrid: ${config.sendgridApiKey ? '***configured***' : '(not set)'}`);
|
|
34
|
+
console.log(` └── Mailgun: ${config.mailgunApiKey ? '***configured***' : '(not set)'}`);
|
|
35
|
+
console.log(` └── SES: ${config.sesAccessKey ? '***configured***' : '(not set)'}`);
|
|
36
|
+
console.log(` └── Database: ${config.databaseUrl ? '***configured***' : '(not set)'}`);
|
|
37
|
+
return config;
|
|
38
|
+
}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { SenderIdentityResult } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Sender error codes
|
|
4
|
+
*/
|
|
5
|
+
export declare enum SenderErrorCode {
|
|
6
|
+
SENDER_NOT_FOUND = "SENDER_NOT_FOUND",
|
|
7
|
+
SENDER_ALREADY_EXISTS = "SENDER_ALREADY_EXISTS",
|
|
8
|
+
SENDER_LOCKED = "SENDER_LOCKED",
|
|
9
|
+
VERIFICATION_EXPIRED = "VERIFICATION_EXPIRED",
|
|
10
|
+
VERIFICATION_FAILED = "VERIFICATION_FAILED",
|
|
11
|
+
DOMAIN_NOT_VERIFIED = "DOMAIN_NOT_VERIFIED",
|
|
12
|
+
PROVIDER_ERROR = "PROVIDER_ERROR",
|
|
13
|
+
INVALID_EMAIL = "INVALID_EMAIL",
|
|
14
|
+
RATE_LIMIT_EXCEEDED = "RATE_LIMIT_EXCEEDED"
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Custom error class for sender verification errors
|
|
18
|
+
*/
|
|
19
|
+
export declare class SenderVerificationError extends Error {
|
|
20
|
+
code: SenderErrorCode;
|
|
21
|
+
details?: unknown | undefined;
|
|
22
|
+
constructor(message: string, code: SenderErrorCode, details?: unknown | undefined);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Error handling wrapper
|
|
26
|
+
*/
|
|
27
|
+
export declare function handleSenderError(error: unknown): SenderIdentityResult<never>;
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SenderVerificationError = exports.SenderErrorCode = void 0;
|
|
11
|
+
exports.handleSenderError = handleSenderError;
|
|
12
|
+
/**
|
|
13
|
+
* Sender error codes
|
|
14
|
+
*/
|
|
15
|
+
var SenderErrorCode;
|
|
16
|
+
(function (SenderErrorCode) {
|
|
17
|
+
SenderErrorCode["SENDER_NOT_FOUND"] = "SENDER_NOT_FOUND";
|
|
18
|
+
SenderErrorCode["SENDER_ALREADY_EXISTS"] = "SENDER_ALREADY_EXISTS";
|
|
19
|
+
SenderErrorCode["SENDER_LOCKED"] = "SENDER_LOCKED";
|
|
20
|
+
SenderErrorCode["VERIFICATION_EXPIRED"] = "VERIFICATION_EXPIRED";
|
|
21
|
+
SenderErrorCode["VERIFICATION_FAILED"] = "VERIFICATION_FAILED";
|
|
22
|
+
SenderErrorCode["DOMAIN_NOT_VERIFIED"] = "DOMAIN_NOT_VERIFIED";
|
|
23
|
+
SenderErrorCode["PROVIDER_ERROR"] = "PROVIDER_ERROR";
|
|
24
|
+
SenderErrorCode["INVALID_EMAIL"] = "INVALID_EMAIL";
|
|
25
|
+
SenderErrorCode["RATE_LIMIT_EXCEEDED"] = "RATE_LIMIT_EXCEEDED";
|
|
26
|
+
})(SenderErrorCode || (exports.SenderErrorCode = SenderErrorCode = {}));
|
|
27
|
+
/**
|
|
28
|
+
* Custom error class for sender verification errors
|
|
29
|
+
*/
|
|
30
|
+
class SenderVerificationError extends Error {
|
|
31
|
+
constructor(message, code, details) {
|
|
32
|
+
super(message);
|
|
33
|
+
this.code = code;
|
|
34
|
+
this.details = details;
|
|
35
|
+
this.name = 'SenderVerificationError';
|
|
36
|
+
Object.setPrototypeOf(this, SenderVerificationError.prototype);
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.SenderVerificationError = SenderVerificationError;
|
|
40
|
+
/**
|
|
41
|
+
* Error handling wrapper
|
|
42
|
+
*/
|
|
43
|
+
function handleSenderError(error) {
|
|
44
|
+
if (error instanceof SenderVerificationError) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
error: error.message,
|
|
48
|
+
errors: [error.code]
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
if (error instanceof Error) {
|
|
52
|
+
return {
|
|
53
|
+
success: false,
|
|
54
|
+
error: error.message
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
success: false,
|
|
59
|
+
error: 'Unknown error occurred'
|
|
60
|
+
};
|
|
61
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { SenderIdentityVerification } from './SenderIdentityVerification.js';
|
|
2
|
+
export { SenderIdentityConfig, loadConfigFromEnv } from './config.js';
|
|
3
|
+
export { SenderVerificationError, SenderErrorCode, handleSenderError } from './errors.js';
|
|
4
|
+
export { SenderIdentity, SenderStatus, EmailProvider, CreateSenderInput, UpdateSenderInput, VerificationResult, ComplianceCheckResult, ListSendersOptions, SenderIdentityResult, DomainVerificationStatus, IEmailDomainVerification } from './types.js';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.EmailProvider = exports.SenderStatus = exports.handleSenderError = exports.SenderErrorCode = exports.SenderVerificationError = exports.loadConfigFromEnv = exports.SenderIdentityVerification = void 0;
|
|
11
|
+
var SenderIdentityVerification_js_1 = require("./SenderIdentityVerification.js");
|
|
12
|
+
Object.defineProperty(exports, "SenderIdentityVerification", { enumerable: true, get: function () { return SenderIdentityVerification_js_1.SenderIdentityVerification; } });
|
|
13
|
+
var config_js_1 = require("./config.js");
|
|
14
|
+
Object.defineProperty(exports, "loadConfigFromEnv", { enumerable: true, get: function () { return config_js_1.loadConfigFromEnv; } });
|
|
15
|
+
var errors_js_1 = require("./errors.js");
|
|
16
|
+
Object.defineProperty(exports, "SenderVerificationError", { enumerable: true, get: function () { return errors_js_1.SenderVerificationError; } });
|
|
17
|
+
Object.defineProperty(exports, "SenderErrorCode", { enumerable: true, get: function () { return errors_js_1.SenderErrorCode; } });
|
|
18
|
+
Object.defineProperty(exports, "handleSenderError", { enumerable: true, get: function () { return errors_js_1.handleSenderError; } });
|
|
19
|
+
var types_js_1 = require("./types.js");
|
|
20
|
+
Object.defineProperty(exports, "SenderStatus", { enumerable: true, get: function () { return types_js_1.SenderStatus; } });
|
|
21
|
+
Object.defineProperty(exports, "EmailProvider", { enumerable: true, get: function () { return types_js_1.EmailProvider; } });
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { SenderIdentity, SenderIdentityResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Mailgun provider for sender verification
|
|
4
|
+
* Note: Mailgun doesn't have sender-level verification, only domain verification
|
|
5
|
+
*/
|
|
6
|
+
export declare class MailgunProvider {
|
|
7
|
+
constructor(_apiKey: string);
|
|
8
|
+
/**
|
|
9
|
+
* Verify sender with Mailgun
|
|
10
|
+
* Mailgun only verifies domains, not individual senders
|
|
11
|
+
*/
|
|
12
|
+
verifySender(_sender: SenderIdentity): Promise<SenderIdentityResult<Record<string, unknown>>>;
|
|
13
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.MailgunProvider = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* Mailgun provider for sender verification
|
|
13
|
+
* Note: Mailgun doesn't have sender-level verification, only domain verification
|
|
14
|
+
*/
|
|
15
|
+
class MailgunProvider {
|
|
16
|
+
constructor(_apiKey) {
|
|
17
|
+
// apiKey parameter reserved for future Mailgun API integration
|
|
18
|
+
void _apiKey;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Verify sender with Mailgun
|
|
22
|
+
* Mailgun only verifies domains, not individual senders
|
|
23
|
+
*/
|
|
24
|
+
async verifySender(_sender) {
|
|
25
|
+
// Mailgun doesn't require individual sender verification
|
|
26
|
+
// Domain verification is handled by @bernierllc/email-domain-verification
|
|
27
|
+
return {
|
|
28
|
+
success: true,
|
|
29
|
+
data: {
|
|
30
|
+
note: 'Mailgun does not require individual sender verification'
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
exports.MailgunProvider = MailgunProvider;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SenderIdentity, SenderIdentityResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* AWS SES provider for sender verification
|
|
4
|
+
*/
|
|
5
|
+
export declare class SESProvider {
|
|
6
|
+
constructor(_accessKey: string, _secretKey: string, _region: string);
|
|
7
|
+
/**
|
|
8
|
+
* Verify sender with AWS SES
|
|
9
|
+
* TODO: Implement AWS SDK integration for SES VerifyEmailIdentity
|
|
10
|
+
*/
|
|
11
|
+
verifySender(_sender: SenderIdentity): Promise<SenderIdentityResult<Record<string, unknown>>>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SESProvider = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* AWS SES provider for sender verification
|
|
13
|
+
*/
|
|
14
|
+
class SESProvider {
|
|
15
|
+
constructor(_accessKey, _secretKey, _region) {
|
|
16
|
+
// Parameters reserved for future AWS SDK integration
|
|
17
|
+
void _accessKey;
|
|
18
|
+
void _secretKey;
|
|
19
|
+
void _region;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Verify sender with AWS SES
|
|
23
|
+
* TODO: Implement AWS SDK integration for SES VerifyEmailIdentity
|
|
24
|
+
*/
|
|
25
|
+
async verifySender(_sender) {
|
|
26
|
+
try {
|
|
27
|
+
// TODO: Call SES VerifyEmailIdentity API
|
|
28
|
+
// For now, return success (stub implementation)
|
|
29
|
+
// Real implementation would use AWS SDK:
|
|
30
|
+
// const ses = new SES({ region: this.region, credentials: {...} });
|
|
31
|
+
// await ses.verifyEmailIdentity({ EmailAddress: sender.email });
|
|
32
|
+
return {
|
|
33
|
+
success: true,
|
|
34
|
+
data: {
|
|
35
|
+
note: 'SES verification stubbed - AWS SDK integration pending'
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
return {
|
|
41
|
+
success: false,
|
|
42
|
+
error: error instanceof Error ? error.message : 'SES verification failed'
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.SESProvider = SESProvider;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { SenderIdentity, SenderIdentityResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* SMTP provider for sender verification
|
|
4
|
+
* Note: SMTP doesn't require provider-level verification
|
|
5
|
+
*/
|
|
6
|
+
export declare class SMTPProvider {
|
|
7
|
+
/**
|
|
8
|
+
* Verify sender with SMTP
|
|
9
|
+
* SMTP doesn't require provider verification - just email validation
|
|
10
|
+
*/
|
|
11
|
+
verifySender(_sender: SenderIdentity): Promise<SenderIdentityResult<Record<string, unknown>>>;
|
|
12
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SMTPProvider = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* SMTP provider for sender verification
|
|
13
|
+
* Note: SMTP doesn't require provider-level verification
|
|
14
|
+
*/
|
|
15
|
+
class SMTPProvider {
|
|
16
|
+
/**
|
|
17
|
+
* Verify sender with SMTP
|
|
18
|
+
* SMTP doesn't require provider verification - just email validation
|
|
19
|
+
*/
|
|
20
|
+
async verifySender(_sender) {
|
|
21
|
+
// SMTP doesn't require provider-level verification
|
|
22
|
+
return {
|
|
23
|
+
success: true,
|
|
24
|
+
data: {
|
|
25
|
+
note: 'SMTP does not require provider-level verification'
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
exports.SMTPProvider = SMTPProvider;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { SenderIdentity, SenderIdentityResult } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* SendGrid provider for sender verification
|
|
4
|
+
*/
|
|
5
|
+
export declare class SendGridProvider {
|
|
6
|
+
private apiKey;
|
|
7
|
+
constructor(apiKey: string);
|
|
8
|
+
/**
|
|
9
|
+
* Verify sender with SendGrid
|
|
10
|
+
*/
|
|
11
|
+
verifySender(sender: SenderIdentity): Promise<SenderIdentityResult<{
|
|
12
|
+
providerId: string;
|
|
13
|
+
metadata: Record<string, unknown>;
|
|
14
|
+
}>>;
|
|
15
|
+
/**
|
|
16
|
+
* Make SendGrid API request
|
|
17
|
+
*/
|
|
18
|
+
private sendGridRequest;
|
|
19
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.SendGridProvider = void 0;
|
|
11
|
+
/**
|
|
12
|
+
* SendGrid provider for sender verification
|
|
13
|
+
*/
|
|
14
|
+
class SendGridProvider {
|
|
15
|
+
constructor(apiKey) {
|
|
16
|
+
this.apiKey = apiKey;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Verify sender with SendGrid
|
|
20
|
+
*/
|
|
21
|
+
async verifySender(sender) {
|
|
22
|
+
try {
|
|
23
|
+
if (!this.apiKey) {
|
|
24
|
+
return {
|
|
25
|
+
success: false,
|
|
26
|
+
error: 'SendGrid API key not configured'
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
// Create verified sender in SendGrid
|
|
30
|
+
const response = await this.sendGridRequest('POST', '/verified_senders', {
|
|
31
|
+
nickname: sender.name,
|
|
32
|
+
from_email: sender.email,
|
|
33
|
+
from_name: sender.name,
|
|
34
|
+
reply_to: sender.replyToEmail,
|
|
35
|
+
reply_to_name: sender.replyToName,
|
|
36
|
+
address: '123 Main St',
|
|
37
|
+
city: 'Anytown',
|
|
38
|
+
state: 'CA', // Must be 2-character state code per SendGrid API requirements
|
|
39
|
+
zip: '12345',
|
|
40
|
+
country: 'US'
|
|
41
|
+
});
|
|
42
|
+
if (!response.success) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: 'SendGrid verification failed',
|
|
46
|
+
errors: [response.error || 'Unknown SendGrid error']
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
return {
|
|
50
|
+
success: true,
|
|
51
|
+
data: {
|
|
52
|
+
providerId: response.data?.id || '',
|
|
53
|
+
metadata: response.data || {}
|
|
54
|
+
}
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return {
|
|
59
|
+
success: false,
|
|
60
|
+
error: error instanceof Error ? error.message : 'SendGrid verification failed'
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Make SendGrid API request
|
|
66
|
+
*/
|
|
67
|
+
async sendGridRequest(method, path, body) {
|
|
68
|
+
try {
|
|
69
|
+
const response = await fetch(`https://api.sendgrid.com/v3${path}`, {
|
|
70
|
+
method,
|
|
71
|
+
headers: {
|
|
72
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
73
|
+
'Content-Type': 'application/json'
|
|
74
|
+
},
|
|
75
|
+
body: body ? JSON.stringify(body) : undefined
|
|
76
|
+
});
|
|
77
|
+
const data = await response.json();
|
|
78
|
+
if (!response.ok) {
|
|
79
|
+
return {
|
|
80
|
+
success: false,
|
|
81
|
+
error: data.errors?.[0]?.message || 'SendGrid API error',
|
|
82
|
+
errors: data.errors?.map((e) => e.message)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
success: true,
|
|
87
|
+
data: data
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
error: error instanceof Error ? error.message : 'SendGrid request failed'
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
exports.SendGridProvider = SendGridProvider;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { SenderIdentity } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Build HTML verification email
|
|
4
|
+
*/
|
|
5
|
+
export declare function buildVerificationEmailHtml(sender: SenderIdentity, verificationUrl: string): string;
|
|
6
|
+
/**
|
|
7
|
+
* Build plain text verification email
|
|
8
|
+
*/
|
|
9
|
+
export declare function buildVerificationEmailText(sender: SenderIdentity, verificationUrl: string): string;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Copyright (c) 2025 Bernier LLC
|
|
4
|
+
|
|
5
|
+
This file is licensed to the client under a limited-use license.
|
|
6
|
+
The client may use and modify this code *only within the scope of the project it was delivered for*.
|
|
7
|
+
Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.buildVerificationEmailHtml = buildVerificationEmailHtml;
|
|
11
|
+
exports.buildVerificationEmailText = buildVerificationEmailText;
|
|
12
|
+
/**
|
|
13
|
+
* Build HTML verification email
|
|
14
|
+
*/
|
|
15
|
+
function buildVerificationEmailHtml(sender, verificationUrl) {
|
|
16
|
+
return `
|
|
17
|
+
<!DOCTYPE html>
|
|
18
|
+
<html>
|
|
19
|
+
<head>
|
|
20
|
+
<style>
|
|
21
|
+
body { font-family: Arial, sans-serif; line-height: 1.6; color: #333; }
|
|
22
|
+
.container { max-width: 600px; margin: 0 auto; padding: 20px; }
|
|
23
|
+
.button { background-color: #007bff; color: white; padding: 12px 24px; text-decoration: none; border-radius: 4px; display: inline-block; margin: 20px 0; }
|
|
24
|
+
.footer { margin-top: 40px; padding-top: 20px; border-top: 1px solid #ddd; font-size: 12px; color: #666; }
|
|
25
|
+
</style>
|
|
26
|
+
</head>
|
|
27
|
+
<body>
|
|
28
|
+
<div class="container">
|
|
29
|
+
<h1>Verify Your Sender Email Address</h1>
|
|
30
|
+
<p>Hello ${sender.name},</p>
|
|
31
|
+
<p>You've added <strong>${sender.email}</strong> as a sender email address for ${sender.provider}.</p>
|
|
32
|
+
<p>To complete the verification process, please click the button below:</p>
|
|
33
|
+
<a href="${verificationUrl}" class="button">Verify Email Address</a>
|
|
34
|
+
<p>Or copy and paste this URL into your browser:</p>
|
|
35
|
+
<p>${verificationUrl}</p>
|
|
36
|
+
<p><strong>This link will expire in 24 hours.</strong></p>
|
|
37
|
+
<div class="footer">
|
|
38
|
+
<p>If you didn't request this verification, please ignore this email.</p>
|
|
39
|
+
<p>Sender ID: ${sender.id}</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
</body>
|
|
43
|
+
</html>
|
|
44
|
+
`;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Build plain text verification email
|
|
48
|
+
*/
|
|
49
|
+
function buildVerificationEmailText(sender, verificationUrl) {
|
|
50
|
+
return `
|
|
51
|
+
Verify Your Sender Email Address
|
|
52
|
+
|
|
53
|
+
Hello ${sender.name},
|
|
54
|
+
|
|
55
|
+
You've added ${sender.email} as a sender email address for ${sender.provider}.
|
|
56
|
+
|
|
57
|
+
To complete the verification process, please visit:
|
|
58
|
+
|
|
59
|
+
${verificationUrl}
|
|
60
|
+
|
|
61
|
+
This link will expire in 24 hours.
|
|
62
|
+
|
|
63
|
+
If you didn't request this verification, please ignore this email.
|
|
64
|
+
|
|
65
|
+
Sender ID: ${sender.id}
|
|
66
|
+
`.trim();
|
|
67
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sender identity record
|
|
3
|
+
*/
|
|
4
|
+
export interface SenderIdentity {
|
|
5
|
+
id: string;
|
|
6
|
+
email: string;
|
|
7
|
+
name: string;
|
|
8
|
+
replyToEmail?: string;
|
|
9
|
+
replyToName?: string;
|
|
10
|
+
domain: string;
|
|
11
|
+
provider: EmailProvider;
|
|
12
|
+
status: SenderStatus;
|
|
13
|
+
isDefault: boolean;
|
|
14
|
+
isActive: boolean;
|
|
15
|
+
verifiedAt?: Date;
|
|
16
|
+
verificationToken?: string;
|
|
17
|
+
verificationSentAt?: Date;
|
|
18
|
+
verificationExpiresAt?: Date;
|
|
19
|
+
verificationAttempts: number;
|
|
20
|
+
providerSenderId?: string;
|
|
21
|
+
providerMetadata?: Record<string, unknown>;
|
|
22
|
+
lastValidated?: Date;
|
|
23
|
+
validationErrors?: string[];
|
|
24
|
+
createdAt: Date;
|
|
25
|
+
updatedAt: Date;
|
|
26
|
+
deletedAt?: Date;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Sender status enum
|
|
30
|
+
*/
|
|
31
|
+
export declare enum SenderStatus {
|
|
32
|
+
PENDING = "pending",// Created, awaiting verification
|
|
33
|
+
VERIFICATION_SENT = "verification_sent",// Verification email sent
|
|
34
|
+
VERIFIED = "verified",// Email verified, ready to use
|
|
35
|
+
FAILED = "failed",// Verification failed
|
|
36
|
+
EXPIRED = "expired",// Verification token expired
|
|
37
|
+
LOCKED = "locked",// Too many failed attempts
|
|
38
|
+
INACTIVE = "inactive"
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Email provider enum
|
|
42
|
+
*/
|
|
43
|
+
export declare enum EmailProvider {
|
|
44
|
+
SENDGRID = "sendgrid",
|
|
45
|
+
MAILGUN = "mailgun",
|
|
46
|
+
SES = "ses",
|
|
47
|
+
SMTP = "smtp"
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Sender creation input
|
|
51
|
+
*/
|
|
52
|
+
export interface CreateSenderInput {
|
|
53
|
+
email: string;
|
|
54
|
+
name: string;
|
|
55
|
+
replyToEmail?: string;
|
|
56
|
+
replyToName?: string;
|
|
57
|
+
provider: EmailProvider;
|
|
58
|
+
isDefault?: boolean;
|
|
59
|
+
skipVerification?: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Sender update input
|
|
63
|
+
*/
|
|
64
|
+
export interface UpdateSenderInput {
|
|
65
|
+
name?: string;
|
|
66
|
+
replyToEmail?: string;
|
|
67
|
+
replyToName?: string;
|
|
68
|
+
isDefault?: boolean;
|
|
69
|
+
isActive?: boolean;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Verification result
|
|
73
|
+
*/
|
|
74
|
+
export interface VerificationResult {
|
|
75
|
+
success: boolean;
|
|
76
|
+
senderId: string;
|
|
77
|
+
status: SenderStatus;
|
|
78
|
+
message: string;
|
|
79
|
+
verifiedAt?: Date;
|
|
80
|
+
errors?: string[];
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Provider compliance check result
|
|
84
|
+
*/
|
|
85
|
+
export interface ComplianceCheckResult {
|
|
86
|
+
isCompliant: boolean;
|
|
87
|
+
checks: {
|
|
88
|
+
domainVerified: boolean;
|
|
89
|
+
spfValid: boolean;
|
|
90
|
+
dkimValid: boolean;
|
|
91
|
+
emailFormat: boolean;
|
|
92
|
+
};
|
|
93
|
+
errors: string[];
|
|
94
|
+
warnings: string[];
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Sender list options
|
|
98
|
+
*/
|
|
99
|
+
export interface ListSendersOptions {
|
|
100
|
+
provider?: EmailProvider;
|
|
101
|
+
status?: SenderStatus;
|
|
102
|
+
isActive?: boolean;
|
|
103
|
+
domain?: string;
|
|
104
|
+
limit?: number;
|
|
105
|
+
offset?: number;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Package result wrapper
|
|
109
|
+
*/
|
|
110
|
+
export interface SenderIdentityResult<T = unknown> {
|
|
111
|
+
success: boolean;
|
|
112
|
+
data?: T;
|
|
113
|
+
error?: string;
|
|
114
|
+
errors?: string[];
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Domain verification status (interface for missing @bernierllc/email-domain-verification)
|
|
118
|
+
* TODO: Replace with actual import when email-domain-verification package is available
|
|
119
|
+
*/
|
|
120
|
+
export interface DomainVerificationStatus {
|
|
121
|
+
isVerified: boolean;
|
|
122
|
+
domain: string;
|
|
123
|
+
verificationUrl?: string;
|
|
124
|
+
dnsRecords?: {
|
|
125
|
+
spf?: {
|
|
126
|
+
isValid: boolean;
|
|
127
|
+
};
|
|
128
|
+
dkim?: {
|
|
129
|
+
isValid: boolean;
|
|
130
|
+
};
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Email domain verification interface (stub for missing dependency)
|
|
135
|
+
* TODO: Replace with actual @bernierllc/email-domain-verification when available
|
|
136
|
+
*/
|
|
137
|
+
export interface IEmailDomainVerification {
|
|
138
|
+
getDomainStatus(domain: string): Promise<DomainVerificationStatus>;
|
|
139
|
+
}
|