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.
Files changed (296) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +8 -2
  3. package/dist/cjs/charsets.d.ts +1 -0
  4. package/dist/cjs/charsets.js +294 -0
  5. package/dist/cjs/commands/append.d.ts +22 -0
  6. package/dist/cjs/commands/append.js +151 -0
  7. package/dist/cjs/commands/authenticate.d.ts +24 -0
  8. package/dist/cjs/commands/authenticate.js +223 -0
  9. package/dist/cjs/commands/capability.d.ts +8 -0
  10. package/dist/cjs/commands/capability.js +32 -0
  11. package/dist/cjs/commands/close.d.ts +8 -0
  12. package/dist/cjs/commands/close.js +39 -0
  13. package/dist/cjs/commands/compress.d.ts +8 -0
  14. package/dist/cjs/commands/compress.js +56 -0
  15. package/dist/cjs/commands/copy.d.ts +13 -0
  16. package/dist/cjs/commands/copy.js +44 -0
  17. package/dist/cjs/commands/copyuid-parser.d.ts +11 -0
  18. package/dist/cjs/commands/copyuid-parser.js +32 -0
  19. package/dist/cjs/commands/create.d.ts +11 -0
  20. package/dist/cjs/commands/create.js +80 -0
  21. package/dist/cjs/commands/delete.d.ts +11 -0
  22. package/dist/cjs/commands/delete.js +40 -0
  23. package/dist/cjs/commands/enable.d.ts +9 -0
  24. package/dist/cjs/commands/enable.js +61 -0
  25. package/dist/cjs/commands/esearch-parser.d.ts +17 -0
  26. package/dist/cjs/commands/esearch-parser.js +91 -0
  27. package/dist/cjs/commands/expunge.d.ts +12 -0
  28. package/dist/cjs/commands/expunge.js +60 -0
  29. package/dist/cjs/commands/fetch.d.ts +30 -0
  30. package/dist/cjs/commands/fetch.js +241 -0
  31. package/dist/cjs/commands/id.d.ts +10 -0
  32. package/dist/cjs/commands/id.js +80 -0
  33. package/dist/cjs/commands/idle.d.ts +9 -0
  34. package/dist/cjs/commands/idle.js +347 -0
  35. package/dist/cjs/commands/list.d.ts +16 -0
  36. package/dist/cjs/commands/list.js +520 -0
  37. package/dist/cjs/commands/login.d.ts +11 -0
  38. package/dist/cjs/commands/login.js +42 -0
  39. package/dist/cjs/commands/logout.d.ts +8 -0
  40. package/dist/cjs/commands/logout.js +47 -0
  41. package/dist/cjs/commands/move.d.ts +13 -0
  42. package/dist/cjs/commands/move.js +57 -0
  43. package/dist/cjs/commands/namespace.d.ts +25 -0
  44. package/dist/cjs/commands/namespace.js +139 -0
  45. package/dist/cjs/commands/noop.d.ts +8 -0
  46. package/dist/cjs/commands/noop.js +22 -0
  47. package/dist/cjs/commands/quota.d.ts +10 -0
  48. package/dist/cjs/commands/quota.js +119 -0
  49. package/dist/cjs/commands/rename.d.ts +12 -0
  50. package/dist/cjs/commands/rename.js +48 -0
  51. package/dist/cjs/commands/search.d.ts +15 -0
  52. package/dist/cjs/commands/search.js +228 -0
  53. package/dist/cjs/commands/select.d.ts +25 -0
  54. package/dist/cjs/commands/select.js +250 -0
  55. package/dist/cjs/commands/starttls.d.ts +8 -0
  56. package/dist/cjs/commands/starttls.js +30 -0
  57. package/dist/cjs/commands/status-fields.d.ts +14 -0
  58. package/dist/cjs/commands/status-fields.js +61 -0
  59. package/dist/cjs/commands/status.d.ts +12 -0
  60. package/dist/cjs/commands/status.js +108 -0
  61. package/dist/cjs/commands/store.d.ts +19 -0
  62. package/dist/cjs/commands/store.js +93 -0
  63. package/dist/cjs/commands/subscribe.d.ts +9 -0
  64. package/dist/cjs/commands/subscribe.js +31 -0
  65. package/dist/cjs/commands/unsubscribe.d.ts +9 -0
  66. package/dist/cjs/commands/unsubscribe.js +31 -0
  67. package/dist/cjs/connection-deadline.d.ts +49 -0
  68. package/dist/cjs/connection-deadline.js +91 -0
  69. package/dist/cjs/errors.d.ts +83 -0
  70. package/dist/cjs/errors.js +13 -0
  71. package/dist/cjs/handler/imap-compiler.d.ts +24 -0
  72. package/dist/cjs/handler/imap-compiler.js +285 -0
  73. package/dist/cjs/handler/imap-formal-syntax.d.ts +28 -0
  74. package/dist/cjs/handler/imap-formal-syntax.js +121 -0
  75. package/dist/cjs/handler/imap-handler.d.ts +9 -0
  76. package/dist/cjs/handler/imap-handler.js +10 -0
  77. package/dist/cjs/handler/imap-parser.d.ts +16 -0
  78. package/dist/cjs/handler/imap-parser.js +90 -0
  79. package/dist/cjs/handler/imap-stream.d.ts +181 -0
  80. package/dist/cjs/handler/imap-stream.js +446 -0
  81. package/dist/cjs/handler/limits.d.ts +25 -0
  82. package/dist/cjs/handler/limits.js +51 -0
  83. package/dist/cjs/handler/parser-instance.d.ts +68 -0
  84. package/dist/cjs/handler/parser-instance.js +223 -0
  85. package/dist/cjs/handler/token-parser.d.ts +91 -0
  86. package/dist/cjs/handler/token-parser.js +673 -0
  87. package/dist/cjs/handler/types.d.ts +91 -0
  88. package/dist/cjs/handler/types.js +4 -0
  89. package/dist/cjs/imap-commands.d.ts +16 -0
  90. package/dist/cjs/imap-commands.js +74 -0
  91. package/dist/cjs/imap-flow.d.ts +676 -0
  92. package/dist/cjs/imap-flow.js +3956 -0
  93. package/dist/cjs/jp-decoder.d.ts +12 -0
  94. package/dist/cjs/jp-decoder.js +79 -0
  95. package/dist/cjs/limited-passthrough.d.ts +25 -0
  96. package/dist/cjs/limited-passthrough.js +54 -0
  97. package/dist/cjs/logger.d.ts +3 -0
  98. package/dist/cjs/logger.js +11 -0
  99. package/dist/cjs/package-info.d.ts +3 -0
  100. package/dist/cjs/package-info.js +7 -0
  101. package/dist/cjs/package.json +3 -0
  102. package/dist/cjs/proxy-connection.d.ts +33 -0
  103. package/dist/cjs/proxy-connection.js +392 -0
  104. package/dist/cjs/search-compiler.d.ts +34 -0
  105. package/dist/cjs/search-compiler.js +476 -0
  106. package/dist/cjs/special-use.d.ts +22 -0
  107. package/dist/cjs/special-use.js +911 -0
  108. package/dist/cjs/tools.d.ts +427 -0
  109. package/dist/cjs/tools.js +1496 -0
  110. package/{lib/imap-flow.d.ts → dist/cjs/types.d.ts} +387 -517
  111. package/dist/cjs/types.js +5 -0
  112. package/dist/esm/charsets.d.ts +1 -0
  113. package/{lib → dist/esm}/charsets.js +1 -6
  114. package/dist/esm/commands/append.d.ts +22 -0
  115. package/{lib → dist/esm}/commands/append.js +22 -52
  116. package/dist/esm/commands/authenticate.d.ts +24 -0
  117. package/{lib → dist/esm}/commands/authenticate.js +62 -87
  118. package/dist/esm/commands/capability.d.ts +8 -0
  119. package/{lib → dist/esm}/commands/capability.js +6 -9
  120. package/dist/esm/commands/close.d.ts +8 -0
  121. package/{lib → dist/esm}/commands/close.js +6 -10
  122. package/dist/esm/commands/compress.d.ts +8 -0
  123. package/{lib → dist/esm}/commands/compress.js +7 -11
  124. package/dist/esm/commands/copy.d.ts +13 -0
  125. package/{lib → dist/esm}/commands/copy.js +12 -20
  126. package/dist/esm/commands/copyuid-parser.d.ts +11 -0
  127. package/{lib → dist/esm}/commands/copyuid-parser.js +9 -15
  128. package/dist/esm/commands/create.d.ts +11 -0
  129. package/{lib → dist/esm}/commands/create.js +13 -27
  130. package/dist/esm/commands/delete.d.ts +11 -0
  131. package/{lib → dist/esm}/commands/delete.js +9 -14
  132. package/dist/esm/commands/enable.d.ts +9 -0
  133. package/{lib → dist/esm}/commands/enable.js +23 -30
  134. package/dist/esm/commands/esearch-parser.d.ts +17 -0
  135. package/dist/esm/commands/esearch-parser.js +88 -0
  136. package/dist/esm/commands/expunge.d.ts +12 -0
  137. package/{lib → dist/esm}/commands/expunge.js +17 -22
  138. package/dist/esm/commands/fetch.d.ts +30 -0
  139. package/{lib → dist/esm}/commands/fetch.js +32 -64
  140. package/dist/esm/commands/id.d.ts +10 -0
  141. package/{lib → dist/esm}/commands/id.js +17 -23
  142. package/dist/esm/commands/idle.d.ts +9 -0
  143. package/{lib → dist/esm}/commands/idle.js +47 -81
  144. package/dist/esm/commands/list.d.ts +16 -0
  145. package/{lib → dist/esm}/commands/list.js +60 -123
  146. package/dist/esm/commands/login.d.ts +11 -0
  147. package/{lib → dist/esm}/commands/login.js +10 -15
  148. package/dist/esm/commands/logout.d.ts +8 -0
  149. package/{lib → dist/esm}/commands/logout.js +9 -11
  150. package/dist/esm/commands/move.d.ts +13 -0
  151. package/{lib → dist/esm}/commands/move.js +13 -21
  152. package/dist/esm/commands/namespace.d.ts +25 -0
  153. package/{lib → dist/esm}/commands/namespace.js +34 -44
  154. package/dist/esm/commands/noop.d.ts +8 -0
  155. package/{lib → dist/esm}/commands/noop.js +6 -7
  156. package/dist/esm/commands/quota.d.ts +10 -0
  157. package/{lib → dist/esm}/commands/quota.js +18 -36
  158. package/dist/esm/commands/rename.d.ts +12 -0
  159. package/{lib → dist/esm}/commands/rename.js +10 -15
  160. package/dist/esm/commands/search.d.ts +15 -0
  161. package/{lib → dist/esm}/commands/search.js +36 -135
  162. package/dist/esm/commands/select.d.ts +25 -0
  163. package/{lib → dist/esm}/commands/select.js +33 -64
  164. package/dist/esm/commands/starttls.d.ts +8 -0
  165. package/{lib → dist/esm}/commands/starttls.js +6 -8
  166. package/dist/esm/commands/status-fields.d.ts +14 -0
  167. package/{lib → dist/esm}/commands/status-fields.js +5 -16
  168. package/dist/esm/commands/status.d.ts +12 -0
  169. package/{lib → dist/esm}/commands/status.js +18 -29
  170. package/dist/esm/commands/store.d.ts +19 -0
  171. package/{lib → dist/esm}/commands/store.js +24 -37
  172. package/dist/esm/commands/subscribe.d.ts +9 -0
  173. package/{lib → dist/esm}/commands/subscribe.js +8 -12
  174. package/dist/esm/commands/unsubscribe.d.ts +9 -0
  175. package/{lib → dist/esm}/commands/unsubscribe.js +8 -12
  176. package/dist/esm/connection-deadline.d.ts +49 -0
  177. package/{lib → dist/esm}/connection-deadline.js +14 -25
  178. package/dist/esm/errors.d.ts +83 -0
  179. package/dist/esm/errors.js +9 -0
  180. package/dist/esm/handler/imap-compiler.d.ts +24 -0
  181. package/{lib → dist/esm}/handler/imap-compiler.js +22 -80
  182. package/dist/esm/handler/imap-formal-syntax.d.ts +28 -0
  183. package/dist/esm/handler/imap-formal-syntax.js +117 -0
  184. package/dist/esm/handler/imap-handler.d.ts +9 -0
  185. package/dist/esm/handler/imap-handler.js +9 -0
  186. package/dist/esm/handler/imap-parser.d.ts +16 -0
  187. package/{lib → dist/esm}/handler/imap-parser.js +31 -44
  188. package/dist/esm/handler/imap-stream.d.ts +181 -0
  189. package/{lib → dist/esm}/handler/imap-stream.js +29 -121
  190. package/dist/esm/handler/limits.d.ts +25 -0
  191. package/{lib → dist/esm}/handler/limits.js +13 -22
  192. package/dist/esm/handler/parser-instance.d.ts +68 -0
  193. package/{lib → dist/esm}/handler/parser-instance.js +19 -47
  194. package/dist/esm/handler/token-parser.d.ts +91 -0
  195. package/{lib → dist/esm}/handler/token-parser.js +71 -155
  196. package/dist/esm/handler/types.d.ts +91 -0
  197. package/dist/esm/handler/types.js +3 -0
  198. package/dist/esm/imap-commands.d.ts +16 -0
  199. package/dist/esm/imap-commands.js +67 -0
  200. package/dist/esm/imap-flow.d.ts +676 -0
  201. package/{lib → dist/esm}/imap-flow.js +769 -1790
  202. package/dist/esm/jp-decoder.d.ts +12 -0
  203. package/{lib → dist/esm}/jp-decoder.js +6 -21
  204. package/dist/esm/limited-passthrough.d.ts +25 -0
  205. package/{lib → dist/esm}/limited-passthrough.js +7 -20
  206. package/dist/esm/logger.d.ts +3 -0
  207. package/dist/esm/logger.js +4 -0
  208. package/dist/esm/package-info.d.ts +3 -0
  209. package/dist/esm/package-info.js +4 -0
  210. package/dist/esm/package.json +3 -0
  211. package/dist/esm/proxy-connection.d.ts +33 -0
  212. package/{lib → dist/esm}/proxy-connection.js +56 -127
  213. package/dist/esm/search-compiler.d.ts +34 -0
  214. package/{lib → dist/esm}/search-compiler.js +54 -110
  215. package/dist/esm/special-use.d.ts +22 -0
  216. package/dist/esm/special-use.js +907 -0
  217. package/dist/esm/tools.d.ts +427 -0
  218. package/dist/esm/tools.js +1446 -0
  219. package/dist/esm/types.d.ts +828 -0
  220. package/dist/esm/types.js +4 -0
  221. package/package.json +60 -20
  222. package/.gitattributes +0 -1
  223. package/.github/CODE_OF_CONDUCT.md +0 -76
  224. package/.github/FUNDING.yml +0 -4
  225. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -40
  226. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -19
  227. package/.github/contributing.md +0 -17
  228. package/.github/workflows/release.yaml +0 -36
  229. package/.github/workflows/stale.yml +0 -29
  230. package/.github/workflows/test.yml +0 -51
  231. package/.ncurc.js +0 -4
  232. package/.prettierignore +0 -4
  233. package/.prettierrc.js +0 -8
  234. package/.release-please-manifest.json +0 -3
  235. package/CLAUDE.md +0 -104
  236. package/Gruntfile.js +0 -23
  237. package/eslint.config.js +0 -45
  238. package/lib/handler/imap-formal-syntax.js +0 -189
  239. package/lib/handler/imap-handler.js +0 -17
  240. package/lib/imap-commands.js +0 -45
  241. package/lib/logger.js +0 -5
  242. package/lib/special-use.js +0 -923
  243. package/lib/tools.js +0 -1612
  244. package/release-please-config.json +0 -10
  245. package/test/authentication-test.js +0 -101
  246. package/test/auto-idle-test.js +0 -470
  247. package/test/bodystructure-test.js +0 -899
  248. package/test/charsets-test.js +0 -161
  249. package/test/commands-branches-test.js +0 -1095
  250. package/test/commands-integration-test.js +0 -11124
  251. package/test/commands-test.js +0 -73
  252. package/test/connection-edge-cases-test.js +0 -1828
  253. package/test/connection-test.js +0 -162
  254. package/test/copyuid-parser-test.js +0 -173
  255. package/test/fetch-generator-test.js +0 -218
  256. package/test/fixtures/fake-timers.js +0 -115
  257. package/test/fixtures/serialized-mimetorture.js +0 -2738
  258. package/test/fixtures/test-client.js +0 -101
  259. package/test/fixtures/test-tls.js +0 -8
  260. package/test/handler-branches-test.js +0 -310
  261. package/test/idle-polling-test.js +0 -518
  262. package/test/imap-compiler-test.js +0 -809
  263. package/test/imap-flow-compress-test.js +0 -166
  264. package/test/imap-flow-coverage-test.js +0 -612
  265. package/test/imap-flow-fetch-download-test.js +0 -909
  266. package/test/imap-flow-internals-test.js +0 -725
  267. package/test/imap-flow-methods-test.js +0 -889
  268. package/test/imap-flow-proxy-paths-test.js +0 -366
  269. package/test/imap-flow-secure-test.js +0 -573
  270. package/test/imap-flow-server-test.js +0 -1474
  271. package/test/imap-formal-syntax-test.js +0 -293
  272. package/test/imap-parser-test.js +0 -1474
  273. package/test/imap-stream-edge-cases-test.js +0 -666
  274. package/test/imap-stream-test.js +0 -177
  275. package/test/imapflow-test.js +0 -258
  276. package/test/integration/README.md +0 -52
  277. package/test/integration/dovecot-test.conf +0 -27
  278. package/test/integration/rev2-live-test.js +0 -431
  279. package/test/integration/run-rev2-tests.sh +0 -75
  280. package/test/integration-test.js +0 -83
  281. package/test/jp-decoder-test.js +0 -304
  282. package/test/limited-passthrough-test.js +0 -299
  283. package/test/memory-cleanup-test.js +0 -144
  284. package/test/memory-leak-test.js +0 -667
  285. package/test/parser-limits-test.js +0 -292
  286. package/test/proxy-connection-test.js +0 -738
  287. package/test/reliability-improvements-test.js +0 -548
  288. package/test/search-compiler-test.js +0 -1300
  289. package/test/search-test.js +0 -329
  290. package/test/special-use-test.js +0 -418
  291. package/test/starttls-injection-test.js +0 -181
  292. package/test/tag-correlation-test.js +0 -333
  293. package/test/timer-policy-test.js +0 -227
  294. package/test/token-parser-test.js +0 -456
  295. package/test/tools-test.js +0 -2013
  296. package/test/unhandled-rejection-test.js +0 -661
