imapflow 1.7.8 → 2.0.1
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 +27 -0
- package/README.md +8 -2
- package/dist/cjs/charsets.d.ts +1 -0
- package/dist/cjs/charsets.js +294 -0
- package/dist/cjs/commands/append.d.ts +22 -0
- package/dist/cjs/commands/append.js +151 -0
- package/dist/cjs/commands/authenticate.d.ts +24 -0
- package/dist/cjs/commands/authenticate.js +223 -0
- package/dist/cjs/commands/capability.d.ts +8 -0
- package/dist/cjs/commands/capability.js +32 -0
- package/dist/cjs/commands/close.d.ts +8 -0
- package/dist/cjs/commands/close.js +39 -0
- package/dist/cjs/commands/compress.d.ts +8 -0
- package/dist/cjs/commands/compress.js +56 -0
- package/dist/cjs/commands/copy.d.ts +13 -0
- package/dist/cjs/commands/copy.js +44 -0
- package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
- package/dist/cjs/commands/copyuid-parser.js +32 -0
- package/dist/cjs/commands/create.d.ts +11 -0
- package/dist/cjs/commands/create.js +80 -0
- package/dist/cjs/commands/delete.d.ts +11 -0
- package/dist/cjs/commands/delete.js +40 -0
- package/dist/cjs/commands/enable.d.ts +9 -0
- package/dist/cjs/commands/enable.js +61 -0
- package/dist/cjs/commands/esearch-parser.d.ts +17 -0
- package/dist/cjs/commands/esearch-parser.js +91 -0
- package/dist/cjs/commands/expunge.d.ts +12 -0
- package/dist/cjs/commands/expunge.js +60 -0
- package/dist/cjs/commands/fetch.d.ts +30 -0
- package/dist/cjs/commands/fetch.js +241 -0
- package/dist/cjs/commands/id.d.ts +10 -0
- package/dist/cjs/commands/id.js +80 -0
- package/dist/cjs/commands/idle.d.ts +9 -0
- package/dist/cjs/commands/idle.js +347 -0
- package/dist/cjs/commands/list.d.ts +16 -0
- package/dist/cjs/commands/list.js +520 -0
- package/dist/cjs/commands/login.d.ts +11 -0
- package/dist/cjs/commands/login.js +42 -0
- package/dist/cjs/commands/logout.d.ts +8 -0
- package/dist/cjs/commands/logout.js +47 -0
- package/dist/cjs/commands/move.d.ts +13 -0
- package/dist/cjs/commands/move.js +57 -0
- package/dist/cjs/commands/namespace.d.ts +25 -0
- package/dist/cjs/commands/namespace.js +139 -0
- package/dist/cjs/commands/noop.d.ts +8 -0
- package/dist/cjs/commands/noop.js +22 -0
- package/dist/cjs/commands/quota.d.ts +10 -0
- package/dist/cjs/commands/quota.js +119 -0
- package/dist/cjs/commands/rename.d.ts +12 -0
- package/dist/cjs/commands/rename.js +48 -0
- package/dist/cjs/commands/search.d.ts +15 -0
- package/dist/cjs/commands/search.js +228 -0
- package/dist/cjs/commands/select.d.ts +25 -0
- package/dist/cjs/commands/select.js +250 -0
- package/dist/cjs/commands/starttls.d.ts +8 -0
- package/dist/cjs/commands/starttls.js +30 -0
- package/dist/cjs/commands/status-fields.d.ts +14 -0
- package/dist/cjs/commands/status-fields.js +61 -0
- package/dist/cjs/commands/status.d.ts +12 -0
- package/dist/cjs/commands/status.js +108 -0
- package/dist/cjs/commands/store.d.ts +19 -0
- package/dist/cjs/commands/store.js +93 -0
- package/dist/cjs/commands/subscribe.d.ts +9 -0
- package/dist/cjs/commands/subscribe.js +31 -0
- package/dist/cjs/commands/unsubscribe.d.ts +9 -0
- package/dist/cjs/commands/unsubscribe.js +31 -0
- package/dist/cjs/connection-deadline.d.ts +49 -0
- package/dist/cjs/connection-deadline.js +91 -0
- package/dist/cjs/errors.d.ts +83 -0
- package/dist/cjs/errors.js +13 -0
- package/dist/cjs/handler/imap-compiler.d.ts +24 -0
- package/dist/cjs/handler/imap-compiler.js +285 -0
- package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/cjs/handler/imap-formal-syntax.js +121 -0
- package/dist/cjs/handler/imap-handler.d.ts +9 -0
- package/dist/cjs/handler/imap-handler.js +10 -0
- package/dist/cjs/handler/imap-parser.d.ts +16 -0
- package/dist/cjs/handler/imap-parser.js +90 -0
- package/dist/cjs/handler/imap-stream.d.ts +181 -0
- package/dist/cjs/handler/imap-stream.js +446 -0
- package/dist/cjs/handler/limits.d.ts +25 -0
- package/dist/cjs/handler/limits.js +51 -0
- package/dist/cjs/handler/parser-instance.d.ts +68 -0
- package/dist/cjs/handler/parser-instance.js +223 -0
- package/dist/cjs/handler/token-parser.d.ts +91 -0
- package/dist/cjs/handler/token-parser.js +673 -0
- package/dist/cjs/handler/types.d.ts +91 -0
- package/dist/cjs/handler/types.js +4 -0
- package/dist/cjs/imap-commands.d.ts +16 -0
- package/dist/cjs/imap-commands.js +74 -0
- package/dist/cjs/imap-flow.d.ts +676 -0
- package/dist/cjs/imap-flow.js +3956 -0
- package/dist/cjs/jp-decoder.d.ts +12 -0
- package/dist/cjs/jp-decoder.js +79 -0
- package/dist/cjs/limited-passthrough.d.ts +25 -0
- package/dist/cjs/limited-passthrough.js +54 -0
- package/dist/cjs/logger.d.ts +3 -0
- package/dist/cjs/logger.js +11 -0
- package/dist/cjs/package-info.d.ts +3 -0
- package/dist/cjs/package-info.js +7 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/proxy-connection.d.ts +33 -0
- package/dist/cjs/proxy-connection.js +392 -0
- package/dist/cjs/search-compiler.d.ts +34 -0
- package/dist/cjs/search-compiler.js +476 -0
- package/dist/cjs/special-use.d.ts +22 -0
- package/dist/cjs/special-use.js +911 -0
- package/dist/cjs/tools.d.ts +427 -0
- package/dist/cjs/tools.js +1496 -0
- package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +387 -517
- package/dist/cjs/types.js +5 -0
- package/dist/esm/charsets.d.ts +1 -0
- package/{lib → dist/esm}/charsets.js +1 -6
- package/dist/esm/commands/append.d.ts +22 -0
- package/{lib → dist/esm}/commands/append.js +22 -52
- package/dist/esm/commands/authenticate.d.ts +24 -0
- package/{lib → dist/esm}/commands/authenticate.js +62 -87
- package/dist/esm/commands/capability.d.ts +8 -0
- package/{lib → dist/esm}/commands/capability.js +6 -9
- package/dist/esm/commands/close.d.ts +8 -0
- package/{lib → dist/esm}/commands/close.js +6 -10
- package/dist/esm/commands/compress.d.ts +8 -0
- package/{lib → dist/esm}/commands/compress.js +7 -11
- package/dist/esm/commands/copy.d.ts +13 -0
- package/{lib → dist/esm}/commands/copy.js +12 -20
- package/dist/esm/commands/copyuid-parser.d.ts +11 -0
- package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
- package/dist/esm/commands/create.d.ts +11 -0
- package/{lib → dist/esm}/commands/create.js +13 -27
- package/dist/esm/commands/delete.d.ts +11 -0
- package/{lib → dist/esm}/commands/delete.js +9 -14
- package/dist/esm/commands/enable.d.ts +9 -0
- package/{lib → dist/esm}/commands/enable.js +23 -30
- package/dist/esm/commands/esearch-parser.d.ts +17 -0
- package/dist/esm/commands/esearch-parser.js +88 -0
- package/dist/esm/commands/expunge.d.ts +12 -0
- package/{lib → dist/esm}/commands/expunge.js +17 -22
- package/dist/esm/commands/fetch.d.ts +30 -0
- package/{lib → dist/esm}/commands/fetch.js +32 -64
- package/dist/esm/commands/id.d.ts +10 -0
- package/{lib → dist/esm}/commands/id.js +17 -23
- package/dist/esm/commands/idle.d.ts +9 -0
- package/{lib → dist/esm}/commands/idle.js +47 -81
- package/dist/esm/commands/list.d.ts +16 -0
- package/{lib → dist/esm}/commands/list.js +60 -123
- package/dist/esm/commands/login.d.ts +11 -0
- package/{lib → dist/esm}/commands/login.js +10 -15
- package/dist/esm/commands/logout.d.ts +8 -0
- package/{lib → dist/esm}/commands/logout.js +9 -11
- package/dist/esm/commands/move.d.ts +13 -0
- package/{lib → dist/esm}/commands/move.js +13 -21
- package/dist/esm/commands/namespace.d.ts +25 -0
- package/{lib → dist/esm}/commands/namespace.js +34 -44
- package/dist/esm/commands/noop.d.ts +8 -0
- package/{lib → dist/esm}/commands/noop.js +6 -7
- package/dist/esm/commands/quota.d.ts +10 -0
- package/{lib → dist/esm}/commands/quota.js +18 -36
- package/dist/esm/commands/rename.d.ts +12 -0
- package/{lib → dist/esm}/commands/rename.js +10 -15
- package/dist/esm/commands/search.d.ts +15 -0
- package/{lib → dist/esm}/commands/search.js +36 -135
- package/dist/esm/commands/select.d.ts +25 -0
- package/{lib → dist/esm}/commands/select.js +33 -64
- package/dist/esm/commands/starttls.d.ts +8 -0
- package/{lib → dist/esm}/commands/starttls.js +6 -8
- package/dist/esm/commands/status-fields.d.ts +14 -0
- package/{lib → dist/esm}/commands/status-fields.js +5 -16
- package/dist/esm/commands/status.d.ts +12 -0
- package/{lib → dist/esm}/commands/status.js +18 -29
- package/dist/esm/commands/store.d.ts +19 -0
- package/{lib → dist/esm}/commands/store.js +24 -37
- package/dist/esm/commands/subscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/subscribe.js +8 -12
- package/dist/esm/commands/unsubscribe.d.ts +9 -0
- package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
- package/dist/esm/connection-deadline.d.ts +49 -0
- package/{lib → dist/esm}/connection-deadline.js +14 -25
- package/dist/esm/errors.d.ts +83 -0
- package/dist/esm/errors.js +9 -0
- package/dist/esm/handler/imap-compiler.d.ts +24 -0
- package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
- package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
- package/dist/esm/handler/imap-formal-syntax.js +117 -0
- package/dist/esm/handler/imap-handler.d.ts +9 -0
- package/dist/esm/handler/imap-handler.js +9 -0
- package/dist/esm/handler/imap-parser.d.ts +16 -0
- package/{lib → dist/esm}/handler/imap-parser.js +31 -44
- package/dist/esm/handler/imap-stream.d.ts +181 -0
- package/{lib → dist/esm}/handler/imap-stream.js +29 -121
- package/dist/esm/handler/limits.d.ts +25 -0
- package/{lib → dist/esm}/handler/limits.js +13 -22
- package/dist/esm/handler/parser-instance.d.ts +68 -0
- package/{lib → dist/esm}/handler/parser-instance.js +19 -47
- package/dist/esm/handler/token-parser.d.ts +91 -0
- package/{lib → dist/esm}/handler/token-parser.js +71 -155
- package/dist/esm/handler/types.d.ts +91 -0
- package/dist/esm/handler/types.js +3 -0
- package/dist/esm/imap-commands.d.ts +16 -0
- package/dist/esm/imap-commands.js +67 -0
- package/dist/esm/imap-flow.d.ts +676 -0
- package/{lib → dist/esm}/imap-flow.js +769 -1790
- package/dist/esm/jp-decoder.d.ts +12 -0
- package/{lib → dist/esm}/jp-decoder.js +6 -21
- package/dist/esm/limited-passthrough.d.ts +25 -0
- package/{lib → dist/esm}/limited-passthrough.js +7 -20
- package/dist/esm/logger.d.ts +3 -0
- package/dist/esm/logger.js +4 -0
- package/dist/esm/package-info.d.ts +3 -0
- package/dist/esm/package-info.js +4 -0
- package/dist/esm/package.json +3 -0
- package/dist/esm/proxy-connection.d.ts +33 -0
- package/{lib → dist/esm}/proxy-connection.js +56 -127
- package/dist/esm/search-compiler.d.ts +34 -0
- package/{lib → dist/esm}/search-compiler.js +54 -110
- package/dist/esm/special-use.d.ts +22 -0
- package/dist/esm/special-use.js +907 -0
- package/dist/esm/tools.d.ts +427 -0
- package/dist/esm/tools.js +1446 -0
- package/dist/esm/types.d.ts +828 -0
- package/dist/esm/types.js +4 -0
- package/package.json +60 -20
- package/.gitattributes +0 -1
- package/.github/CODE_OF_CONDUCT.md +0 -76
- package/.github/FUNDING.yml +0 -4
- package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
- package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
- package/.github/contributing.md +0 -17
- package/.github/workflows/release.yaml +0 -36
- package/.github/workflows/stale.yml +0 -29
- package/.github/workflows/test.yml +0 -51
- package/.ncurc.js +0 -4
- package/.prettierignore +0 -4
- package/.prettierrc.js +0 -8
- package/.release-please-manifest.json +0 -3
- package/CLAUDE.md +0 -104
- package/Gruntfile.js +0 -23
- package/eslint.config.js +0 -45
- package/lib/handler/imap-formal-syntax.js +0 -189
- package/lib/handler/imap-handler.js +0 -17
- package/lib/imap-commands.js +0 -45
- package/lib/logger.js +0 -5
- package/lib/special-use.js +0 -923
- package/lib/tools.js +0 -1612
- package/release-please-config.json +0 -10
- package/test/authentication-test.js +0 -101
- package/test/auto-idle-test.js +0 -470
- package/test/bodystructure-test.js +0 -899
- package/test/charsets-test.js +0 -161
- package/test/commands-branches-test.js +0 -1095
- package/test/commands-integration-test.js +0 -11124
- package/test/commands-test.js +0 -73
- package/test/connection-edge-cases-test.js +0 -1828
- package/test/connection-test.js +0 -162
- package/test/copyuid-parser-test.js +0 -173
- package/test/fetch-generator-test.js +0 -218
- package/test/fixtures/fake-timers.js +0 -115
- package/test/fixtures/serialized-mimetorture.js +0 -2738
- package/test/fixtures/test-client.js +0 -101
- package/test/fixtures/test-tls.js +0 -8
- package/test/handler-branches-test.js +0 -310
- package/test/idle-polling-test.js +0 -518
- package/test/imap-compiler-test.js +0 -809
- package/test/imap-flow-compress-test.js +0 -166
- package/test/imap-flow-coverage-test.js +0 -612
- package/test/imap-flow-fetch-download-test.js +0 -909
- package/test/imap-flow-internals-test.js +0 -725
- package/test/imap-flow-methods-test.js +0 -889
- package/test/imap-flow-proxy-paths-test.js +0 -366
- package/test/imap-flow-secure-test.js +0 -573
- package/test/imap-flow-server-test.js +0 -1474
- package/test/imap-formal-syntax-test.js +0 -293
- package/test/imap-parser-test.js +0 -1474
- package/test/imap-stream-edge-cases-test.js +0 -666
- package/test/imap-stream-test.js +0 -177
- package/test/imapflow-test.js +0 -258
- package/test/integration/README.md +0 -52
- package/test/integration/dovecot-test.conf +0 -27
- package/test/integration/rev2-live-test.js +0 -431
- package/test/integration/run-rev2-tests.sh +0 -75
- package/test/integration-test.js +0 -83
- package/test/jp-decoder-test.js +0 -304
- package/test/limited-passthrough-test.js +0 -299
- package/test/memory-cleanup-test.js +0 -144
- package/test/memory-leak-test.js +0 -667
- package/test/parser-limits-test.js +0 -292
- package/test/proxy-connection-test.js +0 -738
- package/test/reliability-improvements-test.js +0 -548
- package/test/search-compiler-test.js +0 -1300
- package/test/search-test.js +0 -329
- package/test/special-use-test.js +0 -418
- package/test/starttls-injection-test.js +0 -181
- package/test/tag-correlation-test.js +0 -333
- package/test/timer-policy-test.js +0 -227
- package/test/token-parser-test.js +0 -456
- package/test/tools-test.js +0 -2013
- package/test/unhandled-rejection-test.js +0 -661
package/test/search-test.js
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const searchCmd = require('../lib/commands/search');
|
|
4
|
-
const { parseEsearchResponse } = searchCmd;
|
|
5
|
-
const { ImapFlow } = require('../lib/imap-flow');
|
|
6
|
-
|
|
7
|
-
// Mock connection — capabilities is Map (matches real ImapFlow)
|
|
8
|
-
function makeConnection({ hasEsearch = true } = {}) {
|
|
9
|
-
const caps = new Map();
|
|
10
|
-
if (hasEsearch) {
|
|
11
|
-
caps.set('ESEARCH', true);
|
|
12
|
-
}
|
|
13
|
-
return {
|
|
14
|
-
state: 'SELECTED',
|
|
15
|
-
states: { SELECTED: 'SELECTED' },
|
|
16
|
-
capabilities: caps,
|
|
17
|
-
enabled: new Set(),
|
|
18
|
-
exec: async () => ({ next: () => {} }),
|
|
19
|
-
log: { warn: () => {} }
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
// ── Parser tests ───────────────────────────────────────────────────────────
|
|
24
|
-
|
|
25
|
-
module.exports['ESEARCH: parseEsearchResponse COUNT only'] = test => {
|
|
26
|
-
const attrs = [
|
|
27
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
28
|
-
{ type: 'ATOM', value: '42' }
|
|
29
|
-
];
|
|
30
|
-
const result = parseEsearchResponse(attrs);
|
|
31
|
-
test.equal(result.count, 42);
|
|
32
|
-
test.equal(result.min, undefined);
|
|
33
|
-
test.equal(result.max, undefined);
|
|
34
|
-
test.done();
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
module.exports['ESEARCH: parseEsearchResponse MIN MAX'] = test => {
|
|
38
|
-
const attrs = [
|
|
39
|
-
{ type: 'ATOM', value: 'MIN' },
|
|
40
|
-
{ type: 'ATOM', value: '1001' },
|
|
41
|
-
{ type: 'ATOM', value: 'MAX' },
|
|
42
|
-
{ type: 'ATOM', value: '9876' }
|
|
43
|
-
];
|
|
44
|
-
const result = parseEsearchResponse(attrs);
|
|
45
|
-
test.equal(result.min, 1001);
|
|
46
|
-
test.equal(result.max, 9876);
|
|
47
|
-
test.done();
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
module.exports['ESEARCH: parseEsearchResponse ALL keeps compact string'] = test => {
|
|
51
|
-
const attrs = [
|
|
52
|
-
{ type: 'ATOM', value: 'ALL' },
|
|
53
|
-
{ type: 'ATOM', value: '1001,1005:1010,1020' }
|
|
54
|
-
];
|
|
55
|
-
const result = parseEsearchResponse(attrs);
|
|
56
|
-
// Must be preserved as compact string — NOT an array
|
|
57
|
-
test.equal(typeof result.all, 'string');
|
|
58
|
-
test.equal(result.all, '1001,1005:1010,1020');
|
|
59
|
-
test.done();
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
module.exports['ESEARCH: parseEsearchResponse PARTIAL (Array form)'] = test => {
|
|
63
|
-
// Parser represents parenthesized groups as plain Arrays
|
|
64
|
-
const attrs = [
|
|
65
|
-
{ type: 'ATOM', value: 'PARTIAL' },
|
|
66
|
-
[
|
|
67
|
-
{ type: 'ATOM', value: '1:100' },
|
|
68
|
-
{ type: 'ATOM', value: '1001,1003:1010,1015' }
|
|
69
|
-
]
|
|
70
|
-
];
|
|
71
|
-
const result = parseEsearchResponse(attrs);
|
|
72
|
-
test.deepEqual(result.partial, { range: '1:100', messages: '1001,1003:1010,1015' });
|
|
73
|
-
test.done();
|
|
74
|
-
};
|
|
75
|
-
|
|
76
|
-
module.exports['ESEARCH: parseEsearchResponse COUNT + PARTIAL combined'] = test => {
|
|
77
|
-
const attrs = [
|
|
78
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
79
|
-
{ type: 'ATOM', value: '34201' },
|
|
80
|
-
{ type: 'ATOM', value: 'PARTIAL' },
|
|
81
|
-
[
|
|
82
|
-
{ type: 'ATOM', value: '1:100' },
|
|
83
|
-
{ type: 'ATOM', value: '2001,2003:2020' }
|
|
84
|
-
]
|
|
85
|
-
];
|
|
86
|
-
const result = parseEsearchResponse(attrs);
|
|
87
|
-
test.equal(result.count, 34201);
|
|
88
|
-
test.deepEqual(result.partial, { range: '1:100', messages: '2001,2003:2020' });
|
|
89
|
-
test.done();
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
// ── Command-building tests ─────────────────────────────────────────────────
|
|
93
|
-
|
|
94
|
-
module.exports['ESEARCH: emits RETURN clause when returnOptions present and server has ESEARCH'] = test => {
|
|
95
|
-
const conn = makeConnection({ hasEsearch: true });
|
|
96
|
-
let capturedCommand = null;
|
|
97
|
-
let capturedAttributes = null;
|
|
98
|
-
conn.exec = async (command, attributes) => {
|
|
99
|
-
capturedCommand = command;
|
|
100
|
-
capturedAttributes = JSON.stringify(attributes);
|
|
101
|
-
return { next: () => {} };
|
|
102
|
-
};
|
|
103
|
-
searchCmd(conn, { seen: false }, { uid: true, returnOptions: ['COUNT'] })
|
|
104
|
-
.then(() => {
|
|
105
|
-
test.equal(capturedCommand, 'UID SEARCH');
|
|
106
|
-
test.ok(capturedAttributes.includes('"RETURN"'), 'should include RETURN atom');
|
|
107
|
-
test.ok(capturedAttributes.includes('"COUNT"'), 'should include COUNT in return list');
|
|
108
|
-
test.done();
|
|
109
|
-
})
|
|
110
|
-
.catch(err => test.done(err));
|
|
111
|
-
};
|
|
112
|
-
|
|
113
|
-
module.exports['ESEARCH: RETURN clause includes PARTIAL range atom'] = test => {
|
|
114
|
-
const conn = makeConnection({ hasEsearch: true });
|
|
115
|
-
let capturedAttributes = null;
|
|
116
|
-
conn.exec = async (command, attributes) => {
|
|
117
|
-
capturedAttributes = JSON.stringify(attributes);
|
|
118
|
-
return { next: () => {} };
|
|
119
|
-
};
|
|
120
|
-
searchCmd(conn, { seen: false }, { uid: true, returnOptions: [{ partial: '1:100' }] })
|
|
121
|
-
.then(() => {
|
|
122
|
-
test.ok(capturedAttributes.includes('"PARTIAL"'), 'should include PARTIAL atom');
|
|
123
|
-
test.ok(capturedAttributes.includes('"1:100"'), 'should include range string');
|
|
124
|
-
test.done();
|
|
125
|
-
})
|
|
126
|
-
.catch(err => test.done(err));
|
|
127
|
-
};
|
|
128
|
-
|
|
129
|
-
module.exports['ESEARCH: no RETURN clause when server lacks ESEARCH capability'] = test => {
|
|
130
|
-
const conn = makeConnection({ hasEsearch: false });
|
|
131
|
-
let capturedAttributes = null;
|
|
132
|
-
conn.exec = async (command, attributes, handlers) => {
|
|
133
|
-
capturedAttributes = JSON.stringify(attributes);
|
|
134
|
-
// Simulate plain SEARCH response
|
|
135
|
-
const searchHandler = handlers && handlers.untagged && handlers.untagged.SEARCH;
|
|
136
|
-
if (searchHandler) {
|
|
137
|
-
await searchHandler({
|
|
138
|
-
attributes: [{ value: '1' }, { value: '2' }, { value: '3' }]
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
return { next: () => {} };
|
|
142
|
-
};
|
|
143
|
-
searchCmd(conn, { seen: false }, { uid: true, returnOptions: ['COUNT', 'ALL'] })
|
|
144
|
-
.then(result => {
|
|
145
|
-
test.ok(!capturedAttributes.includes('"RETURN"'), 'should NOT include RETURN when no ESEARCH');
|
|
146
|
-
test.ok(Array.isArray(result), 'should return number[] when ESEARCH unavailable');
|
|
147
|
-
test.deepEqual(result, [1, 2, 3]);
|
|
148
|
-
test.done();
|
|
149
|
-
})
|
|
150
|
-
.catch(err => test.done(err));
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
module.exports['ESEARCH: parseEsearchResponse skips non-ATOM tokens'] = test => {
|
|
154
|
-
const attrs = [
|
|
155
|
-
[{ type: 'ATOM', value: 'stray' }], // non-ATOM (Array) token is skipped
|
|
156
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
157
|
-
{ type: 'ATOM', value: '9' }
|
|
158
|
-
];
|
|
159
|
-
const result = parseEsearchResponse(attrs);
|
|
160
|
-
test.equal(result.count, 9);
|
|
161
|
-
test.done();
|
|
162
|
-
};
|
|
163
|
-
|
|
164
|
-
module.exports['ESEARCH: parseEsearchResponse skips trailing key with no value'] = test => {
|
|
165
|
-
const attrs = [{ type: 'ATOM', value: 'COUNT' }]; // dangling key, no value
|
|
166
|
-
const result = parseEsearchResponse(attrs);
|
|
167
|
-
test.deepEqual(result, {});
|
|
168
|
-
test.done();
|
|
169
|
-
};
|
|
170
|
-
|
|
171
|
-
module.exports['ESEARCH: untagged handler parses ESEARCH response'] = test => {
|
|
172
|
-
const conn = makeConnection({ hasEsearch: true });
|
|
173
|
-
conn.exec = async (command, attributes, handlers) => {
|
|
174
|
-
const esearch = handlers && handlers.untagged && handlers.untagged.ESEARCH;
|
|
175
|
-
// leading (TAG "A1") list + UID atom are stripped before parsing
|
|
176
|
-
await esearch({
|
|
177
|
-
attributes: [
|
|
178
|
-
[
|
|
179
|
-
{ type: 'ATOM', value: 'TAG' },
|
|
180
|
-
{ type: 'STRING', value: 'A1' }
|
|
181
|
-
],
|
|
182
|
-
{ type: 'ATOM', value: 'UID' },
|
|
183
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
184
|
-
{ type: 'ATOM', value: '7' }
|
|
185
|
-
]
|
|
186
|
-
});
|
|
187
|
-
// also exercise the empty-attributes guard
|
|
188
|
-
await esearch({ attributes: null });
|
|
189
|
-
return { next: () => {} };
|
|
190
|
-
};
|
|
191
|
-
searchCmd(conn, { seen: false }, { uid: true, returnOptions: ['COUNT'] })
|
|
192
|
-
.then(result => {
|
|
193
|
-
test.equal(result.count, 7);
|
|
194
|
-
test.done();
|
|
195
|
-
})
|
|
196
|
-
.catch(err => test.done(err));
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
module.exports['ESEARCH: command path returns false on exec error'] = test => {
|
|
200
|
-
const conn = makeConnection({ hasEsearch: true });
|
|
201
|
-
conn.exec = async () => {
|
|
202
|
-
let err = new Error('search failed');
|
|
203
|
-
err.responseStatus = 'NO';
|
|
204
|
-
throw err;
|
|
205
|
-
};
|
|
206
|
-
searchCmd(conn, { seen: false }, { uid: true, returnOptions: ['COUNT'] })
|
|
207
|
-
.then(result => {
|
|
208
|
-
test.equal(result, false);
|
|
209
|
-
test.done();
|
|
210
|
-
})
|
|
211
|
-
.catch(err => test.done(err));
|
|
212
|
-
};
|
|
213
|
-
|
|
214
|
-
module.exports['ESEARCH: parseEsearchResponse ignores unknown keywords'] = test => {
|
|
215
|
-
// Unknown result keywords must be skipped with their value so the
|
|
216
|
-
// key/value stream stays aligned for the entries that follow
|
|
217
|
-
const attrs = [
|
|
218
|
-
{ type: 'ATOM', value: 'RELEVANCY' },
|
|
219
|
-
{ type: 'ATOM', value: '87' },
|
|
220
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
221
|
-
{ type: 'ATOM', value: '5' }
|
|
222
|
-
];
|
|
223
|
-
const result = parseEsearchResponse(attrs);
|
|
224
|
-
test.equal(result.count, 5);
|
|
225
|
-
test.equal(result.relevancy, undefined, 'unknown keys should not appear in result');
|
|
226
|
-
test.done();
|
|
227
|
-
};
|
|
228
|
-
|
|
229
|
-
module.exports['ESEARCH: parseEsearchResponse parses MODSEQ as BigInt'] = test => {
|
|
230
|
-
// RFC 7162: CONDSTORE sessions append MODSEQ to ESEARCH responses when the
|
|
231
|
-
// search used a MODSEQ criterion
|
|
232
|
-
const attrs = [
|
|
233
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
234
|
-
{ type: 'ATOM', value: '5' },
|
|
235
|
-
{ type: 'ATOM', value: 'MODSEQ' },
|
|
236
|
-
{ type: 'ATOM', value: '9007199254740993' }
|
|
237
|
-
];
|
|
238
|
-
const result = parseEsearchResponse(attrs);
|
|
239
|
-
test.equal(result.count, 5);
|
|
240
|
-
test.strictEqual(result.modseq, 9007199254740993n, 'modseq should be an exact BigInt');
|
|
241
|
-
test.done();
|
|
242
|
-
};
|
|
243
|
-
|
|
244
|
-
module.exports['ESEARCH: parseEsearchResponse drops non-numeric MODSEQ'] = test => {
|
|
245
|
-
const attrs = [
|
|
246
|
-
{ type: 'ATOM', value: 'MODSEQ' },
|
|
247
|
-
{ type: 'ATOM', value: 'bogus' },
|
|
248
|
-
{ type: 'ATOM', value: 'COUNT' },
|
|
249
|
-
{ type: 'ATOM', value: '3' }
|
|
250
|
-
];
|
|
251
|
-
const result = parseEsearchResponse(attrs);
|
|
252
|
-
test.equal(result.modseq, undefined, 'invalid modseq must be dropped');
|
|
253
|
-
test.equal(result.count, 3, 'stream must stay aligned after a dropped value');
|
|
254
|
-
test.done();
|
|
255
|
-
};
|
|
256
|
-
|
|
257
|
-
module.exports['ESEARCH: backward compat — no returnOptions returns number[]'] = test => {
|
|
258
|
-
const conn = makeConnection({ hasEsearch: true });
|
|
259
|
-
conn.exec = async (command, attributes, handlers) => {
|
|
260
|
-
const searchHandler = handlers && handlers.untagged && handlers.untagged.SEARCH;
|
|
261
|
-
if (searchHandler) {
|
|
262
|
-
await searchHandler({
|
|
263
|
-
attributes: [{ value: '10' }, { value: '20' }]
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
return { next: () => {} };
|
|
267
|
-
};
|
|
268
|
-
// No returnOptions — must return number[] even if server has ESEARCH
|
|
269
|
-
searchCmd(conn, { seen: true }, { uid: true })
|
|
270
|
-
.then(result => {
|
|
271
|
-
test.ok(Array.isArray(result));
|
|
272
|
-
test.deepEqual(result, [10, 20]);
|
|
273
|
-
test.done();
|
|
274
|
-
})
|
|
275
|
-
.catch(err => test.done(err));
|
|
276
|
-
};
|
|
277
|
-
|
|
278
|
-
// ── imap-flow.js public API fallback test ─────────────────────────────────
|
|
279
|
-
module.exports['imap-flow: search() derives ESearchResult when server has no ESEARCH'] = test => {
|
|
280
|
-
const client = new ImapFlow({
|
|
281
|
-
host: 'imap.example.com',
|
|
282
|
-
port: 993,
|
|
283
|
-
auth: { user: 'test', pass: 'test' },
|
|
284
|
-
logger: false
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
// Simulate a selected mailbox and no ESEARCH capability
|
|
288
|
-
client.mailbox = { path: 'INBOX' };
|
|
289
|
-
client.state = client.states.SELECTED;
|
|
290
|
-
client.capabilities = new Map(); // no ESEARCH
|
|
291
|
-
|
|
292
|
-
// Stub run() to return a sorted number[]
|
|
293
|
-
client.run = async () => [10, 20, 30, 40, 50];
|
|
294
|
-
|
|
295
|
-
client
|
|
296
|
-
.search({ seen: false }, { uid: true, returnOptions: ['COUNT', 'MIN', 'MAX', 'ALL'] })
|
|
297
|
-
.then(result => {
|
|
298
|
-
test.equal(typeof result, 'object', 'should return object, not array');
|
|
299
|
-
test.ok(!Array.isArray(result), 'should not be an array');
|
|
300
|
-
test.equal(result.count, 5);
|
|
301
|
-
test.equal(result.min, 10);
|
|
302
|
-
test.equal(result.max, 50);
|
|
303
|
-
// packMessageRange([10,20,30,40,50]) → "10,20,30,40,50" (non-contiguous)
|
|
304
|
-
test.ok(typeof result.all === 'string' && result.all.length > 0, 'all should be non-empty compact string');
|
|
305
|
-
test.done();
|
|
306
|
-
})
|
|
307
|
-
.catch(err => test.done(err));
|
|
308
|
-
};
|
|
309
|
-
|
|
310
|
-
module.exports['imap-flow: search() fallback with empty result set'] = test => {
|
|
311
|
-
const client = new ImapFlow({
|
|
312
|
-
host: 'imap.example.com',
|
|
313
|
-
port: 993,
|
|
314
|
-
auth: { user: 'test', pass: 'test' },
|
|
315
|
-
logger: false
|
|
316
|
-
});
|
|
317
|
-
client.mailbox = { path: 'INBOX' };
|
|
318
|
-
client.state = client.states.SELECTED;
|
|
319
|
-
client.capabilities = new Map();
|
|
320
|
-
client.run = async () => [];
|
|
321
|
-
client
|
|
322
|
-
.search({}, { uid: true, returnOptions: ['COUNT', 'ALL'] })
|
|
323
|
-
.then(result => {
|
|
324
|
-
test.equal(result.count, 0);
|
|
325
|
-
test.equal(result.all, undefined, 'all should be absent for empty result');
|
|
326
|
-
test.done();
|
|
327
|
-
})
|
|
328
|
-
.catch(err => test.done(err));
|
|
329
|
-
};
|