mirror of https://github.com/eleith/emailjs.git
Compare commits
42 Commits
Author | SHA1 | Date |
---|---|---|
eleith | fc175932f6 | |
eleith | e11fac696b | |
eleith | 3a4ba01e01 | |
eleith | 426d270068 | |
eleith | 03694001e5 | |
eleith | ed54a25008 | |
eleith | 70b89d9361 | |
eleith | a6063e44a6 | |
eleith | 8b3c0b16f8 | |
Zack Schuster | 7d772326d9 | |
Zack Schuster | a395f862ec | |
Zack Schuster | 4f45799a3e | |
Zack Schuster | 9a5ce38186 | |
Zack Schuster | a7b468908f | |
Zack Schuster | 793fa7466c | |
eleith | bdf156971e | |
eleith | 8c67cf0160 | |
eleith | 6dc55e013e | |
Zack Schuster | c0f7a171ea | |
Zack Schuster | 2fc0e8c493 | |
Zack Schuster | 851b345d33 | |
Zack Schuster | f242b96dae | |
Zack Schuster | 79a81538aa | |
Zack Schuster | 652684486c | |
Zack Schuster | 5ee7d3b3b8 | |
Zack Schuster | f0cd1ce544 | |
eleith | b91cf6c97f | |
Zack Schuster | 24c313669c | |
Zack Schuster | 1d905b0d66 | |
Zack Schuster | f064ff1302 | |
Jeremy Möglich | 9d5b5376db | |
Zack Schuster | 54e03335bf | |
Zack Schuster | 6ae32a4c8e | |
Zack Schuster | f9b84cf0fd | |
Zack Schuster | c1d0aee0b1 | |
eleith | 607de0f6a6 | |
eleith | a965d9bc42 | |
Zack Schuster | e38e1b426f | |
Zack Schuster | 8496793ed6 | |
Zack Schuster | 495d8fc838 | |
Zack Schuster | d184a82bfe | |
Zack Schuster | 6d48c82aaf |
|
@ -9,7 +9,6 @@
|
|||
"plugin:@typescript-eslint/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": [
|
||||
"error",
|
||||
{
|
||||
|
|
|
@ -7,24 +7,19 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: [^10, ^12, ^14, ^16, ^17]
|
||||
node: [^12, ^14, ^16, ^18]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: install (node 10)
|
||||
if: matrix.node == '^10'
|
||||
run: yarn install --ignore-engines
|
||||
|
||||
- name: install
|
||||
if: matrix.node != '^10'
|
||||
run: yarn install
|
||||
|
||||
- name: lint
|
||||
|
|
|
@ -7,28 +7,20 @@ jobs:
|
|||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
node: [^10, ^12, ^14, ^16, ^17]
|
||||
os: [ubuntu-latest, windows-latest, macos-latest]
|
||||
node: [^12, ^14, ^16, ^18]
|
||||
os: [ubuntu-latest, windows-latest]
|
||||
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: node
|
||||
uses: actions/setup-node@v2
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
|
||||
- name: install (node 10)
|
||||
if: matrix.node == '^10'
|
||||
run: yarn install --ignore-engines
|
||||
|
||||
- name: install
|
||||
if: matrix.node != '^10'
|
||||
run: yarn install
|
||||
|
||||
- name: test
|
||||
run: yarn test
|
||||
|
||||
- name: test-cjs
|
||||
run: yarn test-cjs
|
||||
|
|
|
@ -70,6 +70,7 @@ scripts
|
|||
spec
|
||||
test
|
||||
test-*
|
||||
!test-*.d.ts
|
||||
tests
|
||||
tsserverlibrary.*
|
||||
typescriptservices.*
|
||||
|
|
18
CHANGELOG.md
18
CHANGELOG.md
|
@ -4,6 +4,24 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
||||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [4.0.2] - 2023-05-12
|
||||
### Fixed
|
||||
- redact passwords in error messages [#339](https://github.com/eleith/emailjs/issues/339)
|
||||
|
||||
## [4.0.0] - 2022-04-20
|
||||
### Added
|
||||
- support `isolatedModules` and `preserveValueImports` compilation scenarios [#305](https://github.com/eleith/emailjs/pull/305)
|
||||
|
||||
### Fixed
|
||||
- support `typescript@3.8.3` [#307](https://github.com/eleith/emailjs/issues/307)
|
||||
- the types change in `v3.8.0` for `Client#send` & `Client#sendAsync` unintentionally raised the minimum `typescript` requirement. fixing this involved weakening the types for those functions, which may require modifying your code. this change will be reverted for `v4.0.0`.
|
||||
|
||||
## [3.8.0] - 2022-03-17
|
||||
### Added
|
||||
- support `typescript@4.6`
|
||||
- type allow `Client#send` & `Client#sendAsync` to accept message headers instead of a `Message`
|
||||
- no behavior change: this was previously allowed, but the types didn't acknowledge it
|
||||
|
||||
## [3.7.0] - 2021-11-19
|
||||
### Added
|
||||
- support `typescript@4.5`
|
||||
|
|
|
@ -1,17 +1,12 @@
|
|||
/* global process */
|
||||
const useCjsConfig =
|
||||
process.version.startsWith('v10') ||
|
||||
process.argv.includes('--node-arguments=--title=cjs');
|
||||
|
||||
export default {
|
||||
files: ['test/*.ts'],
|
||||
extensions: useCjsConfig ? ['ts'] : { ts: 'module' },
|
||||
require: useCjsConfig ? ['./email.test.ts'] : undefined,
|
||||
extensions: {
|
||||
ts: 'module',
|
||||
},
|
||||
environmentVariables: {
|
||||
NODE_TLS_REJECT_UNAUTHORIZED: '0',
|
||||
},
|
||||
nonSemVerExperiments: useCjsConfig ? {} : { configurableModuleFormat: true },
|
||||
nodeArguments: useCjsConfig
|
||||
? undefined
|
||||
: ['--loader=ts-node/esm', '--experimental-specifier-resolution=node'],
|
||||
files: ['test/*.ts'],
|
||||
nodeArguments: ['--loader=ts-node/esm'],
|
||||
// makes tests far slower
|
||||
workerThreads: false,
|
||||
};
|
||||
|
|
|
@ -1249,7 +1249,7 @@ class SMTPConnection extends EventEmitter {
|
|||
this.user = () => user;
|
||||
this.password = () => password;
|
||||
if (typeof logger === 'function') {
|
||||
this.log = log;
|
||||
this.log = logger;
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
@ -1726,6 +1726,7 @@ class SMTPConnection extends EventEmitter {
|
|||
const failed = (err, data) => {
|
||||
this.loggedin = false;
|
||||
this.close(); // if auth is bad, close the connection, it won't get better by itself
|
||||
err.message = err.message.replace(this.password(), 'REDACTED');
|
||||
caller(callback, SMTPError.create('authorization.failed', SMTPErrorStates.AUTHFAILED, err, data));
|
||||
};
|
||||
/**
|
||||
|
@ -1853,8 +1854,9 @@ class SMTPClient {
|
|||
}
|
||||
/**
|
||||
* @public
|
||||
* @param {Message} msg the message to send
|
||||
* @param {function(err: Error, msg: Message): void} callback .
|
||||
* @template {Message | MessageHeaders} T
|
||||
* @param {T} msg the message to send
|
||||
* @param {MessageCallback<T>} callback receiver for the error (if any) as well as the passed-in message / headers
|
||||
* @returns {void}
|
||||
*/
|
||||
send(msg, callback) {
|
||||
|
@ -1882,17 +1884,20 @@ class SMTPClient {
|
|||
}
|
||||
/**
|
||||
* @public
|
||||
* @param {Message} msg the message to send
|
||||
* @returns {Promise<Message>} a promise that resolves to the fully processed message
|
||||
* @template {Message | MessageHeaders} T
|
||||
* @param {T} msg the message to send
|
||||
* @returns {Promise<T>} a promise that resolves to the passed-in message / headers
|
||||
*/
|
||||
sendAsync(msg) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.send(msg, (err, msg) => {
|
||||
this.send(msg, (err, message) => {
|
||||
if (err != null) {
|
||||
reject(err);
|
||||
}
|
||||
else {
|
||||
resolve(msg);
|
||||
// unfortunately, the conditional type doesn't reach here
|
||||
// fortunately, we only return a `Message` when err is null, so this is safe
|
||||
resolve(message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -1901,7 +1906,7 @@ class SMTPClient {
|
|||
* @public
|
||||
* @description Converts a message to the raw object used by the internal stack.
|
||||
* @param {Message} message message to convert
|
||||
* @param {function(err: Error, msg: Message): void} callback errback
|
||||
* @param {MessageCallback} callback errback
|
||||
* @returns {MessageStack} raw message object
|
||||
*/
|
||||
createMessageStack(message, callback = function () {
|
||||
|
@ -2118,4 +2123,4 @@ class SMTPClient {
|
|||
}
|
||||
|
||||
export { AUTH_METHODS, BUFFERSIZE, DEFAULT_TIMEOUT, MIME64CHUNK, MIMECHUNK, Message, SMTPClient, SMTPConnection, SMTPError, SMTPErrorStates, SMTPResponseMonitor, SMTPState, addressparser, getRFC2822Date, getRFC2822DateUTC, isRFC2822Date, mimeEncode, mimeWordEncode };
|
||||
//# sourceMappingURL=email.mjs.map
|
||||
//# sourceMappingURL=email.js.map
|
File diff suppressed because one or more lines are too long
|
@ -1,15 +0,0 @@
|
|||
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||
require('ts-node').register({
|
||||
moduleTypes: {
|
||||
'test/*.ts': 'cjs',
|
||||
},
|
||||
compilerOptions: {
|
||||
module: 'commonjs',
|
||||
},
|
||||
});
|
||||
if (process.title === 'cjs') {
|
||||
require('./rollup/email.cjs');
|
||||
require.cache[require.resolve('./email.ts')] =
|
||||
require.cache[require.resolve('./rollup/email.cjs')];
|
||||
console.log('Testing email.cjs...\n');
|
||||
}
|
16
email.ts
16
email.ts
|
@ -1,8 +1,8 @@
|
|||
export * from './smtp/address';
|
||||
export * from './smtp/client';
|
||||
export * from './smtp/connection';
|
||||
export * from './smtp/date';
|
||||
export * from './smtp/error';
|
||||
export * from './smtp/message';
|
||||
export * from './smtp/mime';
|
||||
export * from './smtp/response';
|
||||
export * from './smtp/address.js';
|
||||
export * from './smtp/client.js';
|
||||
export * from './smtp/connection.js';
|
||||
export * from './smtp/date.js';
|
||||
export * from './smtp/error.js';
|
||||
export * from './smtp/message.js';
|
||||
export * from './smtp/mime.js';
|
||||
export * from './smtp/response.js';
|
||||
|
|
56
package.json
56
package.json
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "emailjs",
|
||||
"description": "send text/html emails and attachments (files, streams and strings) from node.js to any smtp server",
|
||||
"version": "3.6.0",
|
||||
"version": "4.0.3",
|
||||
"author": "eleith",
|
||||
"contributors": [
|
||||
"izuzak",
|
||||
|
@ -17,48 +17,52 @@
|
|||
"type": "module",
|
||||
"devDependencies": {
|
||||
"@ledge/configs": "23.3.23322",
|
||||
"@rollup/plugin-typescript": "8.3.0",
|
||||
"@rollup/plugin-typescript": "8.3.2",
|
||||
"@types/mailparser": "3.4.0",
|
||||
"@types/node": "16.11.9",
|
||||
"@types/node": "12.12.6",
|
||||
"@types/smtp-server": "3.5.7",
|
||||
"@typescript-eslint/eslint-plugin": "5.4.0",
|
||||
"@typescript-eslint/parser": "5.4.0",
|
||||
"ava": "3.15.0",
|
||||
"eslint": "7.32.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.21.0",
|
||||
"@typescript-eslint/parser": "5.21.0",
|
||||
"ava": "4.2.0",
|
||||
"eslint": "8.14.0",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"eslint-plugin-prettier": "4.0.0",
|
||||
"mailparser": "3.4.0",
|
||||
"prettier": "2.4.1",
|
||||
"rollup": "2.60.0",
|
||||
"smtp-server": "3.9.0",
|
||||
"ts-node": "10.4.0",
|
||||
"tslib": "2.3.1",
|
||||
"typescript": "4.5.2"
|
||||
"mailparser": "3.5.0",
|
||||
"prettier": "2.6.2",
|
||||
"rollup": "2.70.2",
|
||||
"smtp-server": "3.11.0",
|
||||
"ts-node": "10.9.1",
|
||||
"tslib": "2.4.0",
|
||||
"typescript": "4.3.5"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"typescript": ">=4.3.5"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"typescript": {
|
||||
"optional": true
|
||||
}
|
||||
},
|
||||
"resolutions": {
|
||||
"nodemailer": "6.7.1"
|
||||
"nodemailer": "6.7.4"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=12"
|
||||
},
|
||||
"files": [
|
||||
"email.js",
|
||||
"email.ts",
|
||||
"smtp",
|
||||
"rollup"
|
||||
"smtp"
|
||||
],
|
||||
"main": "./rollup/email.cjs",
|
||||
"types": "./email.ts",
|
||||
"exports": {
|
||||
"import": "./rollup/email.mjs",
|
||||
"require": "./rollup/email.cjs"
|
||||
"default": "./email.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "rollup -c rollup.config.ts",
|
||||
"lint": "eslint *.ts \"+(smtp|test)/*.ts\"",
|
||||
"tsc": "tsc",
|
||||
"test": "ava",
|
||||
"pretest-cjs": "npm run build",
|
||||
"test-cjs": "npm run test -- --node-arguments='--title=cjs'"
|
||||
"pretest": "yarn build",
|
||||
"test": "ava"
|
||||
},
|
||||
"license": "MIT"
|
||||
}
|
||||
|
|
|
@ -3,21 +3,13 @@ import typescript from '@rollup/plugin-typescript';
|
|||
|
||||
export default {
|
||||
input: 'email.ts',
|
||||
output: [
|
||||
{
|
||||
file: 'rollup/email.cjs',
|
||||
format: 'cjs',
|
||||
interop: false,
|
||||
sourcemap: true,
|
||||
},
|
||||
{
|
||||
file: 'rollup/email.mjs',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
],
|
||||
output: {
|
||||
file: 'email.js',
|
||||
format: 'es',
|
||||
sourcemap: true,
|
||||
},
|
||||
external: builtinModules,
|
||||
plugins: [
|
||||
typescript({ removeComments: false, include: ['email.ts', 'smtp/**/*'] }),
|
||||
typescript({ removeComments: false, include: ['email.ts', 'smtp/*'] }),
|
||||
],
|
||||
};
|
||||
|
|
2142
rollup/email.cjs
2142
rollup/email.cjs
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -1,9 +1,19 @@
|
|||
import { addressparser } from './address';
|
||||
import { Message, MessageAttachment, MessageHeaders } from './message';
|
||||
import { SMTPConnection, SMTPConnectionOptions, SMTPState } from './connection';
|
||||
import { addressparser } from './address.js';
|
||||
import type { MessageAttachment, MessageHeaders } from './message.js';
|
||||
import { Message } from './message.js';
|
||||
import type { SMTPConnectionOptions } from './connection.js';
|
||||
import { SMTPConnection, SMTPState } from './connection.js';
|
||||
|
||||
export type MessageCallback<T = Message | MessageHeaders> = <
|
||||
U extends Error | null,
|
||||
V extends U extends Error ? T : Message
|
||||
>(
|
||||
err: U,
|
||||
msg: V
|
||||
) => void;
|
||||
|
||||
export interface MessageStack {
|
||||
callback: (error: Error | null, message: Message) => void;
|
||||
callback: MessageCallback;
|
||||
message: Message;
|
||||
attachment: MessageAttachment;
|
||||
text: string;
|
||||
|
@ -20,7 +30,7 @@ export class SMTPClient {
|
|||
|
||||
protected sending = false;
|
||||
protected ready = false;
|
||||
protected timer: NodeJS.Timer | null = null;
|
||||
protected timer: NodeJS.Timeout | null = null;
|
||||
|
||||
/**
|
||||
* Create a standard SMTP client backed by a self-managed SMTP connection.
|
||||
|
@ -35,15 +45,16 @@ export class SMTPClient {
|
|||
|
||||
/**
|
||||
* @public
|
||||
* @param {Message} msg the message to send
|
||||
* @param {function(err: Error, msg: Message): void} callback .
|
||||
* @template {Message | MessageHeaders} T
|
||||
* @param {T} msg the message to send
|
||||
* @param {MessageCallback<T>} callback receiver for the error (if any) as well as the passed-in message / headers
|
||||
* @returns {void}
|
||||
*/
|
||||
public send(
|
||||
msg: Message,
|
||||
callback: (err: Error | null, msg: Message) => void
|
||||
) {
|
||||
const message: Message | null =
|
||||
public send<T extends Message | MessageHeaders>(
|
||||
msg: T,
|
||||
callback: MessageCallback<T>
|
||||
): void {
|
||||
const message =
|
||||
msg instanceof Message
|
||||
? msg
|
||||
: this._canMakeMessage(msg)
|
||||
|
@ -71,16 +82,19 @@ export class SMTPClient {
|
|||
|
||||
/**
|
||||
* @public
|
||||
* @param {Message} msg the message to send
|
||||
* @returns {Promise<Message>} a promise that resolves to the fully processed message
|
||||
* @template {Message | MessageHeaders} T
|
||||
* @param {T} msg the message to send
|
||||
* @returns {Promise<T>} a promise that resolves to the passed-in message / headers
|
||||
*/
|
||||
public sendAsync(msg: Message) {
|
||||
public sendAsync<T extends Message | MessageHeaders>(msg: T) {
|
||||
return new Promise<Message>((resolve, reject) => {
|
||||
this.send(msg, (err, msg) => {
|
||||
this.send(msg, (err, message) => {
|
||||
if (err != null) {
|
||||
reject(err);
|
||||
} else {
|
||||
resolve(msg);
|
||||
// unfortunately, the conditional type doesn't reach here
|
||||
// fortunately, we only return a `Message` when err is null, so this is safe
|
||||
resolve(message as Message);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -90,12 +104,12 @@ export class SMTPClient {
|
|||
* @public
|
||||
* @description Converts a message to the raw object used by the internal stack.
|
||||
* @param {Message} message message to convert
|
||||
* @param {function(err: Error, msg: Message): void} callback errback
|
||||
* @param {MessageCallback} callback errback
|
||||
* @returns {MessageStack} raw message object
|
||||
*/
|
||||
public createMessageStack(
|
||||
message: Message,
|
||||
callback: (err: Error | null, msg: Message) => void = function () {
|
||||
callback: MessageCallback = function () {
|
||||
/* ø */
|
||||
}
|
||||
) {
|
||||
|
@ -231,7 +245,7 @@ export class SMTPClient {
|
|||
* @returns {boolean} whether the attachment contains inlined html
|
||||
*/
|
||||
protected _containsInlinedHtml(
|
||||
attachment: MessageAttachment | MessageAttachment[]
|
||||
attachment?: MessageAttachment | MessageAttachment[]
|
||||
) {
|
||||
if (Array.isArray(attachment)) {
|
||||
return attachment.some((att) => {
|
||||
|
@ -247,7 +261,7 @@ export class SMTPClient {
|
|||
* @param {MessageAttachment} attachment attachment
|
||||
* @returns {boolean} whether the attachment is inlined html
|
||||
*/
|
||||
protected _isAttachmentInlinedHtml(attachment: MessageAttachment) {
|
||||
protected _isAttachmentInlinedHtml(attachment?: MessageAttachment) {
|
||||
return (
|
||||
attachment &&
|
||||
(attachment.data || attachment.path) &&
|
||||
|
|
|
@ -2,15 +2,11 @@ import { createHmac } from 'crypto';
|
|||
import { EventEmitter } from 'events';
|
||||
import { Socket } from 'net';
|
||||
import { hostname } from 'os';
|
||||
import {
|
||||
connect,
|
||||
createSecureContext,
|
||||
ConnectionOptions,
|
||||
TLSSocket,
|
||||
} from 'tls';
|
||||
import { connect, createSecureContext, TLSSocket } from 'tls';
|
||||
import type { ConnectionOptions } from 'tls';
|
||||
|
||||
import { SMTPError, SMTPErrorStates } from './error';
|
||||
import { SMTPResponseMonitor } from './response';
|
||||
import { SMTPError, SMTPErrorStates } from './error.js';
|
||||
import { SMTPResponseMonitor } from './response.js';
|
||||
|
||||
/**
|
||||
* @readonly
|
||||
|
@ -187,7 +183,7 @@ export class SMTPConnection extends EventEmitter {
|
|||
this.password = () => password as string;
|
||||
|
||||
if (typeof logger === 'function') {
|
||||
this.log = log;
|
||||
this.log = logger;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -783,6 +779,9 @@ export class SMTPConnection extends EventEmitter {
|
|||
const failed = (err: Error, data: unknown) => {
|
||||
this.loggedin = false;
|
||||
this.close(); // if auth is bad, close the connection, it won't get better by itself
|
||||
|
||||
err.message = err.message.replace(this.password(), 'REDACTED');
|
||||
|
||||
caller(
|
||||
callback,
|
||||
SMTPError.create(
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import type { PathLike } from 'fs';
|
||||
import {
|
||||
PathLike,
|
||||
existsSync,
|
||||
open as openFile,
|
||||
close as closeFile,
|
||||
|
@ -10,9 +10,9 @@ import { hostname } from 'os';
|
|||
import { Stream } from 'stream';
|
||||
import type { Readable } from 'stream';
|
||||
|
||||
import { addressparser } from './address';
|
||||
import { getRFC2822Date } from './date';
|
||||
import { mimeWordEncode } from './mime';
|
||||
import { addressparser } from './address.js';
|
||||
import { getRFC2822Date } from './date.js';
|
||||
import { mimeWordEncode } from './mime.js';
|
||||
|
||||
const CRLF = '\r\n' as const;
|
||||
|
||||
|
@ -68,19 +68,20 @@ export interface MessageHeaders {
|
|||
| string
|
||||
| string[]
|
||||
| null
|
||||
| undefined
|
||||
| MessageAttachment
|
||||
| MessageAttachment[];
|
||||
'content-type': string;
|
||||
'message-id': string;
|
||||
'return-path': string | null;
|
||||
date: string;
|
||||
'content-type'?: string;
|
||||
'message-id'?: string;
|
||||
'return-path'?: string | null;
|
||||
date?: string;
|
||||
from: string | string[];
|
||||
to: string | string[];
|
||||
cc: string | string[];
|
||||
bcc: string | string[];
|
||||
cc?: string | string[];
|
||||
bcc?: string | string[];
|
||||
subject: string;
|
||||
text: string | null;
|
||||
attachment: MessageAttachment | MessageAttachment[];
|
||||
attachment?: MessageAttachment | MessageAttachment[];
|
||||
}
|
||||
|
||||
let counter = 0;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { SMTPError, SMTPErrorStates } from './error';
|
||||
import { SMTPError, SMTPErrorStates } from './error.js';
|
||||
import type { Socket } from 'net';
|
||||
import type { TLSSocket } from 'tls';
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import test from 'ava';
|
||||
import { addressparser } from '../email';
|
||||
import { addressparser } from '../email.js';
|
||||
|
||||
test('addressparser should handle single address correctly', async (t) => {
|
||||
t.deepEqual(addressparser('andris@tr.ee'), [
|
||||
|
|
36
test/auth.ts
36
test/auth.ts
|
@ -1,8 +1,10 @@
|
|||
import test, { ExecutionContext } from 'ava';
|
||||
import { simpleParser, AddressObject } from 'mailparser';
|
||||
import test from 'ava';
|
||||
import type { ExecutionContext } from 'ava';
|
||||
import { simpleParser } from 'mailparser';
|
||||
import type { AddressObject } from 'mailparser';
|
||||
import { SMTPServer } from 'smtp-server';
|
||||
|
||||
import { AUTH_METHODS, SMTPClient, Message } from '../email';
|
||||
import { AUTH_METHODS, SMTPClient, Message } from '../email.js';
|
||||
|
||||
let port = 2000;
|
||||
|
||||
|
@ -12,15 +14,15 @@ function send(
|
|||
authMethods = [],
|
||||
authOptional = false,
|
||||
secure = false,
|
||||
password = 'honey',
|
||||
}: {
|
||||
authMethods?: (keyof typeof AUTH_METHODS)[];
|
||||
authOptional?: boolean;
|
||||
secure?: boolean;
|
||||
password?: string;
|
||||
} = {}
|
||||
) {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
t.plan(5);
|
||||
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT message from emailjs',
|
||||
from: 'piglet@gmail.com',
|
||||
|
@ -42,9 +44,14 @@ function send(
|
|||
? accessToken === 'honey'
|
||||
: password === 'honey')
|
||||
) {
|
||||
t.plan(5);
|
||||
callback(null, { user: 'pooh' });
|
||||
} else {
|
||||
return callback(new Error('invalid user / pass'));
|
||||
return callback(
|
||||
new Error(
|
||||
`invalid user or pass: ${username || accessToken} ${password}`
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
async onData(stream, _session, callback: () => void) {
|
||||
|
@ -66,12 +73,12 @@ function send(
|
|||
server.listen(p, () => {
|
||||
const options = Object.assign(
|
||||
{ port: p, ssl: secure, authentication: authMethods },
|
||||
authOptional ? {} : { user: 'pooh', password: 'honey' }
|
||||
authOptional ? {} : { user: 'pooh', password }
|
||||
);
|
||||
new SMTPClient(options).send(new Message(msg), (err) => {
|
||||
server.close(() => {
|
||||
if (err) {
|
||||
reject(err.message);
|
||||
reject(err);
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
|
@ -118,3 +125,16 @@ test('XOAUTH2 authentication (encrypted) should succeed', async (t) => {
|
|||
send(t, { authMethods: [AUTH_METHODS.XOAUTH2], secure: true })
|
||||
);
|
||||
});
|
||||
|
||||
test('on authentication.failed error message should not contain password', async (t) => {
|
||||
t.plan(1);
|
||||
|
||||
const password = 'passpot';
|
||||
await send(t, {
|
||||
authMethods: [AUTH_METHODS.LOGIN],
|
||||
secure: true,
|
||||
password,
|
||||
}).catch((err) => {
|
||||
t.false(err.message.includes(password));
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
import { promisify } from 'util';
|
||||
|
||||
import test from 'ava';
|
||||
import { simpleParser, ParsedMail, AddressObject } from 'mailparser';
|
||||
import { simpleParser } from 'mailparser';
|
||||
import type { ParsedMail, AddressObject } from 'mailparser';
|
||||
import { SMTPServer } from 'smtp-server';
|
||||
|
||||
import type { MessageHeaders } from '../email.js';
|
||||
import {
|
||||
DEFAULT_TIMEOUT,
|
||||
SMTPClient,
|
||||
Message,
|
||||
MessageHeaders,
|
||||
isRFC2822Date,
|
||||
} from '../email';
|
||||
} from '../email.js';
|
||||
|
||||
const parseMap = new Map<string, ParsedMail>();
|
||||
const port = 3000;
|
||||
let greylistPort = 4000;
|
||||
const port = 3333;
|
||||
let greylistPort = 4444;
|
||||
|
||||
const client = new SMTPClient({
|
||||
port,
|
||||
|
@ -72,7 +73,7 @@ test('client invokes callback exactly once for invalid connection', async (t) =>
|
|||
await t.notThrowsAsync(
|
||||
new Promise<void>((resolve, reject) => {
|
||||
let counter = 0;
|
||||
const invalidClient = new SMTPClient({ host: 'bar.baz' });
|
||||
const invalidClient = new SMTPClient({ host: 'localhost' });
|
||||
const incrementCounter = () => {
|
||||
if (counter > 0) {
|
||||
reject();
|
||||
|
@ -163,24 +164,27 @@ test('client accepts array sender', async (t) => {
|
|||
});
|
||||
|
||||
test('client rejects message without `from` header', async (t) => {
|
||||
const { message: error } = await t.throwsAsync(
|
||||
const error = await t.throwsAsync(
|
||||
send({
|
||||
subject: 'this is a test TEXT message from emailjs',
|
||||
text: "It is hard to be brave when you're only a Very Small Animal.",
|
||||
})
|
||||
);
|
||||
t.is(error, 'Message must have a `from` header');
|
||||
t.is(error?.message, 'Message must have a `from` header');
|
||||
});
|
||||
|
||||
test('client rejects message without `to`, `cc`, or `bcc` header', async (t) => {
|
||||
const { message: error } = await t.throwsAsync(
|
||||
const error = await t.throwsAsync(
|
||||
send({
|
||||
subject: 'this is a test TEXT message from emailjs',
|
||||
from: 'piglet@gmail.com',
|
||||
text: "It is hard to be brave when you're only a Very Small Animal.",
|
||||
})
|
||||
);
|
||||
t.is(error, 'Message must have at least one `to`, `cc`, or `bcc` header');
|
||||
t.is(
|
||||
error?.message,
|
||||
'Message must have at least one `to`, `cc`, or `bcc` header'
|
||||
);
|
||||
});
|
||||
|
||||
test('client allows message with only `cc` recipient header', async (t) => {
|
||||
|
@ -315,7 +319,7 @@ test('client only responds once to greylisting', async (t) => {
|
|||
});
|
||||
|
||||
const p = greylistPort++;
|
||||
const { message: error } = await t.throwsAsync(
|
||||
const error = await t.throwsAsync(
|
||||
new Promise<void>((resolve, reject) => {
|
||||
greylistServer.listen(p, () => {
|
||||
new SMTPClient({
|
||||
|
@ -334,7 +338,7 @@ test('client only responds once to greylisting', async (t) => {
|
|||
});
|
||||
})
|
||||
);
|
||||
t.is(error, "bad response on command 'RCPT': greylist");
|
||||
t.is(error?.message, "bad response on command 'RCPT': greylist");
|
||||
});
|
||||
|
||||
test('client send can have result awaited when promisified', async (t) => {
|
||||
|
@ -349,7 +353,7 @@ test('client send can have result awaited when promisified', async (t) => {
|
|||
};
|
||||
|
||||
try {
|
||||
const message = await sendAsync(new Message(msg));
|
||||
const message = (await sendAsync(new Message(msg))) as Message;
|
||||
t.true(message instanceof Message);
|
||||
t.like(message, {
|
||||
alternative: null,
|
||||
|
@ -419,7 +423,7 @@ test('client sendAsync can have error caught when awaited', async (t) => {
|
|||
};
|
||||
|
||||
try {
|
||||
const invalidClient = new SMTPClient({ host: 'bar.baz' });
|
||||
const invalidClient = new SMTPClient({ host: '127.0.0.1' });
|
||||
const message = await invalidClient.sendAsync(new Message(msg));
|
||||
t.true(message instanceof Message);
|
||||
t.fail();
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import test from 'ava';
|
||||
|
||||
import { SMTPConnection } from '../email.js';
|
||||
|
||||
test('accepts a custom logger', async (t) => {
|
||||
const logger = () => {
|
||||
/** ø */
|
||||
};
|
||||
const connection = new SMTPConnection({ logger });
|
||||
t.is(Reflect.get(connection, 'log'), logger);
|
||||
});
|
|
@ -1,5 +1,5 @@
|
|||
import test from 'ava';
|
||||
import { getRFC2822Date, getRFC2822DateUTC, isRFC2822Date } from '../email';
|
||||
import { getRFC2822Date, getRFC2822DateUTC, isRFC2822Date } from '../email.js';
|
||||
|
||||
const toD_utc = (dt: number) => getRFC2822DateUTC(new Date(dt));
|
||||
const toD = (dt: number, utc = false) => getRFC2822Date(new Date(dt), utc);
|
||||
|
|
116
test/message.ts
116
test/message.ts
|
@ -1,19 +1,28 @@
|
|||
import { readFileSync, createReadStream } from 'fs';
|
||||
import { join } from 'path';
|
||||
import { createReadStream, readFileSync } from 'fs';
|
||||
import { URL } from 'url';
|
||||
|
||||
import test from 'ava';
|
||||
import { simpleParser, AddressObject, ParsedMail } from 'mailparser';
|
||||
import { simpleParser } from 'mailparser';
|
||||
import type { AddressObject, ParsedMail } from 'mailparser';
|
||||
import { SMTPServer } from 'smtp-server';
|
||||
|
||||
import { SMTPClient, Message, MessageAttachment } from '../email';
|
||||
import { MessageHeaders } from '../smtp/message';
|
||||
import { SMTPClient, Message } from '../email.js';
|
||||
import type { MessageAttachment, MessageHeaders } from '../email.js';
|
||||
|
||||
// eslint-disable-next-line no-var
|
||||
var __dirname: string;
|
||||
// @ts-expect-error compat hack for node 10
|
||||
if (__dirname == null) {
|
||||
__dirname = join(process.cwd(), 'test');
|
||||
}
|
||||
const textFixtureUrl = new URL('attachments/smtp.txt', import.meta.url);
|
||||
const textFixture = readFileSync(textFixtureUrl, 'utf-8');
|
||||
|
||||
const htmlFixtureUrl = new URL('attachments/smtp.html', import.meta.url);
|
||||
const htmlFixture = readFileSync(htmlFixtureUrl, 'utf-8');
|
||||
|
||||
const pdfFixtureUrl = new URL('attachments/smtp.pdf', import.meta.url);
|
||||
const pdfFixture = readFileSync(pdfFixtureUrl, 'base64');
|
||||
|
||||
const tarFixtureUrl = new URL(
|
||||
'attachments/postfix-2.8.7.tar.gz',
|
||||
import.meta.url
|
||||
);
|
||||
const tarFixture = readFileSync(tarFixtureUrl, 'base64');
|
||||
|
||||
/**
|
||||
* \@types/mailparser@3.0.2 breaks our code
|
||||
|
@ -21,7 +30,7 @@ if (__dirname == null) {
|
|||
*/
|
||||
type ParsedMailCompat = Omit<ParsedMail, 'to'> & { to?: AddressObject };
|
||||
|
||||
const port = 5000;
|
||||
const port = 5555;
|
||||
const parseMap = new Map<string, ParsedMailCompat>();
|
||||
|
||||
const client = new SMTPClient({
|
||||
|
@ -136,7 +145,7 @@ test('very large text message', async (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: textFixture,
|
||||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
|
@ -147,10 +156,7 @@ test('very large text message', async (t) => {
|
|||
});
|
||||
|
||||
test('very large text data message', async (t) => {
|
||||
const text =
|
||||
'<html><body><pre>' +
|
||||
readFileSync(join(__dirname, 'attachments/smtp.txt'), 'utf-8') +
|
||||
'</pre></body></html>';
|
||||
const text = '<html><body><pre>' + textFixture + '</pre></body></html>';
|
||||
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT+DATA message from emailjs',
|
||||
|
@ -172,19 +178,18 @@ test('very large text data message', async (t) => {
|
|||
});
|
||||
|
||||
test('html data message', async (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,
|
||||
data: htmlFixture,
|
||||
alternative: true,
|
||||
},
|
||||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
t.is(mail.html, html.replace(/\r/g, ''));
|
||||
t.is(mail.html, htmlFixture.replace(/\r/g, ''));
|
||||
t.is(mail.text, '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
@ -192,19 +197,18 @@ test('html data message', async (t) => {
|
|||
});
|
||||
|
||||
test('html file message', async (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'),
|
||||
path: new URL('attachments/smtp.html', import.meta.url),
|
||||
alternative: true,
|
||||
},
|
||||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
t.is(mail.html, html.replace(/\r/g, ''));
|
||||
t.is(mail.html, htmlFixture.replace(/\r/g, ''));
|
||||
t.is(mail.text, '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
@ -212,18 +216,18 @@ test('html file message', async (t) => {
|
|||
});
|
||||
|
||||
test('html with image embed message', async (t) => {
|
||||
const html = readFileSync(join(__dirname, 'attachments/smtp2.html'), 'utf-8');
|
||||
const image = readFileSync(join(__dirname, 'attachments/smtp.gif'));
|
||||
const htmlFixture2Url = new URL('attachments/smtp2.html', import.meta.url);
|
||||
const imageFixtureUrl = new URL('attachments/smtp.gif', import.meta.url);
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT+HTML+IMAGE message from emailjs',
|
||||
from: 'ninja@gmail.com',
|
||||
to: 'pirate@gmail.com',
|
||||
attachment: {
|
||||
path: join(__dirname, 'attachments/smtp2.html'),
|
||||
path: htmlFixture2Url,
|
||||
alternative: true,
|
||||
related: [
|
||||
{
|
||||
path: join(__dirname, 'attachments/smtp.gif'),
|
||||
path: imageFixtureUrl,
|
||||
type: 'image/gif',
|
||||
name: 'smtp-diagram.gif',
|
||||
headers: { 'Content-ID': '<smtp-diagram@local>' },
|
||||
|
@ -235,9 +239,9 @@ test('html with image embed message', async (t) => {
|
|||
const mail = await send(msg);
|
||||
t.is(
|
||||
mail.attachments[0].content.toString('base64'),
|
||||
image.toString('base64')
|
||||
readFileSync(imageFixtureUrl, 'base64')
|
||||
);
|
||||
t.is(mail.html, html.replace(/\r/g, ''));
|
||||
t.is(mail.html, readFileSync(htmlFixture2Url, 'utf-8').replace(/\r/g, ''));
|
||||
t.is(mail.text, '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
@ -245,19 +249,21 @@ test('html with image embed message', async (t) => {
|
|||
});
|
||||
|
||||
test('html data and attachment message', async (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 },
|
||||
{ path: join(__dirname, 'attachments/smtp.gif') },
|
||||
{
|
||||
path: new URL('attachments/smtp.html', import.meta.url),
|
||||
alternative: true,
|
||||
},
|
||||
{ path: new URL('attachments/smtp.gif', import.meta.url) },
|
||||
] as MessageAttachment[],
|
||||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
t.is(mail.html, html.replace(/\r/g, ''));
|
||||
t.is(mail.html, htmlFixture.replace(/\r/g, ''));
|
||||
t.is(mail.text, '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
@ -265,21 +271,20 @@ test('html data and attachment message', async (t) => {
|
|||
});
|
||||
|
||||
test('attachment message', async (t) => {
|
||||
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT+ATTACHMENT message from emailjs',
|
||||
from: 'washing@gmail.com',
|
||||
to: 'lincoln@gmail.com',
|
||||
text: 'hello friend, i hope this message and pdf finds you well.',
|
||||
attachment: {
|
||||
path: join(__dirname, 'attachments/smtp.pdf'),
|
||||
path: pdfFixtureUrl,
|
||||
type: 'application/pdf',
|
||||
name: 'smtp-info.pdf',
|
||||
} as MessageAttachment,
|
||||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
t.is(mail.attachments[0].content.toString('base64'), pdf.toString('base64'));
|
||||
t.is(mail.attachments[0].content.toString('base64'), pdfFixture);
|
||||
t.is(mail.text, msg.text + '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
@ -287,21 +292,20 @@ test('attachment message', async (t) => {
|
|||
});
|
||||
|
||||
test('attachment sent with unicode filename message', async (t) => {
|
||||
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT+ATTACHMENT message from emailjs',
|
||||
from: 'washing@gmail.com',
|
||||
to: 'lincoln@gmail.com',
|
||||
text: 'hello friend, i hope this message and pdf finds you well.',
|
||||
attachment: {
|
||||
path: join(__dirname, 'attachments/smtp.pdf'),
|
||||
path: pdfFixtureUrl,
|
||||
type: 'application/pdf',
|
||||
name: 'smtp-✓-info.pdf',
|
||||
} as MessageAttachment,
|
||||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
t.is(mail.attachments[0].content.toString('base64'), pdf.toString('base64'));
|
||||
t.is(mail.attachments[0].content.toString('base64'), pdfFixture);
|
||||
t.is(mail.attachments[0].filename, 'smtp-✓-info.pdf');
|
||||
t.is(mail.text, msg.text + '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
|
@ -310,8 +314,6 @@ test('attachment sent with unicode filename message', async (t) => {
|
|||
});
|
||||
|
||||
test('attachments message', async (t) => {
|
||||
const pdf = readFileSync(join(__dirname, 'attachments/smtp.pdf'));
|
||||
const tar = readFileSync(join(__dirname, 'attachments/postfix-2.8.7.tar.gz'));
|
||||
const msg = {
|
||||
subject: 'this is a test TEXT+2+ATTACHMENTS message from emailjs',
|
||||
from: 'sergey@gmail.com',
|
||||
|
@ -319,12 +321,12 @@ test('attachments message', async (t) => {
|
|||
text: 'hello friend, i hope this message and attachments finds you well.',
|
||||
attachment: [
|
||||
{
|
||||
path: join(__dirname, 'attachments/smtp.pdf'),
|
||||
path: pdfFixtureUrl,
|
||||
type: 'application/pdf',
|
||||
name: 'smtp-info.pdf',
|
||||
},
|
||||
{
|
||||
path: join(__dirname, 'attachments/postfix-2.8.7.tar.gz'),
|
||||
path: tarFixtureUrl,
|
||||
type: 'application/tar-gz',
|
||||
name: 'postfix.source.2.8.7.tar.gz',
|
||||
},
|
||||
|
@ -332,8 +334,8 @@ test('attachments message', async (t) => {
|
|||
};
|
||||
|
||||
const mail = await send(msg);
|
||||
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.attachments[0].content.toString('base64'), pdfFixture);
|
||||
t.is(mail.attachments[1].content.toString('base64'), tarFixture);
|
||||
t.is(mail.text, msg.text + '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
@ -341,34 +343,32 @@ test('attachments message', async (t) => {
|
|||
});
|
||||
|
||||
test('streams message', async (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 msg = {
|
||||
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: [
|
||||
{ stream, type: 'application/pdf', name: 'smtp-info.pdf' },
|
||||
{
|
||||
stream: stream2,
|
||||
stream: createReadStream(pdfFixtureUrl),
|
||||
type: 'application/pdf',
|
||||
name: 'smtp-info.pdf',
|
||||
},
|
||||
{
|
||||
stream: createReadStream(tarFixtureUrl),
|
||||
type: 'application/x-gzip',
|
||||
name: 'postfix.source.2.8.7.tar.gz',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
stream.pause();
|
||||
stream2.pause();
|
||||
for (const { stream } of msg.attachment) {
|
||||
stream.pause();
|
||||
}
|
||||
|
||||
const mail = await send(msg);
|
||||
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.attachments[0].content.toString('base64'), pdfFixture);
|
||||
t.is(mail.attachments[1].content.toString('base64'), tarFixture);
|
||||
t.is(mail.text, msg.text + '\n');
|
||||
t.is(mail.subject, msg.subject);
|
||||
t.is(mail.from?.text, msg.from);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
// adapted from https://github.com/emailjs/emailjs-mime-codec/blob/6909c706b9f09bc0e5c3faf48f723cca53e5b352/src/mimecodec-unit.js
|
||||
import test from 'ava';
|
||||
import { mimeEncode, mimeWordEncode } from '../email';
|
||||
import { mimeEncode, mimeWordEncode } from '../email.js';
|
||||
|
||||
test('mimeEncode should encode UTF-8', async (t) => {
|
||||
t.is(mimeEncode('tere ÕÄÖÕ'), 'tere =C3=95=C3=84=C3=96=C3=95');
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
{
|
||||
"extends": "@ledge/configs/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"esModuleInterop": false,
|
||||
"noPropertyAccessFromIndexSignature": true
|
||||
},
|
||||
"include": [
|
||||
"*.ts",
|
||||
"smtp/**/*.ts",
|
||||
"test/*.ts",
|
||||
"email.ts",
|
||||
"smtp",
|
||||
"test",
|
||||
],
|
||||
"ts-node": {
|
||||
"transpileOnly": true
|
||||
|
|
Loading…
Reference in New Issue