chore: update linting

This commit is contained in:
Zack Schuster 2020-04-22 21:26:49 -07:00
parent 8993c49400
commit 16a23e909e
14 changed files with 521 additions and 295 deletions

View File

@ -1,11 +1,17 @@
{
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"modules": true
},
"sourceType": "module"
},
"env": {
"es6": true,
"mocha": true,
"node": true
},
"plugins": [
"mocha"
"@typescript-eslint"
],
"extends": [
"eslint:recommended",
@ -30,11 +36,6 @@
"ignoreRestSiblings": true
}
],
"valid-jsdoc": "error",
"mocha/handle-done-callback": "error",
"mocha/no-exclusive-tests": "error",
"mocha/no-global-tests": "error",
"mocha/no-mocha-arrows": "error",
"mocha/no-skipped-tests": "error"
"valid-jsdoc": "error"
}
}

View File

@ -25,17 +25,18 @@
"@rollup/plugin-node-resolve": "7.1.3",
"@types/mailparser": "2.7.2",
"@types/smtp-server": "3.5.4",
"@typescript-eslint/eslint-plugin": "2.29.0",
"@typescript-eslint/parser": "2.29.0",
"ava": "3.7.1",
"eslint": "6.8.0",
"eslint-config-prettier": "6.11.0",
"eslint-plugin-mocha": "6.3.0",
"eslint-plugin-prettier": "3.1.3",
"mailparser": "2.7.7",
"prettier": "2.0.5",
"rollup": "2.7.2",
"smtp-server": "3.6.0",
"typescript": "3.8.3",
"ts-node": "8.9.0"
"ts-node": "8.9.0",
"typescript": "3.8.3"
},
"engine": [
"node >= 10"
@ -45,6 +46,7 @@
"scripts": {
"rollup": "rollup -c rollup.config.js && npm run rollup:test",
"rollup:test": "npm run test -- --file rollup/email.bundle.test.js",
"lint": "eslint \"+(smtp|test)/*.ts\"",
"test": "ava"
},
"license": "MIT",

View File

@ -1,12 +1,12 @@
// @ts-ignore
import addressparser from 'addressparser';
import { Message, MessageHeaders, MessageAttachment } from './message';
import { SMTP, SMTPState, SMTPOptions } from './smtp';
import { Message } from './message';
import { SMTP, SMTPState } from './smtp';
export interface MessageStack {
callback: (error: Error | null, message: Message) => void;
message: Message;
attachment: MessageAttachment;
attachment: import('./message').MessageAttachment;
text: string;
returnPath: string;
from: string;
@ -17,7 +17,7 @@ export interface MessageStack {
export class Client {
public smtp: SMTP;
public queue: MessageStack[] = []
public queue: MessageStack[] = [];
public timer: any;
public sending: boolean;
public ready: boolean;
@ -45,7 +45,7 @@ export class Client {
* @constructor
* @param {SMTPOptions} server smtp options
*/
constructor(server: Partial<SMTPOptions>) {
constructor(server: Partial<import('./smtp').SMTPOptions>) {
this.smtp = new SMTP(server);
//this.smtp.debug(1);
@ -70,8 +70,8 @@ export class Client {
msg instanceof Message
? msg
: this._canMakeMessage(msg)
? new Message(msg)
: null;
? new Message(msg)
: null;
if (message == null) {
callback(new Error('message is not a valid Message instance'), msg);
@ -84,7 +84,7 @@ export class Client {
message,
to: addressparser(message.header.to),
from: addressparser(message.header.from)[0].address,
callback: (callback || function() {}).bind(this),
callback: (callback || function () {}).bind(this),
} as MessageStack;
if (message.header.cc) {
@ -107,7 +107,7 @@ export class Client {
this.queue.push(stack);
this._poll();
} else {
callback(new Error(why), /** @type {MessageStack} */ (msg));
callback(new Error(why), /** @type {MessageStack} */ msg);
}
});
}
@ -185,7 +185,7 @@ export class Client {
* @param {MessageStack} msg message stack
* @returns {boolean} can make message
*/
_canMakeMessage(msg: MessageHeaders): boolean {
_canMakeMessage(msg: import('./message').MessageHeaders): boolean {
return !!(
msg.from &&
(msg.to || msg.cc || msg.bcc) &&
@ -200,7 +200,7 @@ export class Client {
*/
_containsInlinedHtml(attachment: any): boolean {
if (Array.isArray(attachment)) {
return attachment.some(att => {
return attachment.some((att) => {
return this._isAttachmentInlinedHtml(att);
});
} else {
@ -227,12 +227,15 @@ export class Client {
* @param {function(MessageStack): void} next next
* @returns {function(Error): void} callback
*/
_sendsmtp(stack: MessageStack, next: (msg: MessageStack) => void): (err: Error) => void {
_sendsmtp(
stack: MessageStack,
next: (msg: MessageStack) => void
): (err: Error) => void {
/**
* @param {Error} [err] error
* @returns {void}
*/
return err => {
return (err) => {
if (!err && next) {
next.apply(this, [stack]);
} else {
@ -288,7 +291,7 @@ export class Client {
_sendmessage(stack: MessageStack): void {
const stream = stack.message.stream();
stream.on('data', data => this.smtp.message(data));
stream.on('data', (data) => this.smtp.message(data));
stream.on('end', () => {
this.smtp.data_end(
this._sendsmtp(stack, () => this._senddone(null, stack))
@ -297,7 +300,7 @@ export class Client {
// there is no way to cancel a message while in the DATA portion,
// so we have to close the socket to prevent a bad email from going out
stream.on('error', err => {
stream.on('error', (err) => {
this.smtp.close();
this._senddone(err, stack);
});

View File

@ -1,7 +1,7 @@
/**
* @param [date] an optional date to convert to RFC2822 format
* @param [useUtc] whether to parse the date as UTC (default: false)
* @returns the converted date
* @param {Date} [date] an optional date to convert to RFC2822 format
* @param {boolean} [useUtc] whether to parse the date as UTC (default: false)
* @returns {string} the converted date
*/
export function getRFC2822Date(date = new Date(), useUtc = false) {
if (useUtc) {
@ -24,8 +24,8 @@ export function getRFC2822Date(date = new Date(), useUtc = false) {
}
/**
* @param [date] an optional date to convert to RFC2822 format (UTC)
* @returns the converted date
* @param {Date} [date] an optional date to convert to RFC2822 format (UTC)
* @returns {string} the converted date
*/
export function getRFC2822DateUTC(date = new Date()) {
const dates = date.toUTCString().split(' ');

View File

@ -1,24 +1,5 @@
class SMTPError extends Error {
public code: number | null = null;
public smtp: any = null;
public previous: Error | null = null;
}
export function makeSMTPError(message: string, code: number, error?: Error, smtp?: any) {
const msg = error != null && error.message ? `${message} (${error.message})` : message;
const err = new SMTPError(msg);
err.code = code;
err.smtp = smtp;
if (error) {
err.previous = error;
}
return err;
};
export const enum SMTPErrorStates {
/* eslint-disable no-unused-vars */
export enum SMTPErrorStates {
COULDNOTCONNECT = 1,
BADRESPONSE = 2,
AUTHFAILED = 3,
@ -30,3 +11,30 @@ export const enum SMTPErrorStates {
CONNECTIONENDED = 9,
CONNECTIONAUTH = 10,
}
/* eslint-enable no-unused-vars */
class SMTPError extends Error {
public code: number | null = null;
public smtp: any = null;
public previous: Error | null = null;
}
export function makeSMTPError(
message: string,
code: number,
error?: Error,
smtp?: any
) {
const msg =
error != null && error.message ? `${message} (${error.message})` : message;
const err = new SMTPError(msg);
err.code = code;
err.smtp = smtp;
if (error) {
err.previous = error;
}
return err;
}

View File

@ -1,15 +1,15 @@
import fs from 'fs';
import { hostname } from 'os';
import { Stream, Duplex } from 'stream';
import { Stream } from 'stream';
// @ts-ignore
import addressparser from 'addressparser';
// @ts-ignore
import { mimeWordEncode } from 'emailjs-mime-codec';
import type { Indexed } from '@ledge/types';
import { getRFC2822Date } from './date';
type Indexed = import('@ledge/types').Indexed;
const CRLF = '\r\n';
/**
@ -27,7 +27,6 @@ export const MIME64CHUNK: 456 = (MIMECHUNK * 6) as 456;
*/
export const BUFFERSIZE: 12768 = (MIMECHUNK * 24 * 7) as 12768;
export interface MessageAttachmentHeaders extends Indexed {
'content-type'?: string;
'content-transfer-encoding'?: string;
@ -49,10 +48,9 @@ export interface MessageAttachment extends AlternateMessageAttachment {
charset: string;
method: string;
path: string;
stream: Duplex;
stream: import('stream').Duplex;
}
export interface MessageHeaders extends Indexed {
'content-type': string;
'message-id': string;
@ -82,7 +80,7 @@ function generate_boundary() {
function convertPersonToAddress(person: string) {
return addressparser(person)
.map(({ name, address }: { name: string, address: string }) => {
.map(({ name, address }: { name: string; address: string }) => {
return name
? `${mimeWordEncode(name).replace(/,/g, '=2C')} <${address}>`
: address;
@ -93,7 +91,7 @@ function convertPersonToAddress(person: string) {
function convertDashDelimitedTextToSnakeCase(text: string) {
return text
.toLowerCase()
.replace(/^(.)|-(.)/g, match => match.toUpperCase());
.replace(/^(.)|-(.)/g, (match) => match.toUpperCase());
}
export class Message {
@ -104,7 +102,6 @@ export class Message {
text: any;
constructor(headers: Partial<MessageHeaders>) {
this.header = {
'message-id': `<${new Date().getTime()}.${counter++}.${
process.pid
@ -133,7 +130,9 @@ export class Message {
} else if (header === 'subject') {
this.header.subject = mimeWordEncode(headers.subject);
} else if (/^(cc|bcc|to|from)/i.test(header)) {
this.header[header.toLowerCase()] = convertPersonToAddress(headers[header]);
this.header[header.toLowerCase()] = convertPersonToAddress(
headers[header]
);
} else {
// allow any headers the user wants to set??
// if(/cc|bcc|to|from|reply-to|sender|subject|date|message-id/i.test(header))
@ -169,9 +168,7 @@ export class Message {
*/
attach_alternative(html: string, charset = 'utf-8'): Message {
this.alternative = {
headers: {
},
headers: {},
data: html,
charset,
type: 'text/html',
@ -197,7 +194,7 @@ export class Message {
} else {
const failed: string[] = [];
this.attachments.forEach(attachment => {
this.attachments.forEach((attachment) => {
if (attachment.path) {
if (fs.existsSync(attachment.path) == false) {
failed.push(`${attachment.path} does not exist`);
@ -230,9 +227,9 @@ export class Message {
read(callback: (arg0: Error, arg1: string) => void): void {
let buffer = '';
const str = this.stream();
str.on('data', data => (buffer += data));
str.on('end', err => callback(err, buffer));
str.on('error', err => callback(err, buffer));
str.on('data', (data) => (buffer += data));
str.on('end', (err) => callback(err, buffer));
str.on('error', (err) => callback(err, buffer));
}
}
@ -301,7 +298,12 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call if index is greater than upper bound
* @returns {void}
*/
const output_message = (boundary: string, list: MessageAttachment[], index: number, callback: () => void): void => {
const output_message = (
boundary: string,
list: MessageAttachment[],
index: number,
callback: () => void
): void => {
if (index < list.length) {
output(`--${boundary}${CRLF}`);
if (list[index].related) {
@ -320,9 +322,12 @@ class MessageStream extends Stream {
};
/**
* @param {MessageAttachment | AlternateMessageAttachment} [attachment] the attachment whose headers you would like to output
* @returns {void}
*/
const output_attachment_headers = (attachment: MessageAttachment | AlternateMessageAttachment): void => {
const output_attachment_headers = (
attachment: MessageAttachment | AlternateMessageAttachment
): void => {
let data: string[] = [];
const headers: Partial<MessageHeaders> = {
'content-type':
@ -352,12 +357,15 @@ class MessageStream extends Stream {
output(data.concat([CRLF]).join(''));
};
const output_attachment = (attachment: MessageAttachment | AlternateMessageAttachment, callback: () => void): void => {
const output_attachment = (
attachment: MessageAttachment | AlternateMessageAttachment,
callback: () => void
): void => {
const build = attachment.path
? output_file
: attachment.stream
? output_stream
: output_data;
? output_stream
: output_data;
output_attachment_headers(attachment);
build(attachment, callback);
};
@ -367,7 +375,10 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished
* @returns {void}
*/
const output_data = (attachment: MessageAttachment | AlternateMessageAttachment, callback: () => void): void => {
const output_data = (
attachment: MessageAttachment | AlternateMessageAttachment,
callback: () => void
): void => {
output_base64(
attachment.encoded
? attachment.data
@ -376,7 +387,10 @@ class MessageStream extends Stream {
);
};
const output_file = (attachment: MessageAttachment | AlternateMessageAttachment, next: (err: NodeJS.ErrnoException) => void): void => {
const output_file = (
attachment: MessageAttachment | AlternateMessageAttachment,
next: (err: NodeJS.ErrnoException) => void
): void => {
const chunk = MIME64CHUNK * 16;
const buffer = Buffer.alloc(chunk);
const closed = (fd: number) => fs.closeSync(fd);
@ -435,7 +449,10 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished
* @returns {void}
*/
const output_stream = (attachment: MessageAttachment | AlternateMessageAttachment, callback: () => void): void => {
const output_stream = (
attachment: MessageAttachment | AlternateMessageAttachment,
callback: () => void
): void => {
if (attachment.stream.readable) {
let previous = Buffer.alloc(0);
@ -448,7 +465,7 @@ class MessageStream extends Stream {
this.removeListener('error', attachment.stream.resume);
});
(attachment as MessageAttachment).stream.on('data', buff => {
(attachment as MessageAttachment).stream.on('data', (buff) => {
// do we have bytes from a previous stream data event?
let buffer = Buffer.isBuffer(buff) ? buff : Buffer.from(buff);
@ -517,7 +534,10 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished
* @returns {void}
*/
const output_alternative = (message: Message & { alternative: AlternateMessageAttachment }, callback: () => void): void => {
const output_alternative = (
message: Message & { alternative: AlternateMessageAttachment },
callback: () => void
): void => {
const boundary = generate_boundary();
output(
`Content-Type: multipart/alternative; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`
@ -545,7 +565,10 @@ class MessageStream extends Stream {
* @param {function(): void} callback the function to call after output is finished
* @returns {void}
*/
const output_related = (message: AlternateMessageAttachment, callback: () => void): void => {
const output_related = (
message: AlternateMessageAttachment,
callback: () => void
): void => {
const boundary = generate_boundary();
output(
`Content-Type: multipart/related; boundary="${boundary}"${CRLF}${CRLF}--${boundary}${CRLF}`
@ -582,7 +605,7 @@ class MessageStream extends Stream {
// do not output BCC in the headers (regex) nor custom Object.prototype functions...
if (
!/bcc/i.test(header) &&
this.message.header.hasOwnProperty(header)
Object.prototype.hasOwnProperty.call(this.message.header, header)
) {
data = data.concat([
convertDashDelimitedTextToSnakeCase(header),
@ -598,16 +621,21 @@ class MessageStream extends Stream {
};
/**
* @param [data] the data to output
* @param [callback] the function
* @param [args] array of arguments to pass to the callback
* @param {string} [data] the data to output
* @param {Function} [callback] the function
* @param {any[]} [args] array of arguments to pass to the callback
* @returns {void}
*/
const output = (data: string, callback?: (...args: any[]) => void, args: any[] = []) => {
const output = (
data: string,
callback?: (...args: any[]) => void,
args: any[] = []
) => {
// can we buffer the data?
if (this.buffer != null) {
const bytes = Buffer.byteLength(data);
if ((bytes + this.bufferIndex) < this.buffer.length) {
if (bytes + this.bufferIndex < this.buffer.length) {
this.buffer.write(data, this.bufferIndex);
this.bufferIndex += bytes;
if (callback) {
@ -617,7 +645,10 @@ class MessageStream extends Stream {
// we can't buffer the data, so ship it out!
else if (bytes > this.buffer.length) {
if (this.bufferIndex) {
this.emit('data', this.buffer.toString('utf-8', 0, this.bufferIndex));
this.emit(
'data',
this.buffer.toString('utf-8', 0, this.bufferIndex)
);
this.bufferIndex = 0;
}
@ -636,7 +667,10 @@ class MessageStream extends Stream {
} // we need to clean out the buffer, it is getting full
else {
if (!this.paused) {
this.emit('data', this.buffer.toString('utf-8', 0, this.bufferIndex));
this.emit(
'data',
this.buffer.toString('utf-8', 0, this.bufferIndex)
);
this.buffer.write(data, 0);
this.bufferIndex = bytes;
// we could get paused after emitting data...
@ -660,7 +694,10 @@ class MessageStream extends Stream {
if (err) {
this.emit('error', err);
} else {
this.emit('data', this.buffer?.toString('utf-8', 0, this.bufferIndex) ?? '');
this.emit(
'data',
this.buffer?.toString('utf-8', 0, this.bufferIndex) ?? ''
);
this.emit('end');
}
this.buffer = null;

View File

@ -1,23 +1,23 @@
import { Socket } from 'net';
import { TLSSocket } from 'tls';
import { makeSMTPError, SMTPErrorStates } from './error';
type Socket = import('net').Socket | import('tls').TLSSocket;
export class SMTPResponse {
private buffer = '';
public stop: (err?: Error) => void;
/**
* @param [stream] The open socket to stream a response from
* @param [timeout] The time to wait (in milliseconds) before closing the socket
* @param [onerror] The function to call on error
*/
constructor(private stream: Socket | TLSSocket, timeout: number, onerror: (err: Error) => void) {
const watch = (data: Parameters<SMTPResponse['watch']>[0]) => this.watch(data);
constructor(
private stream: Socket,
timeout: number,
onerror: (err: Error) => void
) {
const watch = (data: Parameters<SMTPResponse['watch']>[0]) =>
this.watch(data);
const end = () => this.end();
const close = () => this.close();
const error = (data: Parameters<SMTPResponse['error']>[0]) => this.error(data);
const timedout = (data: Parameters<SMTPResponse['timedout']>[0]) => this.timedout(data);
const error = (data: Parameters<SMTPResponse['error']>[0]) =>
this.error(data);
const timedout = (data: Parameters<SMTPResponse['timedout']>[0]) =>
this.timedout(data);
this.stream.on('data', watch);
this.stream.on('end', end);
@ -25,7 +25,7 @@ export class SMTPResponse {
this.stream.on('error', error);
this.stream.setTimeout(timeout, timedout);
this.stop = err => {
this.stop = (err) => {
this.stream.removeAllListeners('response');
this.stream.removeListener('data', watch);
this.stream.removeListener('end', end);
@ -47,7 +47,8 @@ export class SMTPResponse {
.trim()
.split(/\n/)
.pop()
?.match(/^(\d{3})\s/) ?? false
?.match(/^(\d{3})\s/) ??
false
) {
return;
}
@ -66,7 +67,11 @@ export class SMTPResponse {
protected error(err: Error) {
this.stream.emit(
'response',
makeSMTPError('connection encountered an error', SMTPErrorStates.ERROR, err)
makeSMTPError(
'connection encountered an error',
SMTPErrorStates.ERROR,
err
)
);
}
@ -103,11 +108,3 @@ export class SMTPResponse {
);
}
}
/**
* @param [stream] the open socket to stream a response from
* @param [timeout] the time to wait (in milliseconds) before closing the socket
* @param [onerror] the function to call on error
*/
export const monitor = (stream: Socket | TLSSocket, timeout: number, onerror: (err: Error) => void) =>
new SMTPResponse(stream, timeout, onerror);

View File

@ -4,16 +4,37 @@ import { hostname } from 'os';
import { connect, createSecureContext, TLSSocket } from 'tls';
import { EventEmitter } from 'events';
import { SMTPResponse, monitor } from './response';
import { SMTPResponse } from './response';
import { makeSMTPError, SMTPErrorStates } from './error';
import { Indexed } from '@ledge/types';
/* eslint-disable no-unused-vars */
/**
* @readonly
* @enum
*/
export enum AUTH_METHODS {
PLAIN = 'PLAIN',
CRAM_MD5 = 'CRAM-MD5',
LOGIN = 'LOGIN',
XOAUTH2 = 'XOAUTH2',
}
/**
* @readonly
* @enum
*/
export enum SMTPState {
NOTCONNECTED = 0,
CONNECTING = 1,
CONNECTED = 2,
}
/* eslint-enable no-unused-vars */
/**
* @readonly
* @type {5000}
*/
const TIMEOUT: 5000 = 5000;
export { TIMEOUT as DEFAULT_TIMEOUT };
export const DEFAULT_TIMEOUT: 5000 = 5000;
/**
* @readonly
@ -39,23 +60,6 @@ const SMTP_TLS_PORT: 587 = 587;
*/
const CRLF: '\r\n' = '\r\n';
export enum AUTH_METHODS {
PLAIN = 'PLAIN',
CRAM_MD5 = 'CRAM-MD5',
LOGIN = 'LOGIN',
XOAUTH2 = 'XOAUTH2',
};
/**
* @readonly
* @enum
*/
export const SMTPState = {
NOTCONNECTED: 0,
CONNECTING: 1,
CONNECTED: 2,
} as const;
/**
* @type {0 | 1}
*/
@ -67,7 +71,7 @@ let DEBUG: 0 | 1 = 0;
*/
const log = (...args: any[]): void => {
if (DEBUG === 1) {
args.forEach(d =>
args.forEach((d) =>
console.log(
typeof d === 'object'
? d instanceof Error
@ -119,7 +123,7 @@ export class SMTP extends EventEmitter {
private _isSecure = false;
private _user?: string = '';
private _password?: string = '';
private _timeout: number = TIMEOUT;
private _timeout: number = DEFAULT_TIMEOUT;
public set debug(level: 0 | 1) {
DEBUG = level;
@ -146,7 +150,7 @@ export class SMTP extends EventEmitter {
}
protected sock: Socket | TLSSocket | null = null;
protected features: Indexed<string | boolean> = {};
protected features: import('@ledge/types').Indexed<string | boolean> = {};
protected monitor: SMTPResponse | null = null;
protected authentication: any[];
protected domain = hostname();
@ -179,11 +183,11 @@ export class SMTP extends EventEmitter {
this.authentication = Array.isArray(authentication)
? authentication
: [
AUTH_METHODS.CRAM_MD5,
AUTH_METHODS.LOGIN,
AUTH_METHODS.PLAIN,
AUTH_METHODS.XOAUTH2,
];
AUTH_METHODS.CRAM_MD5,
AUTH_METHODS.LOGIN,
AUTH_METHODS.PLAIN,
AUTH_METHODS.XOAUTH2,
];
if (typeof timeout === 'number') {
this._timeout = timeout;
@ -201,16 +205,28 @@ export class SMTP extends EventEmitter {
this.log = log;
}
if (ssl != null && (typeof ssl === 'boolean' || (typeof ssl === 'object' && Array.isArray(ssl) === false))) {
if (
ssl != null &&
(typeof ssl === 'boolean' ||
(typeof ssl === 'object' && Array.isArray(ssl) === false))
) {
this.ssl = ssl;
}
if (tls != null && (typeof tls === 'boolean' || (typeof tls === 'object' && Array.isArray(tls) === false))) {
if (
tls != null &&
(typeof tls === 'boolean' ||
(typeof tls === 'object' && Array.isArray(tls) === false))
) {
this.tls = tls;
}
if (!port) {
this.port = this.ssl ? SMTP_SSL_PORT : this.tls ? SMTP_TLS_PORT : SMTP_PORT;
this.port = this.ssl
? SMTP_SSL_PORT
: this.tls
? SMTP_TLS_PORT
: SMTP_PORT;
}
this._isAuthorized = user && password ? false : true;
@ -226,20 +242,18 @@ export class SMTP extends EventEmitter {
* @param {ConnectOptions} [options={}] the options
* @returns {void}
*/
connect(callback: (...rest: any[]) => void, port: number = this.port, host: string = this.host, options: ConnectOptions = {}): void {
connect(
callback: (...rest: any[]) => void,
port: number = this.port,
host: string = this.host,
options: ConnectOptions = {}
): void {
this.port = port;
this.host = host;
this.ssl = options.ssl || this.ssl;
if (this._state !== SMTPState.NOTCONNECTED) {
this.quit(() =>
this.connect(
callback,
port,
host,
options
)
);
this.quit(() => this.connect(callback, port, host, options));
}
/**
@ -281,12 +295,19 @@ export class SMTP extends EventEmitter {
this.log(err);
caller(
callback,
makeSMTPError('could not connect', SMTPErrorStates.COULDNOTCONNECT, err)
makeSMTPError(
'could not connect',
SMTPErrorStates.COULDNOTCONNECT,
err
)
);
}
};
const response = (err: Error, msg: { code: string | number, data: string }) => {
const response = (
err: Error,
msg: { code: string | number; data: string }
) => {
if (err) {
if (this._state === SMTPState.NOTCONNECTED && !this.sock) {
return;
@ -327,14 +348,10 @@ export class SMTP extends EventEmitter {
);
} else {
this.sock = new Socket();
this.sock.connect(
this.port,
this.host,
connectedErrBack
);
this.sock.connect(this.port, this.host, connectedErrBack);
}
this.monitor = monitor(this.sock, this._timeout, () =>
this.monitor = new SMTPResponse(this.sock, this._timeout, () =>
this.close(true)
);
this.sock.once('response', response);
@ -363,7 +380,10 @@ export class SMTP extends EventEmitter {
this.close(true);
caller(
callback,
makeSMTPError('no connection has been established', SMTPErrorStates.NOCONNECTION)
makeSMTPError(
'no connection has been established',
SMTPErrorStates.NOCONNECTION
)
);
}
}
@ -374,14 +394,21 @@ export class SMTP extends EventEmitter {
* @param {(number[] | number)} [codes=[250]] array codes
* @returns {void}
*/
command(cmd: string, callback: (...rest: any[]) => void, codes: (number[] | number) = [250]): void {
command(
cmd: string,
callback: (...rest: any[]) => void,
codes: number[] | number = [250]
): void {
const codesArray = Array.isArray(codes)
? codes
: typeof codes === 'number'
? [codes]
: [250];
? [codes]
: [250];
const response = (err: Error, msg: { code: string | number, data: string, message: string }) => {
const response = (
err: Error,
msg: { code: string | number; data: string; message: string }
) => {
if (err) {
caller(callback, err);
} else {
@ -391,10 +418,15 @@ export class SMTP extends EventEmitter {
const suffix = msg.message ? `: ${msg.message}` : '';
const errorMessage = `bad response on command '${
cmd.split(' ')[0]
}'${suffix}`;
}'${suffix}`;
caller(
callback,
makeSMTPError(errorMessage, SMTPErrorStates.BADRESPONSE, undefined, msg.data)
makeSMTPError(
errorMessage,
SMTPErrorStates.BADRESPONSE,
undefined,
msg.data
)
);
}
}
@ -451,7 +483,7 @@ export class SMTP extends EventEmitter {
this._isSecure = true;
this.sock = secureSocket;
monitor(this.sock, this._timeout, () => this.close(true));
new SMTPResponse(this.sock, this._timeout, () => this.close(true));
caller(callback, msg.data);
}
};
@ -468,7 +500,7 @@ export class SMTP extends EventEmitter {
// MTA's will disconnect on an ehlo. Toss an exception if
// that happens -ddm
data.split('\n').forEach(ext => {
data.split('\n').forEach((ext) => {
const parse = ext.match(/^(?:\d+[-=]?)\s*?([^\s]+)(?:\s+(.*)\s*?)?$/);
// To be able to communicate with as many SMTP servers as possible,
@ -619,7 +651,10 @@ export class SMTP extends EventEmitter {
* @param {string} [domain] the domain to associate with the command
* @returns {void}
*/
ehlo_or_helo_if_needed(callback: (...rest: any[]) => void, domain?: string): void {
ehlo_or_helo_if_needed(
callback: (...rest: any[]) => void,
domain?: string
): void {
// is this code callable...?
if (Object.keys(this.features).length === 0) {
const response = (err: Error, data: any) => caller(callback, err, data);
@ -647,7 +682,12 @@ export class SMTP extends EventEmitter {
* @param {{ method: string, domain: string }} [options] login options
* @returns {void}
*/
login(callback: (...rest: any[]) => void, user = '', password = '', options: { method?: string; domain?: string; } = {}): void {
login(
callback: (...rest: any[]) => void,
user = '',
password = '',
options: { method?: string; domain?: string } = {}
): void {
const login = {
user: () => user || this.user || '',
password: () => password || this.password || '',
@ -724,7 +764,12 @@ export class SMTP extends EventEmitter {
this.close(); // if auth is bad, close the connection, it won't get better by itself
caller(
callback,
makeSMTPError('authorization.failed', SMTPErrorStates.AUTHFAILED, err, data)
makeSMTPError(
'authorization.failed',
SMTPErrorStates.AUTHFAILED,
err,
data
)
);
};
@ -807,7 +852,12 @@ export class SMTP extends EventEmitter {
break;
default:
const msg = 'no form of authorization supported';
const err = makeSMTPError(msg, SMTPErrorStates.AUTHNOTSUPPORTED, undefined, data);
const err = makeSMTPError(
msg,
SMTPErrorStates.AUTHNOTSUPPORTED,
undefined,
data
);
caller(callback, err);
break;
}

View File

@ -1,5 +1,4 @@
import test from 'ava';
import { Readable } from 'stream';
import { simpleParser } from 'mailparser';
import { SMTPServer } from 'smtp-server';
@ -8,17 +7,29 @@ import { client as c, message as m } from '../email';
type UnPromisify<T> = T extends Promise<infer U> ? U : T;
const port = 2526;
const client = new c.Client({ port, user: 'pooh', password: 'honey', ssl: true })
const client = new c.Client({
port,
user: 'pooh',
password: 'honey',
ssl: true,
});
const smtp = new SMTPServer({ secure: true, authMethods: ['LOGIN'] });
const send = (message: m.Message, verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void) => {
const send = (
message: m.Message,
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void
) => {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // prevent CERT_HAS_EXPIRED errors
smtp.onData = (stream: Readable, _session, callback: () => void) => {
smtp.onData = (
stream: import('stream').Readable,
_session,
callback: () => void
) => {
simpleParser(stream).then(verify);
stream.on('end', callback);
};
client.send(message, err => {
client.send(message, (err) => {
if (err) {
throw err;
}
@ -26,8 +37,8 @@ const send = (message: m.Message, verify: (mail: UnPromisify<ReturnType<typeof s
};
test.before(() => {
smtp.listen(port, function() {
smtp.onAuth = function(auth, _session, callback) {
smtp.listen(port, function () {
smtp.onAuth = function (auth, _session, callback) {
if (auth.username == 'pooh' && auth.password == 'honey') {
callback(null, { user: 'pooh' });
} else {
@ -39,7 +50,7 @@ test.before(() => {
test.after(() => smtp.close());
test.cb('authorize plain', t => {
test.cb('authorize plain', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'piglet@gmail.com',
@ -47,7 +58,7 @@ test.cb('authorize plain', t => {
text: "It is hard to be brave when you're only a Very Small Animal.",
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);

View File

@ -1,5 +1,4 @@
import test from 'ava';
import { Readable } from 'stream';
import { simpleParser } from 'mailparser';
import { SMTPServer } from 'smtp-server';
@ -7,16 +6,28 @@ import { client as c, message as m } from '../email';
const port = 2526;
const client = new c.Client({ port, user: 'pooh', password: 'honey', ssl: true });
const client = new c.Client({
port,
user: 'pooh',
password: 'honey',
ssl: true,
});
const smtp = new SMTPServer({ secure: true, authMethods: ['LOGIN'] });
type UnPromisify<T> = T extends Promise<infer U> ? U : T;
const send = (message: m.Message, verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void) => {
smtp.onData = (stream: Readable, _session, callback: () => void) => {
const send = (
message: m.Message,
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void
) => {
smtp.onData = (
stream: import('stream').Readable,
_session,
callback: () => void
) => {
simpleParser(stream).then(verify);
stream.on('end', callback);
};
client.send(message, err => {
client.send(message, (err) => {
if (err) {
throw err;
}
@ -39,7 +50,7 @@ test.before(() => {
test.after(() => smtp.close());
test.cb('authorize ssl', t => {
test.cb('authorize ssl', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'pooh@gmail.com',
@ -47,7 +58,7 @@ test.cb('authorize ssl', t => {
text: 'hello friend, i hope this message finds you well.',
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);

View File

@ -6,7 +6,7 @@ const { getRFC2822Date, getRFC2822DateUTC } = d;
const toD_utc = (dt: number) => getRFC2822DateUTC(new Date(dt));
const toD = (dt: number, utc = false) => getRFC2822Date(new Date(dt), utc);
test('rfc2822 non-UTC', async t => {
test('rfc2822 non-UTC', async (t) => {
// RFC 2822 regex: For details see https://tools.ietf.org/html/rfc2822#section-3.3
// thanks to moment.js for the listing: https://github.com/moment/moment/blob/a831fc7e2694281ce31e4f090bbcf90a690f0277/src/lib/create/from-string.js#L101
var 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}))$/;
@ -17,7 +17,7 @@ test('rfc2822 non-UTC', async t => {
t.regex(toD(1529629726785), rfc2822re);
});
test('rfc2822 UTC', async t => {
test('rfc2822 UTC', async (t) => {
t.is(toD_utc(0), 'Thu, 01 Jan 1970 00:00:00 +0000');
t.is(toD_utc(0), toD(0, true));

View File

@ -2,25 +2,36 @@ import { readFileSync, createReadStream } from 'fs';
import { join } from 'path';
import test from 'ava';
import { Readable } from 'stream';
import { simpleParser } from 'mailparser';
import { SMTPServer } from 'smtp-server';
import { client as c, message as m } from '../email';
const port = 2526;
const client = new c.Client({ port, user: 'pooh', password: 'honey', ssl: true });
const client = new c.Client({
port,
user: 'pooh',
password: 'honey',
ssl: true,
});
const smtp = new SMTPServer({ secure: true, authMethods: ['LOGIN'] });
type UnPromisify<T> = T extends Promise<infer U> ? U : T;
const send = (message: m.Message, verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void) => {
const send = (
message: m.Message,
verify: (mail: UnPromisify<ReturnType<typeof simpleParser>>) => void
) => {
process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'; // prevent CERT_HAS_EXPIRED errors
smtp.onData = (stream: Readable, _session, callback: () => void) => {
smtp.onData = (
stream: import('stream').Readable,
_session,
callback: () => void
) => {
simpleParser(stream).then(verify);
stream.on('end', callback);
};
client.send(message, err => {
client.send(message, (err) => {
if (err) {
throw err;
}
@ -42,7 +53,7 @@ test.before(() => {
test.after(() => smtp.close());
test.cb('simple text message', t => {
test.cb('simple text message', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'zelda@gmail.com',
@ -51,7 +62,7 @@ test.cb('simple text message', t => {
'message-id': 'this is a special id',
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
@ -61,7 +72,7 @@ test.cb('simple text message', t => {
});
});
test.cb('null text message', t => {
test.cb('null text message', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'zelda@gmail.com',
@ -70,13 +81,13 @@ test.cb('null text message', t => {
'message-id': 'this is a special id',
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, '\n\n\n');
t.end();
});
});
test.cb('empty text message', t => {
test.cb('empty text message', (t) => {
const msg = {
subject: 'this is a test TEXT message from emailjs',
from: 'zelda@gmail.com',
@ -85,13 +96,13 @@ test.cb('empty text message', t => {
'message-id': 'this is a special id',
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, '\n\n\n');
t.end();
});
});
test.cb('simple unicode text message', t => {
test.cb('simple unicode text message', (t) => {
const msg = {
subject: 'this ✓ is a test ✓ TEXT message from emailjs',
from: 'zelda✓ <zelda@gmail.com>',
@ -99,7 +110,7 @@ test.cb('simple unicode text message', t => {
text: 'hello ✓ friend, i hope this message finds you well.',
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, msg.text + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
@ -108,7 +119,7 @@ test.cb('simple unicode text message', t => {
});
});
test.cb('very large text message', t => {
test.cb('very large text message', (t) => {
t.timeout(20000);
// thanks to jart+loberstech for this one!
@ -116,13 +127,10 @@ test.cb('very large text message', t => {
subject: 'this is a test TEXT message from emailjs',
from: 'ninjas@gmail.com',
to: 'pirates@gmail.com',
text: readFileSync(
join(__dirname, 'attachments/smtp.txt'),
'utf-8'
),
text: readFileSync(join(__dirname, 'attachments/smtp.txt'), 'utf-8'),
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.text, msg.text.replace(/\r/g, '') + '\n\n\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
@ -131,7 +139,7 @@ test.cb('very large text message', t => {
});
});
test.cb('very large text data message', t => {
test.cb('very large text data message', (t) => {
t.timeout(10000);
const text =
@ -145,10 +153,13 @@ test.cb('very large text data message', t => {
to: 'lizards@gmail.com',
text:
'hello friend if you are seeing this, you can not view html emails. it is attached inline.',
attachment: { data: text, alternative: true } as unknown as m.MessageAttachment,
attachment: ({
data: text,
alternative: true,
} as unknown) as m.MessageAttachment,
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.html, text.replace(/\r/g, ''));
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
@ -158,19 +169,19 @@ test.cb('very large text data message', t => {
});
});
test.cb('html data message', t => {
const html = readFileSync(
join(__dirname, 'attachments/smtp.html'),
'utf-8'
);
test.cb('html data message', (t) => {
const html = readFileSync(join(__dirname, 'attachments/smtp.html'), 'utf-8');
const msg = {
subject: 'this is a test TEXT+HTML+DATA message from emailjs',
from: 'obama@gmail.com',
to: 'mitt@gmail.com',
attachment: { data: html, alternative: true } as unknown as m.MessageAttachment,
attachment: ({
data: html,
alternative: true,
} as unknown) as m.MessageAttachment,
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
@ -180,19 +191,19 @@ test.cb('html data message', t => {
});
});
test.cb('html file message', t => {
const html = readFileSync(
join(__dirname, 'attachments/smtp.html'),
'utf-8'
);
test.cb('html file message', (t) => {
const html = readFileSync(join(__dirname, 'attachments/smtp.html'), 'utf-8');
const msg = {
subject: 'this is a test TEXT+HTML+FILE message from emailjs',
from: 'thomas@gmail.com',
to: 'nikolas@gmail.com',
attachment: { path: join(__dirname, 'attachments/smtp.html'), alternative: true } as unknown as m.MessageAttachment,
attachment: ({
path: join(__dirname, 'attachments/smtp.html'),
alternative: true,
} as unknown) as m.MessageAttachment,
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
@ -202,17 +213,14 @@ test.cb('html file message', t => {
});
});
test.cb('html with image embed message', t => {
const html = readFileSync(
join(__dirname, 'attachments/smtp2.html'),
'utf-8'
);
test.cb('html with image embed message', (t) => {
const html = readFileSync(join(__dirname, 'attachments/smtp2.html'), 'utf-8');
const image = readFileSync(join(__dirname, 'attachments/smtp.gif'));
const msg = {
subject: 'this is a test TEXT+HTML+IMAGE message from emailjs',
from: 'ninja@gmail.com',
to: 'pirate@gmail.com',
attachment: {
attachment: ({
path: join(__dirname, 'attachments/smtp2.html'),
alternative: true,
related: [
@ -223,11 +231,14 @@ test.cb('html with image embed message', t => {
headers: { 'Content-ID': '<smtp-diagram@local>' },
},
],
} as unknown as m.MessageAttachment,
} as unknown) as m.MessageAttachment,
};
send(new m.Message(msg), mail => {
t.is(mail.attachments[0].content.toString('base64'), image.toString('base64'));
send(new m.Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
image.toString('base64')
);
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
@ -237,7 +248,7 @@ test.cb('html with image embed message', t => {
});
});
test.cb('html data and attachment message', t => {
test.cb('html data and attachment message', (t) => {
const html = readFileSync(join(__dirname, 'attachments/smtp.html'), 'utf-8');
const msg = {
subject: 'this is a test TEXT+HTML+FILE message from emailjs',
@ -249,7 +260,7 @@ test.cb('html data and attachment message', t => {
] as m.MessageAttachment[],
};
send(new m.Message(msg), mail => {
send(new m.Message(msg), (mail) => {
t.is(mail.html, html.replace(/\r/g, ''));
t.is(mail.text, '\n');
t.is(mail.subject, msg.subject);
@ -259,7 +270,7 @@ test.cb('html data and attachment message', t => {
});
});
test.cb('attachment message', t => {
test.cb('attachment message', (t) => {
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
const msg = {
subject: 'this is a test TEXT+ATTACHMENT message from emailjs',
@ -273,8 +284,11 @@ test.cb('attachment message', t => {
} as m.MessageAttachment,
};
send(new m.Message(msg), mail => {
t.is(mail.attachments[0].content.toString('base64'), pdf.toString('base64'));
send(new m.Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
@ -283,7 +297,7 @@ test.cb('attachment message', t => {
});
});
test.cb('attachment sent with unicode filename message', t => {
test.cb('attachment sent with unicode filename message', (t) => {
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
const msg = {
subject: 'this is a test TEXT+ATTACHMENT message from emailjs',
@ -297,8 +311,11 @@ test.cb('attachment sent with unicode filename message', t => {
} as m.MessageAttachment,
};
send(new m.Message(msg), mail => {
t.is(mail.attachments[0].content.toString('base64'), pdf.toString('base64'));
send(new m.Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(mail.attachments[0].filename, 'smtp-✓-info.pdf');
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
@ -308,7 +325,7 @@ test.cb('attachment sent with unicode filename message', t => {
});
});
test.cb('attachments message', t => {
test.cb('attachments message', (t) => {
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
const tar = readFileSync(join(__dirname, 'attachments/postfix-2.8.7.tar.gz'));
const msg = {
@ -330,9 +347,15 @@ test.cb('attachments message', t => {
] as m.MessageAttachment[],
};
send(new m.Message(msg), mail => {
t.is(mail.attachments[0].content.toString('base64'), pdf.toString('base64'));
t.is(mail.attachments[1].content.toString('base64'), tar.toString('base64'));
send(new m.Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(
mail.attachments[1].content.toString('base64'),
tar.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);
@ -341,31 +364,42 @@ test.cb('attachments message', t => {
});
});
test.cb('streams message', t => {
test.cb('streams message', (t) => {
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
const tar = readFileSync(join(__dirname, 'attachments/postfix-2.8.7.tar.gz'));
const stream = createReadStream(join(__dirname, 'attachments/smtp.pdf'));
const stream2 = createReadStream(join(__dirname, 'attachments/postfix-2.8.7.tar.gz'));
const stream2 = createReadStream(
join(__dirname, 'attachments/postfix-2.8.7.tar.gz')
);
const msg = {
subject:
'this is a test TEXT+2+STREAMED+ATTACHMENTS message from emailjs',
subject: 'this is a test TEXT+2+STREAMED+ATTACHMENTS message from emailjs',
from: 'stanford@gmail.com',
to: 'mit@gmail.com',
text:
'hello friend, i hope this message and streamed attachments finds you well.',
attachment: [
attachment: ([
{ stream, type: 'application/pdf', name: 'smtp-info.pdf' },
{ stream: stream2, type: 'application/x-gzip', name: 'postfix.source.2.8.7.tar.gz' },
] as unknown as m.MessageAttachment[],
{
stream: stream2,
type: 'application/x-gzip',
name: 'postfix.source.2.8.7.tar.gz',
},
] as unknown) as m.MessageAttachment[],
};
stream.pause();
stream2.pause();
send(new m.Message(msg), mail => {
t.is(mail.attachments[0].content.toString('base64'), pdf.toString('base64'));
t.is(mail.attachments[1].content.toString('base64'), tar.toString('base64'));
send(new m.Message(msg), (mail) => {
t.is(
mail.attachments[0].content.toString('base64'),
pdf.toString('base64')
);
t.is(
mail.attachments[1].content.toString('base64'),
tar.toString('base64')
);
t.is(mail.text, msg.text + '\n');
t.is(mail.subject, msg.subject);
t.is(mail.from?.text, msg.from);

View File

@ -2,35 +2,47 @@ import test from 'ava';
import { client as c, message as m, SMTP } from '../email';
test.cb('connecting to wrong email server should not invoke callback multiple times', t => {
t.timeout(5000);
test.cb(
'connecting to wrong email server should not invoke callback multiple times',
(t) => {
t.timeout(5000);
const client = new c.Client({ host: 'bar.baz' });
const msg = {
from: 'foo@bar.baz',
to: 'foo@bar.baz',
subject: 'hello world',
text: 'hello world',
};
client.send(new m.Message(msg), err => {
t.not(err, null);
t.end();
});
});
const client = new c.Client({ host: 'bar.baz' });
const msg = {
from: 'foo@bar.baz',
to: 'foo@bar.baz',
subject: 'hello world',
text: 'hello world',
};
client.send(new m.Message(msg), (err) => {
t.not(err, null);
t.end();
});
}
);
test('should have a default timeout', async t => {
test('should have a default timeout', async (t) => {
const connectionOptions = {
user: 'username',
password: 'password',
host: '127.0.0.1',
port: 1234,
timeout: undefined as number | null | undefined
timeout: undefined as number | null | undefined,
};
t.deepEqual(new c.Client(connectionOptions).smtp.timeout, SMTP.DEFAULT_TIMEOUT);
t.deepEqual(
new c.Client(connectionOptions).smtp.timeout,
SMTP.DEFAULT_TIMEOUT
);
connectionOptions.timeout = null;
t.deepEqual(new c.Client(connectionOptions).smtp.timeout, SMTP.DEFAULT_TIMEOUT);
t.deepEqual(
new c.Client(connectionOptions).smtp.timeout,
SMTP.DEFAULT_TIMEOUT
);
connectionOptions.timeout = undefined;
t.deepEqual(new c.Client(connectionOptions).smtp.timeout, SMTP.DEFAULT_TIMEOUT);
t.deepEqual(
new c.Client(connectionOptions).smtp.timeout,
SMTP.DEFAULT_TIMEOUT
);
});

View File

@ -113,6 +113,11 @@
resolved "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
"@types/eslint-visitor-keys@^1.0.0":
version "1.0.0"
resolved "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#1ee30d79544ca84d68d4b3cdb0af4f205663dd2d"
integrity sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==
"@types/estree@0.0.39":
version "0.0.39"
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f"
@ -131,6 +136,11 @@
"@types/minimatch" "*"
"@types/node" "*"
"@types/json-schema@^7.0.3":
version "7.0.4"
resolved "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.4.tgz#38fd73ddfd9b55abb1e1b2ed578cb55bd7b7d339"
integrity sha512-8+KAKzEvSUdeo+kmqnKrqgeE+LcA0tjYWFY7RPProVYwnqDjukzO+3b6dLD56rYX5TdWejnEOLJYOIeh4CXKuA==
"@types/mailparser@2.7.2":
version "2.7.2"
resolved "https://registry.npmjs.org/@types/mailparser/-/mailparser-2.7.2.tgz#4c319f8f8ad770d055850d8f7d32d311d893dfa9"
@ -174,6 +184,49 @@
"@types/node" "*"
"@types/nodemailer" "*"
"@typescript-eslint/eslint-plugin@2.29.0":
version "2.29.0"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.29.0.tgz#c9efab7624e3dd6d144a0e4577a541d1bd42c2ac"
integrity sha512-X/YAY7azKirENm4QRpT7OVmzok02cSkqeIcLmdz6gXUQG4Hk0Fi9oBAynSAyNXeGdMRuZvjBa0c1Lu0dn/u6VA==
dependencies:
"@typescript-eslint/experimental-utils" "2.29.0"
functional-red-black-tree "^1.0.1"
regexpp "^3.0.0"
tsutils "^3.17.1"
"@typescript-eslint/experimental-utils@2.29.0":
version "2.29.0"
resolved "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.29.0.tgz#3cb8060de9265ba131625a96bbfec31ba6d4a0fe"
integrity sha512-H/6VJr6eWYstyqjWXBP2Nn1hQJyvJoFdDtsHxGiD+lEP7piGnGpb/ZQd+z1ZSB1F7dN+WsxUDh8+S4LwI+f3jw==
dependencies:
"@types/json-schema" "^7.0.3"
"@typescript-eslint/typescript-estree" "2.29.0"
eslint-scope "^5.0.0"
eslint-utils "^2.0.0"
"@typescript-eslint/parser@2.29.0":
version "2.29.0"
resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-2.29.0.tgz#6e3c4e21ed6393dc05b9d8b47f0b7e731ef21c9c"
integrity sha512-H78M+jcu5Tf6m/5N8iiFblUUv+HJDguMSdFfzwa6vSg9lKR8Mk9BsgeSjO8l2EshKnJKcbv0e8IDDOvSNjl0EA==
dependencies:
"@types/eslint-visitor-keys" "^1.0.0"
"@typescript-eslint/experimental-utils" "2.29.0"
"@typescript-eslint/typescript-estree" "2.29.0"
eslint-visitor-keys "^1.1.0"
"@typescript-eslint/typescript-estree@2.29.0":
version "2.29.0"
resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-2.29.0.tgz#1be6612bb02fc37ac9f466521c1459a4744e8d3a"
integrity sha512-3YGbtnWy4az16Egy5Fj5CckkVlpIh0MADtAQza+jiMADRSKkjdpzZp/5WuvwK/Qib3Z0HtzrDFeWanS99dNhnA==
dependencies:
debug "^4.1.1"
eslint-visitor-keys "^1.1.0"
glob "^7.1.6"
is-glob "^4.0.1"
lodash "^4.17.15"
semver "^6.3.0"
tsutils "^3.17.1"
acorn-jsx@^5.2.0:
version "5.2.0"
resolved "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
@ -876,14 +929,6 @@ eslint-config-prettier@6.11.0:
dependencies:
get-stdin "^6.0.0"
eslint-plugin-mocha@6.3.0:
version "6.3.0"
resolved "https://registry.npmjs.org/eslint-plugin-mocha/-/eslint-plugin-mocha-6.3.0.tgz#72bfd06a5c4323e17e30ef41cd726030e8cdb8fd"
integrity sha512-Cd2roo8caAyG21oKaaNTj7cqeYRWW1I2B5SfpKRp0Ip1gkfwoR1Ow0IGlPWnNjzywdF4n+kHL8/9vM6zCJUxdg==
dependencies:
eslint-utils "^2.0.0"
ramda "^0.27.0"
eslint-plugin-prettier@3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.3.tgz#ae116a0fc0e598fdae48743a4430903de5b4e6ca"
@ -1140,7 +1185,7 @@ glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0:
dependencies:
is-glob "^4.0.1"
glob@^7.1.2, glob@^7.1.3:
glob@^7.1.2, glob@^7.1.3, glob@^7.1.6:
version "7.1.6"
resolved "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
@ -2092,11 +2137,6 @@ ramda@^0.26.1:
resolved "https://registry.npmjs.org/ramda/-/ramda-0.26.1.tgz#8d41351eb8111c55353617fc3bbffad8e4d35d06"
integrity sha512-hLWjpy7EnsDBb0p+Z3B7rPi3GDeRG5ZtiI33kJhTt+ORCd38AbAIjB/9zRIUoeTbE/AVX5ZkU7m6bznsvrf8eQ==
ramda@^0.27.0:
version "0.27.0"
resolved "https://registry.npmjs.org/ramda/-/ramda-0.27.0.tgz#915dc29865c0800bf3f69b8fd6c279898b59de43"
integrity sha512-pVzZdDpWwWqEVVLshWUHjNwuVP7SfcmPraYuqocJp1yo2U1R7P+5QAfDhdItkuoGqIBnBYrtPp7rEPqDn9HlZA==
rc@^1.2.8:
version "1.2.8"
resolved "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz#cd924bf5200a075b83c188cd6b9e211b7fc0d3ed"
@ -2138,6 +2178,11 @@ regexpp@^2.0.1:
resolved "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
regexpp@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/regexpp/-/regexpp-3.1.0.tgz#206d0ad0a5648cffbdb8ae46438f3dc51c9f78e2"
integrity sha512-ZOIzd8yVsQQA7j8GCSlPGXwg5PfmA1mrq0JP4nGhh54LaKN3xdai/vHUDu74pKwV8OxseMS65u2NImosQcSD0Q==
registry-auth-token@^4.0.0:
version "4.1.1"
resolved "https://registry.npmjs.org/registry-auth-token/-/registry-auth-token-4.1.1.tgz#40a33be1e82539460f94328b0f7f0f84c16d9479"
@ -2343,7 +2388,7 @@ smtp-server@3.6.0:
ipv6-normalize "1.0.1"
nodemailer "6.4.5"
source-map-support@^0.5.16, source-map-support@^0.5.17:
source-map-support@^0.5.16:
version "0.5.17"
resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.17.tgz#29fe1b3c98b9dbd5064ada89052ee8ff070cb46c"
integrity sha512-bwdKOBZ5L0gFRh4KOxNap/J/MpvX9Yxsq9lFDx65s3o7F/NiHy7JRaGIS8MwW6tZPAq9UXE207Il0cfcb5yu/Q==
@ -2351,6 +2396,14 @@ source-map-support@^0.5.16, source-map-support@^0.5.17:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map-support@^0.5.17:
version "0.5.18"
resolved "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.18.tgz#f5f33489e270bd7f7d7e7b8debf283f3a4066960"
integrity sha512-9luZr/BZ2QeU6tO2uG8N2aZpVSli4TSAOAqFOyTO51AJcD9P99c0K1h6dD6r6qo5dyT44BR5exweOaLLeldTkQ==
dependencies:
buffer-from "^1.0.0"
source-map "^0.6.0"
source-map@^0.6.0:
version "0.6.1"
resolved "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
@ -2559,11 +2612,18 @@ ts-node@8.9.0:
source-map-support "^0.5.17"
yn "3.1.1"
tslib@^1.9.0:
tslib@^1.8.1, tslib@^1.9.0:
version "1.11.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tsutils@^3.17.1:
version "3.17.1"
resolved "https://registry.npmjs.org/tsutils/-/tsutils-3.17.1.tgz#ed719917f11ca0dee586272b2ac49e015a2dd759"
integrity sha512-kzeQ5B8H3w60nFY2g8cJIuH7JDpsALXySGtwGJ0p2LSjLgay3NdIpqq5SoOBe46bKDW2iq25irHCr8wjomUS2g==
dependencies:
tslib "^1.8.1"
type-check@~0.3.2:
version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"