Comparing version 3.3.0 to 3.4.0
@@ -7,4 +7,16 @@ # Changelog | ||
## [3.3.0] - TBD | ||
## [3.4.0] - TBD | ||
### Added | ||
- `SMTPClient#sendAsync` API [#267](https://github.com/eleith/emailjs/issues/267) | ||
- `isRFC2822Date` API | ||
### Changed | ||
- use `WeakSet` instead of `WeakMap` for greylist tracking | ||
### Fixed | ||
- use camelCase style for internal function names | ||
- use correct types in jsdoc comments | ||
## [3.3.0] - 2020-08-08 | ||
### Added | ||
- greylist support [#202](https://github.com/eleith/emailjs/issues/202) | ||
@@ -11,0 +23,0 @@ |
{ | ||
"name": "emailjs", | ||
"description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server", | ||
"version": "3.3.0", | ||
"version": "3.4.0", | ||
"author": "eleith", | ||
@@ -19,19 +19,19 @@ "contributors": [ | ||
"devDependencies": { | ||
"@ledge/configs": "23.0.0", | ||
"@rollup/plugin-typescript": "5.0.2", | ||
"@types/mailparser": "2.7.3", | ||
"@types/smtp-server": "3.5.4", | ||
"@typescript-eslint/eslint-plugin": "3.7.1", | ||
"@typescript-eslint/parser": "3.7.1", | ||
"ava": "3.11.0", | ||
"eslint": "7.5.0", | ||
"eslint-config-prettier": "6.11.0", | ||
"@ledge/configs": "23.3.223", | ||
"@rollup/plugin-typescript": "6.1.0", | ||
"@types/mailparser": "3.0.0", | ||
"@types/smtp-server": "3.5.5", | ||
"@typescript-eslint/eslint-plugin": "4.8.2", | ||
"@typescript-eslint/parser": "4.8.2", | ||
"ava": "3.13.0", | ||
"eslint": "7.14.0", | ||
"eslint-config-prettier": "6.15.0", | ||
"eslint-plugin-prettier": "3.1.4", | ||
"mailparser": "2.8.0", | ||
"prettier": "2.0.5", | ||
"rollup": "2.23.0", | ||
"smtp-server": "3.7.0", | ||
"ts-node": "8.10.2", | ||
"tslib": "2.0.0", | ||
"typescript": "3.9.7" | ||
"mailparser": "3.0.1", | ||
"prettier": "2.2.1", | ||
"rollup": "2.34.0", | ||
"smtp-server": "3.8.0", | ||
"ts-node": "9.0.0", | ||
"tslib": "2.0.3", | ||
"typescript": "4.1.2" | ||
}, | ||
@@ -38,0 +38,0 @@ "engine": [ |
@@ -1,2 +0,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) | ||
# 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) | ||
@@ -24,2 +24,3 @@ send emails, html and attachments (files, streams and strings) from node.js to any smtp server | ||
- 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) | ||
@@ -26,0 +27,0 @@ ## EXAMPLE USAGE - text only emails |
@@ -74,2 +74,19 @@ import { addressparser } from './address'; | ||
* @public | ||
* @param {Message} msg the message to send | ||
* @returns {Promise<Message>} a promise that resolves to the fully processed message | ||
*/ | ||
public sendAsync(msg: Message) { | ||
return new Promise<Message>((resolve, reject) => { | ||
this.send(msg, (err, msg) => { | ||
if (err != null) { | ||
reject(err); | ||
} else { | ||
resolve(msg); | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* @public | ||
* @description Converts a message to the raw object used by the internal stack. | ||
@@ -214,4 +231,4 @@ * @param {Message} message message to convert | ||
* @protected | ||
* @param {*} attachment attachment | ||
* @returns {*} whether the attachment contains inlined html | ||
* @param {MessageAttachment | MessageAttachment[]} attachment attachment | ||
* @returns {boolean} whether the attachment contains inlined html | ||
*/ | ||
@@ -218,0 +235,0 @@ protected _containsInlinedHtml( |
@@ -47,3 +47,3 @@ import { createHmac } from 'crypto'; | ||
/** | ||
* @param {...any} args the message(s) to log | ||
* @param {...any[]} args the message(s) to log | ||
* @returns {void} | ||
@@ -66,4 +66,4 @@ */ | ||
/** | ||
* @param {function(...*): void} callback the function to call | ||
* @param {...*} args the arguments to apply to the function | ||
* @param {function(...any[]): void} callback the function to call | ||
* @param {...any[]} args the arguments to apply to the function | ||
* @returns {void} | ||
@@ -125,6 +125,3 @@ */ | ||
private greylistResponseTracker = new WeakMap< | ||
(...rest: any[]) => void, | ||
boolean | ||
>(); | ||
private greylistResponseTracker = new WeakSet<(...rest: any[]) => void>(); | ||
@@ -231,3 +228,3 @@ /** | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {number} [port] the port to use for the connection | ||
@@ -357,3 +354,3 @@ * @param {string} [host] the hostname to use for the connection | ||
* @param {string} str the string to send | ||
* @param {*} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -391,3 +388,3 @@ */ | ||
* @param {string} cmd command to issue | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {(number[] | number)} [codes=[250]] array codes | ||
@@ -420,5 +417,5 @@ * @returns {void} | ||
msg.message.toLowerCase().includes('greylist') && | ||
this.greylistResponseTracker.get(response) === false | ||
this.greylistResponseTracker.has(response) === false | ||
) { | ||
this.greylistResponseTracker.set(response, true); | ||
this.greylistResponseTracker.add(response); | ||
setTimeout(() => { | ||
@@ -445,3 +442,3 @@ this.send(cmd + CRLF, response); | ||
this.greylistResponseTracker.set(response, false); | ||
this.greylistResponseTracker.delete(response); | ||
this.send(cmd + CRLF, response); | ||
@@ -460,3 +457,3 @@ } | ||
* | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} domain the domain to associate with the 'helo' request | ||
@@ -478,3 +475,3 @@ * @returns {void} | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -547,3 +544,3 @@ */ | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} domain the domain to associate with the 'ehlo' request | ||
@@ -574,3 +571,3 @@ * @returns {void} | ||
*/ | ||
public has_extn(opt: string): boolean { | ||
public has_extn(opt: string) { | ||
return (this.features ?? {})[opt.toLowerCase()] === undefined; | ||
@@ -582,3 +579,3 @@ } | ||
* @description SMTP 'help' command, returns text from the server | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} domain the domain to associate with the 'help' request | ||
@@ -593,3 +590,3 @@ * @returns {void} | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -603,3 +600,3 @@ */ | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -613,3 +610,3 @@ */ | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} from the sender | ||
@@ -624,3 +621,3 @@ * @returns {void} | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} to the receiver | ||
@@ -635,3 +632,3 @@ * @returns {void} | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -645,3 +642,3 @@ */ | ||
* @public | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -667,3 +664,3 @@ */ | ||
* @param {string} address the address to validate | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -679,3 +676,3 @@ */ | ||
* @param {string} address the mailing list to expand | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @returns {void} | ||
@@ -694,3 +691,3 @@ */ | ||
* | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} [domain] the domain to associate with the command | ||
@@ -727,3 +724,3 @@ * @returns {void} | ||
* | ||
* @param {function(...*): void} callback function to call after response | ||
* @param {function(...any[]): void} callback function to call after response | ||
* @param {string} [user] the username to authenticate with | ||
@@ -760,3 +757,3 @@ * @param {string} [password] the password for the authentication | ||
*/ | ||
const encode_cram_md5 = (challenge: string): string => { | ||
const encodeCramMd5 = (challenge: string) => { | ||
const hmac = createHmac('md5', login.password()); | ||
@@ -772,3 +769,3 @@ hmac.update(Buffer.from(challenge, 'base64').toString('ascii')); | ||
*/ | ||
const encode_plain = (): string => | ||
const encodePlain = () => | ||
Buffer.from(`\u0000${login.user()}\u0000${login.password()}`).toString( | ||
@@ -782,3 +779,3 @@ 'base64' | ||
*/ | ||
const encode_xoauth2 = (): string => | ||
const encodeXoauth2 = () => | ||
Buffer.from( | ||
@@ -809,3 +806,3 @@ `user=${login.user()}\u0001auth=Bearer ${login.password()}\u0001\u0001` | ||
* @param {Error} err err | ||
* @param {*} data data | ||
* @param {unknown} data data | ||
* @returns {void} | ||
@@ -829,3 +826,3 @@ */ | ||
* @param {Error} err err | ||
* @param {*} data data | ||
* @param {unknown} data data | ||
* @returns {void} | ||
@@ -844,3 +841,3 @@ */ | ||
* @param {Error} err err | ||
* @param {*} data data | ||
* @param {unknown} data data | ||
* @param {string} msg msg | ||
@@ -858,3 +855,3 @@ * @returns {void} | ||
if (method === AUTH_METHODS['CRAM-MD5']) { | ||
this.command(encode_cram_md5(msg), response, [235, 503]); | ||
this.command(encodeCramMd5(msg), response, [235, 503]); | ||
} else if (method === AUTH_METHODS.LOGIN) { | ||
@@ -872,7 +869,7 @@ this.command( | ||
* @param {Error} err err | ||
* @param {*} data data | ||
* @param {unknown} data data | ||
* @param {string} msg msg | ||
* @returns {void} | ||
*/ | ||
const attempt_user = (err: Error, data: unknown) => { | ||
const attemptUser = (err: Error, data: unknown) => { | ||
if (err) { | ||
@@ -896,7 +893,7 @@ failed(err, data); | ||
case AUTH_METHODS.LOGIN: | ||
this.command(`AUTH ${AUTH_METHODS.LOGIN}`, attempt_user, [334]); | ||
this.command(`AUTH ${AUTH_METHODS.LOGIN}`, attemptUser, [334]); | ||
break; | ||
case AUTH_METHODS.PLAIN: | ||
this.command( | ||
`AUTH ${AUTH_METHODS.PLAIN} ${encode_plain()}`, | ||
`AUTH ${AUTH_METHODS.PLAIN} ${encodePlain()}`, | ||
response, | ||
@@ -908,3 +905,3 @@ [235, 503] | ||
this.command( | ||
`AUTH ${AUTH_METHODS.XOAUTH2} ${encode_xoauth2()}`, | ||
`AUTH ${AUTH_METHODS.XOAUTH2} ${encodeXoauth2()}`, | ||
response, | ||
@@ -915,10 +912,11 @@ [235, 503] | ||
default: | ||
const msg = 'no form of authorization supported'; | ||
const err = SMTPError.create( | ||
msg, | ||
SMTPErrorStates.AUTHNOTSUPPORTED, | ||
null, | ||
data | ||
caller( | ||
callback, | ||
SMTPError.create( | ||
'no form of authorization supported', | ||
SMTPErrorStates.AUTHNOTSUPPORTED, | ||
null, | ||
data | ||
) | ||
); | ||
caller(callback, err); | ||
break; | ||
@@ -961,3 +959,3 @@ } | ||
* @public | ||
* @param {function(...*): void} [callback] function to call after response | ||
* @param {function(...any[]): void} [callback] function to call after response | ||
* @returns {void} | ||
@@ -964,0 +962,0 @@ */ |
@@ -36,1 +36,16 @@ /** | ||
} | ||
/** | ||
* RFC 2822 regex | ||
* @see https://tools.ietf.org/html/rfc2822#section-3.3 | ||
* @see https://github.com/moment/moment/blob/a831fc7e2694281ce31e4f090bbcf90a690f0277/src/lib/create/from-string.js#L101 | ||
*/ | ||
const rfc2822re = /^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/.compile(); | ||
/** | ||
* @param {string} [date] a string to check for conformance to the [rfc2822](https://tools.ietf.org/html/rfc2822#section-3.3) standard | ||
* @returns {boolean} the result of the conformance check | ||
*/ | ||
export function isRFC2822Date(date: string) { | ||
return rfc2822re.test(date); | ||
} |
@@ -81,3 +81,3 @@ import fs, { PathLike } from 'fs'; | ||
function generate_boundary() { | ||
function generateBoundary() { | ||
let text = ''; | ||
@@ -174,3 +174,3 @@ const possible = | ||
*/ | ||
public attach(options: MessageAttachment): Message { | ||
public attach(options: MessageAttachment) { | ||
// sender can specify an attachment as an alternative | ||
@@ -237,3 +237,3 @@ if (options.alternative) { | ||
* @public | ||
* @returns {*} a stream of the current message | ||
* @returns {MessageStream} a stream of the current message | ||
*/ | ||
@@ -265,3 +265,3 @@ public stream() { | ||
/** | ||
* @param {*} message the message to stream | ||
* @param {Message} message the message to stream | ||
*/ | ||
@@ -329,3 +329,3 @@ constructor(private message: Message) { | ||
*/ | ||
const output_attachment_headers = (attachment: MessageAttachment) => { | ||
const outputAttachmentHeaders = (attachment: MessageAttachment) => { | ||
let data: string[] = []; | ||
@@ -369,3 +369,3 @@ const headers: Partial<MessageHeaders> = { | ||
*/ | ||
const output_base64 = (data: string, callback?: () => void) => { | ||
const outputBase64 = (data: string, callback?: () => void) => { | ||
const loops = Math.ceil(data.length / MIMECHUNK); | ||
@@ -382,3 +382,3 @@ let loop = 0; | ||
const output_file = ( | ||
const outputFile = ( | ||
attachment: MessageAttachment, | ||
@@ -412,3 +412,3 @@ next: (err: NodeJS.ErrnoException | null) => void | ||
// guaranteed to be encoded without padding unless it is our last read | ||
output_base64(buffer.toString(encoding, 0, bytes), () => { | ||
outputBase64(buffer.toString(encoding, 0, bytes), () => { | ||
if (bytes == chunk) { | ||
@@ -446,3 +446,3 @@ // we read a full chunk, there might be more | ||
*/ | ||
const output_stream = ( | ||
const outputStream = ( | ||
attachment: MessageAttachment, | ||
@@ -458,3 +458,3 @@ callback: () => void | ||
stream.on('end', () => { | ||
output_base64(previous.toString('base64'), callback); | ||
outputBase64(previous.toString('base64'), callback); | ||
this.removeListener('pause', stream.pause); | ||
@@ -481,3 +481,3 @@ this.removeListener('resume', stream.resume); | ||
} | ||
output_base64(buffer.toString('base64', 0, buffer.length - padded)); | ||
outputBase64(buffer.toString('base64', 0, buffer.length - padded)); | ||
}); | ||
@@ -493,3 +493,3 @@ | ||
const output_attachment = ( | ||
const outputAttachment = ( | ||
attachment: MessageAttachment, | ||
@@ -499,7 +499,7 @@ callback: () => void | ||
const build = attachment.path | ||
? output_file | ||
? outputFile | ||
: attachment.stream | ||
? output_stream | ||
: output_data; | ||
output_attachment_headers(attachment); | ||
? outputStream | ||
: outputData; | ||
outputAttachmentHeaders(attachment); | ||
build(attachment, callback); | ||
@@ -515,3 +515,3 @@ }; | ||
*/ | ||
const output_message = ( | ||
const outputMessage = ( | ||
boundary: string, | ||
@@ -525,8 +525,8 @@ list: MessageAttachment[], | ||
if (list[index].related) { | ||
output_related(list[index], () => | ||
output_message(boundary, list, index + 1, callback) | ||
outputRelated(list[index], () => | ||
outputMessage(boundary, list, index + 1, callback) | ||
); | ||
} else { | ||
output_attachment(list[index], () => | ||
output_message(boundary, list, index + 1, callback) | ||
outputAttachment(list[index], () => | ||
outputMessage(boundary, list, index + 1, callback) | ||
); | ||
@@ -540,4 +540,4 @@ } | ||
const output_mixed = () => { | ||
const boundary = generate_boundary(); | ||
const outputMixed = () => { | ||
const boundary = generateBoundary(); | ||
output( | ||
@@ -548,9 +548,9 @@ `Content-Type: multipart/mixed; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}` | ||
if (this.message.alternative == null) { | ||
output_text(this.message); | ||
output_message(boundary, this.message.attachments, 0, close); | ||
outputText(this.message); | ||
outputMessage(boundary, this.message.attachments, 0, close); | ||
} else { | ||
output_alternative( | ||
outputAlternative( | ||
// typescript bug; should narrow to { alternative: MessageAttachment } | ||
this.message as Parameters<typeof output_alternative>[0], | ||
() => output_message(boundary, this.message.attachments, 0, close) | ||
this.message as Parameters<typeof outputAlternative>[0], | ||
() => outputMessage(boundary, this.message.attachments, 0, close) | ||
); | ||
@@ -565,7 +565,7 @@ } | ||
*/ | ||
const output_data = ( | ||
const outputData = ( | ||
attachment: MessageAttachment, | ||
callback: () => void | ||
) => { | ||
output_base64( | ||
outputBase64( | ||
attachment.encoded | ||
@@ -582,3 +582,3 @@ ? attachment.data ?? '' | ||
*/ | ||
const output_text = (message: Message) => { | ||
const outputText = (message: Message) => { | ||
let data: string[] = []; | ||
@@ -604,12 +604,12 @@ | ||
*/ | ||
const output_related = ( | ||
const outputRelated = ( | ||
message: MessageAttachment, | ||
callback: () => void | ||
) => { | ||
const boundary = generate_boundary(); | ||
const boundary = generateBoundary(); | ||
output( | ||
`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}` | ||
); | ||
output_attachment(message, () => { | ||
output_message(boundary, message.related ?? [], 0, () => { | ||
outputAttachment(message, () => { | ||
outputMessage(boundary, message.related ?? [], 0, () => { | ||
output(`${CRLF}--${boundary}--${CRLF}${CRLF}`); | ||
@@ -626,11 +626,11 @@ callback(); | ||
*/ | ||
const output_alternative = ( | ||
const outputAlternative = ( | ||
message: Message & { alternative: MessageAttachment }, | ||
callback: () => void | ||
) => { | ||
const boundary = generate_boundary(); | ||
const boundary = generateBoundary(); | ||
output( | ||
`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}` | ||
); | ||
output_text(message); | ||
outputText(message); | ||
output(`--${boundary}${CRLF}`); | ||
@@ -647,5 +647,5 @@ | ||
if (message.alternative.related) { | ||
output_related(message.alternative, finish); | ||
outputRelated(message.alternative, finish); | ||
} else { | ||
output_attachment(message.alternative, finish); | ||
outputAttachment(message.alternative, finish); | ||
} | ||
@@ -677,9 +677,9 @@ }; | ||
*/ | ||
const output_header_data = () => { | ||
const outputHeaderData = () => { | ||
if (this.message.attachments.length || this.message.alternative) { | ||
output(`MIME-Version: 1.0${CRLF}`); | ||
output_mixed(); | ||
outputMixed(); | ||
} // you only have a text message! | ||
else { | ||
output_text(this.message); | ||
outputText(this.message); | ||
close(); | ||
@@ -692,3 +692,3 @@ } | ||
*/ | ||
const output_header = () => { | ||
const outputHeader = () => { | ||
let data: string[] = []; | ||
@@ -712,7 +712,7 @@ | ||
output(data.join('')); | ||
output_header_data(); | ||
outputHeaderData(); | ||
}; | ||
this.once('destroy', close); | ||
process.nextTick(output_header); | ||
process.nextTick(outputHeader); | ||
} | ||
@@ -719,0 +719,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
340505
6668
280