imapflow 1.7.7 → 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.
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 +518 -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 +3949 -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} +386 -516
  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 +56 -121
  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 +785 -1802
  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 -57
  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 -873
  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 -593
@@ -0,0 +1,673 @@
1
+ "use strict";
2
+ /* eslint new-cap: 0 */
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.TokenParser = void 0;
8
+ const imap_formal_syntax_js_1 = __importDefault(require("./imap-formal-syntax.js"));
9
+ const limits_js_1 = require("./limits.js");
10
+ const STATE_ATOM = 0x001;
11
+ const STATE_LITERAL = 0x002;
12
+ const STATE_NORMAL = 0x003;
13
+ const STATE_PARTIAL = 0x004;
14
+ const STATE_SEQUENCE = 0x005;
15
+ const STATE_STRING = 0x006;
16
+ const RE_DIGITS = /^\d+$/;
17
+ const RE_SINGLE_DIGIT = /^\d$/;
18
+ // Prevents stack overflow from maliciously crafted deeply-nested IMAP input (e.g., (((((...))))))
19
+ const MAX_NODE_DEPTH = 25;
20
+ /**
21
+ * Tokenizes an IMAP attribute string into a tree of typed nodes.
22
+ * Handles all IMAP data types: atoms, quoted strings, literals (including literal8),
23
+ * sequences, lists (parenthesized groups), sections (bracketed groups), and partial ranges.
24
+ * Enforces a maximum nesting depth of {@link MAX_NODE_DEPTH} to prevent stack overflow
25
+ * from malicious input.
26
+ */
27
+ class TokenParser {
28
+ /**
29
+ * Creates a new TokenParser.
30
+ *
31
+ * @param parent - The parent ParserInstance that owns this token parser. Used to access the parsed command for context-sensitive parsing.
32
+ * @param startPos - The starting position offset in the original input, used for error reporting.
33
+ * @param str - The attribute string to tokenize.
34
+ * @param options - Parser options.
35
+ * @param options.literalPlus - Whether the LITERAL+ extension is in use.
36
+ * @param options.literals - Pre-parsed literal values from the input stream.
37
+ * @param options.maxLiteralSize - Maximum size (in bytes) of a literal parsed inline
38
+ * from the input, i.e. when no pre-parsed literal buffers were supplied. Defaults to 1GB.
39
+ */
40
+ constructor(parent, startPos, str, options) {
41
+ this.str = (str || '').toString();
42
+ this.options = options || {};
43
+ this.parent = parent;
44
+ // Same normalization and default as the streaming parser, so a direct user of this parser
45
+ // gets the same bound (an explicit 0 means "reject any non-empty inline literal").
46
+ this.maxLiteralSize = (0, limits_js_1.normalizeLimit)(this.options.maxLiteralSize, limits_js_1.MAX_LITERAL_SIZE);
47
+ this.tree = this.currentNode = this.createNode();
48
+ this.pos = startPos || 0;
49
+ this.currentNode.type = 'TREE';
50
+ this.state = STATE_NORMAL;
51
+ }
52
+ /**
53
+ * Processes the input string and returns the parsed attributes as a flat array of typed objects.
54
+ * Each attribute is an object with a `type` (e.g., "ATOM", "STRING", "LITERAL", "SEQUENCE")
55
+ * and a `value` property. Lists are represented as nested arrays. Sections and partials are
56
+ * attached as properties on the preceding attribute object.
57
+ *
58
+ * @returns A promise that resolves to an array of parsed attribute objects and nested arrays.
59
+ * @throws {Error} If the input contains syntax errors or unclosed nodes.
60
+ */
61
+ async getAttributes() {
62
+ await this.processString();
63
+ const attributes = [];
64
+ let branch = attributes;
65
+ let walk = async (node) => {
66
+ let curBranch = branch;
67
+ let elm;
68
+ let partial;
69
+ if (!node.isClosed && node.type === 'SEQUENCE' && node.value === '*') {
70
+ node.isClosed = true;
71
+ node.type = 'ATOM';
72
+ }
73
+ // If the node was never closed, throw it
74
+ if (!node.isClosed) {
75
+ let error = new Error(`Unexpected end of input at position ${this.pos + this.str.length - 1} [E9]`);
76
+ error.code = 'ParserError9';
77
+ error.parserContext = { input: this.str, pos: this.pos + this.str.length - 1 };
78
+ throw error;
79
+ }
80
+ let type = (node.type || '').toString().toUpperCase();
81
+ switch (type) {
82
+ case 'LITERAL':
83
+ case 'STRING':
84
+ case 'SEQUENCE':
85
+ elm = {
86
+ type: node.type.toUpperCase(),
87
+ value: node.value
88
+ };
89
+ branch.push(elm);
90
+ break;
91
+ case 'ATOM':
92
+ if (node.value.toUpperCase() === 'NIL') {
93
+ branch.push(null);
94
+ break;
95
+ }
96
+ elm = {
97
+ type: node.type.toUpperCase(),
98
+ value: node.value
99
+ };
100
+ branch.push(elm);
101
+ break;
102
+ case 'SECTION':
103
+ branch = branch[branch.length - 1].section = [];
104
+ break;
105
+ case 'LIST':
106
+ elm = [];
107
+ branch.push(elm);
108
+ branch = elm;
109
+ break;
110
+ case 'PARTIAL':
111
+ partial = node.value.split('.').map(Number);
112
+ branch[branch.length - 1].partial = partial;
113
+ break;
114
+ }
115
+ for (let childNode of node.childNodes) {
116
+ await walk(childNode);
117
+ }
118
+ branch = curBranch;
119
+ };
120
+ await walk(this.tree);
121
+ return attributes;
122
+ }
123
+ /**
124
+ * Creates a new node in the parse tree. Each node represents a token or structural
125
+ * element (e.g., atom, string, literal, list, section, partial). The node is automatically
126
+ * appended to the parent's childNodes array if a parent is provided.
127
+ *
128
+ * @param parentNode - The parent node to attach this node to. If omitted, creates a root node.
129
+ * @param startPos - The starting position of this node in the original input string.
130
+ * @returns The newly created node with childNodes, type, value, and isClosed properties.
131
+ * @throws {Error} If the nesting depth exceeds MAX_NODE_DEPTH.
132
+ */
133
+ createNode(parentNode, startPos) {
134
+ let node = {
135
+ childNodes: [],
136
+ type: false,
137
+ value: '',
138
+ isClosed: true,
139
+ depth: 0
140
+ };
141
+ if (parentNode) {
142
+ node.parentNode = parentNode;
143
+ node.depth = parentNode.depth + 1;
144
+ }
145
+ else {
146
+ node.depth = 0;
147
+ }
148
+ if (node.depth > MAX_NODE_DEPTH) {
149
+ let error = new Error('Too much nesting in IMAP string');
150
+ error.code = 'MAX_IMAP_NESTING_REACHED';
151
+ error._imapStr = this.str;
152
+ throw error;
153
+ }
154
+ if (typeof startPos === 'number') {
155
+ node.startPos = startPos;
156
+ }
157
+ if (parentNode) {
158
+ parentNode.childNodes.push(node);
159
+ }
160
+ return node;
161
+ }
162
+ /**
163
+ * Processes the entire input string character by character using a state machine.
164
+ * Transitions between states (NORMAL, ATOM, STRING, LITERAL, SEQUENCE, PARTIAL, TEXT)
165
+ * based on the current character and builds the parse tree. This is the main parsing
166
+ * loop that drives the tokenization.
167
+ *
168
+ * @throws {Error} If the input contains unexpected characters, unclosed structures, or other syntax errors.
169
+ */
170
+ async processString() {
171
+ let chr, i, len;
172
+ const checkSP = () => {
173
+ // jump to the next non whitespace pos
174
+ while (this.str.charAt(i + 1) === ' ') {
175
+ i++;
176
+ }
177
+ };
178
+ // Any ATOM supported char starts a new Atom sequence, otherwise throw an error
179
+ // Allow \ as the first char for atom to support system flags
180
+ // Allow % to support LIST '' %
181
+ // Allow 8bit characters (presumably unicode)
182
+ // Shared by the default branch and by a '[' that is not a response code section, which
183
+ // used to fall through into the default branch
184
+ const startAtom = () => {
185
+ if (!imap_formal_syntax_js_1.default['ATOM-CHAR']().includes(chr) && chr !== '\\' && chr !== '%' && chr.charCodeAt(0) < 0x80) {
186
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E13: ${JSON.stringify(chr)}]`);
187
+ error.code = 'ParserError13';
188
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
189
+ throw error;
190
+ }
191
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
192
+ this.currentNode.type = 'ATOM';
193
+ this.currentNode.value = chr;
194
+ this.state = STATE_ATOM;
195
+ };
196
+ for (i = 0, len = this.str.length; i < len; i++) {
197
+ chr = this.str.charAt(i);
198
+ switch (this.state) {
199
+ case STATE_NORMAL:
200
+ switch (chr) {
201
+ // DQUOTE starts a new string
202
+ case '"':
203
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
204
+ this.currentNode.type = 'string';
205
+ this.state = STATE_STRING;
206
+ this.currentNode.isClosed = false;
207
+ break;
208
+ // ( starts a new list
209
+ case '(':
210
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
211
+ this.currentNode.type = 'LIST';
212
+ this.currentNode.isClosed = false;
213
+ break;
214
+ // ) closes a list
215
+ case ')':
216
+ if (this.currentNode.type !== 'LIST') {
217
+ let error = new Error(`Unexpected list terminator ) at position ${this.pos + i} [E10]`);
218
+ error.code = 'ParserError10';
219
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
220
+ throw error;
221
+ }
222
+ this.currentNode.isClosed = true;
223
+ this.currentNode.endPos = this.pos + i;
224
+ this.currentNode = this.currentNode.parentNode;
225
+ checkSP();
226
+ break;
227
+ // ] closes section group
228
+ case ']':
229
+ if (this.currentNode.type !== 'SECTION') {
230
+ let error = new Error(`Unexpected section terminator ] at position ${this.pos + i} [E11]`);
231
+ error.code = 'ParserError11';
232
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
233
+ throw error;
234
+ }
235
+ this.currentNode.isClosed = true;
236
+ this.currentNode.endPos = this.pos + i;
237
+ this.currentNode = this.currentNode.parentNode;
238
+ checkSP();
239
+ break;
240
+ // < starts a new partial byte range (e.g., BODY[]<0.1024>)
241
+ case '<':
242
+ // '<' is only a partial range marker when it immediately follows ']',
243
+ // which occurs in BODY[section]<origin.length> responses.
244
+ // In all other contexts, '<' is treated as the start of an ATOM.
245
+ if (this.str.charAt(i - 1) !== ']') {
246
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
247
+ this.currentNode.type = 'ATOM';
248
+ this.currentNode.value = chr;
249
+ this.state = STATE_ATOM;
250
+ }
251
+ else {
252
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
253
+ this.currentNode.type = 'PARTIAL';
254
+ this.state = STATE_PARTIAL;
255
+ this.currentNode.isClosed = false;
256
+ }
257
+ break;
258
+ // literal8 (RFC 3516): uses ~{size} prefix instead of {size}
259
+ // literal8 allows binary data containing NUL bytes, unlike regular literals
260
+ case '~': {
261
+ let nextChr = this.str.charAt(i + 1);
262
+ if (nextChr !== '{') {
263
+ if (imap_formal_syntax_js_1.default['ATOM-CHAR']().includes(nextChr)) {
264
+ // '~' not followed by '{' but followed by an ATOM char: treat as ATOM
265
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
266
+ this.currentNode.type = 'ATOM';
267
+ this.currentNode.value = chr;
268
+ this.state = STATE_ATOM;
269
+ break;
270
+ }
271
+ let error = new Error(`Unexpected literal8 marker at position ${this.pos + i} [E12]`);
272
+ error.code = 'ParserError12';
273
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
274
+ throw error;
275
+ }
276
+ // Mark the next literal as literal8 type; consumed when '{' is encountered
277
+ this.expectedLiteralType = 'literal8';
278
+ break;
279
+ }
280
+ // { starts a new literal (regular {size}\r\n or literal8 ~{size}\r\n)
281
+ case '{':
282
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
283
+ this.currentNode.type = 'LITERAL';
284
+ // Use literal8 type if '~' was seen immediately before, otherwise standard literal
285
+ this.currentNode.literalType = this.expectedLiteralType || 'literal';
286
+ this.expectedLiteralType = false;
287
+ this.state = STATE_LITERAL;
288
+ this.currentNode.isClosed = false;
289
+ break;
290
+ // * starts a new sequence
291
+ case '*':
292
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
293
+ this.currentNode.type = 'SEQUENCE';
294
+ this.currentNode.value = chr;
295
+ this.currentNode.isClosed = false;
296
+ this.state = STATE_SEQUENCE;
297
+ break;
298
+ // normally a space should never occur
299
+ case ' ':
300
+ // just ignore
301
+ break;
302
+ // [ starts section
303
+ case '[':
304
+ // If it is the *first* element after response command, then process as a response argument list
305
+ // Status responses (OK/NO/BAD/BYE/PREAUTH) use [code] for response codes
306
+ if (['OK', 'NO', 'BAD', 'BYE', 'PREAUTH'].includes(this.parent.command.toUpperCase()) &&
307
+ this.currentNode === this.tree) {
308
+ this.currentNode.endPos = this.pos + i;
309
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
310
+ this.currentNode.type = 'ATOM';
311
+ this.currentNode = this.createNode(this.currentNode, this.pos + i);
312
+ this.currentNode.type = 'SECTION';
313
+ this.currentNode.isClosed = false;
314
+ this.state = STATE_NORMAL;
315
+ // RFC 2221 REFERRAL special case: the payload is an RFC 2192/RFC 5092
316
+ // IMAP URL (e.g., imap://user@host/mailbox) which contains characters
317
+ // that would break normal ATOM parsing (colons, slashes, etc.).
318
+ // We handle this by consuming everything up to ']' as a single ATOM value.
319
+ if (this.str.substr(i + 1, 9).toUpperCase() === 'REFERRAL ') {
320
+ // create the REFERRAL atom
321
+ this.currentNode = this.createNode(this.currentNode, this.pos + i + 1);
322
+ this.currentNode.type = 'ATOM';
323
+ this.currentNode.endPos = this.pos + i + 8;
324
+ this.currentNode.value = 'REFERRAL';
325
+ this.currentNode = this.currentNode.parentNode;
326
+ // eat all the way through the ] to be the IMAPURL token.
327
+ this.currentNode = this.createNode(this.currentNode, this.pos + i + 10);
328
+ // just call this an ATOM, even though IMAPURL might be more correct
329
+ this.currentNode.type = 'ATOM';
330
+ // jump i to the ']'
331
+ i = this.str.indexOf(']', i + 10);
332
+ if (i < 0) {
333
+ // Malformed REFERRAL with no closing ']'. Consume the rest
334
+ // of the string (there is no ']' to exclude) instead of
335
+ // computing a negative-index substring, which would yield
336
+ // garbage.
337
+ i = this.str.length;
338
+ }
339
+ this.currentNode.endPos = this.pos + i - 1;
340
+ this.currentNode.value = this.str.substring(this.currentNode.startPos - this.pos, this.currentNode.endPos - this.pos + 1);
341
+ this.currentNode = this.currentNode.parentNode;
342
+ // close out the SECTION
343
+ this.currentNode.isClosed = true;
344
+ this.currentNode = this.currentNode.parentNode;
345
+ checkSP();
346
+ }
347
+ break;
348
+ }
349
+ // Not a response code section: handled like any other atom start
350
+ startAtom();
351
+ break;
352
+ default:
353
+ startAtom();
354
+ break;
355
+ }
356
+ break;
357
+ case STATE_ATOM:
358
+ // An atom is terminated by: space, closing delimiter of parent node,
359
+ // or encountering a '[' that starts a section for BODY/BINARY commands.
360
+ // space finishes an atom
361
+ if (chr === ' ') {
362
+ this.currentNode.endPos = this.pos + i - 1;
363
+ this.currentNode = this.currentNode.parentNode;
364
+ this.state = STATE_NORMAL;
365
+ break;
366
+ }
367
+ // ')' or ']' terminates the atom AND closes the enclosing LIST or SECTION
368
+ if (this.currentNode.parentNode &&
369
+ ((chr === ')' && this.currentNode.parentNode.type === 'LIST') || (chr === ']' && this.currentNode.parentNode.type === 'SECTION'))) {
370
+ this.currentNode.endPos = this.pos + i - 1;
371
+ this.currentNode = this.currentNode.parentNode;
372
+ this.currentNode.isClosed = true;
373
+ this.currentNode.endPos = this.pos + i;
374
+ this.currentNode = this.currentNode.parentNode;
375
+ this.state = STATE_NORMAL;
376
+ checkSP();
377
+ break;
378
+ }
379
+ // If the atom so far is all digits and we see ',' or ':', it is actually
380
+ // a sequence set (e.g., "1:5" or "1,3,5"), so reclassify and switch state
381
+ if ((chr === ',' || chr === ':') && RE_DIGITS.test(this.currentNode.value)) {
382
+ this.currentNode.type = 'SEQUENCE';
383
+ this.currentNode.isClosed = true;
384
+ this.state = STATE_SEQUENCE;
385
+ }
386
+ // [ starts a section group for this element
387
+ // Allowed only for selected elements, otherwise falls through to regular ATOM processing
388
+ if (chr === '[' && ['BODY', 'BODY.PEEK', 'BINARY', 'BINARY.PEEK'].includes(this.currentNode.value.toUpperCase())) {
389
+ this.currentNode.endPos = this.pos + i;
390
+ this.currentNode = this.createNode(this.currentNode.parentNode, this.pos + i);
391
+ this.currentNode.type = 'SECTION';
392
+ this.currentNode.isClosed = false;
393
+ this.state = STATE_NORMAL;
394
+ break;
395
+ }
396
+ // if the char is not ATOM compatible, throw. Allow \* as an exception
397
+ if (!imap_formal_syntax_js_1.default['ATOM-CHAR']().includes(chr) &&
398
+ chr.charCodeAt(0) < 0x80 && // allow 8bit (presumably unicode) bytes
399
+ chr !== ']' &&
400
+ !(chr === '*' && this.currentNode.value === '\\') &&
401
+ (!this.parent || !this.parent.command || !['NO', 'BAD', 'OK'].includes(this.parent.command))) {
402
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E16: ${JSON.stringify(chr)}]`);
403
+ error.code = 'ParserError16';
404
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
405
+ throw error;
406
+ }
407
+ else if (this.currentNode.value === '\\*') {
408
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E17: ${JSON.stringify(chr)}]`);
409
+ error.code = 'ParserError17';
410
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
411
+ throw error;
412
+ }
413
+ this.currentNode.value += chr;
414
+ break;
415
+ case STATE_STRING:
416
+ // DQUOTE ends the string sequence
417
+ if (chr === '"') {
418
+ this.currentNode.endPos = this.pos + i;
419
+ this.currentNode.isClosed = true;
420
+ this.currentNode = this.currentNode.parentNode;
421
+ this.state = STATE_NORMAL;
422
+ checkSP();
423
+ break;
424
+ }
425
+ // \ Escapes the following char
426
+ if (chr === '\\') {
427
+ i++;
428
+ if (i >= len) {
429
+ let error = new Error(`Unexpected end of input at position ${this.pos + i} [E18]`);
430
+ error.code = 'ParserError18';
431
+ error.parserContext = { input: this.str, pos: this.pos + i };
432
+ throw error;
433
+ }
434
+ chr = this.str.charAt(i);
435
+ }
436
+ this.currentNode.value += chr;
437
+ break;
438
+ case STATE_PARTIAL:
439
+ if (chr === '>') {
440
+ if (this.currentNode.value.at(-1) === '.') {
441
+ let error = new Error(`Unexpected end of partial at position ${this.pos + i} [E19]`);
442
+ error.code = 'ParserError19';
443
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
444
+ throw error;
445
+ }
446
+ this.currentNode.endPos = this.pos + i;
447
+ this.currentNode.isClosed = true;
448
+ this.currentNode = this.currentNode.parentNode;
449
+ this.state = STATE_NORMAL;
450
+ checkSP();
451
+ break;
452
+ }
453
+ if (chr === '.' && (this.currentNode.value === '' || this.currentNode.value.includes('.'))) {
454
+ let error = new Error(`Unexpected partial separator . at position ${this.pos + i} [E20]`);
455
+ error.code = 'ParserError20';
456
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
457
+ throw error;
458
+ }
459
+ if (!imap_formal_syntax_js_1.default.DIGIT().includes(chr) && chr !== '.') {
460
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E21: ${JSON.stringify(chr)}]`);
461
+ error.code = 'ParserError21';
462
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
463
+ throw error;
464
+ }
465
+ if ((this.currentNode.value === '0' || this.currentNode.value.endsWith('.0')) && chr !== '.') {
466
+ let error = new Error(`Invalid partial at position ${this.pos + i} [E22: ${JSON.stringify(chr)}]`);
467
+ error.code = 'ParserError22';
468
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
469
+ throw error;
470
+ }
471
+ this.currentNode.value += chr;
472
+ break;
473
+ case STATE_LITERAL:
474
+ if (this.currentNode.started) {
475
+ // only relevant if literals are not already parsed out from input
476
+ // Disabled NULL byte check
477
+ // See https://github.com/emailjs/emailjs-imap-handler/commit/f11b2822bedabe492236e8263afc630134a3c41c
478
+ /*
479
+ if (chr === '\u0000') {
480
+ throw new Error('Unexpected \\x00 at position ' + (this.pos + i));
481
+ }
482
+ */
483
+ this.currentNode.chBuffer[this.currentNode.chPos++] = chr.charCodeAt(0);
484
+ if (this.currentNode.chPos >= this.currentNode.literalLength) {
485
+ this.currentNode.endPos = this.pos + i;
486
+ this.currentNode.isClosed = true;
487
+ this.currentNode.value = this.currentNode.chBuffer.toString('binary');
488
+ this.currentNode.chBuffer = Buffer.alloc(0);
489
+ this.currentNode = this.currentNode.parentNode;
490
+ this.state = STATE_NORMAL;
491
+ checkSP();
492
+ }
493
+ break;
494
+ }
495
+ if (chr === '+' && this.options.literalPlus) {
496
+ this.currentNode.literalPlus = true;
497
+ break;
498
+ }
499
+ if (chr === '}') {
500
+ if (!('literalLength' in this.currentNode)) {
501
+ let error = new Error(`Unexpected literal prefix end char } at position ${this.pos + i} [E23]`);
502
+ error.code = 'ParserError23';
503
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
504
+ throw error;
505
+ }
506
+ if (this.str.charAt(i + 1) === '\n') {
507
+ i++;
508
+ }
509
+ else if (this.str.charAt(i + 1) === '\r' && this.str.charAt(i + 2) === '\n') {
510
+ i += 2;
511
+ }
512
+ else {
513
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E24: ${JSON.stringify(chr)}]`);
514
+ error.code = 'ParserError24';
515
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
516
+ throw error;
517
+ }
518
+ this.currentNode.literalLength = Number(this.currentNode.literalLength);
519
+ if (!this.currentNode.literalLength) {
520
+ // special case where literal content length is 0
521
+ // close the node right away, do not wait for additional input
522
+ if (this.options.literals && this.options.literals.length) {
523
+ // ImapStream queues a Buffer for every literal marker it
524
+ // extracts, including {0} - consume the queue entry so
525
+ // subsequent literals in the same response stay aligned
526
+ // with their markers instead of shifting by one
527
+ this.currentNode.value = this.options.literals.shift();
528
+ }
529
+ this.currentNode.endPos = this.pos + i;
530
+ this.currentNode.isClosed = true;
531
+ this.currentNode = this.currentNode.parentNode;
532
+ this.state = STATE_NORMAL;
533
+ checkSP();
534
+ }
535
+ else if (this.options.literals) {
536
+ // use the next precached literal values
537
+ this.currentNode.value = this.options.literals.shift();
538
+ // only APPEND arguments are kept as Buffers
539
+ /*
540
+ if ((this.parent.command || '').toString().toUpperCase() !== 'APPEND') {
541
+ this.currentNode.value = this.currentNode.value.toString('binary');
542
+ }
543
+ */
544
+ this.currentNode.endPos = this.pos + i + this.currentNode.value.length;
545
+ this.currentNode.started = false;
546
+ this.currentNode.isClosed = true;
547
+ this.currentNode = this.currentNode.parentNode;
548
+ this.state = STATE_NORMAL;
549
+ checkSP();
550
+ }
551
+ else {
552
+ // No pre-parsed literal buffers were supplied, so the literal is read
553
+ // inline from this input and its declared length decides an
554
+ // allocation. ImapStream always supplies buffers (and has already
555
+ // enforced its own cap), so this branch means the parser is being used
556
+ // directly and the declared length is untrusted: bound it before
557
+ // allocating anything.
558
+ // Two bounds apply: the configured maximum, and the bytes actually
559
+ // available here - an inline literal has to be present in the input
560
+ // being parsed, so a longer declaration can never be satisfied and
561
+ // must not reserve memory for itself.
562
+ let available = this.str.length - i - 1;
563
+ let literalLength = this.currentNode.literalLength;
564
+ if (literalLength > this.maxLiteralSize || literalLength > available) {
565
+ let overMax = literalLength > this.maxLiteralSize;
566
+ let error = (0, limits_js_1.createLiteralTooLargeError)(literalLength, overMax ? this.maxLiteralSize : available, overMax ? null : `the ${available} bytes available in the input`);
567
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
568
+ throw error;
569
+ }
570
+ this.currentNode.started = true;
571
+ // Allocate expected size buffer.
572
+ // Maybe should use allocUnsafe instead?
573
+ this.currentNode.chBuffer = Buffer.alloc(this.currentNode.literalLength);
574
+ this.currentNode.chPos = 0;
575
+ }
576
+ break;
577
+ }
578
+ if (!imap_formal_syntax_js_1.default.DIGIT().includes(chr)) {
579
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E25: ${JSON.stringify(chr)}]`);
580
+ error.code = 'ParserError25';
581
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
582
+ throw error;
583
+ }
584
+ if (this.currentNode.literalLength === '0') {
585
+ let error = new Error(`Invalid literal at position ${this.pos + i} [E26]`);
586
+ error.code = 'ParserError26';
587
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
588
+ throw error;
589
+ }
590
+ this.currentNode.literalLength = (this.currentNode.literalLength || '') + chr;
591
+ break;
592
+ case STATE_SEQUENCE:
593
+ // space finishes the sequence set
594
+ if (chr === ' ') {
595
+ if (!RE_SINGLE_DIGIT.test(this.currentNode.value.at(-1)) && this.currentNode.value.at(-1) !== '*') {
596
+ let error = new Error(`Unexpected whitespace at position ${this.pos + i} [E27]`);
597
+ error.code = 'ParserError27';
598
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
599
+ throw error;
600
+ }
601
+ if (this.currentNode.value !== '*' &&
602
+ this.currentNode.value.at(-1) === '*' &&
603
+ this.currentNode.value.at(-2) !== ':') {
604
+ let error = new Error(`Unexpected whitespace at position ${this.pos + i} [E28]`);
605
+ error.code = 'ParserError28';
606
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
607
+ throw error;
608
+ }
609
+ this.currentNode.isClosed = true;
610
+ this.currentNode.endPos = this.pos + i - 1;
611
+ this.currentNode = this.currentNode.parentNode;
612
+ this.state = STATE_NORMAL;
613
+ break;
614
+ }
615
+ else if (this.currentNode.parentNode && chr === ']' && this.currentNode.parentNode.type === 'SECTION') {
616
+ this.currentNode.endPos = this.pos + i - 1;
617
+ this.currentNode = this.currentNode.parentNode;
618
+ this.currentNode.isClosed = true;
619
+ this.currentNode.endPos = this.pos + i;
620
+ this.currentNode = this.currentNode.parentNode;
621
+ this.state = STATE_NORMAL;
622
+ checkSP();
623
+ break;
624
+ }
625
+ if (chr === ':') {
626
+ if (!RE_SINGLE_DIGIT.test(this.currentNode.value.at(-1)) && this.currentNode.value.at(-1) !== '*') {
627
+ let error = new Error(`Unexpected range separator : at position ${this.pos + i} [E29]`);
628
+ error.code = 'ParserError29';
629
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
630
+ throw error;
631
+ }
632
+ }
633
+ else if (chr === '*') {
634
+ if (![',', ':'].includes(this.currentNode.value.at(-1))) {
635
+ let error = new Error(`Unexpected range wildcard at position ${this.pos + i} [E30]`);
636
+ error.code = 'ParserError30';
637
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
638
+ throw error;
639
+ }
640
+ }
641
+ else if (chr === ',') {
642
+ if (!RE_SINGLE_DIGIT.test(this.currentNode.value.at(-1)) && this.currentNode.value.at(-1) !== '*') {
643
+ let error = new Error(`Unexpected sequence separator , at position ${this.pos + i} [E31]`);
644
+ error.code = 'ParserError31';
645
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
646
+ throw error;
647
+ }
648
+ if (this.currentNode.value.at(-1) === '*' && this.currentNode.value.at(-2) !== ':') {
649
+ let error = new Error(`Unexpected sequence separator , at position ${this.pos + i} [E32]`);
650
+ error.code = 'ParserError32';
651
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
652
+ throw error;
653
+ }
654
+ }
655
+ else if (!RE_SINGLE_DIGIT.test(chr)) {
656
+ let error = new Error(`Unexpected char at position ${this.pos + i} [E33: ${JSON.stringify(chr)}]`);
657
+ error.code = 'ParserError33';
658
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
659
+ throw error;
660
+ }
661
+ if (RE_SINGLE_DIGIT.test(chr) && this.currentNode.value.at(-1) === '*') {
662
+ let error = new Error(`Unexpected number at position ${this.pos + i} [E34: ${JSON.stringify(chr)}]`);
663
+ error.code = 'ParserError34';
664
+ error.parserContext = { input: this.str, pos: this.pos + i, chr };
665
+ throw error;
666
+ }
667
+ this.currentNode.value += chr;
668
+ break;
669
+ }
670
+ }
671
+ }
672
+ }
673
+ exports.TokenParser = TokenParser;