unemail
Version:
A modern TypeScript email library with zero dependencies, supporting multiple providers including AWS SES, Resend, MailCrab, and HTTP APIs
142 lines (139 loc) • 4.08 kB
JavaScript
import smtpProvider from 'unemail/providers/smtp';
import { createError } from 'unemail/utils';
export { defineProvider } from './providers/base.mjs';
const DEFAULT_PROVIDER = smtpProvider;
class EmailService {
provider;
options;
initialized = false;
/**
* Creates a new email service instance
*
* @param options Configuration options for the email service
*/
constructor(options = {}) {
this.options = {
debug: options.debug || false,
timeout: options.timeout || 3e4,
retries: options.retries || 3,
provider: options.provider,
config: options.config
};
}
/**
* Get the provider instance
*/
async getProvider() {
if (!this.provider) {
try {
const providerOption = this.options.provider || DEFAULT_PROVIDER;
if (typeof providerOption === "function") {
const config = this.options.config?.options || {};
if (providerOption === DEFAULT_PROVIDER && !("host" in config)) {
config.host = "localhost";
config.port = 1025;
}
this.provider = providerOption(config);
} else if (providerOption && typeof providerOption === "object" && "initialize" in providerOption) {
this.provider = providerOption;
} else if (providerOption && typeof providerOption === "object" && "name" in providerOption) {
throw new Error(`Provider specification with name property is no longer supported. Please import the provider directly and pass the provider instance or factory.`);
} else {
throw new Error("Invalid provider configuration. Please provide a valid provider instance or factory function.");
}
} catch (error) {
throw createError(
"core",
`Failed to initialize provider: ${error.message}`,
{ cause: error }
);
}
}
return this.provider;
}
/**
* Initializes the email service and underlying provider
*/
async initialize() {
if (this.initialized) {
return;
}
try {
const provider = await this.getProvider();
await provider.initialize();
this.initialized = true;
} catch (error) {
throw createError(
"core",
`Failed to initialize email service: ${error.message}`,
{ cause: error }
);
}
}
/**
* Checks if the configured provider is available
*
* @returns Promise resolving to a boolean indicating availability
*/
async isAvailable() {
try {
const provider = await this.getProvider();
return await provider.isAvailable();
} catch (error) {
if (this.options.debug) {
console.error("Error checking provider availability:", error);
}
return false;
}
}
/**
* Sends an email using the configured provider
*
* @param options Email sending options
* @returns Promise resolving to email result
*/
async sendEmail(options) {
try {
if (!this.initialized) {
await this.initialize();
}
const provider = await this.getProvider();
return await provider.sendEmail(options);
} catch (error) {
return {
success: false,
error: createError(
"core",
`Failed to send email: ${error.message}`,
{ cause: error }
)
};
}
}
/**
* Validates credentials for the current provider
*
* @returns Promise resolving to a boolean indicating if credentials are valid
*/
async validateCredentials() {
try {
if (!this.initialized) {
await this.initialize();
}
const provider = await this.getProvider();
if (provider.validateCredentials) {
return await provider.validateCredentials();
}
return true;
} catch (error) {
if (this.options.debug) {
console.error("Error validating credentials:", error);
}
return false;
}
}
}
function createEmailService(options = {}) {
return new EmailService(options);
}
export { EmailService, createEmailService };