@social-mail/shared 1.0.3

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 (69) hide show
  1. package/.gitlab-ci.yml +16 -0
  2. package/.vscode/launch.json +21 -0
  3. package/.vscode/settings.json +55 -0
  4. package/README.md +3 -0
  5. package/dist/QueryIterator.js +28 -0
  6. package/dist/QueryIterator.js.map +1 -0
  7. package/dist/mime-parser/AttachmentFile.js +21 -0
  8. package/dist/mime-parser/AttachmentFile.js.map +1 -0
  9. package/dist/mime-parser/HeaderContentDisposition.js +32 -0
  10. package/dist/mime-parser/HeaderContentDisposition.js.map +1 -0
  11. package/dist/mime-parser/HeaderContentType.js +33 -0
  12. package/dist/mime-parser/HeaderContentType.js.map +1 -0
  13. package/dist/mime-parser/MimeMessage.js +99 -0
  14. package/dist/mime-parser/MimeMessage.js.map +1 -0
  15. package/dist/mime-parser/MimeNode.js +406 -0
  16. package/dist/mime-parser/MimeNode.js.map +1 -0
  17. package/dist/mime-parser/encoder/RawBuffer.js +57 -0
  18. package/dist/mime-parser/encoder/RawBuffer.js.map +1 -0
  19. package/dist/mime-parser/encoder/base64-to-blob.js +30 -0
  20. package/dist/mime-parser/encoder/base64-to-blob.js.map +1 -0
  21. package/dist/mime-parser/encoder/quoted-printable.js +71 -0
  22. package/dist/mime-parser/encoder/quoted-printable.js.map +1 -0
  23. package/dist/mime-parser/encoder/word-encoding.js +45 -0
  24. package/dist/mime-parser/encoder/word-encoding.js.map +1 -0
  25. package/dist/mime-parser/parsePairs.js +40 -0
  26. package/dist/mime-parser/parsePairs.js.map +1 -0
  27. package/dist/mime-parser/stream/LineStream.js +99 -0
  28. package/dist/mime-parser/stream/LineStream.js.map +1 -0
  29. package/dist/mime-parser/stream/TextWriter.js +36 -0
  30. package/dist/mime-parser/stream/TextWriter.js.map +1 -0
  31. package/dist/mime-parser/tokenizer.js +46 -0
  32. package/dist/mime-parser/tokenizer.js.map +1 -0
  33. package/dist/tests/mime/lines-test.js +19 -0
  34. package/dist/tests/mime/lines-test.js.map +1 -0
  35. package/dist/tests/mime/message1.js +58 -0
  36. package/dist/tests/mime/message1.js.map +1 -0
  37. package/dist/tests/mime/message2.js +77 -0
  38. package/dist/tests/mime/message2.js.map +1 -0
  39. package/dist/tests/mime/pairs.js +33 -0
  40. package/dist/tests/mime/pairs.js.map +1 -0
  41. package/dist/tests/mime/parse-headers.js +104 -0
  42. package/dist/tests/mime/parse-headers.js.map +1 -0
  43. package/dist/tests/mime/word-encoding-test.js +21 -0
  44. package/dist/tests/mime/word-encoding-test.js.map +1 -0
  45. package/dist/tsconfig.tsbuildinfo +1 -0
  46. package/index.js +4 -0
  47. package/package.json +17 -0
  48. package/src/QueryIterator.js +33 -0
  49. package/src/mime-parser/AttachmentFile.js +29 -0
  50. package/src/mime-parser/HeaderContentDisposition.js +32 -0
  51. package/src/mime-parser/HeaderContentType.js +34 -0
  52. package/src/mime-parser/MimeMessage.js +100 -0
  53. package/src/mime-parser/MimeNode.js +435 -0
  54. package/src/mime-parser/encoder/RawBuffer.js +60 -0
  55. package/src/mime-parser/encoder/base64-to-blob.js +26 -0
  56. package/src/mime-parser/encoder/quoted-printable.js +118 -0
  57. package/src/mime-parser/encoder/word-encoding.js +43 -0
  58. package/src/mime-parser/parsePairs.js +34 -0
  59. package/src/mime-parser/stream/LineStream.js +85 -0
  60. package/src/mime-parser/stream/TextWriter.js +27 -0
  61. package/src/mime-parser/tokenizer.js +37 -0
  62. package/src/tests/mime/lines-test.js +17 -0
  63. package/src/tests/mime/message1.js +46 -0
  64. package/src/tests/mime/message2.js +73 -0
  65. package/src/tests/mime/pairs.js +38 -0
  66. package/src/tests/mime/parse-headers.js +90 -0
  67. package/src/tests/mime/word-encoding-test.js +13 -0
  68. package/test.js +70 -0
  69. package/tsconfig.json +16 -0
