imapflow 1.7.8 → 2.0.0
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 +20 -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 +518 -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 +3949 -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} +386 -516
- 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 +56 -121
- 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 +761 -1789
- 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
|
@@ -1,456 +0,0 @@
|
|
|
1
|
-
/*eslint no-unused-expressions: 0, prefer-arrow-callback: 0 */
|
|
2
|
-
|
|
3
|
-
'use strict';
|
|
4
|
-
|
|
5
|
-
const { parser } = require('../lib/handler/imap-handler');
|
|
6
|
-
|
|
7
|
-
let asyncWrapper = (test, handler) => {
|
|
8
|
-
handler(test)
|
|
9
|
-
.then(() => test.done())
|
|
10
|
-
.catch(err => {
|
|
11
|
-
test.ifError(err);
|
|
12
|
-
test.done();
|
|
13
|
-
});
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
// Error path tests
|
|
17
|
-
//
|
|
18
|
-
// NOTE: All error inputs use a non-status command (FETCH) rather than OK/NO/BAD/BYE.
|
|
19
|
-
// Status commands consume their entire remainder as human-readable text and never
|
|
20
|
-
// invoke the TokenParser, so errors like E9/E10/E13 cannot be triggered through
|
|
21
|
-
// the public parser() API when the command is a status response.
|
|
22
|
-
//
|
|
23
|
-
// Error-path tests use asyncWrapper with an internal try/catch so that err.code
|
|
24
|
-
// can be asserted. The handler re-throws when no error was caught so that a missing
|
|
25
|
-
// rejection is still surfaced as a test failure.
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* E9: Unclosed quoted string. The node's isClosed flag remains false when
|
|
29
|
-
* getAttributes() walks the tree, triggering ParserError9.
|
|
30
|
-
*/
|
|
31
|
-
module.exports['Token Parser: E9: unclosed quoted string throws ParserError9'] = test =>
|
|
32
|
-
asyncWrapper(test, async test => {
|
|
33
|
-
let err;
|
|
34
|
-
try {
|
|
35
|
-
await parser('* FETCH "unterminated');
|
|
36
|
-
} catch (e) {
|
|
37
|
-
err = e;
|
|
38
|
-
}
|
|
39
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
40
|
-
test.ok(err, 'expected an error to be thrown');
|
|
41
|
-
test.equal(err.code, 'ParserError9');
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* E10: Unexpected list terminator. A ')' is encountered when the current node
|
|
46
|
-
* is not a LIST node, triggering ParserError10.
|
|
47
|
-
*/
|
|
48
|
-
module.exports['Token Parser: E10: unexpected ) throws ParserError10'] = test =>
|
|
49
|
-
asyncWrapper(test, async test => {
|
|
50
|
-
let err;
|
|
51
|
-
try {
|
|
52
|
-
await parser('* FETCH )');
|
|
53
|
-
} catch (e) {
|
|
54
|
-
err = e;
|
|
55
|
-
}
|
|
56
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
57
|
-
test.ok(err, 'expected an error to be thrown');
|
|
58
|
-
test.equal(err.code, 'ParserError10');
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* E13: Unexpected control character. A character below 0x80 that is not in
|
|
63
|
-
* ATOM-CHAR and not one of the explicitly allowed exceptions (\, %) triggers
|
|
64
|
-
* ParserError13 in the STATE_NORMAL default branch.
|
|
65
|
-
* \x01 is a control character excluded from ATOM-CHAR.
|
|
66
|
-
*/
|
|
67
|
-
module.exports['Token Parser: E13: control character in attribute position throws ParserError13'] = test =>
|
|
68
|
-
asyncWrapper(test, async test => {
|
|
69
|
-
let err;
|
|
70
|
-
try {
|
|
71
|
-
await parser('* FETCH \x01rest');
|
|
72
|
-
} catch (e) {
|
|
73
|
-
err = e;
|
|
74
|
-
}
|
|
75
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
76
|
-
test.ok(err, 'expected an error to be thrown');
|
|
77
|
-
test.equal(err.code, 'ParserError13');
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
/**
|
|
81
|
-
* E18: Backslash escape at end of quoted string input. When a backslash is
|
|
82
|
-
* encountered in STATE_STRING and there is no following character (i >= len),
|
|
83
|
-
* ParserError18 is thrown.
|
|
84
|
-
* The JS string '* FETCH "test\\' represents the IMAP input: * FETCH "test\
|
|
85
|
-
*/
|
|
86
|
-
module.exports['Token Parser: E18: escape at end of quoted string throws ParserError18'] = test =>
|
|
87
|
-
asyncWrapper(test, async test => {
|
|
88
|
-
let err;
|
|
89
|
-
try {
|
|
90
|
-
await parser('* FETCH "test\\');
|
|
91
|
-
} catch (e) {
|
|
92
|
-
err = e;
|
|
93
|
-
}
|
|
94
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
95
|
-
test.ok(err, 'expected an error to be thrown');
|
|
96
|
-
test.equal(err.code, 'ParserError18');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* E23: Empty literal braces. When '}' is encountered in STATE_LITERAL before
|
|
101
|
-
* any digit has been stored in literalLength, ParserError23 is thrown because
|
|
102
|
-
* the check is: if (!('literalLength' in this.currentNode)) throw E23.
|
|
103
|
-
*/
|
|
104
|
-
module.exports['Token Parser: E23: empty literal braces throws ParserError23'] = test =>
|
|
105
|
-
asyncWrapper(test, async test => {
|
|
106
|
-
let err;
|
|
107
|
-
try {
|
|
108
|
-
await parser('* FETCH {}', { literals: [] });
|
|
109
|
-
} catch (e) {
|
|
110
|
-
err = e;
|
|
111
|
-
}
|
|
112
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
113
|
-
test.ok(err, 'expected an error to be thrown');
|
|
114
|
-
test.equal(err.code, 'ParserError23');
|
|
115
|
-
});
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* E25: Non-digit character inside literal size braces. Any character inside
|
|
119
|
-
* {…} that is not a digit (and not '}' or '+' with literalPlus) triggers
|
|
120
|
-
* ParserError25.
|
|
121
|
-
*/
|
|
122
|
-
module.exports['Token Parser: E25: non-digit in literal size throws ParserError25'] = test =>
|
|
123
|
-
asyncWrapper(test, async test => {
|
|
124
|
-
let err;
|
|
125
|
-
try {
|
|
126
|
-
await parser('* FETCH {abc}', { literals: [] });
|
|
127
|
-
} catch (e) {
|
|
128
|
-
err = e;
|
|
129
|
-
}
|
|
130
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
131
|
-
test.ok(err, 'expected an error to be thrown');
|
|
132
|
-
test.equal(err.code, 'ParserError25');
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* E26: Leading zero in literal size. After '0' is stored as literalLength,
|
|
137
|
-
* any following digit triggers ParserError26 because a leading zero is invalid.
|
|
138
|
-
*/
|
|
139
|
-
module.exports['Token Parser: E26: leading zero in literal size throws ParserError26'] = test =>
|
|
140
|
-
asyncWrapper(test, async test => {
|
|
141
|
-
let err;
|
|
142
|
-
try {
|
|
143
|
-
await parser('* FETCH {01}', { literals: [Buffer.from('x')] });
|
|
144
|
-
} catch (e) {
|
|
145
|
-
err = e;
|
|
146
|
-
}
|
|
147
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
148
|
-
test.ok(err, 'expected an error to be thrown');
|
|
149
|
-
test.equal(err.code, 'ParserError26');
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* MAX_IMAP_NESTING_REACHED: Nesting depth exceeds MAX_NODE_DEPTH (25).
|
|
154
|
-
* createNode() throws when node.depth > 25. The root TREE node starts at
|
|
155
|
-
* depth 0, so depth 1 is the first LIST ('('), and 26 open parens reaches
|
|
156
|
-
* depth 26 which exceeds the limit.
|
|
157
|
-
*/
|
|
158
|
-
module.exports['Token Parser: MAX_IMAP_NESTING_REACHED: deep nesting throws MAX_IMAP_NESTING_REACHED'] = test =>
|
|
159
|
-
asyncWrapper(test, async test => {
|
|
160
|
-
let err;
|
|
161
|
-
try {
|
|
162
|
-
const depth = 26;
|
|
163
|
-
const input = '* FETCH ' + '('.repeat(depth) + 'x' + ')'.repeat(depth);
|
|
164
|
-
await parser(input);
|
|
165
|
-
} catch (e) {
|
|
166
|
-
err = e;
|
|
167
|
-
}
|
|
168
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
169
|
-
test.ok(err, 'expected an error to be thrown');
|
|
170
|
-
test.equal(err.code, 'MAX_IMAP_NESTING_REACHED');
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// Happy-path tests
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* NIL atom is returned as null in the attributes array. The ATOM node whose
|
|
177
|
-
* value is "NIL" (case-insensitive) is converted to null in getAttributes().
|
|
178
|
-
*/
|
|
179
|
-
module.exports['Token Parser: NIL atom is parsed as null'] = test =>
|
|
180
|
-
asyncWrapper(test, async test => {
|
|
181
|
-
const result = await parser('* FETCH NIL');
|
|
182
|
-
const attrs = result.attributes;
|
|
183
|
-
test.ok(Array.isArray(attrs), 'attributes should be an array');
|
|
184
|
-
test.equal(attrs.length, 1, 'should have exactly one attribute');
|
|
185
|
-
test.strictEqual(attrs[0], null, 'NIL should be parsed as null');
|
|
186
|
-
});
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Nested lists produce nested arrays in the attributes output.
|
|
190
|
-
* Input ((a b)) produces: [ [ [ {a}, {b} ] ] ] — the attributes array contains
|
|
191
|
-
* one outer list which contains one inner list with two atom elements.
|
|
192
|
-
*/
|
|
193
|
-
module.exports['Token Parser: nested lists produce nested arrays'] = test =>
|
|
194
|
-
asyncWrapper(test, async test => {
|
|
195
|
-
const result = await parser('* FETCH ((a b))');
|
|
196
|
-
const attrs = result.attributes;
|
|
197
|
-
test.ok(Array.isArray(attrs), 'attributes should be an array');
|
|
198
|
-
const outer = attrs[0];
|
|
199
|
-
test.ok(Array.isArray(outer), 'outer list should be an array');
|
|
200
|
-
const inner = outer[0];
|
|
201
|
-
test.ok(Array.isArray(inner), 'inner list should be an array');
|
|
202
|
-
test.equal(inner.length, 2, 'inner list should have 2 elements');
|
|
203
|
-
test.equal(inner[0].value, 'a', 'first inner element value should be a');
|
|
204
|
-
test.equal(inner[1].value, 'b', 'second inner element value should be b');
|
|
205
|
-
});
|
|
206
|
-
|
|
207
|
-
/**
|
|
208
|
-
* Quoted string with escaped double-quote characters. The backslash-escape
|
|
209
|
-
* mechanism converts \" to " in the parsed value. The JS literal
|
|
210
|
-
* '* FETCH "hello \\"world\\""' represents: * FETCH "hello \"world\""
|
|
211
|
-
*/
|
|
212
|
-
module.exports['Token Parser: quoted string with escaped quotes is parsed correctly'] = test =>
|
|
213
|
-
asyncWrapper(test, async test => {
|
|
214
|
-
const result = await parser('* FETCH "hello \\"world\\""');
|
|
215
|
-
const attrs = result.attributes;
|
|
216
|
-
test.ok(Array.isArray(attrs), 'attributes should be an array');
|
|
217
|
-
test.equal(attrs.length, 1, 'should have exactly one attribute');
|
|
218
|
-
test.equal(attrs[0].type, 'STRING', 'type should be STRING');
|
|
219
|
-
test.equal(attrs[0].value, 'hello "world"', 'escaped quotes should be unescaped');
|
|
220
|
-
});
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Empty quoted string results in a STRING attribute with an empty value.
|
|
224
|
-
*/
|
|
225
|
-
module.exports['Token Parser: empty quoted string produces STRING with empty value'] = test =>
|
|
226
|
-
asyncWrapper(test, async test => {
|
|
227
|
-
const result = await parser('* FETCH ""');
|
|
228
|
-
const attrs = result.attributes;
|
|
229
|
-
test.ok(Array.isArray(attrs), 'attributes should be an array');
|
|
230
|
-
test.equal(attrs.length, 1, 'should have exactly one attribute');
|
|
231
|
-
test.equal(attrs[0].type, 'STRING', 'type should be STRING');
|
|
232
|
-
test.equal(attrs[0].value, '', 'value should be empty string');
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Literal data provided via the literals option is parsed into a LITERAL attribute.
|
|
237
|
-
* When options.literals is set and '}' is encountered with a valid size, the next
|
|
238
|
-
* Buffer in the literals array is used as the literal value directly.
|
|
239
|
-
* The literal size in braces must be followed by \r\n or \n per IMAP protocol.
|
|
240
|
-
*/
|
|
241
|
-
module.exports['Token Parser: literal with pre-parsed data produces LITERAL attribute'] = test =>
|
|
242
|
-
asyncWrapper(test, async test => {
|
|
243
|
-
const data = Buffer.from('hello');
|
|
244
|
-
const result = await parser('* FETCH {5}\r\n', { literals: [data] });
|
|
245
|
-
const attrs = result.attributes;
|
|
246
|
-
test.ok(Array.isArray(attrs), 'attributes should be an array');
|
|
247
|
-
test.equal(attrs.length, 1, 'should have exactly one attribute');
|
|
248
|
-
test.equal(attrs[0].type, 'LITERAL', 'type should be LITERAL');
|
|
249
|
-
test.ok(Buffer.isBuffer(attrs[0].value), 'literal value should be a Buffer');
|
|
250
|
-
test.equal(attrs[0].value.toString(), 'hello', 'literal value should match provided buffer');
|
|
251
|
-
});
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* A plain ATOM token produces an ATOM attribute with the correct string value.
|
|
255
|
-
*/
|
|
256
|
-
module.exports['Token Parser: atom value produces ATOM attribute'] = test =>
|
|
257
|
-
asyncWrapper(test, async test => {
|
|
258
|
-
const result = await parser('* FETCH INBOX');
|
|
259
|
-
const attrs = result.attributes;
|
|
260
|
-
test.ok(Array.isArray(attrs), 'attributes should be an array');
|
|
261
|
-
test.equal(attrs.length, 1, 'should have exactly one attribute');
|
|
262
|
-
test.equal(attrs[0].type, 'ATOM', 'type should be ATOM');
|
|
263
|
-
test.equal(attrs[0].value, 'INBOX', 'value should be INBOX');
|
|
264
|
-
});
|
|
265
|
-
|
|
266
|
-
/**
|
|
267
|
-
* E29: Range separator ':' after a character that is not a digit or '*'.
|
|
268
|
-
* Input "1,:5" starts as ATOM "1", ',' triggers SEQUENCE reclassification and
|
|
269
|
-
* appends ',' (value becomes "1,"). Then ':' fires E29 because last char ','
|
|
270
|
-
* is not a digit or '*'.
|
|
271
|
-
*/
|
|
272
|
-
module.exports['Token Parser: E29: range separator after non-digit/non-star throws ParserError29'] = test =>
|
|
273
|
-
asyncWrapper(test, async test => {
|
|
274
|
-
let err;
|
|
275
|
-
try {
|
|
276
|
-
await parser('* FETCH 1,:5');
|
|
277
|
-
} catch (e) {
|
|
278
|
-
err = e;
|
|
279
|
-
}
|
|
280
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
281
|
-
test.ok(err, 'expected an error to be thrown');
|
|
282
|
-
test.equal(err.code, 'ParserError29');
|
|
283
|
-
});
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* E30: Wildcard '*' when last char is not ',' or ':'.
|
|
287
|
-
* Input "1:2*" enters SEQUENCE after "1:" and appends "2" (value "1:2").
|
|
288
|
-
* Then '*' fires E30 because last char '2' is not ',' or ':'.
|
|
289
|
-
*/
|
|
290
|
-
module.exports['Token Parser: E30: wildcard after digit throws ParserError30'] = test =>
|
|
291
|
-
asyncWrapper(test, async test => {
|
|
292
|
-
let err;
|
|
293
|
-
try {
|
|
294
|
-
await parser('* FETCH 1:2*');
|
|
295
|
-
} catch (e) {
|
|
296
|
-
err = e;
|
|
297
|
-
}
|
|
298
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
299
|
-
test.ok(err, 'expected an error to be thrown');
|
|
300
|
-
test.equal(err.code, 'ParserError30');
|
|
301
|
-
});
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* E31: Separator ',' after a character that is not a digit or '*'.
|
|
305
|
-
* Input "1:,5" enters SEQUENCE after "1" and appends ':' (value "1:").
|
|
306
|
-
* Then ',' fires E31 because last char ':' is not a digit or '*'.
|
|
307
|
-
*/
|
|
308
|
-
module.exports['Token Parser: E31: comma after colon throws ParserError31'] = test =>
|
|
309
|
-
asyncWrapper(test, async test => {
|
|
310
|
-
let err;
|
|
311
|
-
try {
|
|
312
|
-
await parser('* FETCH 1:,5');
|
|
313
|
-
} catch (e) {
|
|
314
|
-
err = e;
|
|
315
|
-
}
|
|
316
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
317
|
-
test.ok(err, 'expected an error to be thrown');
|
|
318
|
-
test.equal(err.code, 'ParserError31');
|
|
319
|
-
});
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* E32: Separator ',' after bare '*' (not in a range).
|
|
323
|
-
* Input "*,5" starts SEQUENCE with value "*". Then ',' passes E31
|
|
324
|
-
* (last char '*' satisfies the check) but fires E32 because last char
|
|
325
|
-
* is '*' and the char before it (at(-2)) is not ':'.
|
|
326
|
-
*/
|
|
327
|
-
module.exports['Token Parser: E32: comma after bare star throws ParserError32'] = test =>
|
|
328
|
-
asyncWrapper(test, async test => {
|
|
329
|
-
let err;
|
|
330
|
-
try {
|
|
331
|
-
await parser('* FETCH *,5');
|
|
332
|
-
} catch (e) {
|
|
333
|
-
err = e;
|
|
334
|
-
}
|
|
335
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
336
|
-
test.ok(err, 'expected an error to be thrown');
|
|
337
|
-
test.equal(err.code, 'ParserError32');
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
/**
|
|
341
|
-
* E33: Non-digit, non-special character in sequence position.
|
|
342
|
-
* Input "1:a" enters SEQUENCE after "1" and appends ':' (value "1:").
|
|
343
|
-
* Then 'a' is not a digit, not ':', not '*', not ',' so E33 fires.
|
|
344
|
-
*/
|
|
345
|
-
module.exports['Token Parser: E33: non-digit non-special char in sequence throws ParserError33'] = test =>
|
|
346
|
-
asyncWrapper(test, async test => {
|
|
347
|
-
let err;
|
|
348
|
-
try {
|
|
349
|
-
await parser('* FETCH 1:a');
|
|
350
|
-
} catch (e) {
|
|
351
|
-
err = e;
|
|
352
|
-
}
|
|
353
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
354
|
-
test.ok(err, 'expected an error to be thrown');
|
|
355
|
-
test.equal(err.code, 'ParserError33');
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
/**
|
|
359
|
-
* E34: Digit immediately after '*'.
|
|
360
|
-
* Input "*1" starts SEQUENCE with value "*". Then '1' is a digit but
|
|
361
|
-
* last char is '*', so E34 fires (digits cannot follow '*').
|
|
362
|
-
*/
|
|
363
|
-
module.exports['Token Parser: E34: digit after star throws ParserError34'] = test =>
|
|
364
|
-
asyncWrapper(test, async test => {
|
|
365
|
-
let err;
|
|
366
|
-
try {
|
|
367
|
-
await parser('* FETCH *1');
|
|
368
|
-
} catch (e) {
|
|
369
|
-
err = e;
|
|
370
|
-
}
|
|
371
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
372
|
-
test.ok(err, 'expected an error to be thrown');
|
|
373
|
-
test.equal(err.code, 'ParserError34');
|
|
374
|
-
});
|
|
375
|
-
|
|
376
|
-
module.exports['Token Parser: REFERRAL response code is parsed as an IMAP URL atom'] = test =>
|
|
377
|
-
asyncWrapper(test, async test => {
|
|
378
|
-
let r = await parser('* OK [REFERRAL imap://user@host/INBOX] please use another server');
|
|
379
|
-
let section = r.attributes[0].section;
|
|
380
|
-
test.equal(section[0].value, 'REFERRAL');
|
|
381
|
-
test.equal(section[1].value, 'imap://user@host/INBOX');
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
module.exports['Token Parser: malformed REFERRAL with no closing bracket consumes the rest'] = test =>
|
|
385
|
-
asyncWrapper(test, async test => {
|
|
386
|
-
// Missing ']' previously produced a negative-index substring (garbage). The whole
|
|
387
|
-
// remaining string should be captured as the URL instead, without throwing.
|
|
388
|
-
let r = await parser('* OK [REFERRAL imap://user@host/INBOX');
|
|
389
|
-
let section = r.attributes[0].section;
|
|
390
|
-
test.equal(section[0].value, 'REFERRAL');
|
|
391
|
-
test.equal(section[1].value, 'imap://user@host/INBOX');
|
|
392
|
-
});
|
|
393
|
-
|
|
394
|
-
// ---------------------------------------------------------------------------
|
|
395
|
-
// Additional error-path and edge-case coverage (E11, E12, E16, E17, E19-E22,
|
|
396
|
-
// E24, E27) plus the bare-"*" sequence and "~"-as-atom normalization paths.
|
|
397
|
-
// All use a non-status command (FETCH) so the TokenParser is actually invoked.
|
|
398
|
-
// ---------------------------------------------------------------------------
|
|
399
|
-
|
|
400
|
-
const expectParserError = (input, code) => test =>
|
|
401
|
-
asyncWrapper(test, async test => {
|
|
402
|
-
let err;
|
|
403
|
-
try {
|
|
404
|
-
await parser(input);
|
|
405
|
-
} catch (e) {
|
|
406
|
-
err = e;
|
|
407
|
-
}
|
|
408
|
-
if (!err) throw new Error('Expected parser to throw but it did not');
|
|
409
|
-
test.equal(err.code, code);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
module.exports['Token Parser: E11: unexpected ] throws ParserError11'] = expectParserError('* 1 FETCH (])', 'ParserError11');
|
|
413
|
-
module.exports['Token Parser: E12: ~ not followed by { throws ParserError12'] = expectParserError('* 1 FETCH ~%', 'ParserError12');
|
|
414
|
-
module.exports['Token Parser: E16: \\* followed by char throws ParserError17'] = expectParserError('* 1 FETCH \\*x', 'ParserError17');
|
|
415
|
-
module.exports['Token Parser: E19: partial ending with . throws ParserError19'] = expectParserError('* 1 FETCH BODY[]<0.>', 'ParserError19');
|
|
416
|
-
module.exports['Token Parser: E20: partial leading . throws ParserError20'] = expectParserError('* 1 FETCH BODY[]<.>', 'ParserError20');
|
|
417
|
-
module.exports['Token Parser: E21: non-digit in partial throws ParserError21'] = expectParserError('* 1 FETCH BODY[]<0.x>', 'ParserError21');
|
|
418
|
-
module.exports['Token Parser: E22: invalid leading-zero partial throws ParserError22'] = expectParserError('* 1 FETCH BODY[]<00>', 'ParserError22');
|
|
419
|
-
module.exports['Token Parser: E24: literal prefix not followed by CRLF throws ParserError24'] = expectParserError('* 1 FETCH {3}xyz', 'ParserError24');
|
|
420
|
-
module.exports['Token Parser: E27: whitespace mid-sequence throws ParserError27'] = expectParserError('* 1 FETCH 1: 2', 'ParserError27');
|
|
421
|
-
|
|
422
|
-
module.exports['Token Parser: bare * is normalized to an ATOM'] = test =>
|
|
423
|
-
asyncWrapper(test, async test => {
|
|
424
|
-
let r = await parser('* 1 FETCH *');
|
|
425
|
-
let last = r.attributes[r.attributes.length - 1];
|
|
426
|
-
test.equal(last.type, 'ATOM');
|
|
427
|
-
test.equal(last.value, '*');
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
module.exports['Token Parser: ~ followed by atom chars becomes an ATOM'] = test =>
|
|
431
|
-
asyncWrapper(test, async test => {
|
|
432
|
-
let r = await parser('* 1 FETCH ~abc');
|
|
433
|
-
let last = r.attributes[r.attributes.length - 1];
|
|
434
|
-
test.equal(last.type, 'ATOM');
|
|
435
|
-
test.equal(last.value, '~abc');
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
module.exports['Token Parser: E16: invalid char in atom throws ParserError16'] = expectParserError('* 1 FETCH 3* 4', 'ParserError16');
|
|
439
|
-
|
|
440
|
-
module.exports['Token Parser: literal+ (non-synchronizing) marker is accepted'] = test =>
|
|
441
|
-
asyncWrapper(test, async test => {
|
|
442
|
-
// {3+} is a LITERAL+ prefix; with literalPlus enabled and no pre-parsed
|
|
443
|
-
// literals, the parser reads the literal bytes inline.
|
|
444
|
-
let r = await parser('* 1 FETCH {3+}\r\nabc', { literalPlus: true });
|
|
445
|
-
let lit = r.attributes[r.attributes.length - 1];
|
|
446
|
-
test.equal(lit.type, 'LITERAL');
|
|
447
|
-
test.equal(lit.value.toString(), 'abc');
|
|
448
|
-
});
|
|
449
|
-
|
|
450
|
-
module.exports['Token Parser: literal prefix terminated by bare LF is accepted'] = test =>
|
|
451
|
-
asyncWrapper(test, async test => {
|
|
452
|
-
let r = await parser('* 1 FETCH {3}\nabc');
|
|
453
|
-
let lit = r.attributes[r.attributes.length - 1];
|
|
454
|
-
test.equal(lit.type, 'LITERAL');
|
|
455
|
-
test.equal(lit.value.toString(), 'abc');
|
|
456
|
-
});
|