imapflow 1.0.89 → 1.0.92

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.
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { normalizePath, getStatusCode, getErrorText } = require('../tools.js');
3
+ const { encodePath, normalizePath, getStatusCode, getErrorText } = require('../tools.js');
4
4
 
5
5
  // Selects a mailbox
6
6
  module.exports = async (connection, path, options) => {
@@ -47,101 +47,105 @@ module.exports = async (connection, path, options) => {
47
47
  map.qresync = true;
48
48
  }
49
49
 
50
- response = await connection.exec(!options.readOnly ? 'SELECT' : 'EXAMINE', [{ type: 'ATOM', value: path }].concat(extraArgs || []), {
51
- untagged: {
52
- OK: async untagged => {
53
- if (!untagged.attributes || !untagged.attributes.length) {
54
- return;
55
- }
56
- let section = !untagged.attributes[0].value && untagged.attributes[0].section;
57
- if (section && section.length > 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
58
- let key = section[0].value.toLowerCase();
59
- let value;
60
-
61
- if (typeof section[1].value === 'string') {
62
- value = section[1].value;
63
- } else if (Array.isArray(section[1])) {
64
- value = section[1].map(entry => (typeof entry.value === 'string' ? entry.value : false)).filter(entry => entry);
50
+ response = await connection.exec(
51
+ !options.readOnly ? 'SELECT' : 'EXAMINE',
52
+ [{ type: 'ATOM', value: encodePath(connection, path) }].concat(extraArgs || []),
53
+ {
54
+ untagged: {
55
+ OK: async untagged => {
56
+ if (!untagged.attributes || !untagged.attributes.length) {
57
+ return;
65
58
  }
66
-
67
- switch (key) {
68
- case 'highestmodseq':
69
- key = 'highestModseq';
70
- if (/^[0-9]+$/.test(value)) {
71
- value = BigInt(value);
72
- }
73
- break;
74
-
75
- case 'mailboxid':
76
- key = 'mailboxId';
77
- if (Array.isArray(value) && value.length) {
78
- value = value[0];
79
- }
80
- break;
81
-
82
- case 'permanentflags':
83
- key = 'permanentFlags';
84
- value = new Set(value);
85
- break;
86
-
87
- case 'uidnext':
88
- key = 'uidNext';
89
- value = Number(value);
90
- break;
91
-
92
- case 'uidvalidity':
93
- key = 'uidValidity';
94
- if (/^[0-9]+$/.test(value)) {
95
- value = BigInt(value);
96
- }
97
- break;
59
+ let section = !untagged.attributes[0].value && untagged.attributes[0].section;
60
+ if (section && section.length > 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
61
+ let key = section[0].value.toLowerCase();
62
+ let value;
63
+
64
+ if (typeof section[1].value === 'string') {
65
+ value = section[1].value;
66
+ } else if (Array.isArray(section[1])) {
67
+ value = section[1].map(entry => (typeof entry.value === 'string' ? entry.value : false)).filter(entry => entry);
68
+ }
69
+
70
+ switch (key) {
71
+ case 'highestmodseq':
72
+ key = 'highestModseq';
73
+ if (/^[0-9]+$/.test(value)) {
74
+ value = BigInt(value);
75
+ }
76
+ break;
77
+
78
+ case 'mailboxid':
79
+ key = 'mailboxId';
80
+ if (Array.isArray(value) && value.length) {
81
+ value = value[0];
82
+ }
83
+ break;
84
+
85
+ case 'permanentflags':
86
+ key = 'permanentFlags';
87
+ value = new Set(value);
88
+ break;
89
+
90
+ case 'uidnext':
91
+ key = 'uidNext';
92
+ value = Number(value);
93
+ break;
94
+
95
+ case 'uidvalidity':
96
+ key = 'uidValidity';
97
+ if (/^[0-9]+$/.test(value)) {
98
+ value = BigInt(value);
99
+ }
100
+ break;
101
+ }
102
+
103
+ map[key] = value;
98
104
  }
99
105
 
100
- map[key] = value;
101
- }
102
-
103
- if (section && section.length === 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
104
- let key = section[0].value.toLowerCase();
105
- switch (key) {
106
- case 'nomodseq':
107
- key = 'noModseq';
108
- map[key] = true;
109
- break;
106
+ if (section && section.length === 1 && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
107
+ let key = section[0].value.toLowerCase();
108
+ switch (key) {
109
+ case 'nomodseq':
110
+ key = 'noModseq';
111
+ map[key] = true;
112
+ break;
113
+ }
114
+ }
115
+ },
116
+ FLAGS: async untagged => {
117
+ if (!untagged.attributes || (!untagged.attributes.length && Array.isArray(untagged.attributes[0]))) {
118
+ return;
119
+ }
120
+ let flags = untagged.attributes[0].map(flag => (typeof flag.value === 'string' ? flag.value : false)).filter(flag => flag);
121
+ map.flags = new Set(flags);
122
+ },
123
+ EXISTS: async untagged => {
124
+ let num = Number(untagged.command);
125
+ if (isNaN(num)) {
126
+ return false;
110
127
  }
111
- }
112
- },
113
- FLAGS: async untagged => {
114
- if (!untagged.attributes || (!untagged.attributes.length && Array.isArray(untagged.attributes[0]))) {
115
- return;
116
- }
117
- let flags = untagged.attributes[0].map(flag => (typeof flag.value === 'string' ? flag.value : false)).filter(flag => flag);
118
- map.flags = new Set(flags);
119
- },
120
- EXISTS: async untagged => {
121
- let num = Number(untagged.command);
122
- if (isNaN(num)) {
123
- return false;
124
- }
125
128
 
126
- map.exists = num;
127
- },
128
- VANISHED: async untagged => {
129
- await connection.untaggedVanished(
130
- untagged,
131
- // mailbox is not yet open, so use a dummy mailbox object
132
- { path, uidNext: false, uidValidity: false }
133
- );
134
- },
135
- // we should only get an untagged FETCH for a SELECT/EXAMINE if QRESYNC was asked for
136
- FETCH: async untagged => {
137
- await connection.untaggedFetch(
138
- untagged,
139
- // mailbox is not yet open, so use a dummy mailbox object
140
- { path, uidNext: false, uidValidity: false }
141
- );
129
+ map.exists = num;
130
+ },
131
+ VANISHED: async untagged => {
132
+ await connection.untaggedVanished(
133
+ untagged,
134
+ // mailbox is not yet open, so use a dummy mailbox object
135
+ { path, uidNext: false, uidValidity: false }
136
+ );
137
+ },
138
+ // we should only get an untagged FETCH for a SELECT/EXAMINE if QRESYNC was asked for
139
+ FETCH: async untagged => {
140
+ await connection.untaggedFetch(
141
+ untagged,
142
+ // mailbox is not yet open, so use a dummy mailbox object
143
+ { path, uidNext: false, uidValidity: false }
144
+ );
145
+ }
142
146
  }
143
147
  }
144
- });
148
+ );
145
149
 
146
150
  let section = !response.response.attributes[0].value && response.response.attributes[0].section;
147
151
  if (section && section.length && section[0].type === 'ATOM' && typeof section[0].value === 'string') {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { normalizePath } = require('../tools.js');
3
+ const { encodePath, normalizePath } = require('../tools.js');
4
4
 
5
5
  // Requests info about a mailbox
6
6
  module.exports = async (connection, path, query) => {
@@ -11,7 +11,7 @@ module.exports = async (connection, path, query) => {
11
11
 
12
12
  path = normalizePath(connection, path);
13
13
 
14
- let attributes = [{ type: 'ATOM', value: path }];
14
+ let attributes = [{ type: 'ATOM', value: encodePath(connection, path) }];
15
15
 
16
16
  let queryAttributes = [];
17
17
  Object.keys(query || {}).forEach(key => {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { normalizePath, getStatusCode, getErrorText } = require('../tools.js');
3
+ const { encodePath, normalizePath, getStatusCode, getErrorText } = require('../tools.js');
4
4
 
5
5
  // Subscribes to a mailbox
6
6
  module.exports = async (connection, path) => {
@@ -13,7 +13,7 @@ module.exports = async (connection, path) => {
13
13
 
14
14
  let response;
15
15
  try {
16
- response = await connection.exec('SUBSCRIBE', [{ type: 'ATOM', value: path }]);
16
+ response = await connection.exec('SUBSCRIBE', [{ type: 'ATOM', value: encodePath(connection, path) }]);
17
17
  response.next();
18
18
  return true;
19
19
  } catch (err) {
@@ -1,6 +1,6 @@
1
1
  'use strict';
2
2
 
3
- const { normalizePath, getStatusCode, getErrorText } = require('../tools.js');
3
+ const { encodePath, normalizePath, getStatusCode, getErrorText } = require('../tools.js');
4
4
 
5
5
  // Unsubscribes from a mailbox
6
6
  module.exports = async (connection, path) => {
@@ -13,7 +13,7 @@ module.exports = async (connection, path) => {
13
13
 
14
14
  let response;
15
15
  try {
16
- response = await connection.exec('UNSUBSCRIBE', [{ type: 'ATOM', value: path }]);
16
+ response = await connection.exec('UNSUBSCRIBE', [{ type: 'ATOM', value: encodePath(connection, path) }]);
17
17
  response.next();
18
18
  return true;
19
19
  } catch (err) {
@@ -85,7 +85,7 @@ module.exports = async (response, options) => {
85
85
  let canAppend = !asArray || literalPlus || (literalMinus && literalLength <= 4096);
86
86
  let usePlus = canAppend && (literalMinus || literalPlus);
87
87
 
88
- resp += `{${literalLength}${usePlus ? '+' : ''}}\r\n`;
88
+ resp += `${node.isLiteral8 ? '~' : ''}{${literalLength}${usePlus ? '+' : ''}}\r\n`;
89
89
 
90
90
  if (canAppend) {
91
91
  resp += (node.value || '').toString('binary');
@@ -26,7 +26,7 @@ function excludeChars(source, exclude) {
26
26
  module.exports = {
27
27
  CHAR() {
28
28
  let value = expandRange(0x01, 0x7f);
29
- this.CHAR = function() {
29
+ this.CHAR = function () {
30
30
  return value;
31
31
  };
32
32
  return value;
@@ -34,7 +34,7 @@ module.exports = {
34
34
 
35
35
  CHAR8() {
36
36
  let value = expandRange(0x01, 0xff);
37
- this.CHAR8 = function() {
37
+ this.CHAR8 = function () {
38
38
  return value;
39
39
  };
40
40
  return value;
@@ -46,7 +46,7 @@ module.exports = {
46
46
 
47
47
  CTL() {
48
48
  let value = expandRange(0x00, 0x1f) + '\x7F';
49
- this.CTL = function() {
49
+ this.CTL = function () {
50
50
  return value;
51
51
  };
52
52
  return value;
@@ -58,7 +58,7 @@ module.exports = {
58
58
 
59
59
  ALPHA() {
60
60
  let value = expandRange(0x41, 0x5a) + expandRange(0x61, 0x7a);
61
- this.ALPHA = function() {
61
+ this.ALPHA = function () {
62
62
  return value;
63
63
  };
64
64
  return value;
@@ -66,7 +66,7 @@ module.exports = {
66
66
 
67
67
  DIGIT() {
68
68
  let value = expandRange(0x30, 0x39) + expandRange(0x61, 0x7a);
69
- this.DIGIT = function() {
69
+ this.DIGIT = function () {
70
70
  return value;
71
71
  };
72
72
  return value;
@@ -74,7 +74,7 @@ module.exports = {
74
74
 
75
75
  'ATOM-CHAR'() {
76
76
  let value = excludeChars(this.CHAR(), this['atom-specials']());
77
- this['ATOM-CHAR'] = function() {
77
+ this['ATOM-CHAR'] = function () {
78
78
  return value;
79
79
  };
80
80
  return value;
@@ -82,7 +82,7 @@ module.exports = {
82
82
 
83
83
  'ASTRING-CHAR'() {
84
84
  let value = this['ATOM-CHAR']() + this['resp-specials']();
85
- this['ASTRING-CHAR'] = function() {
85
+ this['ASTRING-CHAR'] = function () {
86
86
  return value;
87
87
  };
88
88
  return value;
@@ -90,7 +90,7 @@ module.exports = {
90
90
 
91
91
  'TEXT-CHAR'() {
92
92
  let value = excludeChars(this.CHAR(), '\r\n');
93
- this['TEXT-CHAR'] = function() {
93
+ this['TEXT-CHAR'] = function () {
94
94
  return value;
95
95
  };
96
96
  return value;
@@ -98,7 +98,7 @@ module.exports = {
98
98
 
99
99
  'atom-specials'() {
100
100
  let value = '(' + ')' + '{' + this.SP() + this.CTL() + this['list-wildcards']() + this['quoted-specials']() + this['resp-specials']();
101
- this['atom-specials'] = function() {
101
+ this['atom-specials'] = function () {
102
102
  return value;
103
103
  };
104
104
  return value;
@@ -109,8 +109,8 @@ module.exports = {
109
109
  },
110
110
 
111
111
  'quoted-specials'() {
112
- let value = this.DQUOTE() + '\\';
113
- this['quoted-specials'] = function() {
112
+ let value = this.DQUOTE() + "'\\";
113
+ this['quoted-specials'] = function () {
114
114
  return value;
115
115
  };
116
116
  return value;
@@ -122,7 +122,7 @@ module.exports = {
122
122
 
123
123
  tag() {
124
124
  let value = excludeChars(this['ASTRING-CHAR'](), '+');
125
- this.tag = function() {
125
+ this.tag = function () {
126
126
  return value;
127
127
  };
128
128
  return value;
@@ -130,7 +130,7 @@ module.exports = {
130
130
 
131
131
  command() {
132
132
  let value = this.ALPHA() + this.DIGIT() + '-';
133
- this.command = function() {
133
+ this.command = function () {
134
134
  return value;
135
135
  };
136
136
  return value;
@@ -40,6 +40,7 @@ class ImapStream extends Transform {
40
40
  this.literals = [];
41
41
 
42
42
  this.compress = false;
43
+ this.secureConnection = this.options.secureConnection;
43
44
 
44
45
  this.processingInput = false;
45
46
  this.inputQueue = []; // unprocessed input chunks
@@ -197,7 +198,14 @@ class ImapStream extends Transform {
197
198
  }
198
199
 
199
200
  if (this.options.logRaw) {
200
- this.log.trace({ src: 's', msg: 'read from socket', data: chunk.toString('base64'), compress: !!this.compress, cid: this.cid });
201
+ this.log.trace({
202
+ src: 's',
203
+ msg: 'read from socket',
204
+ data: chunk.toString('base64'),
205
+ compress: !!this.compress,
206
+ secure: !!this.secureConnection,
207
+ cid: this.cid
208
+ });
201
209
  }
202
210
 
203
211
  if (chunk && chunk.length) {
@@ -29,13 +29,13 @@ class TokenParser {
29
29
  let elm;
30
30
  let partial;
31
31
 
32
- if (!node.closed && node.type === 'SEQUENCE' && node.value === '*') {
33
- node.closed = true;
32
+ if (!node.isClosed && node.type === 'SEQUENCE' && node.value === '*') {
33
+ node.isClosed = true;
34
34
  node.type = 'ATOM';
35
35
  }
36
36
 
37
37
  // If the node was never closed, throw it
38
- if (!node.closed) {
38
+ if (!node.isClosed) {
39
39
  throw new Error('Unexpected end of input at position ' + (this.pos + this.str.length - 1));
40
40
  }
41
41
 
@@ -97,7 +97,7 @@ class TokenParser {
97
97
  childNodes: [],
98
98
  type: false,
99
99
  value: '',
100
- closed: true
100
+ isClosed: true
101
101
  };
102
102
 
103
103
  if (parentNode) {
@@ -136,14 +136,14 @@ class TokenParser {
136
136
  this.currentNode = this.createNode(this.currentNode, this.pos + i);
137
137
  this.currentNode.type = 'string';
138
138
  this.state = 'STRING';
139
- this.currentNode.closed = false;
139
+ this.currentNode.isClosed = false;
140
140
  break;
141
141
 
142
142
  // ( starts a new list
143
143
  case '(':
144
144
  this.currentNode = this.createNode(this.currentNode, this.pos + i);
145
145
  this.currentNode.type = 'LIST';
146
- this.currentNode.closed = false;
146
+ this.currentNode.isClosed = false;
147
147
  break;
148
148
 
149
149
  // ) closes a list
@@ -152,7 +152,7 @@ class TokenParser {
152
152
  throw new Error('Unexpected list terminator ) at position ' + (this.pos + i));
153
153
  }
154
154
 
155
- this.currentNode.closed = true;
155
+ this.currentNode.isClosed = true;
156
156
  this.currentNode.endPos = this.pos + i;
157
157
  this.currentNode = this.currentNode.parentNode;
158
158
 
@@ -164,7 +164,7 @@ class TokenParser {
164
164
  if (this.currentNode.type !== 'SECTION') {
165
165
  throw new Error('Unexpected section terminator ] at position ' + (this.pos + i));
166
166
  }
167
- this.currentNode.closed = true;
167
+ this.currentNode.isClosed = true;
168
168
  this.currentNode.endPos = this.pos + i;
169
169
  this.currentNode = this.currentNode.parentNode;
170
170
  checkSP();
@@ -181,24 +181,36 @@ class TokenParser {
181
181
  this.currentNode = this.createNode(this.currentNode, this.pos + i);
182
182
  this.currentNode.type = 'PARTIAL';
183
183
  this.state = 'PARTIAL';
184
- this.currentNode.closed = false;
184
+ this.currentNode.isClosed = false;
185
185
  }
186
186
  break;
187
187
 
188
+ // binary literal8
189
+ case '~': {
190
+ let nextChr = this.str.charAt(i + 1);
191
+ if (nextChr !== '{') {
192
+ throw new Error('Unexpected literal8 marker at position ' + (this.pos + i));
193
+ }
194
+ this.expectedLiteralType = 'literal8';
195
+ break;
196
+ }
197
+
188
198
  // { starts a new literal
189
199
  case '{':
190
200
  this.currentNode = this.createNode(this.currentNode, this.pos + i);
191
201
  this.currentNode.type = 'LITERAL';
202
+ this.currentNode.literalType = this.expectedLiteralType || 'literal';
203
+ this.expectedLiteralType = false;
192
204
  this.state = 'LITERAL';
193
- this.currentNode.closed = false;
205
+ this.currentNode.isClosed = false;
194
206
  break;
195
207
 
196
- // ( starts a new sequence
208
+ // * starts a new sequence
197
209
  case '*':
198
210
  this.currentNode = this.createNode(this.currentNode, this.pos + i);
199
211
  this.currentNode.type = 'SEQUENCE';
200
212
  this.currentNode.value = chr;
201
- this.currentNode.closed = false;
213
+ this.currentNode.isClosed = false;
202
214
  this.state = 'SEQUENCE';
203
215
  break;
204
216
 
@@ -218,7 +230,7 @@ class TokenParser {
218
230
 
219
231
  this.currentNode = this.createNode(this.currentNode, this.pos + i);
220
232
  this.currentNode.type = 'SECTION';
221
- this.currentNode.closed = false;
233
+ this.currentNode.isClosed = false;
222
234
  this.state = 'NORMAL';
223
235
 
224
236
  // RFC2221 defines a response code REFERRAL whose payload is an
@@ -244,7 +256,7 @@ class TokenParser {
244
256
  this.currentNode = this.currentNode.parentNode;
245
257
 
246
258
  // close out the SECTION
247
- this.currentNode.closed = true;
259
+ this.currentNode.isClosed = true;
248
260
  this.currentNode = this.currentNode.parentNode;
249
261
  checkSP();
250
262
  }
@@ -286,7 +298,7 @@ class TokenParser {
286
298
  this.currentNode.endPos = this.pos + i - 1;
287
299
  this.currentNode = this.currentNode.parentNode;
288
300
 
289
- this.currentNode.closed = true;
301
+ this.currentNode.isClosed = true;
290
302
  this.currentNode.endPos = this.pos + i;
291
303
  this.currentNode = this.currentNode.parentNode;
292
304
  this.state = 'NORMAL';
@@ -297,20 +309,20 @@ class TokenParser {
297
309
 
298
310
  if ((chr === ',' || chr === ':') && this.currentNode.value.match(/^\d+$/)) {
299
311
  this.currentNode.type = 'SEQUENCE';
300
- this.currentNode.closed = true;
312
+ this.currentNode.isClosed = true;
301
313
  this.state = 'SEQUENCE';
302
314
  }
303
315
 
304
316
  // [ starts a section group for this element
305
317
  if (chr === '[') {
306
318
  // allowed only for selected elements
307
- if (['BODY', 'BODY.PEEK'].indexOf(this.currentNode.value.toUpperCase()) < 0) {
319
+ if (['BODY', 'BODY.PEEK', 'BINARY', 'BINARY.PEEK'].indexOf(this.currentNode.value.toUpperCase()) < 0) {
308
320
  throw new Error('Unexpected section start char [ at position ' + this.pos);
309
321
  }
310
322
  this.currentNode.endPos = this.pos + i;
311
323
  this.currentNode = this.createNode(this.currentNode.parentNode, this.pos + i);
312
324
  this.currentNode.type = 'SECTION';
313
- this.currentNode.closed = false;
325
+ this.currentNode.isClosed = false;
314
326
  this.state = 'NORMAL';
315
327
  break;
316
328
  }
@@ -333,7 +345,7 @@ class TokenParser {
333
345
  // DQUOTE ends the string sequence
334
346
  if (chr === '"') {
335
347
  this.currentNode.endPos = this.pos + i;
336
- this.currentNode.closed = true;
348
+ this.currentNode.isClosed = true;
337
349
  this.currentNode = this.currentNode.parentNode;
338
350
  this.state = 'NORMAL';
339
351
 
@@ -359,7 +371,7 @@ class TokenParser {
359
371
  throw new Error('Unexpected end of partial at position ' + this.pos);
360
372
  }
361
373
  this.currentNode.endPos = this.pos + i;
362
- this.currentNode.closed = true;
374
+ this.currentNode.isClosed = true;
363
375
  this.currentNode = this.currentNode.parentNode;
364
376
  this.state = 'NORMAL';
365
377
  checkSP();
@@ -385,16 +397,19 @@ class TokenParser {
385
397
  if (this.currentNode.started) {
386
398
  // only relevant if literals are not already parsed out from input
387
399
 
388
- //if(imapFormalSyntax['CHAR8']().indexOf(chr) < 0){
400
+ // Disabled NULL byte check
401
+ // See https://github.com/emailjs/emailjs-imap-handler/commit/f11b2822bedabe492236e8263afc630134a3c41c
402
+ /*
389
403
  if (chr === '\u0000') {
390
404
  throw new Error('Unexpected \\x00 at position ' + (this.pos + i));
391
405
  }
406
+ */
392
407
 
393
408
  this.currentNode.chBuffer[this.currentNode.chPos++] = chr.charCodeAt(0);
394
409
 
395
410
  if (this.currentNode.chPos >= this.currentNode.literalLength) {
396
411
  this.currentNode.endPos = this.pos + i;
397
- this.currentNode.closed = true;
412
+ this.currentNode.isClosed = true;
398
413
  this.currentNode.value = this.currentNode.chBuffer.toString('binary');
399
414
  this.currentNode.chBuffer = Buffer.alloc(0);
400
415
  this.currentNode = this.currentNode.parentNode;
@@ -427,7 +442,7 @@ class TokenParser {
427
442
  // special case where literal content length is 0
428
443
  // close the node right away, do not wait for additional input
429
444
  this.currentNode.endPos = this.pos + i;
430
- this.currentNode.closed = true;
445
+ this.currentNode.isClosed = true;
431
446
  this.currentNode = this.currentNode.parentNode;
432
447
  this.state = 'NORMAL';
433
448
  checkSP();
@@ -445,7 +460,7 @@ class TokenParser {
445
460
  this.currentNode.endPos = this.pos + i + this.currentNode.value.length;
446
461
 
447
462
  this.currentNode.started = false;
448
- this.currentNode.closed = true;
463
+ this.currentNode.isClosed = true;
449
464
  this.currentNode = this.currentNode.parentNode;
450
465
  this.state = 'NORMAL';
451
466
  checkSP();
@@ -478,7 +493,7 @@ class TokenParser {
478
493
  throw new Error('Unexpected whitespace at position ' + (this.pos + i));
479
494
  }
480
495
 
481
- this.currentNode.closed = true;
496
+ this.currentNode.isClosed = true;
482
497
  this.currentNode.endPos = this.pos + i - 1;
483
498
  this.currentNode = this.currentNode.parentNode;
484
499
  this.state = 'NORMAL';
@@ -487,7 +502,7 @@ class TokenParser {
487
502
  this.currentNode.endPos = this.pos + i - 1;
488
503
  this.currentNode = this.currentNode.parentNode;
489
504
 
490
- this.currentNode.closed = true;
505
+ this.currentNode.isClosed = true;
491
506
  this.currentNode.endPos = this.pos + i;
492
507
  this.currentNode = this.currentNode.parentNode;
493
508
  this.state = 'NORMAL';