mirror of https://github.com/eleith/emailjs.git
chore: update linting
This commit is contained in:
parent
8993c49400
commit
16a23e909e
|
@ -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"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
});
|
||||
|
|
10
smtp/date.ts
10
smtp/date.ts
|
@ -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(' ');
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
111
smtp/message.ts
111
smtp/message.ts
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
174
smtp/smtp.ts
174
smtp/smtp.ts
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
||||
|
|
168
test/message.ts
168
test/message.ts
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
);
|
||||
});
|
||||
|
|
92
yarn.lock
92
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue