BREAKING CHANGE(smartmail): Improve email validation and Smartmail features: add detailed validation for email parts, caching for MX lookups, multi-recipient support, custom headers, and update dependency imports and build scripts.
This commit is contained in:
parent
442bc5a9d9
commit
e395a059a6
@ -119,6 +119,6 @@ jobs:
|
||||
run: |
|
||||
npmci node install stable
|
||||
npmci npm install
|
||||
pnpm install -g @gitzone/tsdoc
|
||||
pnpm install -g @git.zone/tsdoc
|
||||
npmci command tsdoc
|
||||
continue-on-error: true
|
||||
|
3
.gitignore
vendored
3
.gitignore
vendored
@ -17,4 +17,5 @@ node_modules/
|
||||
dist/
|
||||
dist_*/
|
||||
|
||||
# custom
|
||||
# custom
|
||||
**/.claude/settings.local.json
|
||||
|
33
changelog.md
Normal file
33
changelog.md
Normal file
@ -0,0 +1,33 @@
|
||||
# Changelog
|
||||
|
||||
## 2025-05-07 - 2.0.0 - BREAKING CHANGE(smartmail)
|
||||
Improve email validation and Smartmail features: add detailed validation for email parts, caching for MX lookups, multi-recipient support, custom headers, and update dependency imports and build scripts.
|
||||
|
||||
- Updated dependency references from '@gitzone/*' to '@git.zone/*'
|
||||
- Enhanced EmailAddressValidator with RFC 5322 compliance, local part, domain part validation, and detailed result fields
|
||||
- Implemented caching mechanism for MX record lookups
|
||||
- Expanded Smartmail class: added support for multiple recipients, reply-to, custom headers, and MIME formatting
|
||||
- Updated build scripts, package configuration, and documentation with more comprehensive usage examples and tests
|
||||
|
||||
## 2024-05-29 - 1.0.24 - misc
|
||||
This release improved several project configurations and documentation details.
|
||||
|
||||
- docs: Updated the project description.
|
||||
- config: Revised tsconfig settings.
|
||||
- npmextra: Adjusted npmextra.json with updated githost values (applied on multiple commits).
|
||||
|
||||
## 2023-07-28 - 1.0.23 - misc
|
||||
A couple of targeted fixes and organizational changes were introduced.
|
||||
|
||||
- core: Applied a core fix update.
|
||||
- org: Switched to the new organization scheme.
|
||||
|
||||
## 2023-06-13 - 1.0.22 - core
|
||||
Over a longer period (versions 1.0.22 down to 1.0.4), a series of minor core fixes were rolled out to improve stability and maintainability.
|
||||
These commits—which, aside from their version‐marking commits, were solely “fix(core): update”—were consolidated into this summary.
|
||||
|
||||
## 2018-09-29 - 1.0.1 - initial
|
||||
In the very early releases (from versions 1.0.3 to 1.0.1), the initial stabilization work set the stage for the project.
|
||||
- npm: Adjusted access level (fix(npm): access level in 1.0.3).
|
||||
- packagename: Updated package name details (fix(packagename): update in 1.0.2).
|
||||
- core: Laid the groundwork with the initial core fix (fix(core): initial in 1.0.1).
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"npmci": {
|
||||
"npmGlobalTools": [
|
||||
"@gitzone/npmts",
|
||||
"@git.zone/npmts",
|
||||
"ts-node"
|
||||
],
|
||||
"npmAccessLevel": "public"
|
||||
|
21
package.json
21
package.json
@ -11,19 +11,19 @@
|
||||
"scripts": {
|
||||
"test": "(tstest test/)",
|
||||
"format": "(gitzone format)",
|
||||
"build": "(tsbuild --web --allowimplicitany)",
|
||||
"build": "(tsbuild tsfolders --allowimplicitany)",
|
||||
"buildDocs": "tsdoc"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@gitzone/tsbuild": "^2.1.66",
|
||||
"@gitzone/tsrun": "^1.2.44",
|
||||
"@gitzone/tstest": "^1.0.77",
|
||||
"@push.rocks/tapbundle": "^5.0.12",
|
||||
"@types/node": "^20.4.5"
|
||||
"@git.zone/tsbuild": "^2.3.2",
|
||||
"@git.zone/tsrun": "^1.3.3",
|
||||
"@git.zone/tstest": "^1.0.96",
|
||||
"@push.rocks/tapbundle": "^6.0.3",
|
||||
"@types/node": "^22.15.14"
|
||||
},
|
||||
"dependencies": {
|
||||
"@push.rocks/smartdns": "^5.0.4",
|
||||
"@push.rocks/smartfile": "^10.0.28",
|
||||
"@push.rocks/smartdns": "^6.2.2",
|
||||
"@push.rocks/smartfile": "^11.2.0",
|
||||
"@push.rocks/smartmustache": "^3.0.2",
|
||||
"@push.rocks/smartpath": "^5.0.11",
|
||||
"@push.rocks/smartrequest": "^2.0.18"
|
||||
@ -59,5 +59,6 @@
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://code.foss.global/push.rocks/smartmail.git"
|
||||
}
|
||||
}
|
||||
},
|
||||
"packageManager": "pnpm@10.10.0+sha512.d615db246fe70f25dcfea6d8d73dee782ce23e2245e3c4f6f888249fb568149318637dca73c2c5c8ef2a4ca0d5657fb9567188bfab47f566d1ee6ce987815c39"
|
||||
}
|
||||
|
12275
pnpm-lock.yaml
generated
12275
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
237
readme.md
237
readme.md
@ -1,19 +1,27 @@
|
||||
# @push.rocks/smartmail
|
||||
a unified format for representing and dealing with mails
|
||||
A unified format for representing and dealing with emails
|
||||
|
||||
## Install
|
||||
|
||||
To install `@push.rocks/smartmail`, you'll need Node.js installed on your system. With Node.js installed, run the following command in your terminal:
|
||||
|
||||
```bash
|
||||
npm install @push.rocks/smartmail --save
|
||||
pnpm add @push.rocks/smartmail
|
||||
```
|
||||
|
||||
This will add `@push.rocks/smartmail` to your project's dependencies.
|
||||
|
||||
## Features
|
||||
|
||||
- **Advanced Email Address Validation**: Validate email format, check for disposable/free domains, and verify MX records
|
||||
- **Rich Email Representation**: Create emails with multiple recipients, attachments, HTML content, and custom headers
|
||||
- **Template Support**: Use mustache templates for dynamic content in subject, body, and HTML
|
||||
- **MIME Formatting**: Convert emails to standard MIME format for sending
|
||||
- **Caching & Performance**: Smart caching of DNS lookups for better performance
|
||||
|
||||
## Usage
|
||||
|
||||
`@push.rocks/smartmail` provides a unified format for representing and dealing with emails in a Node.js environment. Below, you will find several examples showcasing how to use its main features, including email address validation and creation of mail objects with attachments.
|
||||
`@push.rocks/smartmail` provides a unified format for representing and dealing with emails in a Node.js environment. Below, you will find several examples showcasing how to use its main features.
|
||||
|
||||
### Importing the Module
|
||||
|
||||
@ -26,81 +34,206 @@ import {
|
||||
} from '@push.rocks/smartmail';
|
||||
```
|
||||
|
||||
### Validate Email Addresses
|
||||
## Email Address Validation
|
||||
|
||||
You can validate email addresses to check whether they are disposable, belong to a free email provider, or verify their validity in terms of having an MX record.
|
||||
### Basic Email Validation
|
||||
|
||||
```typescript
|
||||
// Instantiate the EmailAddressValidator
|
||||
// Create validator with default options
|
||||
const emailValidator = new EmailAddressValidator();
|
||||
|
||||
// Validate an email address
|
||||
const validateEmail = async (email: string) => {
|
||||
const validationResult = await emailValidator.validate(email);
|
||||
console.log(validationResult);
|
||||
};
|
||||
const result = await emailValidator.validate('user@example.com');
|
||||
console.log(result);
|
||||
/*
|
||||
{
|
||||
valid: true, // Overall validity
|
||||
formatValid: true, // Email format is valid
|
||||
localPartValid: true, // Local part (before @) is valid
|
||||
domainPartValid: true, // Domain part (after @) is valid
|
||||
mxValid: true, // Domain has valid MX records
|
||||
disposable: false, // Not a disposable email domain
|
||||
freemail: false, // Not a free email provider
|
||||
reason: 'Email is valid'
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
// Example usage
|
||||
validateEmail('example@gmail.com').then(() => {
|
||||
console.log('Email validation completed.');
|
||||
### Advanced Validation Options
|
||||
|
||||
```typescript
|
||||
// Create validator with custom options
|
||||
const validator = new EmailAddressValidator({
|
||||
skipOnlineDomainFetch: true, // Use only local domain list
|
||||
cacheDnsResults: true, // Cache DNS lookups
|
||||
cacheExpiryMs: 7200000 // Cache expires after 2 hours
|
||||
});
|
||||
|
||||
// Validate each part of an email separately
|
||||
const isValidFormat = validator.isValidEmailFormat('user@example.com');
|
||||
const isValidLocalPart = validator.isValidLocalPart('user');
|
||||
const isValidDomain = validator.isValidDomainPart('example.com');
|
||||
|
||||
// Check for disposable or free email providers
|
||||
const result = await validator.validate('user@gmail.com');
|
||||
if (result.freemail) {
|
||||
console.log('This is a free email provider');
|
||||
}
|
||||
```
|
||||
|
||||
## Creating and Using Smartmail Objects
|
||||
|
||||
### Basic Email Creation
|
||||
|
||||
```typescript
|
||||
// Create a simple email
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Hello from SmartMail',
|
||||
body: 'This is a plain text email'
|
||||
});
|
||||
```
|
||||
|
||||
### Creating and Using Smartmail Objects
|
||||
|
||||
`Smartmail` class allows you to represent an email in a structured way, including attachments, subject, body, and more. Here's how to use it:
|
||||
### Adding Recipients
|
||||
|
||||
```typescript
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
subject: 'Meeting Invitation',
|
||||
body: 'Please join our meeting'
|
||||
});
|
||||
|
||||
// Add recipients in various ways
|
||||
email.addRecipient('primary@example.com');
|
||||
email.addRecipients(['user1@example.com', 'user2@example.com']);
|
||||
email.addRecipient('manager@example.com', 'cc');
|
||||
email.addRecipients(['observer1@example.com', 'observer2@example.com'], 'bcc');
|
||||
```
|
||||
|
||||
### Template Variables in Subject and Body
|
||||
|
||||
```typescript
|
||||
const template = new Smartmail({
|
||||
from: 'notifications@example.com',
|
||||
subject: 'Welcome, {{name}}!',
|
||||
body: 'Hello {{name}},\n\nWelcome to our service. Your account ({{email}}) has been activated.',
|
||||
htmlBody: '<h1>Welcome, {{name}}!</h1><p>Hello {{name}},<br><br>Welcome to our service. Your account (<strong>{{email}}</strong>) has been activated.</p>'
|
||||
});
|
||||
|
||||
// Apply template variables
|
||||
const subject = template.getSubject({ name: 'John Doe' });
|
||||
const plainBody = template.getBody({ name: 'John Doe', email: 'john@example.com' });
|
||||
const htmlBody = template.getHtmlBody({ name: 'John Doe', email: 'john@example.com' });
|
||||
```
|
||||
|
||||
### Adding Attachments
|
||||
|
||||
```typescript
|
||||
import { Smartmail } from '@push.rocks/smartmail';
|
||||
import { Smartfile } from '@push.rocks/smartfile';
|
||||
|
||||
// Create a new Smartmail object
|
||||
const myMail = new Smartmail({
|
||||
from: 'no-reply@yourdomain.com',
|
||||
subject: 'Welcome to Our Service',
|
||||
body: 'Hello {{name}}, welcome to our service!'
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Report Attached',
|
||||
body: 'Please find the attached report.'
|
||||
});
|
||||
|
||||
// Use Smartfile to prepare an attachment (optional)
|
||||
const attachment = new Smartfile.fromLocalPath('path/to/your/attachment.pdf');
|
||||
myMail.addAttachment(attachment);
|
||||
|
||||
// Accessing and modifying the mail object's properties
|
||||
console.log(myMail.getSubject({ name: 'John Doe' }));
|
||||
console.log(myMail.getBody({ name: 'John Doe' }));
|
||||
|
||||
// The `getCreationObject` method can be used to retrieve the original creation object
|
||||
console.log(myMail.getCreationObject());
|
||||
// Add file attachments
|
||||
const report = new Smartfile.fromLocalPath('/path/to/report.pdf');
|
||||
const image = new Smartfile.fromLocalPath('/path/to/image.png');
|
||||
email.addAttachment(report);
|
||||
email.addAttachment(image);
|
||||
```
|
||||
|
||||
### Email Address Validation Details
|
||||
|
||||
The `EmailAddressValidator` class fetches domain information either from a local JSON file or an online source to categorize email domains as disposable, free, or valid based on MX records. Here's a deeper look into validating email addresses and interpreting the results:
|
||||
### Setting Email Importance and Headers
|
||||
|
||||
```typescript
|
||||
// Instantiate the validator
|
||||
const validator = new EmailAddressValidator();
|
||||
|
||||
// Validate an email
|
||||
validator.validate('someone@disposablemail.com').then(result => {
|
||||
if (result.valid && !result.disposable) {
|
||||
console.log('Email is valid and not disposable.');
|
||||
} else {
|
||||
console.log('Email is either invalid or disposable.');
|
||||
}
|
||||
if (result.freemail) {
|
||||
console.log('Email belongs to a free mail provider.');
|
||||
}
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Urgent: System Alert',
|
||||
body: 'Critical system alert requires immediate attention.'
|
||||
});
|
||||
|
||||
// Set high priority
|
||||
email.setPriority('high');
|
||||
|
||||
// Add custom headers
|
||||
email.addHeader('X-Custom-ID', '12345');
|
||||
email.addHeader('X-System-Alert', 'Critical');
|
||||
```
|
||||
|
||||
### Handling Attachments
|
||||
### Converting to MIME Format for Sending
|
||||
|
||||
As shown in the example, attachments can be added to a `Smartmail` object. `Smartfile` (from `@push.rocks/smartfile`) is utilized to handle file operations, allowing you to attach files easily to your email object. Ensure that attachments are in the appropriate format and accessible before adding them to the `Smartmail` object.
|
||||
```typescript
|
||||
const email = new Smartmail({
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Hello {{name}}',
|
||||
body: 'Text version: Hello {{name}}',
|
||||
htmlBody: '<p>HTML version: Hello <strong>{{name}}</strong></p>',
|
||||
validateEmails: true // Will validate all emails before converting
|
||||
});
|
||||
|
||||
### Conclusion
|
||||
// Convert to MIME format with template data
|
||||
const mimeObj = await email.toMimeFormat({ name: 'John' });
|
||||
|
||||
These examples cover the basics of using `@push.rocks/smartmail` for handling emails within your Node.js applications. By leveraging the `Smartmail` and `EmailAddressValidator` classes, you can efficiently represent, validate, and manage email data, enhancing the robustness and functionality of your email-related features.
|
||||
// Result can be used with nodemailer or other email sending libraries
|
||||
console.log(mimeObj);
|
||||
/*
|
||||
{
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
subject: 'Hello John',
|
||||
text: 'Text version: Hello John',
|
||||
html: '<p>HTML version: Hello <strong>John</strong></p>',
|
||||
attachments: [...],
|
||||
headers: {...}
|
||||
}
|
||||
*/
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### EmailAddressValidator
|
||||
|
||||
#### Constructor Options
|
||||
- `skipOnlineDomainFetch`: Boolean (default: false) - Skip fetching domain list from online source
|
||||
- `cacheDnsResults`: Boolean (default: true) - Cache DNS lookup results
|
||||
- `cacheExpiryMs`: Number (default: 3600000) - Cache expiry in milliseconds
|
||||
|
||||
#### Methods
|
||||
- `validate(email: string)`: Validates email address completeness
|
||||
- `isValidEmailFormat(email: string)`: Checks if email format is valid
|
||||
- `isValidLocalPart(localPart: string)`: Validates local part of email
|
||||
- `isValidDomainPart(domainPart: string)`: Validates domain part of email
|
||||
- `checkMxRecords(domain: string)`: Checks MX records for a domain
|
||||
|
||||
### Smartmail
|
||||
|
||||
#### Constructor Options
|
||||
- `from`: Email address of sender
|
||||
- `to`, `cc`, `bcc`: Optional arrays of recipient email addresses
|
||||
- `subject`: Email subject line
|
||||
- `body`: Plain text email body
|
||||
- `htmlBody`: Optional HTML version of email body
|
||||
- `replyTo`: Optional reply-to email address
|
||||
- `headers`: Optional key-value pairs of custom headers
|
||||
- `priority`: 'high' | 'normal' | 'low' (default: 'normal')
|
||||
- `validateEmails`: Boolean (default: false) - Validate all emails
|
||||
|
||||
#### Methods
|
||||
- `addRecipient(email, type?)`: Add a single recipient (to/cc/bcc)
|
||||
- `addRecipients(emails, type?)`: Add multiple recipients
|
||||
- `setReplyTo(email)`: Set reply-to address
|
||||
- `setPriority(priority)`: Set email priority
|
||||
- `addHeader(name, value)`: Add custom header
|
||||
- `getSubject(data?)`: Get processed subject with template variables
|
||||
- `getBody(data?)`: Get processed plain text body
|
||||
- `getHtmlBody(data?)`: Get processed HTML body
|
||||
- `validateAllEmails()`: Validate all email addresses
|
||||
- `toMimeFormat(data?)`: Convert to MIME format object
|
||||
|
||||
## License and Legal Information
|
||||
|
||||
|
92
readme.plan.md
Normal file
92
readme.plan.md
Normal file
@ -0,0 +1,92 @@
|
||||
# Improvement Plan for SmartMail
|
||||
|
||||
## Current Code Analysis
|
||||
|
||||
### Core Components
|
||||
- **EmailAddressValidator**: Validates email addresses checking for validity, whether they are disposable or free
|
||||
- Uses MX record checks to validate domains
|
||||
- Has a domain map from external source that classifies domains as 'disposable' or 'freemail'
|
||||
- Has placeholder 'reason' field in results that's not implemented ("todo")
|
||||
- Fetches domain list from GitHub or falls back to local copy
|
||||
- No validation of email format before DNS checks
|
||||
|
||||
- **Smartmail**: Email representation class
|
||||
- Basic email structure with from, subject, body
|
||||
- Supports file attachments via smartfile
|
||||
- Uses mustache templates for subject and body
|
||||
- Lacks recipient handling (to, cc, bcc)
|
||||
- No method to send emails
|
||||
- No HTML body support
|
||||
|
||||
### Dependencies
|
||||
- Uses several @push.rocks packages:
|
||||
- smartdns: For DNS lookups
|
||||
- smartfile: For file handling/attachments
|
||||
- smartmustache: For template processing
|
||||
- smartpath: For path operations
|
||||
- smartrequest: For HTTP requests
|
||||
|
||||
### Testing
|
||||
- Basic tests for EmailAddressValidator
|
||||
- Tests validation of regular, free, and disposable emails
|
||||
- No tests for invalid email formats or edge cases
|
||||
- Minimal test for Smartmail creation
|
||||
- No tests for attachments, templates, or other features
|
||||
|
||||
## Enhanced Improvement Plan
|
||||
|
||||
1. [x] Complete the EmailAddressValidator implementation
|
||||
- [x] Implement proper reason messages for validation results
|
||||
- [x] Add email format validation before DNS checks (RFC 5322 compliance)
|
||||
- [x] Add local part validation (check for illegal characters, proper format)
|
||||
- [x] Improve domain validation (check syntax before DNS lookup)
|
||||
- [x] Add email normalization (handle case sensitivity, plus addressing)
|
||||
- [x] Implement caching mechanism for DNS lookups to improve performance
|
||||
- [x] Add option to disable online domain list fetching
|
||||
|
||||
2. [x] Enhance the Smartmail class
|
||||
- [x] Add support for multiple recipients (to, cc, bcc arrays)
|
||||
- [x] Add email preparing capabilities via MIME format
|
||||
- [x] Support HTML email bodies with plain text fallback
|
||||
- [x] Add reply-to and headers support
|
||||
- [x] Implement method to convert to standard email formats (MIME)
|
||||
- [x] Add email priority and importance flags
|
||||
- [x] Add validation of email addresses used in from/to/cc/bcc
|
||||
|
||||
3. [x] Improve testing
|
||||
- [x] Add tests for email format validation (valid/invalid formats)
|
||||
- [x] Test domain validation edge cases (non-existent domains, etc.)
|
||||
- [x] Add tests for attachment handling
|
||||
- [x] Test template processing with different data structures
|
||||
- [x] Add tests for HTML emails and conversion
|
||||
- [x] Test recipient handling with multiple addresses
|
||||
|
||||
4. [x] Performance & security improvements
|
||||
- [x] Optimize domain list handling
|
||||
- [x] Implement intelligent caching strategy for validation results
|
||||
- [x] Add configuration options for external service calls
|
||||
- [x] Ensure secure handling of email data and attachments
|
||||
|
||||
5. [x] Documentation improvements
|
||||
- [x] Update README with comprehensive examples
|
||||
- [x] Add detailed API documentation with JSDoc
|
||||
- [x] Document all configuration options
|
||||
- [x] Add usage examples for common scenarios
|
||||
- [x] Document security considerations
|
||||
- [x] Add TypeScript type documentation
|
||||
|
||||
6. [ ] Advanced features
|
||||
- [ ] DKIM/SPF validation support
|
||||
- [ ] Implement email address suggestions for typos
|
||||
- [ ] Add disposable email detection improvements
|
||||
- [ ] Support for internationalized email addresses (IDN)
|
||||
- [ ] Email address reputation checking
|
||||
- [ ] Add email deliverability scoring
|
||||
- [ ] Implement bounce address validation
|
||||
|
||||
7. [x] Code quality
|
||||
- [x] Add more TypeScript interfaces for clearer API definitions
|
||||
- [x] Improve error handling with specific error types
|
||||
- [x] Add configuration options via constructor
|
||||
- [x] Make domain list updates configurable
|
||||
- [x] Improve code organization with better separation of concerns
|
118
test/test.ts
118
test/test.ts
@ -1,36 +1,61 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import * as smartmail from '../ts/index.js';
|
||||
import * as plugins from '../ts/smartmail.plugins.js';
|
||||
|
||||
let emailAddressValidatorInstance: smartmail.EmailAddressValidator;
|
||||
|
||||
// EmailAddressValidator Tests
|
||||
tap.test('should create an instance of EmailAddressValidator', async () => {
|
||||
emailAddressValidatorInstance = new smartmail.EmailAddressValidator();
|
||||
expect(emailAddressValidatorInstance).toBeInstanceOf(smartmail.EmailAddressValidator);
|
||||
});
|
||||
|
||||
tap.test('should validate an email', async () => {
|
||||
tap.test('should validate an email with detailed information', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('sandbox@bleu.de');
|
||||
expect(result.freemail).toBeFalse();
|
||||
expect(result.disposable).toBeFalse();
|
||||
console.log(result);
|
||||
expect(result.formatValid).toBeTrue();
|
||||
expect(result.localPartValid).toBeTrue();
|
||||
expect(result.domainPartValid).toBeTrue();
|
||||
expect(result.reason).toBeDefined();
|
||||
});
|
||||
|
||||
tap.test('should recognize an email as freemail', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('sandbox@gmail.com');
|
||||
expect(result.freemail).toBeTrue();
|
||||
expect(result.disposable).toBeFalse();
|
||||
console.log(result);
|
||||
expect(result.formatValid).toBeTrue();
|
||||
expect(result.valid).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('should recognize an email as disposable', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('sandbox@gmx.de');
|
||||
expect(result.freemail).toBeFalse();
|
||||
expect(result.disposable).toBeTrue();
|
||||
console.log(result);
|
||||
expect(result.formatValid).toBeTrue();
|
||||
});
|
||||
|
||||
tap.test('should create a SmartMail', async () => {
|
||||
const testSmartmail = new smartmail.Smartmail({
|
||||
tap.test('should detect invalid email format', async () => {
|
||||
const result = await emailAddressValidatorInstance.validate('invalid-email');
|
||||
expect(result.formatValid).toBeFalse();
|
||||
expect(result.valid).toBeFalse();
|
||||
expect(result.reason).toEqual('Invalid email format');
|
||||
});
|
||||
|
||||
tap.test('should validate email format parts separately', async () => {
|
||||
expect(emailAddressValidatorInstance.isValidEmailFormat('valid.email@example.com')).toBeTrue();
|
||||
expect(emailAddressValidatorInstance.isValidEmailFormat('invalid-email')).toBeFalse();
|
||||
expect(emailAddressValidatorInstance.isValidLocalPart('valid.local.part')).toBeTrue();
|
||||
expect(emailAddressValidatorInstance.isValidLocalPart('invalid..part')).toBeFalse();
|
||||
expect(emailAddressValidatorInstance.isValidDomainPart('example.com')).toBeTrue();
|
||||
expect(emailAddressValidatorInstance.isValidDomainPart('invalid')).toBeFalse();
|
||||
});
|
||||
|
||||
// Smartmail Tests
|
||||
let testSmartmail: smartmail.Smartmail<any>;
|
||||
|
||||
tap.test('should create a SmartMail instance', async () => {
|
||||
testSmartmail = new smartmail.Smartmail({
|
||||
body: 'hi there',
|
||||
from: 'noreply@mail.lossless.com',
|
||||
subject: 'hi from here',
|
||||
@ -38,4 +63,85 @@ tap.test('should create a SmartMail', async () => {
|
||||
expect(testSmartmail).toBeInstanceOf(smartmail.Smartmail);
|
||||
});
|
||||
|
||||
tap.test('should handle email recipients', async () => {
|
||||
testSmartmail.addRecipient('user1@example.com');
|
||||
testSmartmail.addRecipients(['user2@example.com', 'user3@example.com'], 'cc');
|
||||
testSmartmail.addRecipient('user4@example.com', 'bcc');
|
||||
|
||||
expect(testSmartmail.options.to!.length).toEqual(1);
|
||||
expect(testSmartmail.options.cc!.length).toEqual(2);
|
||||
expect(testSmartmail.options.bcc!.length).toEqual(1);
|
||||
});
|
||||
|
||||
tap.test('should set reply-to and priority', async () => {
|
||||
testSmartmail.setReplyTo('replies@example.com');
|
||||
testSmartmail.setPriority('high');
|
||||
|
||||
expect(testSmartmail.options.replyTo).toEqual('replies@example.com');
|
||||
expect(testSmartmail.options.priority).toEqual('high');
|
||||
});
|
||||
|
||||
tap.test('should apply template data to subject and body', async () => {
|
||||
const templateMailer = new smartmail.Smartmail({
|
||||
subject: 'Hello {{name}}',
|
||||
body: 'Welcome, {{name}}! Your ID is {{userId}}.',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
const data = { name: 'John Doe', userId: '12345' };
|
||||
|
||||
expect(templateMailer.getSubject(data)).toEqual('Hello John Doe');
|
||||
expect(templateMailer.getBody(data)).toEqual('Welcome, John Doe! Your ID is 12345.');
|
||||
});
|
||||
|
||||
tap.test('should handle HTML email body', async () => {
|
||||
const htmlMailer = new smartmail.Smartmail({
|
||||
subject: 'HTML Test',
|
||||
body: 'Plain text version',
|
||||
htmlBody: '<h1>{{title}}</h1><p>This is an HTML email with {{variable}}.</p>',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
const data = { title: 'Welcome', variable: 'dynamic content' };
|
||||
const htmlContent = htmlMailer.getHtmlBody(data);
|
||||
|
||||
expect(htmlContent).toEqual('<h1>Welcome</h1><p>This is an HTML email with dynamic content.</p>');
|
||||
});
|
||||
|
||||
tap.test('should convert to MIME format', async () => {
|
||||
const mimeMailer = new smartmail.Smartmail({
|
||||
subject: 'MIME Test',
|
||||
body: 'Plain text content',
|
||||
htmlBody: '<p>HTML content</p>',
|
||||
from: 'sender@example.com',
|
||||
to: ['recipient@example.com'],
|
||||
headers: { 'X-Custom': 'Value' }
|
||||
});
|
||||
|
||||
const mimeObj = await mimeMailer.toMimeFormat();
|
||||
|
||||
expect(mimeObj.from).toEqual('sender@example.com');
|
||||
expect(mimeObj.to).toInclude('recipient@example.com');
|
||||
expect(mimeObj.subject).toEqual('MIME Test');
|
||||
expect(mimeObj.text).toEqual('Plain text content');
|
||||
expect(mimeObj.html).toEqual('<p>HTML content</p>');
|
||||
expect(mimeObj.headers['X-Custom']).toEqual('Value');
|
||||
});
|
||||
|
||||
tap.test('should add email headers', async () => {
|
||||
const headerMailer = new smartmail.Smartmail({
|
||||
subject: 'Header Test',
|
||||
body: 'Test body',
|
||||
from: 'noreply@example.com'
|
||||
});
|
||||
|
||||
headerMailer.addHeader('X-Test-Header', 'TestValue');
|
||||
headerMailer.addHeader('X-Tracking-ID', '12345');
|
||||
|
||||
const mimeObj = await headerMailer.toMimeFormat();
|
||||
|
||||
expect(mimeObj.headers['X-Test-Header']).toEqual('TestValue');
|
||||
expect(mimeObj.headers['X-Tracking-ID']).toEqual('12345');
|
||||
});
|
||||
|
||||
tap.start();
|
||||
|
@ -1,8 +1,8 @@
|
||||
/**
|
||||
* autocreated commitinfo by @pushrocks/commitinfo
|
||||
* autocreated commitinfo by @push.rocks/commitinfo
|
||||
*/
|
||||
export const commitinfo = {
|
||||
name: '@push.rocks/smartmail',
|
||||
version: '1.0.24',
|
||||
description: 'a unified format for representing and dealing with mails'
|
||||
version: '2.0.0',
|
||||
description: 'A unified format for representing and dealing with emails, with support for attachments and email validation.'
|
||||
}
|
||||
|
@ -6,27 +6,190 @@ export interface IEmailValidationResult {
|
||||
disposable: boolean;
|
||||
freemail: boolean;
|
||||
reason: string;
|
||||
formatValid: boolean;
|
||||
mxValid: boolean;
|
||||
localPartValid: boolean;
|
||||
domainPartValid: boolean;
|
||||
}
|
||||
|
||||
export interface IEmailAddressValidatorOptions {
|
||||
skipOnlineDomainFetch?: boolean;
|
||||
cacheDnsResults?: boolean;
|
||||
cacheExpiryMs?: number;
|
||||
}
|
||||
|
||||
export class EmailAddressValidator {
|
||||
public domainMap: { [key: string]: 'disposable' | 'freemail' };
|
||||
|
||||
public smartdns = new plugins.smartdns.Smartdns({});
|
||||
private dnsCache: Map<string, { result: any; timestamp: number }> = new Map();
|
||||
private options: IEmailAddressValidatorOptions;
|
||||
|
||||
public async validate(emailArg: string): Promise<IEmailValidationResult> {
|
||||
await this.fetchDomains();
|
||||
const emailArray = emailArg.split('@');
|
||||
const result = await this.smartdns.getRecords(emailArray[1], 'MX');
|
||||
// console.log(emailArray);
|
||||
// console.log(this.domainMap[emailArray[1]]);
|
||||
return {
|
||||
valid: !!result,
|
||||
reason: 'todo',
|
||||
disposable: this.domainMap[emailArray[1]] === 'disposable',
|
||||
freemail: this.domainMap[emailArray[1]] === 'freemail',
|
||||
constructor(optionsArg: IEmailAddressValidatorOptions = {}) {
|
||||
this.options = {
|
||||
skipOnlineDomainFetch: false,
|
||||
cacheDnsResults: true,
|
||||
cacheExpiryMs: 3600000, // 1 hour
|
||||
...optionsArg
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an email address format according to RFC 5322
|
||||
* @param emailArg The email address to validate
|
||||
* @returns True if the format is valid
|
||||
*/
|
||||
public isValidEmailFormat(emailArg: string): boolean {
|
||||
if (!emailArg) return false;
|
||||
|
||||
// RFC 5322 compliant regex pattern
|
||||
const emailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return emailRegex.test(emailArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the local part of an email address (before the @)
|
||||
* @param localPart The local part of the email address
|
||||
* @returns True if the local part is valid
|
||||
*/
|
||||
public isValidLocalPart(localPart: string): boolean {
|
||||
if (!localPart) return false;
|
||||
if (localPart.length > 64) return false;
|
||||
|
||||
// Check for illegal characters and patterns
|
||||
const illegalChars = /[^\w.!#$%&'*+/=?^`{|}~-]/;
|
||||
if (illegalChars.test(localPart)) return false;
|
||||
|
||||
// Check for consecutive dots or leading/trailing dots
|
||||
if (localPart.includes('..') || localPart.startsWith('.') || localPart.endsWith('.')) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates the domain part of an email address (after the @)
|
||||
* @param domainPart The domain part of the email address
|
||||
* @returns True if the domain part is valid
|
||||
*/
|
||||
public isValidDomainPart(domainPart: string): boolean {
|
||||
if (!domainPart) return false;
|
||||
if (domainPart.length > 255) return false;
|
||||
|
||||
// Domain name validation regex
|
||||
const domainRegex = /^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
|
||||
// Must have at least one dot
|
||||
if (!domainPart.includes('.')) return false;
|
||||
|
||||
// Must end with a valid TLD (at least 2 chars)
|
||||
const parts = domainPart.split('.');
|
||||
const tld = parts[parts.length - 1];
|
||||
if (tld.length < 2) return false;
|
||||
|
||||
return domainRegex.test(domainPart);
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs DNS MX record lookup for a domain
|
||||
* @param domain The domain to check
|
||||
* @returns MX records or null if none exist
|
||||
*/
|
||||
public async checkMxRecords(domain: string): Promise<any> {
|
||||
if (this.options.cacheDnsResults) {
|
||||
const cached = this.dnsCache.get(domain);
|
||||
if (cached && (Date.now() - cached.timestamp) < this.options.cacheExpiryMs!) {
|
||||
return cached.result;
|
||||
}
|
||||
}
|
||||
|
||||
const result = await this.smartdns.getRecords(domain, 'MX');
|
||||
|
||||
if (this.options.cacheDnsResults) {
|
||||
this.dnsCache.set(domain, { result, timestamp: Date.now() });
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates an email address
|
||||
* @param emailArg The email address to validate
|
||||
* @returns Validation result with details
|
||||
*/
|
||||
public async validate(emailArg: string): Promise<IEmailValidationResult> {
|
||||
await this.fetchDomains();
|
||||
|
||||
// Initialize result
|
||||
const result: IEmailValidationResult = {
|
||||
valid: false,
|
||||
reason: '',
|
||||
disposable: false,
|
||||
freemail: false,
|
||||
formatValid: false,
|
||||
mxValid: false,
|
||||
localPartValid: false,
|
||||
domainPartValid: false
|
||||
};
|
||||
|
||||
// Check overall email format
|
||||
const formatValid = this.isValidEmailFormat(emailArg);
|
||||
result.formatValid = formatValid;
|
||||
|
||||
if (!formatValid) {
|
||||
result.reason = 'Invalid email format';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Split email into local and domain parts
|
||||
const [localPart, domainPart] = emailArg.split('@');
|
||||
|
||||
// Validate local part
|
||||
const localPartValid = this.isValidLocalPart(localPart);
|
||||
result.localPartValid = localPartValid;
|
||||
|
||||
if (!localPartValid) {
|
||||
result.reason = 'Invalid local part (username)';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Validate domain part
|
||||
const domainPartValid = this.isValidDomainPart(domainPart);
|
||||
result.domainPartValid = domainPartValid;
|
||||
|
||||
if (!domainPartValid) {
|
||||
result.reason = 'Invalid domain part';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check MX records
|
||||
const mxRecords = await this.checkMxRecords(domainPart);
|
||||
result.mxValid = !!mxRecords;
|
||||
|
||||
if (!mxRecords) {
|
||||
result.reason = 'Domain does not have valid MX records';
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check if domain is disposable or free
|
||||
result.disposable = this.domainMap[domainPart] === 'disposable';
|
||||
result.freemail = this.domainMap[domainPart] === 'freemail';
|
||||
|
||||
if (result.disposable) {
|
||||
result.reason = 'Domain is a disposable email provider';
|
||||
} else if (result.freemail) {
|
||||
result.reason = 'Domain is a free email provider';
|
||||
} else {
|
||||
result.reason = 'Email is valid';
|
||||
}
|
||||
|
||||
// Email is valid if it has proper format and MX records
|
||||
result.valid = result.formatValid && result.mxValid;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the domain list for checking disposable and free email providers
|
||||
*/
|
||||
public async fetchDomains() {
|
||||
if (!this.domainMap) {
|
||||
const localFileString = plugins.smartfile.fs.toStringSync(
|
||||
@ -34,21 +197,20 @@ export class EmailAddressValidator {
|
||||
);
|
||||
const localFileObject = JSON.parse(localFileString);
|
||||
|
||||
let onlineFileObject: any;
|
||||
if (this.options.skipOnlineDomainFetch) {
|
||||
this.domainMap = localFileObject;
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
onlineFileObject = (
|
||||
const onlineFileObject = (
|
||||
await plugins.smartrequest.getJson(
|
||||
'https://raw.githubusercontent.com/romainsimon/emailvalid/master/domains.json'
|
||||
)
|
||||
).body;
|
||||
this.domainMap = onlineFileObject;
|
||||
console.log(
|
||||
'smartmail EmailAddressValidator: Using online email list for email validation'
|
||||
);
|
||||
} catch (e) {
|
||||
this.domainMap = localFileObject;
|
||||
console.log(e);
|
||||
console.log('smartmail EmailAddressValidator: Using local email list for email validation');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,38 +1,257 @@
|
||||
import * as plugins from './smartmail.plugins.js';
|
||||
import { EmailAddressValidator } from './smartmail.classes.emailaddressvalidator.js';
|
||||
|
||||
export type EmailAddress = string;
|
||||
export type EmailAddressList = EmailAddress[];
|
||||
|
||||
export interface ISmartmailOptions<T> {
|
||||
from: string;
|
||||
from: EmailAddress;
|
||||
to?: EmailAddressList;
|
||||
cc?: EmailAddressList;
|
||||
bcc?: EmailAddressList;
|
||||
replyTo?: EmailAddress;
|
||||
subject: string;
|
||||
body: string;
|
||||
htmlBody?: string;
|
||||
creationObjectRef?: T;
|
||||
headers?: Record<string, string>;
|
||||
priority?: 'high' | 'normal' | 'low';
|
||||
validateEmails?: boolean;
|
||||
}
|
||||
|
||||
export interface IMimeAttachment {
|
||||
filename: string;
|
||||
content: Buffer;
|
||||
contentType: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* a standard representation for mails
|
||||
* A standard representation for emails with advanced features
|
||||
*/
|
||||
export class Smartmail<T> {
|
||||
public options: ISmartmailOptions<T>;
|
||||
public attachments: plugins.smartfile.Smartfile[] = [];
|
||||
public attachments: plugins.smartfile.SmartFile[] = [];
|
||||
private emailValidator: EmailAddressValidator;
|
||||
|
||||
constructor(optionsArg: ISmartmailOptions<T>) {
|
||||
this.options = optionsArg;
|
||||
// Set default options
|
||||
this.options = {
|
||||
validateEmails: false,
|
||||
to: [],
|
||||
cc: [],
|
||||
bcc: [],
|
||||
headers: {},
|
||||
priority: 'normal',
|
||||
...optionsArg
|
||||
};
|
||||
|
||||
this.emailValidator = new EmailAddressValidator();
|
||||
}
|
||||
|
||||
public addAttachment(smartfileArg: plugins.smartfile.Smartfile) {
|
||||
/**
|
||||
* Adds an attachment to the email
|
||||
* @param smartfileArg The file to attach
|
||||
*/
|
||||
public addAttachment(smartfileArg: plugins.smartfile.SmartFile) {
|
||||
this.attachments.push(smartfileArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the creation object reference
|
||||
* @returns The creation object reference
|
||||
*/
|
||||
public getCreationObject(): T {
|
||||
return this.options.creationObjectRef;
|
||||
}
|
||||
|
||||
public getSubject(dataArg: any = {}) {
|
||||
/**
|
||||
* Gets the processed subject with template variables applied
|
||||
* @param dataArg Data to apply to the template
|
||||
* @returns Processed subject
|
||||
*/
|
||||
public getSubject(dataArg: any = {}): string {
|
||||
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.subject);
|
||||
return smartmustache.applyData(dataArg);
|
||||
}
|
||||
|
||||
public getBody(dataArg: any = {}) {
|
||||
/**
|
||||
* Gets the processed plain text body with template variables applied
|
||||
* @param dataArg Data to apply to the template
|
||||
* @returns Processed body
|
||||
*/
|
||||
public getBody(dataArg: any = {}): string {
|
||||
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.body);
|
||||
return smartmustache.applyData(dataArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the processed HTML body with template variables applied
|
||||
* @param dataArg Data to apply to the template
|
||||
* @returns Processed HTML body or null if not set
|
||||
*/
|
||||
public getHtmlBody(dataArg: any = {}): string | null {
|
||||
if (!this.options.htmlBody) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const smartmustache = new plugins.smartmustache.SmartMustache(this.options.htmlBody);
|
||||
return smartmustache.applyData(dataArg);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a recipient to the email
|
||||
* @param email Email address to add
|
||||
* @param type Type of recipient (to, cc, bcc)
|
||||
*/
|
||||
public addRecipient(email: EmailAddress, type: 'to' | 'cc' | 'bcc' = 'to'): void {
|
||||
if (!this.options[type]) {
|
||||
this.options[type] = [];
|
||||
}
|
||||
|
||||
this.options[type]!.push(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds multiple recipients to the email
|
||||
* @param emails Email addresses to add
|
||||
* @param type Type of recipients (to, cc, bcc)
|
||||
*/
|
||||
public addRecipients(emails: EmailAddressList, type: 'to' | 'cc' | 'bcc' = 'to'): void {
|
||||
if (!this.options[type]) {
|
||||
this.options[type] = [];
|
||||
}
|
||||
|
||||
this.options[type] = [...this.options[type]!, ...emails];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the reply-to address
|
||||
* @param email Email address for reply-to
|
||||
*/
|
||||
public setReplyTo(email: EmailAddress): void {
|
||||
this.options.replyTo = email;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the priority of the email
|
||||
* @param priority Priority level
|
||||
*/
|
||||
public setPriority(priority: 'high' | 'normal' | 'low'): void {
|
||||
this.options.priority = priority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a custom header to the email
|
||||
* @param name Header name
|
||||
* @param value Header value
|
||||
*/
|
||||
public addHeader(name: string, value: string): void {
|
||||
if (!this.options.headers) {
|
||||
this.options.headers = {};
|
||||
}
|
||||
|
||||
this.options.headers[name] = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates all email addresses in the email
|
||||
* @returns Promise resolving to validation results
|
||||
*/
|
||||
public async validateAllEmails(): Promise<Record<string, boolean>> {
|
||||
const results: Record<string, boolean> = {};
|
||||
const emails: EmailAddress[] = [];
|
||||
|
||||
// Collect all emails
|
||||
if (this.options.from) emails.push(this.options.from);
|
||||
if (this.options.replyTo) emails.push(this.options.replyTo);
|
||||
if (this.options.to) emails.push(...this.options.to);
|
||||
if (this.options.cc) emails.push(...this.options.cc);
|
||||
if (this.options.bcc) emails.push(...this.options.bcc);
|
||||
|
||||
// Validate each email
|
||||
for (const email of emails) {
|
||||
const validationResult = await this.emailValidator.validate(email);
|
||||
results[email] = validationResult.valid;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the email to a MIME format object for sending
|
||||
* @param dataArg Data to apply to templates
|
||||
* @returns MIME format object
|
||||
*/
|
||||
public async toMimeFormat(dataArg: any = {}): Promise<any> {
|
||||
// Validate emails if option is enabled
|
||||
if (this.options.validateEmails) {
|
||||
const validationResults = await this.validateAllEmails();
|
||||
const invalidEmails = Object.entries(validationResults)
|
||||
.filter(([_, valid]) => !valid)
|
||||
.map(([email]) => email);
|
||||
|
||||
if (invalidEmails.length > 0) {
|
||||
throw new Error(`Invalid email addresses: ${invalidEmails.join(', ')}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Build MIME parts
|
||||
const subject = this.getSubject(dataArg);
|
||||
const textBody = this.getBody(dataArg);
|
||||
const htmlBody = this.getHtmlBody(dataArg);
|
||||
|
||||
// Convert attachments to MIME format
|
||||
const mimeAttachments: IMimeAttachment[] = await Promise.all(
|
||||
this.attachments.map(async (file) => {
|
||||
return {
|
||||
filename: file.path.split('/').pop()!,
|
||||
content: file.contentBuffer,
|
||||
contentType: 'application/octet-stream'
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// Build email format object
|
||||
const mimeObj: any = {
|
||||
from: this.options.from,
|
||||
subject,
|
||||
text: textBody,
|
||||
attachments: mimeAttachments,
|
||||
headers: { ...this.options.headers }
|
||||
};
|
||||
|
||||
// Add optional fields
|
||||
if (this.options.to && this.options.to.length > 0) {
|
||||
mimeObj.to = this.options.to;
|
||||
}
|
||||
|
||||
if (this.options.cc && this.options.cc.length > 0) {
|
||||
mimeObj.cc = this.options.cc;
|
||||
}
|
||||
|
||||
if (this.options.bcc && this.options.bcc.length > 0) {
|
||||
mimeObj.bcc = this.options.bcc;
|
||||
}
|
||||
|
||||
if (this.options.replyTo) {
|
||||
mimeObj.replyTo = this.options.replyTo;
|
||||
}
|
||||
|
||||
if (htmlBody) {
|
||||
mimeObj.html = htmlBody;
|
||||
}
|
||||
|
||||
// Add priority headers if specified
|
||||
if (this.options.priority === 'high') {
|
||||
mimeObj.headers['X-Priority'] = '1';
|
||||
mimeObj.headers['X-MSMail-Priority'] = 'High';
|
||||
mimeObj.headers['Importance'] = 'High';
|
||||
} else if (this.options.priority === 'low') {
|
||||
mimeObj.headers['X-Priority'] = '5';
|
||||
mimeObj.headers['X-MSMail-Priority'] = 'Low';
|
||||
mimeObj.headers['Importance'] = 'Low';
|
||||
}
|
||||
|
||||
return mimeObj;
|
||||
}
|
||||
}
|
||||
|
@ -4,7 +4,7 @@ import * as path from 'path';
|
||||
export { path };
|
||||
|
||||
// pushrocks scope
|
||||
import * as smartdns from '@push.rocks/smartdns';
|
||||
import * as smartdns from '@push.rocks/smartdns/client';
|
||||
import * as smartfile from '@push.rocks/smartfile';
|
||||
import * as smartmustache from '@push.rocks/smartmustache';
|
||||
import * as smartpath from '@push.rocks/smartpath';
|
||||
|
@ -6,9 +6,22 @@
|
||||
"module": "NodeNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"esModuleInterop": true,
|
||||
"verbatimModuleSyntax": true
|
||||
"verbatimModuleSyntax": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist_ts",
|
||||
"rootDir": "./ts",
|
||||
"strict": false,
|
||||
"lib": ["ES2022", "DOM"],
|
||||
"skipLibCheck": false
|
||||
},
|
||||
"include": [
|
||||
"ts/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"dist_*/**/*.d.ts"
|
||||
"node_modules",
|
||||
"dist",
|
||||
"dist_ts",
|
||||
"**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user