imapflow 1.0.78 → 1.0.82
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/README.md +3 -3
- package/lib/commands/expunge.js +11 -0
- package/lib/commands/fetch.js +13 -4
- package/lib/commands/select.js +39 -1
- package/lib/commands/store.js +1 -1
- package/lib/handler/imap-compiler.js +6 -4
- package/lib/imap-flow.js +76 -12
- package/lib/types.d.ts +4 -0
- package/package.json +9 -9
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# CHANGELOG
|
|
2
2
|
|
|
3
|
+
- 1.0.82 - 2022-02-15
|
|
4
|
+
|
|
5
|
+
- Added extra option `expungeHandler`
|
|
6
|
+
|
|
7
|
+
- 1.0.81 - 2022-02-15
|
|
8
|
+
|
|
9
|
+
- Added support for the QRESYNC extension and untagged VANISHED responses
|
|
10
|
+
|
|
11
|
+
- 1.0.80 - 2022-02-07
|
|
12
|
+
|
|
13
|
+
- Added support for the missing NOMODSEQ mailbox modifier
|
|
14
|
+
|
|
15
|
+
- 1.0.79 - 2021-12-29
|
|
16
|
+
|
|
17
|
+
- Added property `greeting` that contains that first response from the server
|
|
18
|
+
|
|
3
19
|
- 1.0.78 - 2021-11-28
|
|
4
20
|
|
|
5
21
|
- Proxy support. Use configuration options `proxy: "url"` to use proxies
|
package/README.md
CHANGED
|
@@ -2,13 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
---
|
|
4
4
|
|
|
5
|
-
> Managing IMAP connections is cool but if you are only looking for an easy way to integrate email accounts, then ImapFlow was built for [EmailEngine Email API](https://emailengine.app/). It's a self
|
|
5
|
+
> Managing IMAP connections is cool, but if you are only looking for an easy way to integrate email accounts, then ImapFlow was built for [EmailEngine Email API](https://emailengine.app/). It's a self-hosted software that converts all IMAP accounts to easy-to-use REST interfaces.
|
|
6
6
|
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
The focus for ImapFlow is to provide easy to use API over IMAP. Using ImapFlow does not expect knowledge about specific IMAP details
|
|
9
|
+
The focus for ImapFlow is to provide easy to use API over IMAP. Using ImapFlow does not expect knowledge about specific IMAP details. A general understanding is good enough.
|
|
10
10
|
|
|
11
|
-
IMAP extensions are handled in the background, so for example you can always request `labels` value from a {@link FetchQueryObject|fetch()} call but if the IMAP server does not support `X-GM-EXT1` extension, then `labels` value is not included in the response.
|
|
11
|
+
IMAP extensions are handled in the background, so for example, you can always request `labels` value from a {@link FetchQueryObject|fetch()} call, but if the IMAP server does not support `X-GM-EXT1` extension, then `labels` value is not included in the response.
|
|
12
12
|
|
|
13
13
|
## Source
|
|
14
14
|
|
package/lib/commands/expunge.js
CHANGED
|
@@ -20,6 +20,17 @@ module.exports = async (connection, range, options) => {
|
|
|
20
20
|
let response;
|
|
21
21
|
try {
|
|
22
22
|
response = await connection.exec(command, attributes);
|
|
23
|
+
|
|
24
|
+
// A OK [HIGHESTMODSEQ 9122] Expunge completed (0.010 + 0.000 + 0.012 secs).
|
|
25
|
+
let section = response.response.attributes && response.response.attributes[0] && response.response.attributes[0].section;
|
|
26
|
+
let responseCode = section && section.length && section[0] && typeof section[0].value === 'string' ? section[0].value : '';
|
|
27
|
+
if (responseCode.toUpperCase() === 'HIGHESTMODSEQ') {
|
|
28
|
+
let highestModseq = section[1] && typeof section[1].value === 'string' && !isNaN(section[1].value) ? BigInt(section[1].value) : false;
|
|
29
|
+
if (highestModseq && (!connection.mailbox.highestModseq || highestModseq > connection.mailbox.highestModseq)) {
|
|
30
|
+
connection.mailbox.highestModseq = highestModseq;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
23
34
|
response.next();
|
|
24
35
|
return true;
|
|
25
36
|
} catch (err) {
|
package/lib/commands/fetch.js
CHANGED
|
@@ -86,7 +86,7 @@ module.exports = async (connection, range, query, options) => {
|
|
|
86
86
|
}
|
|
87
87
|
|
|
88
88
|
// always ask for modseq if possible
|
|
89
|
-
if (connection.enabled.has('CONDSTORE')) {
|
|
89
|
+
if (connection.enabled.has('CONDSTORE') && !mailbox.noModseq) {
|
|
90
90
|
queryStructure.push({ type: 'ATOM', value: 'MODSEQ' });
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -137,8 +137,8 @@ module.exports = async (connection, range, query, options) => {
|
|
|
137
137
|
|
|
138
138
|
attributes.push(queryStructure);
|
|
139
139
|
|
|
140
|
-
if (options.changedSince && connection.enabled.has('CONDSTORE')) {
|
|
141
|
-
|
|
140
|
+
if (options.changedSince && connection.enabled.has('CONDSTORE') && !mailbox.noModseq) {
|
|
141
|
+
let changedSinceArgs = [
|
|
142
142
|
{
|
|
143
143
|
type: 'ATOM',
|
|
144
144
|
value: 'CHANGEDSINCE'
|
|
@@ -147,7 +147,16 @@ module.exports = async (connection, range, query, options) => {
|
|
|
147
147
|
type: 'ATOM',
|
|
148
148
|
value: options.changedSince.toString()
|
|
149
149
|
}
|
|
150
|
-
]
|
|
150
|
+
];
|
|
151
|
+
|
|
152
|
+
if (options.uid && connection.enabled.has('QRESYNC')) {
|
|
153
|
+
changedSinceArgs.push({
|
|
154
|
+
type: 'ATOM',
|
|
155
|
+
value: 'VANISHED'
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
attributes.push(changedSinceArgs);
|
|
151
160
|
}
|
|
152
161
|
|
|
153
162
|
response = await connection.exec(options.uid ? 'UID FETCH' : 'FETCH', attributes, {
|
package/lib/commands/select.js
CHANGED
|
@@ -21,6 +21,7 @@ module.exports = async (connection, path, options) => {
|
|
|
21
21
|
connection.folders.set(folder.path, folder);
|
|
22
22
|
});
|
|
23
23
|
}
|
|
24
|
+
|
|
24
25
|
let folderListData = connection.folders.has(path) ? connection.folders.get(path) : false;
|
|
25
26
|
|
|
26
27
|
let response;
|
|
@@ -33,7 +34,19 @@ module.exports = async (connection, path, options) => {
|
|
|
33
34
|
}
|
|
34
35
|
});
|
|
35
36
|
}
|
|
36
|
-
|
|
37
|
+
|
|
38
|
+
let extraArgs = [];
|
|
39
|
+
if (connection.enabled.has('QRESYNC') && options.changedSince && options.uidValidity) {
|
|
40
|
+
extraArgs.push([
|
|
41
|
+
{ type: 'ATOM', value: 'QRESYNC' },
|
|
42
|
+
[
|
|
43
|
+
{ type: 'ATOM', value: options.uidValidity },
|
|
44
|
+
{ type: 'ATOM', value: options.changedSince }
|
|
45
|
+
]
|
|
46
|
+
]);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
response = await connection.exec(!options.readOnly ? 'SELECT' : 'EXAMINE', [{ type: 'ATOM', value: path }].concat(extraArgs || []), {
|
|
37
50
|
untagged: {
|
|
38
51
|
OK: async untagged => {
|
|
39
52
|
if (!untagged.attributes || !untagged.attributes.length) {
|
|
@@ -85,6 +98,16 @@ module.exports = async (connection, path, options) => {
|
|
|
85
98
|
|
|
86
99
|
map[key] = value;
|
|
87
100
|
}
|
|
101
|
+
|
|
102
|
+
if (section && section.length === 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
|
|
103
|
+
let key = section[0].value.toLowerCase();
|
|
104
|
+
switch (key) {
|
|
105
|
+
case 'nomodseq':
|
|
106
|
+
key = 'noModseq';
|
|
107
|
+
map[key] = true;
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
88
111
|
},
|
|
89
112
|
FLAGS: async untagged => {
|
|
90
113
|
if (!untagged.attributes || (!untagged.attributes.length && Array.isArray(untagged.attributes[0]))) {
|
|
@@ -100,6 +123,21 @@ module.exports = async (connection, path, options) => {
|
|
|
100
123
|
}
|
|
101
124
|
|
|
102
125
|
map.exists = num;
|
|
126
|
+
},
|
|
127
|
+
VANISHED: async untagged => {
|
|
128
|
+
await connection.untaggedVanished(
|
|
129
|
+
untagged,
|
|
130
|
+
// mailbox is not yet open, so use a dummy mailbox object
|
|
131
|
+
{ path, uidNext: false, uidValidity: false }
|
|
132
|
+
);
|
|
133
|
+
},
|
|
134
|
+
// we should only get an untagged FETCH for a SELECT/EXAMINE if QRESYNC was asked for
|
|
135
|
+
FETCH: async untagged => {
|
|
136
|
+
await connection.untaggedFetch(
|
|
137
|
+
untagged,
|
|
138
|
+
// mailbox is not yet open, so use a dummy mailbox object
|
|
139
|
+
{ path, uidNext: false, uidValidity: false }
|
|
140
|
+
);
|
|
103
141
|
}
|
|
104
142
|
}
|
|
105
143
|
});
|
package/lib/commands/store.js
CHANGED
|
@@ -53,7 +53,7 @@ module.exports = async (connection, range, flags, options) => {
|
|
|
53
53
|
|
|
54
54
|
let attributes = [{ type: 'SEQUENCE', value: range }, { type: 'ATOM', value: operation }, flags.map(flag => ({ type: 'ATOM', value: flag }))];
|
|
55
55
|
|
|
56
|
-
if (options.unchangedSince && connection.enabled.has('CONDSTORE')) {
|
|
56
|
+
if (options.unchangedSince && connection.enabled.has('CONDSTORE') && !connection.mailbox.noModseq) {
|
|
57
57
|
attributes.push([
|
|
58
58
|
{
|
|
59
59
|
type: 'ATOM',
|
|
@@ -117,11 +117,13 @@ module.exports = async (response, options) => {
|
|
|
117
117
|
case 'SECTION':
|
|
118
118
|
val = (node.value || '').toString('binary');
|
|
119
119
|
|
|
120
|
-
if (node.
|
|
121
|
-
val
|
|
122
|
-
|
|
120
|
+
if (!node.section || val) {
|
|
121
|
+
if (node.value === '' || imapFormalSyntax.verify(val.charAt(0) === '\\' ? val.substr(1) : val, imapFormalSyntax['ATOM-CHAR']()) >= 0) {
|
|
122
|
+
val = JSON.stringify(val);
|
|
123
|
+
}
|
|
123
124
|
|
|
124
|
-
|
|
125
|
+
resp += val;
|
|
126
|
+
}
|
|
125
127
|
|
|
126
128
|
if (node.section) {
|
|
127
129
|
resp += '[';
|
package/lib/imap-flow.js
CHANGED
|
@@ -29,7 +29,7 @@ const { PassThrough } = require('stream');
|
|
|
29
29
|
|
|
30
30
|
const { proxyConnection } = require('./proxy-connection');
|
|
31
31
|
|
|
32
|
-
const { comparePaths, updateCapabilities, getFolderTree, formatMessageResponse, getDecoder, packMessageRange, normalizePath } = require('./tools');
|
|
32
|
+
const { comparePaths, updateCapabilities, getFolderTree, formatMessageResponse, getDecoder, packMessageRange, normalizePath, expandRange } = require('./tools');
|
|
33
33
|
|
|
34
34
|
const imapCommands = require('./imap-commands.js');
|
|
35
35
|
|
|
@@ -58,6 +58,7 @@ const states = {
|
|
|
58
58
|
* @property {Set<string>} permanentFlags A Set of flags available to use in this mailbox. If it is not set or includes special flag "\\\*" then any flag can be used.
|
|
59
59
|
* @property {String} [mailboxId] unique mailbox ID if server has `OBJECTID` extension enabled
|
|
60
60
|
* @property {BigInt} [highestModseq] latest known modseq value if server has CONDSTORE or XYMHIGHESTMODSEQ enabled
|
|
61
|
+
* @property {String} [noModseq] if true then the server doesn't support the persistent storage of mod-sequences for the mailbox
|
|
61
62
|
* @property {BigInt} uidValidity Mailbox `UIDVALIDITY` value
|
|
62
63
|
* @property {Number} uidNext Next predicted UID
|
|
63
64
|
* @property {Number} exists Messages in this folder
|
|
@@ -127,9 +128,10 @@ class ImapFlow extends EventEmitter {
|
|
|
127
128
|
* @param {Boolean} [options.emitLogs=false] If `true` then in addition of sending data to logger, ImapFlow emits 'log' events with the same data
|
|
128
129
|
* @param {Boolean} [options.verifyOnly=false] If `true` then logs out automatically after successful authentication
|
|
129
130
|
* @param {Boolean} [options.proxy] Optional proxy URL. Supports HTTP CONNECT (`http://`, `https://`) and SOCKS (`socks://`, `socks4://`, `socks5://`) proxies
|
|
131
|
+
* @param {Boolean} [options.qresync=false] If true, then enables QRESYNC support. EXPUNGE notifications will include `uid` property instead of `seq`
|
|
130
132
|
*/
|
|
131
133
|
constructor(options) {
|
|
132
|
-
super();
|
|
134
|
+
super({ captureRejections: true });
|
|
133
135
|
|
|
134
136
|
this.options = options || {};
|
|
135
137
|
|
|
@@ -616,7 +618,7 @@ class ImapFlow extends EventEmitter {
|
|
|
616
618
|
}
|
|
617
619
|
|
|
618
620
|
// enable extensions if possible
|
|
619
|
-
await this.run('ENABLE', ['CONDSTORE', 'UTF8=ACCEPT']);
|
|
621
|
+
await this.run('ENABLE', ['CONDSTORE', 'UTF8=ACCEPT'].concat(this.options.qresync ? 'QRESYNC' : []));
|
|
620
622
|
|
|
621
623
|
this.usable = true;
|
|
622
624
|
}
|
|
@@ -808,7 +810,13 @@ class ImapFlow extends EventEmitter {
|
|
|
808
810
|
return false;
|
|
809
811
|
}
|
|
810
812
|
|
|
811
|
-
async initialOK() {
|
|
813
|
+
async initialOK(message) {
|
|
814
|
+
this.greeting = (message.attributes || [])
|
|
815
|
+
.filter(entry => entry.type === 'TEXT')
|
|
816
|
+
.map(entry => entry.value)
|
|
817
|
+
.filter(entry => entry)
|
|
818
|
+
.join('');
|
|
819
|
+
|
|
812
820
|
clearTimeout(this.greetingTimeout);
|
|
813
821
|
this.untaggedHandlers.OK = null;
|
|
814
822
|
this.untaggedHandlers.PREAUTH = null;
|
|
@@ -934,23 +942,76 @@ class ImapFlow extends EventEmitter {
|
|
|
934
942
|
let seq = Number(untagged.command);
|
|
935
943
|
if (seq && seq <= this.mailbox.exists) {
|
|
936
944
|
this.mailbox.exists--;
|
|
937
|
-
|
|
945
|
+
let payload = {
|
|
938
946
|
path: this.mailbox.path,
|
|
939
|
-
seq
|
|
940
|
-
|
|
947
|
+
seq,
|
|
948
|
+
vanished: false
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
if (typeof this.options.expungeHandler === 'function') {
|
|
952
|
+
try {
|
|
953
|
+
await this.options.expungeHandler(payload);
|
|
954
|
+
} catch (err) {
|
|
955
|
+
this.log.error({ msg: 'Failed to notify expunge event', payload, error: err, cid: this.id });
|
|
956
|
+
}
|
|
957
|
+
} else {
|
|
958
|
+
this.emit('expunge', payload);
|
|
959
|
+
}
|
|
941
960
|
}
|
|
942
961
|
}
|
|
943
962
|
|
|
944
|
-
async
|
|
945
|
-
|
|
963
|
+
async untaggedVanished(untagged, mailbox) {
|
|
964
|
+
mailbox = mailbox || this.mailbox;
|
|
965
|
+
if (!mailbox) {
|
|
966
|
+
// mailbox closed, ignore
|
|
967
|
+
return;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
let tags = [];
|
|
971
|
+
let uids = false;
|
|
972
|
+
|
|
973
|
+
if (untagged.attributes.length > 1 && Array.isArray(untagged.attributes[0])) {
|
|
974
|
+
tags = untagged.attributes[0].map(entry => (typeof entry.value === 'string' ? entry.value.toUpperCase() : false)).filter(value => value);
|
|
975
|
+
untagged.attributes.shift();
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
if (untagged.attributes[0] && typeof untagged.attributes[0].value === 'string') {
|
|
979
|
+
uids = untagged.attributes[0].value;
|
|
980
|
+
}
|
|
981
|
+
|
|
982
|
+
let uidList = expandRange(uids);
|
|
983
|
+
|
|
984
|
+
for (let uid of uidList) {
|
|
985
|
+
let payload = {
|
|
986
|
+
path: mailbox.path,
|
|
987
|
+
uid,
|
|
988
|
+
vanished: true,
|
|
989
|
+
earlier: tags.includes('EARLIER')
|
|
990
|
+
};
|
|
991
|
+
|
|
992
|
+
if (typeof this.options.expungeHandler === 'function') {
|
|
993
|
+
try {
|
|
994
|
+
await this.options.expungeHandler(payload);
|
|
995
|
+
} catch (err) {
|
|
996
|
+
this.log.error({ msg: 'Failed to notify expunge event', payload, error: err, cid: this.id });
|
|
997
|
+
}
|
|
998
|
+
} else {
|
|
999
|
+
this.emit('expunge', payload);
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
async untaggedFetch(untagged, mailbox) {
|
|
1005
|
+
mailbox = mailbox || this.mailbox;
|
|
1006
|
+
if (!mailbox) {
|
|
946
1007
|
// mailbox closed, ignore
|
|
947
1008
|
return;
|
|
948
1009
|
}
|
|
949
1010
|
|
|
950
|
-
let message = await formatMessageResponse(untagged,
|
|
1011
|
+
let message = await formatMessageResponse(untagged, mailbox);
|
|
951
1012
|
if (message.flags) {
|
|
952
1013
|
let updateEvent = {
|
|
953
|
-
path:
|
|
1014
|
+
path: mailbox.path,
|
|
954
1015
|
seq: message.seq
|
|
955
1016
|
};
|
|
956
1017
|
|
|
@@ -1053,10 +1114,13 @@ class ImapFlow extends EventEmitter {
|
|
|
1053
1114
|
this.untaggedHandlers.CAPABILITY = (...args) => this.untaggedCapability(...args);
|
|
1054
1115
|
this.sectionHandlers.CAPABILITY = (...args) => this.sectionCapability(...args);
|
|
1055
1116
|
|
|
1056
|
-
this.untaggedHandlers.FETCH = (...args) => this.untaggedFetch(...args);
|
|
1057
1117
|
this.untaggedHandlers.EXISTS = (...args) => this.untaggedExists(...args);
|
|
1058
1118
|
this.untaggedHandlers.EXPUNGE = (...args) => this.untaggedExpunge(...args);
|
|
1059
1119
|
|
|
1120
|
+
// these methods take an optional second argument, so make sure that some random IMAP tag is not used as the second argument
|
|
1121
|
+
this.untaggedHandlers.FETCH = untagged => this.untaggedFetch(untagged);
|
|
1122
|
+
this.untaggedHandlers.VANISHED = untagged => this.untaggedVanished(untagged);
|
|
1123
|
+
|
|
1060
1124
|
let socket = false;
|
|
1061
1125
|
if (this.options.proxy) {
|
|
1062
1126
|
try {
|
package/lib/types.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ declare module "imapflow" {
|
|
|
27
27
|
* @param [options.emitLogs = false] - If `true` then in addition of sending data to logger, ImapFlow emits 'log' events with the same data
|
|
28
28
|
* @param [options.verifyOnly = false] - If `true` then logs out automatically after successful authentication
|
|
29
29
|
* @param [options.proxy] - Optional proxy URL. Supports HTTP CONNECT (`http://`, `https://`) and SOCKS (`socks://`, `socks4://`, `socks5://`) proxies
|
|
30
|
+
* @param [options.qresync = false] - If true, then enables QRESYNC support. EXPUNGE notifications will include `uid` property instead of `seq`
|
|
30
31
|
*/
|
|
31
32
|
class ImapFlow extends EventEmitter {
|
|
32
33
|
constructor(options: {
|
|
@@ -52,6 +53,7 @@ declare module "imapflow" {
|
|
|
52
53
|
emitLogs?: boolean;
|
|
53
54
|
verifyOnly?: boolean;
|
|
54
55
|
proxy?: boolean;
|
|
56
|
+
qresync?: boolean;
|
|
55
57
|
});
|
|
56
58
|
/**
|
|
57
59
|
* Current module version as a static class property
|
|
@@ -483,6 +485,7 @@ declare module "imapflow" {
|
|
|
483
485
|
* @property permanentFlags - A Set of flags available to use in this mailbox. If it is not set or includes special flag "\\\*" then any flag can be used.
|
|
484
486
|
* @property [mailboxId] - unique mailbox ID if server has `OBJECTID` extension enabled
|
|
485
487
|
* @property [highestModseq] - latest known modseq value if server has CONDSTORE or XYMHIGHESTMODSEQ enabled
|
|
488
|
+
* @property [noModseq] - if true then the server doesn't support the persistent storage of mod-sequences for the mailbox
|
|
486
489
|
* @property uidValidity - Mailbox `UIDVALIDITY` value
|
|
487
490
|
* @property uidNext - Next predicted UID
|
|
488
491
|
* @property exists - Messages in this folder
|
|
@@ -497,6 +500,7 @@ declare type MailboxObject = {
|
|
|
497
500
|
permanentFlags: Set<string>;
|
|
498
501
|
mailboxId?: string;
|
|
499
502
|
highestModseq?: bigint;
|
|
503
|
+
noModseq?: string;
|
|
500
504
|
uidValidity: bigint;
|
|
501
505
|
uidNext: number;
|
|
502
506
|
exists: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "imapflow",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.82",
|
|
4
4
|
"description": "IMAP Client for Node",
|
|
5
5
|
"main": "./lib/imap-flow.js",
|
|
6
6
|
"scripts": {
|
|
@@ -26,20 +26,20 @@
|
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://imapflow.com/",
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@babel/eslint-parser": "7.
|
|
30
|
-
"@babel/eslint-plugin": "7.
|
|
29
|
+
"@babel/eslint-parser": "7.17.0",
|
|
30
|
+
"@babel/eslint-plugin": "7.16.5",
|
|
31
31
|
"@babel/plugin-syntax-class-properties": "7.12.13",
|
|
32
|
-
"@babel/preset-env": "7.16.
|
|
33
|
-
"@types/node": "
|
|
32
|
+
"@babel/preset-env": "7.16.11",
|
|
33
|
+
"@types/node": "17.0.18",
|
|
34
34
|
"braintree-jsdoc-template": "3.3.0",
|
|
35
|
-
"eslint": "8.
|
|
35
|
+
"eslint": "8.9.0",
|
|
36
36
|
"eslint-config-nodemailer": "1.2.0",
|
|
37
37
|
"eslint-config-prettier": "8.3.0",
|
|
38
38
|
"grunt": "1.4.1",
|
|
39
39
|
"grunt-cli": "1.4.3",
|
|
40
40
|
"grunt-contrib-nodeunit": "4.0.0",
|
|
41
41
|
"grunt-eslint": "24.0.0",
|
|
42
|
-
"jsdoc": "3.6.
|
|
42
|
+
"jsdoc": "3.6.10",
|
|
43
43
|
"tsd-jsdoc": "2.5.0"
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"libqp": "1.1.0",
|
|
51
51
|
"mailsplit": "5.3.1",
|
|
52
52
|
"nodemailer": "6.7.2",
|
|
53
|
-
"pino": "7.
|
|
54
|
-
"socks": "2.6.
|
|
53
|
+
"pino": "7.8.0",
|
|
54
|
+
"socks": "2.6.2"
|
|
55
55
|
}
|
|
56
56
|
}
|