Comparing version 3.5.0 to 3.6.0
{ | ||
"name": "emailjs", | ||
"description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server", | ||
"version": "3.5.0", | ||
"version": "3.6.0", | ||
"author": "eleith", | ||
@@ -19,21 +19,24 @@ "contributors": [ | ||
"devDependencies": { | ||
"@ledge/configs": "23.3.22332", | ||
"@rollup/plugin-typescript": "8.2.1", | ||
"@types/mailparser": "3.0.2", | ||
"@types/node": "15.12.5", | ||
"@types/smtp-server": "3.5.6", | ||
"@typescript-eslint/eslint-plugin": "4.28.0", | ||
"@typescript-eslint/parser": "4.28.0", | ||
"@ledge/configs": "23.3.23232", | ||
"@rollup/plugin-typescript": "8.2.5", | ||
"@types/mailparser": "3.0.3", | ||
"@types/node": "16.7.10", | ||
"@types/smtp-server": "3.5.7", | ||
"@typescript-eslint/eslint-plugin": "4.30.0", | ||
"@typescript-eslint/parser": "4.30.0", | ||
"ava": "3.15.0", | ||
"eslint": "7.29.0", | ||
"eslint": "7.32.0", | ||
"eslint-config-prettier": "8.3.0", | ||
"eslint-plugin-prettier": "3.4.0", | ||
"mailparser": "3.2.0", | ||
"eslint-plugin-prettier": "4.0.0", | ||
"mailparser": "3.3.0", | ||
"prettier": "2.3.2", | ||
"rollup": "2.52.3", | ||
"rollup": "2.56.3", | ||
"smtp-server": "3.9.0", | ||
"ts-node": "9.0.0", | ||
"tslib": "2.3.0", | ||
"ts-node": "10.2.1", | ||
"tslib": "2.3.1", | ||
"typescript": "4.3.4" | ||
}, | ||
"resolutions": { | ||
"nodemailer": "6.6.3" | ||
}, | ||
"engines": { | ||
@@ -58,5 +61,6 @@ "node": ">=10" | ||
"test": "ava", | ||
"test-cjs": "npm run build && npm run test -- --node-arguments='--title=cjs'" | ||
"pretest-cjs": "npm run build", | ||
"test-cjs": "npm run test -- --node-arguments='--title=cjs'" | ||
}, | ||
"license": "MIT" | ||
} |
@@ -24,3 +24,2 @@ # emailjs [![Test Status](https://github.com/eleith/emailjs/workflows/.github/workflows/test.yml/badge.svg)](https://github.com/eleith/emailjs/actions?query=workflow%3A.github%2Fworkflows%2Ftest.yml) [![Lint Status](https://github.com/eleith/emailjs/workflows/.github/workflows/lint.yml/badge.svg)](https://github.com/eleith/emailjs/actions?query=workflow%3A.github%2Fworkflows%2Flint.yml) | ||
- if your service (ex: gmail) uses two-step authentication, use an application specific password | ||
- if using TypeScript, enable [`esModuleInterop`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-7.html#support-for-import-d-from-cjs-from-commonjs-modules-with---esmoduleinterop) or [`allowSyntheticDefaultImports`](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-1-8.html#support-for-default-import-interop-with-systemjs) | ||
@@ -267,2 +266,17 @@ ## EXAMPLE USAGE - text only emails | ||
## Message#checkValidity() | ||
Synchronously validate that a Message is properly formed. | ||
```js | ||
const message = new Message(options); | ||
const { isValid, validationError } = message.checkValidity(); | ||
if (isValid) { | ||
// ... | ||
} else { | ||
// first error encountered | ||
console.error(validationError); | ||
} | ||
``` | ||
## new SMTPConnection(options={}) | ||
@@ -269,0 +283,0 @@ |
@@ -58,14 +58,14 @@ import { addressparser } from './address'; | ||
message.valid((valid, why) => { | ||
if (valid) { | ||
const stack = this.createMessageStack(message, callback); | ||
if (stack.to.length === 0) { | ||
return callback(new Error('No recipients found in message'), msg); | ||
} | ||
this.queue.push(stack); | ||
this._poll(); | ||
} else { | ||
callback(new Error(why), msg); | ||
const { isValid, validationError } = message.checkValidity(); | ||
if (isValid) { | ||
const stack = this.createMessageStack(message, callback); | ||
if (stack.to.length === 0) { | ||
return callback(new Error('No recipients found in message'), msg); | ||
} | ||
}); | ||
this.queue.push(stack); | ||
this._poll(); | ||
} else { | ||
callback(new Error(validationError), msg); | ||
} | ||
} | ||
@@ -72,0 +72,0 @@ |
@@ -1,2 +0,9 @@ | ||
import fs, { PathLike } from 'fs'; | ||
import { | ||
PathLike, | ||
existsSync, | ||
open as openFile, | ||
close as closeFile, | ||
closeSync as closeFileSync, | ||
read as readFile, | ||
} from 'fs'; | ||
import { hostname } from 'os'; | ||
@@ -189,6 +196,5 @@ import { Stream } from 'stream'; | ||
* @public | ||
* @param {function(isValid: boolean, invalidReason: string): void} callback . | ||
* @returns {void} | ||
* @returns {{ isValid: boolean, validationError: (string | undefined) }} an object specifying whether this message is validly formatted, and the first validation error if it is not. | ||
*/ | ||
public valid(callback: (isValid: boolean, invalidReason?: string) => void) { | ||
public checkValidity() { | ||
if ( | ||
@@ -198,4 +204,9 @@ typeof this.header.from !== 'string' && | ||
) { | ||
callback(false, 'Message must have a `from` header'); | ||
} else if ( | ||
return { | ||
isValid: false, | ||
validationError: 'Message must have a `from` header', | ||
}; | ||
} | ||
if ( | ||
typeof this.header.to !== 'string' && | ||
@@ -208,9 +219,10 @@ Array.isArray(this.header.to) === false && | ||
) { | ||
callback( | ||
false, | ||
'Message must have at least one `to`, `cc`, or `bcc` header' | ||
); | ||
} else if (this.attachments.length === 0) { | ||
callback(true, undefined); | ||
} else { | ||
return { | ||
isValid: false, | ||
validationError: | ||
'Message must have at least one `to`, `cc`, or `bcc` header', | ||
}; | ||
} | ||
if (this.attachments.length > 0) { | ||
const failed: string[] = []; | ||
@@ -220,3 +232,3 @@ | ||
if (attachment.path) { | ||
if (fs.existsSync(attachment.path) == false) { | ||
if (existsSync(attachment.path) === false) { | ||
failed.push(`${attachment.path} does not exist`); | ||
@@ -232,5 +244,9 @@ } | ||
}); | ||
return { | ||
isValid: failed.length === 0, | ||
validationError: failed.join(', '), | ||
}; | ||
} | ||
callback(failed.length === 0, failed.join(', ')); | ||
} | ||
return { isValid: true, validationError: undefined }; | ||
} | ||
@@ -240,2 +256,13 @@ | ||
* @public | ||
* @deprecated does not conform to the `errback` style followed by the rest of the library, and will be removed in the next major version. use `checkValidity` instead. | ||
* @param {function(isValid: boolean, invalidReason: (string | undefined)): void} callback . | ||
* @returns {void} | ||
*/ | ||
public valid(callback: (isValid: boolean, invalidReason?: string) => void) { | ||
const { isValid, validationError } = this.checkValidity(); | ||
callback(isValid, validationError); | ||
} | ||
/** | ||
* @public | ||
* @returns {MessageStream} a stream of the current message | ||
@@ -259,2 +286,14 @@ */ | ||
} | ||
public readAsync() { | ||
return new Promise<string>((resolve, reject) => { | ||
this.read((err, buffer) => { | ||
if (err != null) { | ||
reject(err); | ||
} else { | ||
resolve(buffer); | ||
} | ||
}); | ||
}); | ||
} | ||
} | ||
@@ -389,4 +428,12 @@ | ||
const buffer = Buffer.alloc(chunk); | ||
const closed = (fd: number) => fs.closeSync(fd); | ||
const inputEncoding = | ||
attachment?.headers?.['content-transfer-encoding'] || 'base64'; | ||
const encoding = | ||
inputEncoding === '7bit' | ||
? 'ascii' | ||
: inputEncoding === '8bit' | ||
? 'binary' | ||
: inputEncoding; | ||
/** | ||
@@ -398,43 +445,34 @@ * @param {Error} err the error to emit | ||
const opened = (err: NodeJS.ErrnoException | null, fd: number) => { | ||
if (!err) { | ||
const read = (err: NodeJS.ErrnoException | null, bytes: number) => { | ||
if (!err && this.readable) { | ||
let encoding = | ||
attachment && attachment.headers | ||
? attachment.headers['content-transfer-encoding'] || 'base64' | ||
: 'base64'; | ||
if (encoding === 'ascii' || encoding === '7bit') { | ||
encoding = 'ascii'; | ||
} else if (encoding === 'binary' || encoding === '8bit') { | ||
encoding = 'binary'; | ||
} else { | ||
encoding = 'base64'; | ||
} | ||
// guaranteed to be encoded without padding unless it is our last read | ||
outputBase64(buffer.toString(encoding, 0, bytes), () => { | ||
if (bytes == chunk) { | ||
// we read a full chunk, there might be more | ||
fs.read(fd, buffer, 0, chunk, null, read); | ||
} // that was the last chunk, we are done reading the file | ||
else { | ||
this.removeListener('error', closed); | ||
fs.close(fd, next); | ||
} | ||
}); | ||
} else { | ||
this.emit( | ||
'error', | ||
err || { message: 'message stream was interrupted somehow!' } | ||
); | ||
} | ||
}; | ||
fs.read(fd, buffer, 0, chunk, null, read); | ||
this.once('error', closed); | ||
} else { | ||
if (err) { | ||
this.emit('error', err); | ||
return; | ||
} | ||
const readBytes = ( | ||
err: NodeJS.ErrnoException | null, | ||
bytes: number | ||
) => { | ||
if (err || this.readable === false) { | ||
this.emit( | ||
'error', | ||
err || new Error('message stream was interrupted somehow!') | ||
); | ||
return; | ||
} | ||
// guaranteed to be encoded without padding unless it is our last read | ||
outputBase64(buffer.toString(encoding, 0, bytes), () => { | ||
if (bytes == chunk) { | ||
// we read a full chunk, there might be more | ||
readFile(fd, buffer, 0, chunk, null, readBytes); | ||
} // that was the last chunk, we are done reading the file | ||
else { | ||
this.removeListener('error', closeFileSync); | ||
closeFile(fd, next); | ||
} | ||
}); | ||
}; | ||
readFile(fd, buffer, 0, chunk, null, readBytes); | ||
this.once('error', closeFileSync); | ||
}; | ||
fs.open(attachment.path as PathLike, 'r', opened); | ||
openFile(attachment.path as PathLike, 'r', opened); | ||
}; | ||
@@ -441,0 +479,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
6740
321
340781
16