@@ -0,0 +1,60 @@
1
+ const toBuffer = (
2
+ /** @type {string} */ text
3
+ ) => {
4
+ const bytes = new Uint8Array(text.length);
5
+ for (let index = 0; index < text.length; index++) {
6
+ bytes[index] = text.charCodeAt(index);
7
+ }
8
+ return bytes.buffer;
9
+ };
10
+
11
+ export const RawBuffer = {
12
+ decode: (
13
+ /** @type {string} */ bytesAsString,
14
+ /** @type {string} */ encoding = "utf-8"
15
+ ) => {
16
+ if (!encoding) {
17
+ return bytesAsString;
18
+ }
19
+ const te = new TextDecoder(encoding);
20
+ return te.decode(toBuffer(bytesAsString));
21
+ },
22
+
23
+ encode: (
24
+ /** @type {string} */ text,
25
+ /** @type {string} */ encoding = "utf-8"
26
+ ) => {
27
+ if (!(/utf\-?8/i.test(encoding))) {
28
+ throw new Error(`Encoding ${encoding} not supported`);
29
+ }
30
+ const te = new TextEncoder();
31
+ const array = te.encode(text);
32
+ const a = [];
33
+ for (const iterator of array) {
34
+ a.push(String.fromCharCode(iterator));
35
+ }
36
+ return a.join("");
37
+ },
38
+
39
+ toBase64Async(
40
+ /** @type {Blob} */ blob,
41
+ asDataUrl = false
42
+ ) {
43
+ return new Promise<string>((resolve, reject) => {
44
+ const reader = new FileReader();
45
+ reader.onerror = () => {
46
+ reject(reader.error);
47
+ };
48
+ reader.onload = () => {
49
+ const /** @type {string} */ text = reader.result;
50
+ if (asDataUrl) {
51
+ resolve(text);
52
+ return;
53
+ }
54
+ const index = text.indexOf(",");
55
+ resolve(text.substring(index + 1));
56
+ };
57
+ reader.readAsDataURL(blob);
58
+ });
59
+ }
60
+ };
@@ -0,0 +1,26 @@
1
+ /**
2
+ *
3
+ * @param {string} base64Data
4
+ * @param {string} contentType
5
+ * @returns
6
+ */
7
+ export function base64toBlob(base64Data, contentType) {
8
+ contentType = contentType || '';
9
+ const sliceSize = 1024;
10
+ const byteCharacters = atob(base64Data);
11
+ const bytesLength = byteCharacters.length;
12
+ const slicesCount = Math.ceil(bytesLength / sliceSize);
13
+ const byteArrays = new Array(slicesCount);
14
+
15
+ for (let sliceIndex = 0; sliceIndex < slicesCount; ++sliceIndex) {
16
+ const begin = sliceIndex * sliceSize;
17
+ const end = Math.min(begin + sliceSize, bytesLength);
18
+
19
+ const bytes = new Array(end - begin);
20
+ for (let offset = begin, i = 0; offset < end; ++i, ++offset) {
21
+ bytes[i] = byteCharacters[offset].charCodeAt(0);
22
+ }
23
+ byteArrays[sliceIndex] = new Uint8Array(bytes);
24
+ }
25
+ return new Blob(byteArrays, { type: contentType });
26
+ }
@@ -0,0 +1,118 @@
1
+ // Original Source Code: https://github.com/mathiasbynens/quoted-printable/blob/master/quoted-printable.js
2
+
3
+
4
+ const stringFromCharCode = String.fromCharCode;
5
+ const decode = function(input) {
6
+ return input
7
+ // https://tools.ietf.org/html/rfc2045#section-6.7, rule 3:
8
+ // “Therefore, when decoding a `Quoted-Printable` body, any trailing white
9
+ // space on a line must be deleted, as it will necessarily have been added
10
+ // by intermediate transport agents.”
11
+ .replace(/[\t\x20]$/gm, '')
12
+ // Remove hard line breaks preceded by `=`. Proper `Quoted-Printable`-
13
+ // encoded data only contains CRLF line endings, but for compatibility
14
+ // reasons we support separate CR and LF too.
15
+ .replace(/=(?:\r\n?|\n|$)/g, '')
16
+ // Decode escape sequences of the form `=XX` where `XX` is any
17
+ // combination of two hexidecimal digits. For optimal compatibility,
18
+ // lowercase hexadecimal digits are supported as well. See
19
+ // https://tools.ietf.org/html/rfc2045#section-6.7, note 1.
20
+ .replace(/=([a-fA-F0-9]{2})/g, function($0, $1) {
21
+ const codePoint = parseInt($1, 16);
22
+ return stringFromCharCode(codePoint);
23
+ });
24
+ };
25
+
26
+ const handleTrailingCharacters =
27
+ function(
28
+ /** @type {string} */
29
+ text) {
30
+ return text
31
+ .replace(/\x20$/, '=20') // Handle trailing space.
32
+ .replace(/\t$/, '=09'); // Handle trailing tab.
33
+ };
34
+
35
+ const regexUnsafeSymbols = /[\0-\x08\n-\x1F=\x7F-\uFFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]/g;
36
+ const encode = function(text) {
37
+
38
+ // Encode symbols that are definitely unsafe (i.e. unsafe in any context).
39
+ const encoded = text.replace(regexUnsafeSymbols, function(symbol) {
40
+ if (symbol > '\xFF') {
41
+ throw RangeError(
42
+ '`quotedPrintable.encode()` expects extended ASCII input only. ' +
43
+ 'Don\u2019t forget to encode the input first using a character ' +
44
+ 'encoding like UTF-8.'
45
+ );
46
+ }
47
+ const codePoint = symbol.charCodeAt(0);
48
+ const hexadecimal = codePoint.toString(16).toUpperCase();
49
+ return '=' + ('0' + hexadecimal).slice(-2);
50
+ });
51
+
52
+ const LINE_LENGTH = 75;
53
+
54
+ // Limit lines to 76 characters (not counting the CRLF line endings).
55
+ const lines = encoded.split(/\r\n?|\n/g);
56
+ let lineIndex = -1;
57
+ const lineCount = lines.length;
58
+ const result = [];
59
+ let buffer;
60
+ while (++lineIndex < lineCount) {
61
+ const line = lines[lineIndex];
62
+ // Leave room for the trailing `=` for soft line breaks.
63
+ let index = 0;
64
+ const length = line.length;
65
+ while (index < length) {
66
+ buffer = encoded.slice(index, index + LINE_LENGTH);
67
+ // If this line ends with `=`, optionally followed by a single uppercase
68
+ // hexadecimal digit, we broke an escape sequence in half. Fix it by
69
+ // moving these characters to the next line.
70
+ if (/=$/.test(buffer)) {
71
+ buffer = buffer.slice(0, LINE_LENGTH - 1);
72
+ index += LINE_LENGTH - 1;
73
+ } else if (/=[A-F0-9]$/.test(buffer)) {
74
+ buffer = buffer.slice(0, LINE_LENGTH - 2);
75
+ index += LINE_LENGTH - 2;
76
+ } else {
77
+ index += LINE_LENGTH;
78
+ }
79
+ result.push(buffer);
80
+ }
81
+ }
82
+
83
+ // Encode space and tab characters at the end of encoded lines. Note that
84
+ // with the current implementation, this can only occur at the very end of
85
+ // the encoded string — every other line ends with `=` anyway.
86
+ const lastLineLength = buffer.length;
87
+ if (/[\t\x20]$/.test(buffer)) {
88
+ // There’s a space or a tab at the end of the last encoded line. Remove
89
+ // this line from the `result` array, as it needs to change.
90
+ result.pop();
91
+ if (lastLineLength + 2 <= LINE_LENGTH + 1) {
92
+ // It’s possible to encode the character without exceeding the line
93
+ // length limit.
94
+ result.push(
95
+ handleTrailingCharacters(buffer)
96
+ );
97
+ } else {
98
+ // It’s not possible to encode the character without exceeding the line
99
+ // length limit. Remvoe the character from the line, and insert a new
100
+ // line that contains only the encoded character.
101
+ result.push(
102
+ buffer.slice(0, lastLineLength - 1),
103
+ handleTrailingCharacters(
104
+ buffer.slice(lastLineLength - 1, lastLineLength)
105
+ )
106
+ );
107
+ }
108
+ }
109
+
110
+ // `Quoted-Printable` uses CRLF.
111
+ return result.join('=\r\n');
112
+ };
113
+
114
+ export const quotedPrintable = {
115
+ encode,
116
+ decode,
117
+ 'version': '1.0.1'
118
+ };
@@ -0,0 +1,43 @@
1
+ import { RawBuffer } from "./RawBuffer.js";
2
+
3
+ const isEncodedRegEx = /\=\?([^\?\s]+)\?([^\?\s]{1})\?([^\s\?]+)\?\=\s*/gm;
4
+
5
+ const hexToUnicode = /(\=([0-9a-f]{2}))|(\_)/gmi;
6
+
7
+ const decode = (
8
+ /** @type {string} */ text
9
+ ) => {
10
+ return text.replace(isEncodedRegEx, (matched, encoding, format, /** @type {string} */ buffer) => {
11
+ if (/b/i.test(format)) {
12
+ return RawBuffer.decode(atob(buffer), encoding);
13
+ }
14
+ if (/q/i.test(format)) {
15
+ const replaced = buffer.replace(hexToUnicode, (m, group, /** @type {string} */ code, ) => {
16
+ if (m === "_") {
17
+ return " ";
18
+ }
19
+ return String.fromCharCode(parseInt(code, 16));
20
+ });
21
+ return RawBuffer.decode( replaced, encoding);
22
+ }
23
+ return matched;
24
+ });
25
+ };
26
+
27
+ const isSimpleText = /[0-9a-zA-Z\x20]/;
28
+
29
+ export const wordEncoding = {
30
+ decode,
31
+
32
+ encode: (/** @type {string} */ word, ifNeeded = false) => {
33
+
34
+ if(ifNeeded) {
35
+ if(isSimpleText.test(word)) {
36
+ return word;
37
+ }
38
+ }
39
+
40
+ word = RawBuffer.encode(word);
41
+ return `=?UTF-8?B?${btoa(word)}?=`;
42
+ }
43
+ };
@@ -0,0 +1,34 @@
1
+ import { tokenize, tokenizeMax } from "./tokenizer.js";
2
+
3
+
4
+ export const parsePairs = (
5
+ /** @type {string} */ text,
6
+ /** @type {string} */ emptyName) => {
7
+ const pairs = {
8
+ toString() {
9
+ return text;
10
+ }
11
+ };
12
+ for (const iterator of tokenize(text, ";")) {
13
+ const trimmed = iterator.trim();
14
+ if (!trimmed) {
15
+ break;
16
+ }
17
+ const items = tokenizeMax(trimmed, "=", 2).map((x) => x.trim());
18
+ let [key, value] = items;
19
+ if (items.length === 1) {
20
+ value = key;
21
+ key = emptyName ?? key;
22
+ }
23
+ try {
24
+ if (value.startsWith('"')) {
25
+ value = JSON.parse(value);
26
+ }
27
+ } catch {
28
+ // ignore
29
+ }
30
+ // pairs[key] = wordEncoding.decode(value);
31
+ pairs[key] = value;
32
+ }
33
+ return pairs;
34
+ };
@@ -0,0 +1,85 @@
1
+ import { tokenize } from "../tokenizer.js";
2
+
3
+ export default class LineStream {
4
+
5
+ /** @type {AsyncGenerator<string, any, any>} */ g;
6
+
7
+ /**
8
+ * @returns {AsyncGenerator<string, any, any>}
9
+ */
10
+ async *read() {
11
+ const g = this.g ??= this.lines();
12
+ for(;;) {
13
+ const { value , done } = await g.next();
14
+ if (done) {
15
+ break;
16
+ }
17
+ yield value;
18
+ }
19
+ }
20
+
21
+ /** @returns {AsyncGenerator<string, any,any>} */
22
+ lines() {
23
+ throw new Error("Not Implemented");
24
+ }
25
+ }
26
+
27
+ export class StringLineStream extends LineStream {
28
+
29
+ /** @type {string} */ text;
30
+
31
+ constructor( /** @type {string} */ text) {
32
+ super();
33
+ this.text = text;
34
+ }
35
+
36
+ /** @returns {AsyncGenerator<string, any, any>} */
37
+ async *lines() {
38
+ for (const iterator of tokenize(this.text, "\n")) {
39
+ yield iterator;
40
+ }
41
+ }
42
+ }
43
+
44
+ export class ReadableLineStream extends LineStream {
45
+
46
+ /** @type {ReadableStream} */
47
+ readable;
48
+
49
+ constructor( /** @type {ReadableStream} */ readable) {
50
+ super();
51
+ this.readable = readable;
52
+ }
53
+
54
+ /** @returns {AsyncGenerator<string, any, any>} */
55
+ async *lines() {
56
+ const utf8Decoder = new TextDecoder("utf-8");
57
+ const reader = this.readable.getReader();
58
+ let { value: chunk, done: readerDone } = await reader.read();
59
+ chunk = chunk ? utf8Decoder.decode(chunk, { stream: true }) : "";
60
+
61
+ const re = /\r\n|\n|\r/gm;
62
+ let startIndex = 0;
63
+
64
+ for (;;) {
65
+ const result = re.exec(chunk);
66
+ if (!result) {
67
+ if (readerDone) {
68
+ break;
69
+ }
70
+ const remainder = chunk.substr(startIndex);
71
+ ({ value: chunk, done: readerDone } = await reader.read());
72
+ chunk =
73
+ remainder + (chunk ? utf8Decoder.decode(chunk, { stream: true }) : "");
74
+ startIndex = re.lastIndex = 0;
75
+ continue;
76
+ }
77
+ yield chunk.substring(startIndex, result.index);
78
+ startIndex = re.lastIndex;
79
+ }
80
+ if (startIndex < chunk.length) {
81
+ // last line didn't end in a newline char
82
+ yield chunk.substr(startIndex);
83
+ }
84
+ }
85
+ }
@@ -0,0 +1,27 @@
1
+ export default class TextWriter {
2
+
3
+ writeLine(/** @type {string} */ line) {
4
+ throw new Error("Not Implemented");
5
+ }
6
+
7
+ }
8
+
9
+ export class BlobWriter extends TextWriter {
10
+
11
+ /** @type {string[]} */
12
+ lines = [];
13
+
14
+ writeLine(/** @type {string} */line) {
15
+ this.lines.push(line);
16
+ }
17
+
18
+ toBlob() {
19
+ const all = this.lines.join("\n");
20
+ return new Blob([all], {});
21
+ }
22
+
23
+ toString() {
24
+ return this.lines.join("\n");
25
+ }
26
+
27
+ }
@@ -0,0 +1,37 @@
1
+ export function *tokenize (
2
+ /** @type {string} */ text,
3
+ /** @type {string} */ sep = ",") {
4
+ let start = 0;
5
+ for(;;) {
6
+ const index = text.indexOf(sep, start);
7
+ if (index === -1) {
8
+ break;
9
+ }
10
+ yield text.substring(start, index);
11
+ start = index + 1;
12
+ }
13
+ yield text.substring(start);
14
+ };
15
+
16
+
17
+ export function tokenizeMax(
18
+ /** @type {string} */ text,
19
+ /** @type {string} */ sep,
20
+ /** @type {number} */ max = Number.MAX_SAFE_INTEGER) {
21
+ let start = 0;
22
+ const items = [];
23
+ max--;
24
+ for(;;) {
25
+ const index = text.indexOf(sep, start);
26
+ if (index === -1) {
27
+ break;
28
+ }
29
+ items.push(text.substring(start, index));
30
+ start = index + 1;
31
+ if (--max === 0) {
32
+ break;
33
+ }
34
+ }
35
+ items.push(text.substring(start));
36
+ return items;
37
+ };
@@ -0,0 +1,17 @@
1
+ import assert from "assert";
2
+ import { tokenize } from "../../mime-parser/tokenizer.js";
3
+
4
+
5
+ export default function () {
6
+
7
+ assert.equal(
8
+ "a,b",
9
+ Array.from(tokenize("a\nb", "\n")));
10
+
11
+ assert.equal(
12
+ "a b",
13
+ Array.from(tokenize("a b", "\n")));
14
+ assert.equal(
15
+ "a,,b",
16
+ Array.from(tokenize("a\n\nb", "\n")));
17
+ }
@@ -0,0 +1,46 @@
1
+ import assert from "assert";
2
+ import { MimeNode } from "../../mime-parser/MimeNode.js";
3
+ import { StringLineStream } from "../../mime-parser/stream/LineStream.js";
4
+
5
+ const msg1 = `From: Some One <someone@example.com>
6
+ MIME-Version: 1.0
7
+ Content-Type: multipart/mixed;
8
+ boundary="XXXXboundary text"
9
+
10
+ This is a multipart message in MIME format.
11
+
12
+ --XXXXboundary text
13
+ Content-Type: text/plain
14
+
15
+ this is the body text
16
+
17
+ --XXXXboundary text
18
+ Content-Type: text/plain;
19
+ Content-Disposition: attachment;
20
+ filename="test.txt"
21
+
22
+ this is the attachment text
23
+
24
+ --XXXXboundary text--
25
+
26
+ `;
27
+
28
+ export default async function () {
29
+ const root = new MimeNode();
30
+ await root.parse(new StringLineStream(msg1));
31
+
32
+ assert.strictEqual("multipart/mixed", root.contentType.type);
33
+ assert.strictEqual("XXXXboundary text", root.contentType.boundary);
34
+ assert.strictEqual("Some One <someone@example.com>", root.header("from"));
35
+ // Assert.strictEqual("This is a multipart message in MIME format.", root.encoded.trim());
36
+ assert.strictEqual(2, root.children.length);
37
+
38
+ const body = root.children[0];
39
+ assert.strictEqual("text/plain", body.contentType.type);
40
+ assert.strictEqual("this is the body text", body.encoded.trim());
41
+
42
+ const attachment = root.children[1];
43
+ assert.strictEqual("text/plain", attachment.contentType.type);
44
+ assert.strictEqual("this is the attachment text", attachment.encoded.trim());
45
+ assert.strictEqual("test.txt", attachment.contentDisposition.filename);
46
+ }
@@ -0,0 +1,73 @@
1
+ import assert from "assert";
2
+ import { MimeNode } from "../../mime-parser/MimeNode.js";
3
+ import { StringLineStream } from "../../mime-parser/stream/LineStream.js";
4
+ import { BlobWriter } from "../../mime-parser/stream/TextWriter.js";
5
+
6
+ const msg1 = `From: Some One <someone@example.com>
7
+ MIME-Version: 1.0
8
+ Content-Type: multipart/mixed;
9
+ boundary="XXXXboundary text"
10
+
11
+ This is a multipart message in MIME format.
12
+
13
+ --XXXXboundary text
14
+ Content-Type: text/plain
15
+
16
+ this is the body text
17
+
18
+ --XXXXboundary text
19
+ Content-Type: text/html; charset="UTF-8";
20
+ Content-Transfer-Encoding: quoted-printable
21
+
22
+ <div dir=3D"ltr"><font face=3D"trebuchet ms, sans-serif">Seeking Actors and=
23
+ Actresses for a micro budget feature. The story revolves around a couple a=
24
+ nd their guests who find themselves in a tense and gripping situation. They=
25
+ are taken hostage in their own home by a radical gunman who lurks outside.=
26
+ We are looking for individuals who can bring depth and authenticity to the=
27
+ ir characters. The ability to convey a range of emotions and create believa=
28
+ ble performances is crucial for this project.<br><br>Submit ASAP through:=
29
+ =C2=A0<a href=3D"https://socialmail.in">https://socialmail.in</a><br><br>=
30
+ #newmexicoactors #newmexicocasting=C2=A0</font><br></div>
31
+
32
+ --XXXXboundary text--
33
+
34
+ `;
35
+
36
+ export default async function () {
37
+ const root = new MimeNode();
38
+ await root.parse(new StringLineStream(msg1));
39
+
40
+ assertAll(root);
41
+
42
+ // write and read it again...
43
+
44
+ const blob = new BlobWriter();
45
+ await root.save(blob);
46
+
47
+ const encoded = blob.toString();
48
+
49
+ // reparse...
50
+ const root2 = new MimeNode();
51
+ await root2.parse(new StringLineStream(encoded));
52
+
53
+ assertAll(root2);
54
+ }
55
+
56
+ function assertAll( /** @type {MimeNode} */ root) {
57
+ assert.strictEqual("multipart/mixed", root.contentType.type);
58
+ assert.strictEqual("XXXXboundary text", root.contentType.boundary);
59
+ assert.strictEqual("Some One <someone@example.com>", root.header("from"));
60
+ // assert.strictEqual("This is a multipart message in MIME format.", root.encoded.trim());
61
+ assert.strictEqual(2, root.children.length);
62
+
63
+ const body = root.children[0];
64
+ assert.strictEqual("text/plain", body.contentType.type);
65
+ assert.strictEqual("this is the body text", body.encoded.trim());
66
+
67
+ const html = root.children[1];
68
+ assert.strictEqual("text/html", html.contentType.type);
69
+ assert.strictEqual("quoted-printable", html.contentTransferEncoding);
70
+
71
+ const data = html.text;
72
+ assert.strictEqual(`<div dir="ltr"><font face="trebuchet ms, sans-serif">Seeking Actors and Actresses for a micro budget feature. The story revolves around a couple and their guests who find themselves in a tense and gripping situation. They are taken hostage in their own home by a radical gunman who lurks outside. We are looking for individuals who can bring depth and authenticity to their characters. The ability to convey a range of emotions and create believable performances is crucial for this project.<br><br>Submit ASAP through: <a href="https://socialmail.in">https://socialmail.in</a><br><br>#newmexicoactors #newmexicocasting </font><br></div>`, data);
73
+ }
@@ -0,0 +1,38 @@
1
+ import { parsePairs } from "../../mime-parser/parsePairs.js";
2
+
3
+ const equalJson = (e, t, m) => {
4
+ e = JSON.stringify(e);
5
+ t = JSON.stringify(t);
6
+ // eslint-disable-next-line eqeqeq
7
+ if (e !== t) {
8
+ throw new Error(m ?? `Assertion failed ${e} !== ${t}`);
9
+ }
10
+ }
11
+
12
+ export default function() {
13
+
14
+ equalJson(
15
+ parsePairs("multipart/mixed; boundary=--abcd"),
16
+ {
17
+ "multipart/mixed": "multipart/mixed",
18
+ boundary: "--abcd"
19
+ }
20
+ );
21
+
22
+
23
+ equalJson(
24
+ parsePairs("multipart/mixed; boundary=--abcd", "type"),
25
+ {
26
+ type: "multipart/mixed",
27
+ boundary: "--abcd"
28
+ }
29
+ );
30
+
31
+ equalJson(
32
+ parsePairs("multipart/mixed;", "type"),
33
+ {
34
+ type: "multipart/mixed"
35
+ }
36
+ );
37
+
38
+ }