Compare commits

...

4 Commits

Author SHA1 Message Date
Zack Schuster 1ab1da4cda test/client: remove auth from shared client & server 2022-05-06 03:16:30 -07:00
Zack Schuster f6bd3bb6a0 test/connection: catch server exceptions 2022-05-06 03:15:34 -07:00
Zack Schuster dace0f0a6b chore: fix jsdoc errors in source & bundle 2022-05-06 02:32:18 -07:00
Zack Schuster 17e2b7a7df smtp/message: remove deprecated api 2022-05-03 11:38:32 -07:00
9 changed files with 59 additions and 55 deletions

View File

@ -7,6 +7,25 @@ export default {
file: 'email.js',
format: 'es',
sourcemap: true,
banner: '/* eslint-disable no-undef */',
footer: `
/**
* @typedef {{ [index: string]: any }} AddressObject
* @typedef {{ [index: string]: any }} AddressToken
* @typedef {{ [index: string]: any }} ConnectOptions
* @typedef {{ [index: string]: any }} MessageAttachment
* @typedef {{ [index: string]: any }} MessageHeaders
* @typedef {{ [index: string]: any }} MessageStack
* @typedef {{ [index: string]: any }} SMTPConnectionOptions
* @typedef {Function} SMTPCommandCallback
*/
/**
* @template {Message | MessageHeaders} T
* @typedef {<T>(err: Error, msg: T) => void} MessageCallback
*/
`
.trim()
.replace(/\t/g, ''),
},
external: builtinModules,
plugins: [

View File

@ -94,7 +94,7 @@ export class SMTPClient {
* @public
* @description Converts a message to the raw object used by the internal stack.
* @param {Message} message message to convert
* @param {MessageCallback} callback errback
* @param {MessageCallback<Message>} callback errback
* @returns {MessageStack} raw message object
*/
public createMessageStack(
@ -322,7 +322,7 @@ export class SMTPClient {
/**
* @protected
* @param {Error} err err
* @param {Error | null} err err
* @param {MessageStack} stack stack
* @returns {void}
*/

View File

@ -9,8 +9,8 @@ import { SMTPError, SMTPErrorStates } from './error.js';
import { SMTPResponseMonitor } from './response.js';
/**
* @readonly
* @enum
* @constant
* @enum {string}
*/
export const AUTH_METHODS = {
PLAIN: 'PLAIN',
@ -20,8 +20,8 @@ export const AUTH_METHODS = {
} as const;
/**
* @readonly
* @enum
* @constant
* @enum {number}
*/
export const SMTPState = {
NOTCONNECTED: 0,
@ -122,6 +122,8 @@ export class SMTPConnection extends EventEmitter {
* To target a Message Transfer Agent (MTA), omit all options.
*
* NOTE: `host` is trimmed before being used to establish a connection; however, the original untrimmed value will still be visible in configuration.
*
* @param {Partial<SMTPConnectionOptions>} options options
*/
constructor({
timeout,
@ -443,7 +445,7 @@ export class SMTPConnection extends EventEmitter {
* @see https://tools.ietf.org/html/rfc2821#appendix-F.3
*
* @param {SMTPCommandCallback} callback function to call after response
* @param {string} domain the domain to associate with the 'helo' request
* @param {string} [domain] the domain to associate with the 'helo' request
* @returns {void}
*/
public helo(callback: SMTPCommandCallback, domain?: string) {
@ -528,7 +530,7 @@ export class SMTPConnection extends EventEmitter {
/**
* @public
* @param {SMTPCommandCallback} callback function to call after response
* @param {string} domain the domain to associate with the 'ehlo' request
* @param {string} [domain] the domain to associate with the 'ehlo' request
* @returns {void}
*/
public ehlo(callback: SMTPCommandCallback, domain?: string) {
@ -695,7 +697,9 @@ export class SMTPConnection extends EventEmitter {
* @param {SMTPCommandCallback} callback function to call after response
* @param {string} [user] the username to authenticate with
* @param {string} [password] the password for the authentication
* @param {{ method: string, domain: string }} [options] login options
* @param {Object} [options] login options
* @param {string} [options.method] login method
* @param {string} [options.domain] login domain
* @returns {void}
*/
public login(
@ -755,7 +759,10 @@ export class SMTPConnection extends EventEmitter {
const preferred = this.authentication;
let auth = '';
if (typeof this.features?.['auth'] === 'string') {
if (
this.features != null &&
typeof this.features['auth'] === 'string'
) {
auth = this.features['auth'];
}

View File

@ -43,7 +43,7 @@ const rfc2822re =
/^(?:(Mon|Tue|Wed|Thu|Fri|Sat|Sun),?\s)?(\d{1,2})\s(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s(\d{2,4})\s(\d\d):(\d\d)(?::(\d\d))?\s(?:(UT|GMT|[ECMP][SD]T)|([Zz])|([+-]\d{4}))$/;
/**
* @param {string} [date] a string to check for conformance to the [rfc2822](https://tools.ietf.org/html/rfc2822#section-3.3) standard
* @param {string} date a string to check for conformance to the [rfc2822](https://tools.ietf.org/html/rfc2822#section-3.3) standard
* @returns {boolean} the result of the conformance check
*/
export function isRFC2822Date(date: string) {

View File

@ -1,6 +1,6 @@
/**
* @readonly
* @enum
* @constant
* @enum {number}
*/
export const SMTPErrorStates = {
COULDNOTCONNECT: 1,
@ -32,8 +32,8 @@ export class SMTPError extends Error {
*
* @param {string} message error message
* @param {number} code smtp error state
* @param {Error | null} error previous error
* @param {unknown} smtp arbitrary data
* @param {Error | null} [error] previous error
* @param {unknown} [smtp] arbitrary data
* @returns {SMTPError} error
*/
public static create(
@ -42,7 +42,8 @@ export class SMTPError extends Error {
error?: Error | null,
smtp?: unknown
) {
const msg = error?.message ? `${message} (${error.message})` : message;
const { message: errorMsg } = error ?? { message: undefined };
const msg = errorMsg ? `${message} (${errorMsg})` : message;
const err = new SMTPError(msg);
err.code = code;

View File

@ -247,17 +247,6 @@ export class Message {
return { isValid: true, validationError: undefined };
}
/**
* @public
* @deprecated does not conform to the `errback` style followed by the rest of the library, and will be removed in the next major version. use `checkValidity` instead.
* @param {function(isValid: boolean, invalidReason: (string | undefined)): void} callback .
* @returns {void}
*/
public valid(callback: (isValid: boolean, invalidReason?: string) => void) {
const { isValid, validationError } = this.checkValidity();
callback(isValid, validationError);
}
/**
* @public
* @returns {MessageStream} a stream of the current message
@ -305,9 +294,7 @@ class MessageStream extends Stream {
super();
/**
* @param {string} [data] the data to output
* @param {Function} [callback] the function
* @param {any[]} [args] array of arguments to pass to the callback
* @param {string} data the data to output
* @returns {void}
*/
const output = (data: string) => {
@ -359,7 +346,7 @@ class MessageStream extends Stream {
};
/**
* @param {MessageAttachment} [attachment] the attachment whose headers you would like to output
* @param {MessageAttachment} attachment the attachment whose headers you would like to output
* @returns {void}
*/
const outputAttachmentHeaders = (attachment: MessageAttachment) => {
@ -430,7 +417,7 @@ class MessageStream extends Stream {
: inputEncoding;
/**
* @param {Error} err the error to emit
* @param {NodeJS.ErrnoException | null} err the error to emit
* @param {number} fd the file descriptor
* @returns {void}
*/
@ -572,7 +559,7 @@ class MessageStream extends Stream {
outputMessage(boundary, this.message.attachments, 0, close);
} else {
outputAlternative(
// typescript bug; should narrow to { alternative: MessageAttachment }
// typescript bug; should narrow to Message & { alternative: MessageAttachment }
this.message as Parameters<typeof outputAlternative>[0],
() => outputMessage(boundary, this.message.attachments, 0, close)
);
@ -661,10 +648,11 @@ class MessageStream extends Stream {
callback();
};
if (message.alternative.related) {
outputRelated(message.alternative, finish);
const { alternative } = message;
if (alternative.related) {
outputRelated(alternative, finish);
} else {
outputAttachment(message.alternative, finish);
outputAttachment(alternative, finish);
}
};

View File

@ -120,7 +120,7 @@ function splitMimeEncodedString(str: string, maxlen = 12) {
*/
function checkRanges(nr: number) {
return RANGES.reduce(
(val, range) =>
(/** @type {boolean} */ val, range) =>
val ||
(range.length === 1 && nr === range[0]) ||
(range.length === 2 && nr >= range[0] && nr <= range[1]),

View File

@ -1,7 +1,7 @@
import { promisify } from 'util';
import test from 'ava';
import { SMTPServer } from 'smtp-server';
import { SMTPServer, SMTPServerOptions } from 'smtp-server';
import {
DEFAULT_TIMEOUT,
@ -13,22 +13,11 @@ import {
const port = 3333;
let greylistPort = 4444;
const client = new SMTPClient({
port,
user: 'pooh',
password: 'honey',
ssl: true,
});
const client = new SMTPClient({ port });
const server = new SMTPServer({
secure: true,
onAuth(auth, _session, callback) {
if (auth.username === 'pooh' && auth.password === 'honey') {
callback(null, { user: 'pooh' });
} else {
return callback(new Error('invalid user / pass'));
}
},
});
authOptional: true,
maxAllowedUnauthenticatedCommands: Infinity,
} as unknown as SMTPServerOptions);
test.before(async (t) => {
server.listen(port, t.pass);

View File

@ -10,8 +10,8 @@ let counter = 0;
function testConnection(options: Partial<SMTPConnectionOptions> = {}) {
const increment = counter++;
return new Promise<void>((resolve, reject) => {
const { ssl } = options;
const server = new SMTPServer(ssl ? { secure: true } : undefined);
const server = new SMTPServer({ secure: options.ssl as boolean });
server.on('error', (err) => reject(err));
server.listen(port + increment, () => {
const connection = new SMTPConnection(options);
connection.connect((err) => {