postal-mime 2.4.7 → 2.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.6.0](https://github.com/postalsys/postal-mime/compare/v2.5.0...v2.6.0) (2025-10-24)
4
+
5
+
6
+ ### Features
7
+
8
+ * add CommonJS build support for dual package compatibility ([0ea3de3](https://github.com/postalsys/postal-mime/commit/0ea3de39c713aac2bc79218dfb496c0e2ff4b0b8))
9
+
10
+ ## [2.5.0](https://github.com/postalsys/postal-mime/compare/v2.4.7...v2.5.0) (2025-10-07)
11
+
12
+
13
+ ### Features
14
+
15
+ * add comprehensive type validation and TypeScript support ([81a6467](https://github.com/postalsys/postal-mime/commit/81a6467b6c5379c502bac9ee7023e2c2dee976cb))
16
+
3
17
  ## [2.4.7](https://github.com/postalsys/postal-mime/compare/v2.4.6...v2.4.7) (2025-10-07)
4
18
 
5
19
 
package/README.md CHANGED
@@ -2,9 +2,18 @@
2
2
 
3
3
  **postal-mime** is an email parsing library that runs in browser environments (including Web Workers) and serverless functions (like Cloudflare Email Workers). It takes in a raw email message (RFC822 format) and outputs a structured object containing headers, recipients, attachments, and more.
4
4
 
5
- > **Tip**
5
+ > [!TIP]
6
6
  > PostalMime is developed by the makers of [EmailEngine](https://emailengine.app/?utm_source=github&utm_campaign=imapflow&utm_medium=readme-link)—a self-hosted email gateway that provides a REST API for IMAP and SMTP servers and sends webhooks whenever something changes in registered accounts.
7
7
 
8
+ ## Features
9
+
10
+ - **Browser & Node.js compatible** - Works in browsers, Web Workers, Node.js, and serverless environments
11
+ - **TypeScript support** - Fully typed with comprehensive type definitions
12
+ - **Zero dependencies** - No external dependencies
13
+ - **RFC compliant** - Follows RFC 2822/5322 email standards
14
+ - **Handles complex MIME structures** - Multipart messages, nested parts, attachments
15
+ - **Security limits** - Built-in protection against deeply nested messages and oversized headers
16
+
8
17
  ## Table of Contents
9
18
 
10
19
  - [Source](#source)
@@ -14,6 +23,7 @@
14
23
  - [Browser](#browser)
15
24
  - [Node.js](#nodejs)
16
25
  - [Cloudflare Email Workers](#cloudflare-email-workers)
26
+ - [TypeScript Support](#typescript-support)
17
27
  - [API](#api)
18
28
  - [PostalMime.parse()](#postalmimeparse)
19
29
  - [Utility Functions](#utility-functions)
@@ -58,6 +68,23 @@ Content-Type: text/html; charset=utf-8
58
68
  console.log(email.subject); // "My awesome email 🤓"
59
69
  ```
60
70
 
71
+ <details>
72
+ <summary><strong>TypeScript</strong></summary>
73
+
74
+ ```typescript
75
+ import PostalMime from './node_modules/postal-mime/src/postal-mime.js';
76
+ import type { Email } from 'postal-mime';
77
+
78
+ const email: Email = await PostalMime.parse(`Subject: My awesome email 🤓
79
+ Content-Type: text/html; charset=utf-8
80
+
81
+ <p>Hello world 😵‍💫</p>`);
82
+
83
+ console.log(email.subject); // "My awesome email 🤓"
84
+ ```
85
+
86
+ </details>
87
+
61
88
  ### Node.js
62
89
 
63
90
  In Node.js (including serverless functions), import it directly from `postal-mime`:
@@ -75,6 +102,48 @@ Content-Type: text/html; charset=utf-8
75
102
  console.log(util.inspect(email, false, 22, true));
76
103
  ```
77
104
 
105
+ <details>
106
+ <summary><strong>TypeScript</strong></summary>
107
+
108
+ ```typescript
109
+ import PostalMime from 'postal-mime';
110
+ import type { Email, PostalMimeOptions } from 'postal-mime';
111
+ import util from 'node:util';
112
+
113
+ const options: PostalMimeOptions = {
114
+ attachmentEncoding: 'base64'
115
+ };
116
+
117
+ const email: Email = await PostalMime.parse(`Subject: My awesome email 🤓
118
+ Content-Type: text/html; charset=utf-8
119
+
120
+ <p>Hello world 😵‍💫</p>`, options);
121
+
122
+ // Use 'util.inspect' for pretty-printing
123
+ console.log(util.inspect(email, false, 22, true));
124
+ ```
125
+
126
+ </details>
127
+
128
+ ### CommonJS
129
+
130
+ For projects using CommonJS (with `require()`), postal-mime automatically provides the CommonJS build:
131
+
132
+ ```js
133
+ const PostalMime = require('postal-mime');
134
+ const { addressParser, decodeWords } = require('postal-mime');
135
+
136
+ const email = await PostalMime.parse(`Subject: My awesome email 🤓
137
+ Content-Type: text/html; charset=utf-8
138
+
139
+ <p>Hello world 😵‍💫</p>`);
140
+
141
+ console.log(email.subject); // "My awesome email 🤓"
142
+ ```
143
+
144
+ > [!NOTE]
145
+ > The CommonJS build is automatically generated from the ESM source code during the build process. The package supports dual module format, so both `import` and `require()` work seamlessly.
146
+
78
147
  ### Cloudflare Email Workers
79
148
 
80
149
  Use the `message.raw` as the raw email data for parsing:
@@ -93,6 +162,77 @@ export default {
93
162
  };
94
163
  ```
95
164
 
165
+ <details>
166
+ <summary><strong>TypeScript</strong></summary>
167
+
168
+ ```typescript
169
+ import PostalMime from 'postal-mime';
170
+ import type { Email } from 'postal-mime';
171
+
172
+ export default {
173
+ async email(message: ForwardableEmailMessage, env: Env, ctx: ExecutionContext): Promise<void> {
174
+ const email: Email = await PostalMime.parse(message.raw);
175
+
176
+ console.log('Subject:', email.subject);
177
+ console.log('HTML:', email.html);
178
+ console.log('Text:', email.text);
179
+ }
180
+ };
181
+ ```
182
+
183
+ </details>
184
+
185
+ ---
186
+
187
+ ## TypeScript Support
188
+
189
+ PostalMime includes comprehensive TypeScript type definitions. All types are exported and can be imported from the main package:
190
+
191
+ ```typescript
192
+ import PostalMime, { addressParser, decodeWords } from 'postal-mime';
193
+ import type {
194
+ Email,
195
+ Address,
196
+ Mailbox,
197
+ Header,
198
+ Attachment,
199
+ PostalMimeOptions,
200
+ AddressParserOptions,
201
+ RawEmail
202
+ } from 'postal-mime';
203
+ ```
204
+
205
+ > [!NOTE]
206
+ > PostalMime is written in JavaScript but provides comprehensive TypeScript type definitions. All types are validated through both compile-time type checking and runtime type validation tests to ensure accuracy.
207
+
208
+ ### Available Types
209
+
210
+ - **`Email`** - The main parsed email object returned by `PostalMime.parse()`
211
+ - **`Address`** - Union type representing either a `Mailbox` or an address group
212
+ - **`Mailbox`** - Individual email address with name and address fields
213
+ - **`Header`** - Email header with key and value
214
+ - **`Attachment`** - Email attachment with metadata and content
215
+ - **`PostalMimeOptions`** - Configuration options for parsing
216
+ - **`AddressParserOptions`** - Configuration options for address parsing
217
+ - **`RawEmail`** - Union type for all accepted email input formats
218
+
219
+ ### Type Narrowing
220
+
221
+ TypeScript users can use type guards to narrow address types:
222
+
223
+ ```typescript
224
+ import type { Address, Mailbox } from 'postal-mime';
225
+
226
+ function isMailbox(addr: Address): addr is Mailbox {
227
+ return !('group' in addr) || addr.group === undefined;
228
+ }
229
+
230
+ // Usage
231
+ if (email.from && isMailbox(email.from)) {
232
+ console.log(email.from.address); // TypeScript knows this is a Mailbox
233
+ }
234
+ ```
235
+
96
236
  ---
97
237
 
98
238
  ## API
@@ -100,7 +240,7 @@ export default {
100
240
  ### PostalMime.parse()
101
241
 
102
242
  ```js
103
- PostalMime.parse(email, options) -> Promise<ParsedEmail>
243
+ PostalMime.parse(email, options) -> Promise<Email>
104
244
  ```
105
245
 
106
246
  - **email**: An RFC822 formatted email. This can be a `string`, `ArrayBuffer/Uint8Array`, `Blob` (browser only), `Buffer` (Node.js), or a [ReadableStream](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream).
@@ -114,29 +254,86 @@ PostalMime.parse(email, options) -> Promise<ParsedEmail>
114
254
  - **maxNestingDepth** (number, default: `256`): Maximum allowed MIME part nesting depth. Throws an error if exceeded.
115
255
  - **maxHeadersSize** (number, default: `2097152`): Maximum allowed total header size in bytes (default 2MB). Throws an error if exceeded.
116
256
 
117
- **Returns**: A Promise that resolves to a structured object with the following properties:
257
+ > [!IMPORTANT]
258
+ > The `maxNestingDepth` and `maxHeadersSize` options provide built-in security against malicious emails with deeply nested MIME structures or oversized headers that could cause performance issues or memory exhaustion.
259
+
260
+ **Returns**: A Promise that resolves to a structured `Email` object with the following properties:
118
261
 
119
- - **headers**: An array of header objects, each containing:
262
+ - **headers**: An array of `Header` objects, each containing:
120
263
  - `key`: Lowercase header name (e.g., `"dkim-signature"`).
121
264
  - `value`: Unprocessed header value as a string.
122
- - **from**, **sender**: Processed address objects:
265
+ - **from**, **sender**: Processed `Address` objects (can be a `Mailbox` or address group):
123
266
  - `name`: Decoded display name, or an empty string if not set.
124
267
  - `address`: Email address.
268
+ - `group`: Array of `Mailbox` objects (only for address groups).
125
269
  - **deliveredTo**, **returnPath**: Single email addresses as strings.
126
- - **to**, **cc**, **bcc**, **replyTo**: Arrays of processed address objects (same structure as `from`).
270
+ - **to**, **cc**, **bcc**, **replyTo**: Arrays of `Address` objects (same structure as `from`).
127
271
  - **subject**: Subject line of the email.
128
272
  - **messageId**, **inReplyTo**, **references**: Values from their corresponding headers.
129
- - **date**: The emails sending time in ISO 8601 format (or the original string if parsing fails).
273
+ - **date**: The email's sending time in ISO 8601 format (or the original string if parsing fails).
130
274
  - **html**: String containing the HTML content of the email.
131
275
  - **text**: String containing the plain text content of the email.
132
- - **attachments**: Array of attachment objects:
133
- - `filename`
134
- - `mimeType`
135
- - `disposition` (e.g., `"attachment"`, `"inline"`, or `null`)
136
- - `related` (boolean, `true` if its an inline image)
137
- - `contentId`
138
- - `content` (array buffer or string, depending on `attachmentEncoding`)
139
- - `encoding` (e.g., `"base64"`)
276
+ - **attachments**: Array of `Attachment` objects:
277
+ - `filename`: String or `null`
278
+ - `mimeType`: String
279
+ - `disposition`: `"attachment"`, `"inline"`, or `null`
280
+ - `related`: Boolean (optional, `true` if it's an inline image)
281
+ - `contentId`: String (optional)
282
+ - `content`: `ArrayBuffer` or string, depending on `attachmentEncoding`
283
+ - `encoding`: `"base64"` or `"utf8"` (optional)
284
+
285
+ <details>
286
+ <summary><strong>TypeScript Types</strong></summary>
287
+
288
+ ```typescript
289
+ import type {
290
+ Email,
291
+ Address,
292
+ Mailbox,
293
+ Header,
294
+ Attachment,
295
+ PostalMimeOptions,
296
+ RawEmail
297
+ } from 'postal-mime';
298
+
299
+ // Main email parsing
300
+ const email: Email = await PostalMime.parse(rawEmail);
301
+
302
+ // With options
303
+ const options: PostalMimeOptions = {
304
+ attachmentEncoding: 'base64',
305
+ maxNestingDepth: 100
306
+ };
307
+ const email: Email = await PostalMime.parse(rawEmail, options);
308
+
309
+ // Working with addresses
310
+ if (email.from) {
311
+ // Address can be either a Mailbox or a Group
312
+ if ('group' in email.from && email.from.group) {
313
+ // It's a group
314
+ email.from.group.forEach((member: Mailbox) => {
315
+ console.log(member.address);
316
+ });
317
+ } else {
318
+ // It's a mailbox
319
+ const mailbox = email.from as Mailbox;
320
+ console.log(mailbox.address);
321
+ }
322
+ }
323
+
324
+ // Working with attachments
325
+ email.attachments.forEach((att: Attachment) => {
326
+ if (att.encoding === 'base64') {
327
+ // content is a string
328
+ const base64Content: string = att.content as string;
329
+ } else {
330
+ // content is ArrayBuffer (default)
331
+ const buffer: ArrayBuffer = att.content as ArrayBuffer;
332
+ }
333
+ });
334
+ ```
335
+
336
+ </details>
140
337
 
141
338
  ---
142
339
 
@@ -147,23 +344,42 @@ PostalMime.parse(email, options) -> Promise<ParsedEmail>
147
344
  ```js
148
345
  import { addressParser } from 'postal-mime';
149
346
 
150
- addressParser(addressStr, opts) -> Array
347
+ addressParser(addressStr, opts) -> Address[]
151
348
  ```
152
349
 
153
350
  - **addressStr**: A raw address header string.
154
351
  - **opts**: Optional configuration:
155
352
  - **flatten** (boolean, default: `false`): If `true`, ignores address groups and returns a flat array of addresses.
156
353
 
157
- **Returns**: An array of address objects, which can be nested if address groups are present.
354
+ **Returns**: An array of `Address` objects, which can be nested if address groups are present.
158
355
 
159
356
  **Example**:
160
357
 
161
358
  ```js
359
+ import { addressParser } from 'postal-mime';
360
+
162
361
  const addressStr = '=?utf-8?B?44Ko44Od44K544Kr44O844OJ?= <support@example.com>';
163
362
  console.log(addressParser(addressStr));
164
363
  // [ { name: 'エポスカード', address: 'support@example.com' } ]
165
364
  ```
166
365
 
366
+ <details>
367
+ <summary><strong>TypeScript</strong></summary>
368
+
369
+ ```typescript
370
+ import { addressParser } from 'postal-mime';
371
+ import type { Address, AddressParserOptions } from 'postal-mime';
372
+
373
+ const addressStr = '=?utf-8?B?44Ko44Od44K544Kr44O844OJ?= <support@example.com>';
374
+ const addresses: Address[] = addressParser(addressStr);
375
+
376
+ // With options
377
+ const options: AddressParserOptions = { flatten: true };
378
+ const flatAddresses: Address[] = addressParser(addressStr, options);
379
+ ```
380
+
381
+ </details>
382
+
167
383
  #### decodeWords()
168
384
 
169
385
  ```js
@@ -179,11 +395,26 @@ decodeWords(encodedStr) -> string
179
395
  **Example**:
180
396
 
181
397
  ```js
398
+ import { decodeWords } from 'postal-mime';
399
+
182
400
  const encodedStr = 'Hello, =?utf-8?B?44Ko44Od44K544Kr44O844OJ?=';
183
401
  console.log(decodeWords(encodedStr));
184
402
  // Hello, エポスカード
185
403
  ```
186
404
 
405
+ <details>
406
+ <summary><strong>TypeScript</strong></summary>
407
+
408
+ ```typescript
409
+ import { decodeWords } from 'postal-mime';
410
+
411
+ const encodedStr = 'Hello, =?utf-8?B?44Ko44Od44K544Kr44O844OJ?=';
412
+ const decoded: string = decodeWords(encodedStr);
413
+ console.log(decoded); // Hello, エポスカード
414
+ ```
415
+
416
+ </details>
417
+
187
418
  ---
188
419
 
189
420
  ## License
@@ -0,0 +1,317 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var address_parser_exports = {};
20
+ __export(address_parser_exports, {
21
+ default: () => address_parser_default
22
+ });
23
+ module.exports = __toCommonJS(address_parser_exports);
24
+ var import_decode_strings = require("./decode-strings.cjs");
25
+ function _handleAddress(tokens) {
26
+ let isGroup = false;
27
+ let state = "text";
28
+ let address;
29
+ let addresses = [];
30
+ let data = {
31
+ address: [],
32
+ comment: [],
33
+ group: [],
34
+ text: [],
35
+ textWasQuoted: []
36
+ // Track which text tokens came from inside quotes
37
+ };
38
+ let i;
39
+ let len;
40
+ let insideQuotes = false;
41
+ for (i = 0, len = tokens.length; i < len; i++) {
42
+ let token = tokens[i];
43
+ let prevToken = i ? tokens[i - 1] : null;
44
+ if (token.type === "operator") {
45
+ switch (token.value) {
46
+ case "<":
47
+ state = "address";
48
+ insideQuotes = false;
49
+ break;
50
+ case "(":
51
+ state = "comment";
52
+ insideQuotes = false;
53
+ break;
54
+ case ":":
55
+ state = "group";
56
+ isGroup = true;
57
+ insideQuotes = false;
58
+ break;
59
+ case '"':
60
+ insideQuotes = !insideQuotes;
61
+ state = "text";
62
+ break;
63
+ default:
64
+ state = "text";
65
+ insideQuotes = false;
66
+ break;
67
+ }
68
+ } else if (token.value) {
69
+ if (state === "address") {
70
+ token.value = token.value.replace(/^[^<]*<\s*/, "");
71
+ }
72
+ if (prevToken && prevToken.noBreak && data[state].length) {
73
+ data[state][data[state].length - 1] += token.value;
74
+ if (state === "text" && insideQuotes) {
75
+ data.textWasQuoted[data.textWasQuoted.length - 1] = true;
76
+ }
77
+ } else {
78
+ data[state].push(token.value);
79
+ if (state === "text") {
80
+ data.textWasQuoted.push(insideQuotes);
81
+ }
82
+ }
83
+ }
84
+ }
85
+ if (!data.text.length && data.comment.length) {
86
+ data.text = data.comment;
87
+ data.comment = [];
88
+ }
89
+ if (isGroup) {
90
+ data.text = data.text.join(" ");
91
+ let groupMembers = [];
92
+ if (data.group.length) {
93
+ let parsedGroup = addressParser(data.group.join(","));
94
+ parsedGroup.forEach((member) => {
95
+ if (member.group) {
96
+ groupMembers = groupMembers.concat(member.group);
97
+ } else {
98
+ groupMembers.push(member);
99
+ }
100
+ });
101
+ }
102
+ addresses.push({
103
+ name: (0, import_decode_strings.decodeWords)(data.text || address && address.name),
104
+ group: groupMembers
105
+ });
106
+ } else {
107
+ if (!data.address.length && data.text.length) {
108
+ for (i = data.text.length - 1; i >= 0; i--) {
109
+ if (!data.textWasQuoted[i] && data.text[i].match(/^[^@\s]+@[^@\s]+$/)) {
110
+ data.address = data.text.splice(i, 1);
111
+ data.textWasQuoted.splice(i, 1);
112
+ break;
113
+ }
114
+ }
115
+ let _regexHandler = function(address2) {
116
+ if (!data.address.length) {
117
+ data.address = [address2.trim()];
118
+ return " ";
119
+ } else {
120
+ return address2;
121
+ }
122
+ };
123
+ if (!data.address.length) {
124
+ for (i = data.text.length - 1; i >= 0; i--) {
125
+ if (!data.textWasQuoted[i]) {
126
+ data.text[i] = data.text[i].replace(/\s*\b[^@\s]+@[^\s]+\b\s*/, _regexHandler).trim();
127
+ if (data.address.length) {
128
+ break;
129
+ }
130
+ }
131
+ }
132
+ }
133
+ }
134
+ if (!data.text.length && data.comment.length) {
135
+ data.text = data.comment;
136
+ data.comment = [];
137
+ }
138
+ if (data.address.length > 1) {
139
+ data.text = data.text.concat(data.address.splice(1));
140
+ }
141
+ data.text = data.text.join(" ");
142
+ data.address = data.address.join(" ");
143
+ if (!data.address && /^=\?[^=]+?=$/.test(data.text.trim())) {
144
+ const parsedSubAddresses = addressParser((0, import_decode_strings.decodeWords)(data.text));
145
+ if (parsedSubAddresses && parsedSubAddresses.length) {
146
+ return parsedSubAddresses;
147
+ }
148
+ }
149
+ if (!data.address && isGroup) {
150
+ return [];
151
+ } else {
152
+ address = {
153
+ address: data.address || data.text || "",
154
+ name: (0, import_decode_strings.decodeWords)(data.text || data.address || "")
155
+ };
156
+ if (address.address === address.name) {
157
+ if ((address.address || "").match(/@/)) {
158
+ address.name = "";
159
+ } else {
160
+ address.address = "";
161
+ }
162
+ }
163
+ addresses.push(address);
164
+ }
165
+ }
166
+ return addresses;
167
+ }
168
+ class Tokenizer {
169
+ constructor(str) {
170
+ this.str = (str || "").toString();
171
+ this.operatorCurrent = "";
172
+ this.operatorExpecting = "";
173
+ this.node = null;
174
+ this.escaped = false;
175
+ this.list = [];
176
+ this.operators = {
177
+ '"': '"',
178
+ "(": ")",
179
+ "<": ">",
180
+ ",": "",
181
+ ":": ";",
182
+ // Semicolons are not a legal delimiter per the RFC2822 grammar other
183
+ // than for terminating a group, but they are also not valid for any
184
+ // other use in this context. Given that some mail clients have
185
+ // historically allowed the semicolon as a delimiter equivalent to the
186
+ // comma in their UI, it makes sense to treat them the same as a comma
187
+ // when used outside of a group.
188
+ ";": ""
189
+ };
190
+ }
191
+ /**
192
+ * Tokenizes the original input string
193
+ *
194
+ * @return {Array} An array of operator|text tokens
195
+ */
196
+ tokenize() {
197
+ let list = [];
198
+ for (let i = 0, len = this.str.length; i < len; i++) {
199
+ let chr = this.str.charAt(i);
200
+ let nextChr = i < len - 1 ? this.str.charAt(i + 1) : null;
201
+ this.checkChar(chr, nextChr);
202
+ }
203
+ this.list.forEach((node) => {
204
+ node.value = (node.value || "").toString().trim();
205
+ if (node.value) {
206
+ list.push(node);
207
+ }
208
+ });
209
+ return list;
210
+ }
211
+ /**
212
+ * Checks if a character is an operator or text and acts accordingly
213
+ *
214
+ * @param {String} chr Character from the address field
215
+ */
216
+ checkChar(chr, nextChr) {
217
+ if (this.escaped) {
218
+ } else if (chr === this.operatorExpecting) {
219
+ this.node = {
220
+ type: "operator",
221
+ value: chr
222
+ };
223
+ if (nextChr && ![" ", " ", "\r", "\n", ",", ";"].includes(nextChr)) {
224
+ this.node.noBreak = true;
225
+ }
226
+ this.list.push(this.node);
227
+ this.node = null;
228
+ this.operatorExpecting = "";
229
+ this.escaped = false;
230
+ return;
231
+ } else if (!this.operatorExpecting && chr in this.operators) {
232
+ this.node = {
233
+ type: "operator",
234
+ value: chr
235
+ };
236
+ this.list.push(this.node);
237
+ this.node = null;
238
+ this.operatorExpecting = this.operators[chr];
239
+ this.escaped = false;
240
+ return;
241
+ } else if (['"', "'"].includes(this.operatorExpecting) && chr === "\\") {
242
+ this.escaped = true;
243
+ return;
244
+ }
245
+ if (!this.node) {
246
+ this.node = {
247
+ type: "text",
248
+ value: ""
249
+ };
250
+ this.list.push(this.node);
251
+ }
252
+ if (chr === "\n") {
253
+ chr = " ";
254
+ }
255
+ if (chr.charCodeAt(0) >= 33 || [" ", " "].includes(chr)) {
256
+ this.node.value += chr;
257
+ }
258
+ this.escaped = false;
259
+ }
260
+ }
261
+ function addressParser(str, options) {
262
+ options = options || {};
263
+ let tokenizer = new Tokenizer(str);
264
+ let tokens = tokenizer.tokenize();
265
+ let addresses = [];
266
+ let address = [];
267
+ let parsedAddresses = [];
268
+ tokens.forEach((token) => {
269
+ if (token.type === "operator" && (token.value === "," || token.value === ";")) {
270
+ if (address.length) {
271
+ addresses.push(address);
272
+ }
273
+ address = [];
274
+ } else {
275
+ address.push(token);
276
+ }
277
+ });
278
+ if (address.length) {
279
+ addresses.push(address);
280
+ }
281
+ addresses.forEach((address2) => {
282
+ address2 = _handleAddress(address2);
283
+ if (address2.length) {
284
+ parsedAddresses = parsedAddresses.concat(address2);
285
+ }
286
+ });
287
+ if (options.flatten) {
288
+ let addresses2 = [];
289
+ let walkAddressList = (list) => {
290
+ list.forEach((address2) => {
291
+ if (address2.group) {
292
+ return walkAddressList(address2.group);
293
+ } else {
294
+ addresses2.push(address2);
295
+ }
296
+ });
297
+ };
298
+ walkAddressList(parsedAddresses);
299
+ return addresses2;
300
+ }
301
+ return parsedAddresses;
302
+ }
303
+ var address_parser_default = addressParser;
304
+
305
+ // Make default export work naturally with require()
306
+ if (module.exports.default) {
307
+ var defaultExport = module.exports.default;
308
+ var namedExports = {};
309
+ for (var key in module.exports) {
310
+ if (key !== 'default') {
311
+ namedExports[key] = module.exports[key];
312
+ }
313
+ }
314
+ module.exports = defaultExport;
315
+ Object.assign(module.exports, namedExports);
316
+ }
317
+