@@ -1,304 +0,0 @@
1
- 'use strict';
2
-
3
- const { JPDecoder } = require('../lib/jp-decoder');
4
- const { PassThrough, Transform } = require('stream');
5
- const encodingJapanese = require('encoding-japanese');
6
-
7
- // Helper to collect stream output
8
- const collectStream = stream =>
9
- new Promise((resolve, reject) => {
10
- const chunks = [];
11
- stream.on('data', chunk => chunks.push(chunk));
12
- stream.on('end', () => resolve(Buffer.concat(chunks)));
13
- stream.on('error', reject);
14
- });
15
-
16
- // ============================================
17
- // Constructor tests
18
- // ============================================
19
-
20
- module.exports['JPDecoder: constructor sets charset'] = test => {
21
- let decoder = new JPDecoder('iso-2022-jp');
22
- test.equal(decoder.charset, 'iso-2022-jp');
23
- test.deepEqual(decoder.chunks, []);
24
- test.equal(decoder.chunklen, 0);
25
- test.done();
26
- };
27
-
28
- module.exports['JPDecoder: is a Transform stream'] = test => {
29
- let decoder = new JPDecoder('iso-2022-jp');
30
- test.ok(decoder instanceof Transform);
31
- test.ok(typeof decoder.pipe === 'function');
32
- test.ok(typeof decoder.write === 'function');
33
- test.done();
34
- };
35
-
36
- // ============================================
37
- // _transform tests
38
- // ============================================
39
-
40
- module.exports['JPDecoder: _transform accumulates buffer chunks'] = test => {
41
- let decoder = new JPDecoder('iso-2022-jp');
42
- let chunk1 = Buffer.from('hello');
43
- let chunk2 = Buffer.from('world');
44
-
45
- decoder._transform(chunk1, 'buffer', () => {
46
- test.equal(decoder.chunks.length, 1);
47
- test.equal(decoder.chunklen, 5);
48
-
49
- decoder._transform(chunk2, 'buffer', () => {
50
- test.equal(decoder.chunks.length, 2);
51
- test.equal(decoder.chunklen, 10);
52
- test.done();
53
- });
54
- });
55
- };
56
-
57
- module.exports['JPDecoder: _transform converts string to buffer'] = test => {
58
- let decoder = new JPDecoder('iso-2022-jp');
59
- let stringChunk = 'hello';
60
-
61
- decoder._transform(stringChunk, 'utf8', () => {
62
- test.equal(decoder.chunks.length, 1);
63
- test.ok(Buffer.isBuffer(decoder.chunks[0]));
64
- test.equal(decoder.chunks[0].toString(), 'hello');
65
- test.done();
66
- });
67
- };
68
-
69
- // ============================================
70
- // _flush tests
71
- // ============================================
72
-
73
- module.exports['JPDecoder: _flush outputs accumulated data'] = async test => {
74
- let decoder = new JPDecoder('utf-8');
75
- let output = collectStream(decoder);
76
-
77
- decoder.write(Buffer.from('hello '));
78
- decoder.write(Buffer.from('world'));
79
- decoder.end();
80
-
81
- let result = await output;
82
- test.equal(result.toString(), 'hello world');
83
- test.done();
84
- };
85
-
86
- module.exports['JPDecoder: _flush converts ISO-2022-JP to Unicode'] = async test => {
87
- let decoder = new JPDecoder('iso-2022-jp');
88
- let output = collectStream(decoder);
89
-
90
- // ISO-2022-JP encoded Japanese text for "nihongo" (Japanese)
91
- // ESC $ B sequence switches to JIS X 0208, ESC ( B switches back to ASCII
92
- let iso2022jp = Buffer.from([
93
- 0x1b,
94
- 0x24,
95
- 0x42, // ESC $ B - switch to JIS X 0208
96
- 0x46,
97
- 0x7c, // ni
98
- 0x4b,
99
- 0x5c, // hon
100
- 0x38,
101
- 0x6c, // go
102
- 0x1b,
103
- 0x28,
104
- 0x42 // ESC ( B - switch back to ASCII
105
- ]);
106
-
107
- decoder.write(iso2022jp);
108
- decoder.end();
109
-
110
- let result = await output;
111
- test.ok(result.length > 0);
112
- test.done();
113
- };
114
-
115
- module.exports['JPDecoder: _flush handles conversion errors gracefully'] = async test => {
116
- // Use an invalid/unknown charset to trigger error path
117
- let decoder = new JPDecoder('invalid-charset-xyz');
118
- let output = collectStream(decoder);
119
-
120
- let input = Buffer.from('test data');
121
- decoder.write(input);
122
- decoder.end();
123
-
124
- let result = await output;
125
- // On error, should return original input
126
- test.equal(result.toString(), 'test data');
127
- test.done();
128
- };
129
-
130
- module.exports['JPDecoder: _flush falls back to raw input when convert throws'] = async test => {
131
- // encoding-japanese rarely throws on its own, so force it: the module object
132
- // is shared by reference with the copy held inside jp-decoder.js, so patching
133
- // .convert here affects the decoder's call too.
134
- let originalConvert = encodingJapanese.convert;
135
- encodingJapanese.convert = () => {
136
- throw new Error('forced conversion failure');
137
- };
138
-
139
- try {
140
- let decoder = new JPDecoder('iso-2022-jp');
141
- let output = collectStream(decoder);
142
-
143
- let input = Buffer.from('raw bytes preserved');
144
- decoder.write(input);
145
- decoder.end();
146
-
147
- let result = await output;
148
- // On a thrown error the catch block pushes the original, untouched input
149
- test.equal(result.toString(), 'raw bytes preserved');
150
- } finally {
151
- encodingJapanese.convert = originalConvert;
152
- }
153
- test.done();
154
- };
155
-
156
- module.exports['JPDecoder: _flush converts non-string convert output to buffer'] = async test => {
157
- // When convert returns a non-string (e.g. an array of code units), it must be
158
- // pushed as-is without the Buffer.from(string) branch.
159
- let originalConvert = encodingJapanese.convert;
160
- encodingJapanese.convert = () => Buffer.from('converted buffer');
161
-
162
- try {
163
- let decoder = new JPDecoder('iso-2022-jp');
164
- let output = collectStream(decoder);
165
-
166
- decoder.write(Buffer.from('whatever'));
167
- decoder.end();
168
-
169
- let result = await output;
170
- test.equal(result.toString(), 'converted buffer');
171
- } finally {
172
- encodingJapanese.convert = originalConvert;
173
- }
174
- test.done();
175
- };
176
-
177
- module.exports['JPDecoder: _flush handles empty input'] = async test => {
178
- let decoder = new JPDecoder('iso-2022-jp');
179
- let output = collectStream(decoder);
180
-
181
- decoder.end();
182
-
183
- let result = await output;
184
- test.equal(result.length, 0);
185
- test.done();
186
- };
187
-
188
- // ============================================
189
- // Integration tests
190
- // ============================================
191
-
192
- module.exports['JPDecoder: works with pipe'] = async test => {
193
- let source = new PassThrough();
194
- let decoder = new JPDecoder('utf-8');
195
- let output = collectStream(source.pipe(decoder));
196
-
197
- source.write('hello ');
198
- source.write('world');
199
- source.end();
200
-
201
- let result = await output;
202
- test.equal(result.toString(), 'hello world');
203
- test.done();
204
- };
205
-
206
- module.exports['JPDecoder: handles multiple small chunks'] = async test => {
207
- let decoder = new JPDecoder('utf-8');
208
- let output = collectStream(decoder);
209
-
210
- // Write character by character
211
- 'hello'.split('').forEach(char => decoder.write(char));
212
- decoder.end();
213
-
214
- let result = await output;
215
- test.equal(result.toString(), 'hello');
216
- test.done();
217
- };
218
-
219
- module.exports['JPDecoder: handles Shift_JIS charset'] = async test => {
220
- let decoder = new JPDecoder('shift_jis');
221
- let output = collectStream(decoder);
222
-
223
- // Shift_JIS encoded "test" in katakana (tesuto)
224
- let shiftJis = Buffer.from([0x83, 0x65, 0x83, 0x58, 0x83, 0x67]);
225
-
226
- decoder.write(shiftJis);
227
- decoder.end();
228
-
229
- let result = await output;
230
- test.ok(result.length > 0);
231
- test.done();
232
- };
233
-
234
- module.exports['JPDecoder: handles EUC-JP charset'] = async test => {
235
- let decoder = new JPDecoder('euc-jp');
236
- let output = collectStream(decoder);
237
-
238
- // EUC-JP encoded Japanese character
239
- let eucJp = Buffer.from([0xc6, 0xfc, 0xcb, 0xdc]); // nihon
240
-
241
- decoder.write(eucJp);
242
- decoder.end();
243
-
244
- let result = await output;
245
- test.ok(result.length > 0);
246
- test.done();
247
- };
248
-
249
- // ============================================
250
- // maxBytes buffering bound
251
- // ============================================
252
-
253
- module.exports['JPDecoder: buffers at most maxBytes'] = test => {
254
- // The whole-input buffering must honor the caller's download limit: without the
255
- // bound a server could defeat maxBytes just by labelling a part with a Japanese
256
- // charset, since nothing is emitted (and limited) until _flush
257
- let decoder = new JPDecoder('iso-2022-jp', 8);
258
- decoder.resume();
259
-
260
- decoder.write(Buffer.from('hello '));
261
- decoder.write(Buffer.from('world'));
262
-
263
- test.equal(decoder.chunklen, 8);
264
- test.equal(Buffer.concat(decoder.chunks).toString(), 'hello wo');
265
- decoder.end();
266
- test.done();
267
- };
268
-
269
- module.exports['JPDecoder: a fractional maxBytes still bounds the decoder'] = test => {
270
- // A computed limit (a percentage of a budget, say) is not an integer, and silently turning
271
- // that into "no limit at all" is exactly the exposure the bound exists to close
272
- const decoder = new JPDecoder('sjis', 512.5);
273
- test.equal(decoder.maxBytes, 512, 'a fractional bound is floored to a whole byte count');
274
- decoder.resume();
275
- decoder.write(Buffer.alloc(1024, 0x61));
276
- test.equal(decoder.chunklen, 512, 'nothing beyond the bound is buffered');
277
- test.equal(decoder.limited, true, 'the decoder reports that it is full');
278
- decoder.end();
279
- test.done();
280
- };
281
-
282
- module.exports['JPDecoder: a non-numeric maxBytes leaves the decoder unbounded'] = test => {
283
- test.equal(new JPDecoder('sjis').maxBytes, Infinity);
284
- test.equal(new JPDecoder('sjis', Infinity).maxBytes, Infinity);
285
- test.equal(new JPDecoder('sjis', 'lots').maxBytes, Infinity);
286
- test.equal(new JPDecoder('sjis', -1).maxBytes, Infinity);
287
- test.equal(new JPDecoder('sjis').limited, false);
288
- test.done();
289
- };
290
-
291
- module.exports['JPDecoder: reports limited once the bound is reached'] = test => {
292
- // The decoder emits nothing until _flush, so a limiter downstream cannot tell that it is
293
- // full - the download loop reads this flag instead to stop pulling from the server
294
- const decoder = new JPDecoder('sjis', 10);
295
- decoder.resume();
296
- test.equal(decoder.limited, false);
297
- decoder.write(Buffer.alloc(4, 0x61));
298
- test.equal(decoder.limited, false, 'not full yet');
299
- decoder.write(Buffer.alloc(6, 0x61));
300
- test.equal(decoder.limited, true);
301
- test.equal(decoder.chunklen, 10);
302
- decoder.end();
303
- test.done();
304
- };
@@ -1,299 +0,0 @@
1
- 'use strict';
2
-
3
- const { LimitedPassthrough } = require('../lib/limited-passthrough');
4
- const { PassThrough, Transform } = require('stream');
5
-
6
- // Helper to collect stream output
7
- const collectStream = stream =>
8
- new Promise((resolve, reject) => {
9
- const chunks = [];
10
- stream.on('data', chunk => chunks.push(chunk));
11
- stream.on('end', () => resolve(Buffer.concat(chunks)));
12
- stream.on('error', reject);
13
- });
14
-
15
- // ============================================
16
- // Constructor tests
17
- // ============================================
18
-
19
- module.exports['LimitedPassthrough: constructor with options'] = test => {
20
- let stream = new LimitedPassthrough({ maxBytes: 100 });
21
- test.equal(stream.maxBytes, 100);
22
- test.equal(stream.processed, 0);
23
- test.equal(stream.limited, false);
24
- test.done();
25
- };
26
-
27
- module.exports['LimitedPassthrough: constructor with no options'] = test => {
28
- let stream = new LimitedPassthrough();
29
- test.equal(stream.maxBytes, Infinity);
30
- test.equal(stream.processed, 0);
31
- test.equal(stream.limited, false);
32
- test.done();
33
- };
34
-
35
- module.exports['LimitedPassthrough: constructor with null options'] = test => {
36
- let stream = new LimitedPassthrough(null);
37
- test.equal(stream.maxBytes, Infinity);
38
- test.done();
39
- };
40
-
41
- module.exports['LimitedPassthrough: is a Transform stream'] = test => {
42
- let stream = new LimitedPassthrough();
43
- test.ok(stream instanceof Transform);
44
- test.ok(typeof stream.pipe === 'function');
45
- test.ok(typeof stream.write === 'function');
46
- test.done();
47
- };
48
-
49
- // ============================================
50
- // _transform tests - no limit
51
- // ============================================
52
-
53
- module.exports['LimitedPassthrough: passes all data when no limit'] = async test => {
54
- let stream = new LimitedPassthrough();
55
- let output = collectStream(stream);
56
-
57
- stream.write(Buffer.from('hello '));
58
- stream.write(Buffer.from('world'));
59
- stream.end();
60
-
61
- let result = await output;
62
- test.equal(result.toString(), 'hello world');
63
- test.equal(stream.processed, 11);
64
- test.equal(stream.limited, false);
65
- test.done();
66
- };
67
-
68
- // ============================================
69
- // _transform tests - with limit
70
- // ============================================
71
-
72
- module.exports['LimitedPassthrough: limits output to maxBytes'] = async test => {
73
- let stream = new LimitedPassthrough({ maxBytes: 5 });
74
- let output = collectStream(stream);
75
-
76
- stream.write(Buffer.from('hello world'));
77
- stream.end();
78
-
79
- let result = await output;
80
- test.equal(result.toString(), 'hello');
81
- test.equal(stream.processed, 5);
82
- test.equal(stream.limited, true);
83
- test.done();
84
- };
85
-
86
- module.exports['LimitedPassthrough: limits across multiple chunks'] = async test => {
87
- let stream = new LimitedPassthrough({ maxBytes: 8 });
88
- let output = collectStream(stream);
89
-
90
- stream.write(Buffer.from('hello ')); // 6 bytes
91
- stream.write(Buffer.from('world')); // 5 bytes, only 2 should pass
92
- stream.end();
93
-
94
- let result = await output;
95
- test.equal(result.toString(), 'hello wo');
96
- test.equal(stream.processed, 8);
97
- test.equal(stream.limited, true);
98
- test.done();
99
- };
100
-
101
- module.exports['LimitedPassthrough: drops data after limit reached'] = async test => {
102
- let stream = new LimitedPassthrough({ maxBytes: 5 });
103
- let output = collectStream(stream);
104
-
105
- stream.write(Buffer.from('hello')); // exactly 5 bytes
106
- stream.write(Buffer.from(' world')); // should be dropped
107
- stream.write(Buffer.from('!')); // should be dropped
108
- stream.end();
109
-
110
- let result = await output;
111
- test.equal(result.toString(), 'hello');
112
- test.equal(stream.limited, true);
113
- test.done();
114
- };
115
-
116
- module.exports['LimitedPassthrough: handles exact boundary'] = async test => {
117
- let stream = new LimitedPassthrough({ maxBytes: 5 });
118
- let output = collectStream(stream);
119
-
120
- stream.write(Buffer.from('hello')); // exactly 5 bytes
121
- stream.end();
122
-
123
- let result = await output;
124
- test.equal(result.toString(), 'hello');
125
- test.equal(stream.processed, 5);
126
- test.equal(stream.limited, true);
127
- test.done();
128
- };
129
-
130
- module.exports['LimitedPassthrough: zero maxBytes treated as Infinity'] = async test => {
131
- // Note: maxBytes: 0 is falsy, so constructor uses Infinity instead
132
- let stream = new LimitedPassthrough({ maxBytes: 0 });
133
- test.equal(stream.maxBytes, Infinity);
134
-
135
- let output = collectStream(stream);
136
- stream.write(Buffer.from('hello'));
137
- stream.end();
138
-
139
- let result = await output;
140
- // All data passes through since 0 is treated as Infinity
141
- test.equal(result.toString(), 'hello');
142
- test.done();
143
- };
144
-
145
- module.exports['LimitedPassthrough: drops chunk when no remaining budget but not yet limited'] = async test => {
146
- // Force the remainingBytes<1 guard (distinct from the `this.limited` fast path):
147
- // override maxBytes to 0 after construction so the very first chunk hits it
148
- // while `limited` is still false.
149
- let stream = new LimitedPassthrough();
150
- stream.maxBytes = 0;
151
- let output = collectStream(stream);
152
-
153
- stream.write(Buffer.from('dropped'));
154
- stream.end();
155
-
156
- let result = await output;
157
- test.equal(result.length, 0, 'chunk dropped when no budget remains');
158
- test.equal(stream.limited, false, 'limited flag never set via this path');
159
- test.done();
160
- };
161
-
162
- module.exports['LimitedPassthrough: handles limit of 1 byte'] = async test => {
163
- let stream = new LimitedPassthrough({ maxBytes: 1 });
164
- let output = collectStream(stream);
165
-
166
- stream.write(Buffer.from('hello'));
167
- stream.end();
168
-
169
- let result = await output;
170
- test.equal(result.toString(), 'h');
171
- test.equal(stream.processed, 1);
172
- test.equal(stream.limited, true);
173
- test.done();
174
- };
175
-
176
- // ============================================
177
- // Edge cases
178
- // ============================================
179
-
180
- module.exports['LimitedPassthrough: handles empty writes'] = async test => {
181
- let stream = new LimitedPassthrough({ maxBytes: 10 });
182
- let output = collectStream(stream);
183
-
184
- stream.write(Buffer.from(''));
185
- stream.write(Buffer.from('hello'));
186
- stream.write(Buffer.from(''));
187
- stream.end();
188
-
189
- let result = await output;
190
- test.equal(result.toString(), 'hello');
191
- test.equal(stream.processed, 5);
192
- test.done();
193
- };
194
-
195
- module.exports['LimitedPassthrough: handles no writes'] = async test => {
196
- let stream = new LimitedPassthrough({ maxBytes: 10 });
197
- let output = collectStream(stream);
198
-
199
- stream.end();
200
-
201
- let result = await output;
202
- test.equal(result.length, 0);
203
- test.equal(stream.processed, 0);
204
- test.equal(stream.limited, false);
205
- test.done();
206
- };
207
-
208
- module.exports['LimitedPassthrough: tracks processed bytes correctly'] = async test => {
209
- let stream = new LimitedPassthrough({ maxBytes: 100 });
210
- let output = collectStream(stream);
211
-
212
- stream.write(Buffer.from('12345')); // 5 bytes
213
- test.equal(stream.processed, 5);
214
-
215
- stream.write(Buffer.from('67890')); // 5 more bytes
216
- test.equal(stream.processed, 10);
217
-
218
- stream.end();
219
-
220
- await output;
221
- test.equal(stream.processed, 10);
222
- test.done();
223
- };
224
-
225
- // ============================================
226
- // Integration tests
227
- // ============================================
228
-
229
- module.exports['LimitedPassthrough: works with pipe'] = async test => {
230
- let source = new PassThrough();
231
- let limiter = new LimitedPassthrough({ maxBytes: 10 });
232
- let output = collectStream(source.pipe(limiter));
233
-
234
- source.write('hello ');
235
- source.write('wonderful ');
236
- source.write('world');
237
- source.end();
238
-
239
- let result = await output;
240
- test.equal(result.toString(), 'hello wond');
241
- test.equal(limiter.limited, true);
242
- test.done();
243
- };
244
-
245
- module.exports['LimitedPassthrough: handles large data'] = async test => {
246
- let stream = new LimitedPassthrough({ maxBytes: 1000 });
247
- let output = collectStream(stream);
248
-
249
- // Write 100 bytes at a time
250
- for (let i = 0; i < 20; i++) {
251
- stream.write(Buffer.alloc(100, 'x'));
252
- }
253
- stream.end();
254
-
255
- let result = await output;
256
- test.equal(result.length, 1000);
257
- test.equal(stream.limited, true);
258
- test.done();
259
- };
260
-
261
- module.exports['LimitedPassthrough: single byte writes'] = async test => {
262
- let stream = new LimitedPassthrough({ maxBytes: 3 });
263
- let output = collectStream(stream);
264
-
265
- stream.write(Buffer.from('a'));
266
- stream.write(Buffer.from('b'));
267
- stream.write(Buffer.from('c'));
268
- stream.write(Buffer.from('d')); // should be dropped
269
- stream.write(Buffer.from('e')); // should be dropped
270
- stream.end();
271
-
272
- let result = await output;
273
- test.equal(result.toString(), 'abc');
274
- test.done();
275
- };
276
-
277
- module.exports['LimitedPassthrough: a fractional maxBytes is floored so limited is reachable'] = async test => {
278
- // With a fractional bound `processed` can only ever reach its floor, so `limited` would
279
- // never flip - and the download loop polls exactly that flag to stop pulling
280
- let stream = new LimitedPassthrough({ maxBytes: 512.5 });
281
- test.equal(stream.maxBytes, 512);
282
-
283
- let chunks = [];
284
- stream.on('data', chunk => chunks.push(chunk));
285
- stream.write(Buffer.alloc(1024, 0x61));
286
- stream.end();
287
- await new Promise(resolve => stream.on('end', resolve));
288
-
289
- test.equal(Buffer.concat(chunks).length, 512);
290
- test.equal(stream.limited, true, 'the limiter must report that it is full');
291
- test.done();
292
- };
293
-
294
- module.exports['LimitedPassthrough: a numeric-string maxBytes is honored'] = test => {
295
- test.equal(new LimitedPassthrough({ maxBytes: '100' }).maxBytes, 100);
296
- test.equal(new LimitedPassthrough({ maxBytes: 'lots' }).maxBytes, Infinity);
297
- test.equal(new LimitedPassthrough({ maxBytes: -5 }).maxBytes, Infinity);
298
- test.done();
299
- };