@social-mail/shared 1.1.3 → 1.1.5
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/.vscode/settings.json +1 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/mime-parser/HeaderContactList.d.ts +15 -0
- package/dist/mime-parser/HeaderContactList.d.ts.map +1 -0
- package/dist/mime-parser/HeaderContactList.js +37 -0
- package/dist/mime-parser/HeaderContactList.js.map +1 -0
- package/dist/mime-parser/HeaderContentDisposition.d.ts +1 -1
- package/dist/mime-parser/HeaderContentDisposition.d.ts.map +1 -1
- package/dist/mime-parser/HeaderContentDisposition.js +4 -6
- package/dist/mime-parser/HeaderContentDisposition.js.map +1 -1
- package/dist/mime-parser/HeaderContentType.d.ts +1 -1
- package/dist/mime-parser/HeaderContentType.d.ts.map +1 -1
- package/dist/mime-parser/HeaderContentType.js +4 -6
- package/dist/mime-parser/HeaderContentType.js.map +1 -1
- package/dist/mime-parser/HeaderEmailAddress.d.ts +10 -0
- package/dist/mime-parser/HeaderEmailAddress.d.ts.map +1 -0
- package/dist/mime-parser/HeaderEmailAddress.js +27 -0
- package/dist/mime-parser/HeaderEmailAddress.js.map +1 -0
- package/dist/mime-parser/HeaderLinks.d.ts +9 -0
- package/dist/mime-parser/HeaderLinks.d.ts.map +1 -0
- package/dist/mime-parser/HeaderLinks.js +42 -0
- package/dist/mime-parser/HeaderLinks.js.map +1 -0
- package/dist/mime-parser/MimeEmailAddressParser.d.ts +13 -0
- package/dist/mime-parser/MimeEmailAddressParser.d.ts.map +1 -0
- package/dist/mime-parser/MimeEmailAddressParser.js +17 -0
- package/dist/mime-parser/MimeEmailAddressParser.js.map +1 -0
- package/dist/mime-parser/MimeHeaders.d.ts +19 -0
- package/dist/mime-parser/MimeHeaders.d.ts.map +1 -0
- package/dist/mime-parser/MimeHeaders.js +47 -0
- package/dist/mime-parser/MimeHeaders.js.map +1 -0
- package/dist/mime-parser/MimeMessage.d.ts +34 -1
- package/dist/mime-parser/MimeMessage.d.ts.map +1 -1
- package/dist/mime-parser/MimeMessage.js +41 -7
- package/dist/mime-parser/MimeMessage.js.map +1 -1
- package/dist/mime-parser/MimeNode.d.ts +7 -4
- package/dist/mime-parser/MimeNode.d.ts.map +1 -1
- package/dist/mime-parser/MimeNode.js +24 -32
- package/dist/mime-parser/MimeNode.js.map +1 -1
- package/dist/mime-parser/RegexGenerator.d.ts +9 -0
- package/dist/mime-parser/RegexGenerator.d.ts.map +1 -0
- package/dist/mime-parser/RegexGenerator.js +40 -0
- package/dist/mime-parser/RegexGenerator.js.map +1 -0
- package/dist/mime-parser/stream/TextWriter.d.ts +1 -0
- package/dist/mime-parser/stream/TextWriter.d.ts.map +1 -1
- package/dist/mime-parser/stream/TextWriter.js +13 -0
- package/dist/mime-parser/stream/TextWriter.js.map +1 -1
- package/package.json +1 -1
- package/src/mime-parser/HeaderContactList.ts +41 -0
- package/src/mime-parser/HeaderContentDisposition.ts +6 -7
- package/src/mime-parser/HeaderContentType.ts +6 -7
- package/src/mime-parser/HeaderEmailAddress.ts +38 -0
- package/src/mime-parser/HeaderLinks.ts +48 -0
- package/src/mime-parser/MimeEmailAddressParser.ts +22 -0
- package/src/mime-parser/MimeHeaders.ts +62 -0
- package/src/mime-parser/MimeMessage.ts +47 -7
- package/src/mime-parser/MimeNode.ts +28 -37
- package/src/mime-parser/RegexGenerator.ts +42 -0
- package/src/mime-parser/stream/TextWriter.ts +14 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import MimeAddressParser from "./MimeEmailAddressParser.js";
|
|
2
|
+
|
|
3
|
+
export default class HeaderEmailAddress {
|
|
4
|
+
|
|
5
|
+
static parse(text: string) {
|
|
6
|
+
if (/(\<|\>)/i.test(text)) {
|
|
7
|
+
for(const g of MimeAddressParser.parseContactAddressList(text)) {
|
|
8
|
+
const e1 = new HeaderEmailAddress(g.address, g.name);
|
|
9
|
+
return e1;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
const e = new HeaderEmailAddress(text);
|
|
13
|
+
return e;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Username (part before the @ sign)
|
|
18
|
+
*/
|
|
19
|
+
public readonly username: string;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Domain name part of the email address
|
|
23
|
+
*/
|
|
24
|
+
public readonly domain: string;
|
|
25
|
+
|
|
26
|
+
constructor(public emailAddress: string, public name?: string) {
|
|
27
|
+
const r = MimeAddressParser.parseEmailAddress(emailAddress);
|
|
28
|
+
this.domain = r?.domain;
|
|
29
|
+
this.username = r?.user;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
toString() {
|
|
33
|
+
if (this.name) {
|
|
34
|
+
return `${this.name} <${this.emailAddress}>`;
|
|
35
|
+
}
|
|
36
|
+
return `<${this.emailAddress}>`;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export default class HeaderLinks {
|
|
2
|
+
|
|
3
|
+
static parse(value: string) {
|
|
4
|
+
const h = new HeaderLinks();
|
|
5
|
+
const { urls, mailTos, rawLinks } = h;
|
|
6
|
+
for(const line of value.split(/\[\,\;]/g)) {
|
|
7
|
+
if (!line) {
|
|
8
|
+
continue;
|
|
9
|
+
}
|
|
10
|
+
const a = /\<([^\>]+)\>/.exec(line.trim());
|
|
11
|
+
if(!a) {
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
const s = a[1];
|
|
15
|
+
rawLinks.push(s);
|
|
16
|
+
if(/https?\:\/\//i.test(s)) {
|
|
17
|
+
try {
|
|
18
|
+
urls.push(new URL(s));
|
|
19
|
+
} catch {
|
|
20
|
+
// do nothing...
|
|
21
|
+
}
|
|
22
|
+
continue;
|
|
23
|
+
}
|
|
24
|
+
if(/mailto\:/i.test(s)) {
|
|
25
|
+
try {
|
|
26
|
+
mailTos.push(new URL(s));
|
|
27
|
+
} catch {
|
|
28
|
+
// do nothing..
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return h;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public readonly urls = [] as URL[];
|
|
36
|
+
|
|
37
|
+
public readonly mailTos = [] as URL[];
|
|
38
|
+
|
|
39
|
+
public readonly rawLinks = [] as string[];
|
|
40
|
+
|
|
41
|
+
constructor() {
|
|
42
|
+
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
toString() {
|
|
46
|
+
return this.rawLinks.join(",");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import RegexGenerator from "./RegexGenerator.js";
|
|
2
|
+
|
|
3
|
+
export const emailAddressHeaderRegex = /(?<name>(("(("")|[^"])+")|([^\<]+)))?\s*\<(?<address>[^\>]+)\>/g;
|
|
4
|
+
|
|
5
|
+
export const emailAddressRegex = /(?<user>(("(""|[^"])+")|([^\@])+))\@(?<domain>[^\s\,\;]*)$/g;
|
|
6
|
+
|
|
7
|
+
export default class MimeAddressParser {
|
|
8
|
+
|
|
9
|
+
public static parseEmailAddress(text: string) {
|
|
10
|
+
const r = emailAddressRegex.exec(text);
|
|
11
|
+
return r?.groups as { user: string, domain: string } ;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
public static *parseContactAddressList(text: string) {
|
|
15
|
+
const rg = new RegexGenerator(emailAddressHeaderRegex);
|
|
16
|
+
for(const g of rg.groups(text)) {
|
|
17
|
+
const { name, address } = g.groups;
|
|
18
|
+
yield { name, address };
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export class MimeHeader {
|
|
2
|
+
|
|
3
|
+
public readonly keyLowerCase: string;
|
|
4
|
+
|
|
5
|
+
private lastParsed: { c, value };
|
|
6
|
+
|
|
7
|
+
constructor(public readonly key: string, public readonly value: string) {
|
|
8
|
+
this.keyLowerCase = key.toLowerCase();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
as<Parsable>(c: { parse(text: string): Parsable }): Parsable {
|
|
12
|
+
const { lastParsed } = this;
|
|
13
|
+
if (lastParsed?.c === c) {
|
|
14
|
+
return lastParsed.value;
|
|
15
|
+
}
|
|
16
|
+
const value = c.parse(this.value);
|
|
17
|
+
this.lastParsed = { c, value };
|
|
18
|
+
return value as Parsable;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* This class holds the headers in the way they were received.
|
|
24
|
+
* It has different methods to retrive headers.
|
|
25
|
+
* `first` will only display first header.
|
|
26
|
+
* `last` will only display last header in the order.
|
|
27
|
+
*/
|
|
28
|
+
export default class MimeHeaders {
|
|
29
|
+
|
|
30
|
+
private headers = [] as MimeHeader[];
|
|
31
|
+
|
|
32
|
+
private map = new Map<string, MimeHeader[]>()
|
|
33
|
+
|
|
34
|
+
public entries() {
|
|
35
|
+
return this.headers[Symbol.iterator]();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
public append(key: string, value: string) {
|
|
39
|
+
const h = new MimeHeader(key, value);
|
|
40
|
+
this.headers.push(h);
|
|
41
|
+
const lowerKey = key.toLowerCase();
|
|
42
|
+
let existing = this.map.get(lowerKey);
|
|
43
|
+
if (!existing) {
|
|
44
|
+
existing = [];
|
|
45
|
+
this.map.set(lowerKey, existing);
|
|
46
|
+
}
|
|
47
|
+
existing.push(h);
|
|
48
|
+
return h;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
public set(key: string, value: string) {
|
|
52
|
+
const lowerkey = key.toLowerCase();
|
|
53
|
+
this.headers = this.headers.filter((x) => x.keyLowerCase !== lowerkey);
|
|
54
|
+
this.map.delete(lowerkey);
|
|
55
|
+
this.append(key, value);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
public first(key: string) {
|
|
59
|
+
return this.map.get(key.toLowerCase())?.[0];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
}
|
|
@@ -2,12 +2,11 @@ import { MimeNode } from "./MimeNode.js";
|
|
|
2
2
|
import { AttachmentFile } from "./AttachmentFile.js";
|
|
3
3
|
import LineStream, { ReadableLineStream, StringLineStream } from "./stream/LineStream.js";
|
|
4
4
|
import { BlobWriter } from "./stream/TextWriter.js";
|
|
5
|
+
import HeaderLinks from "./HeaderLinks.js";
|
|
6
|
+
import HeaderEmailAddress from "./HeaderEmailAddress.js";
|
|
7
|
+
import HeaderContactList from "./HeaderContactList.js";
|
|
5
8
|
|
|
6
9
|
export default class MimeMessage {
|
|
7
|
-
constructor(
|
|
8
|
-
private node: MimeNode = new MimeNode("multipart/mixed"),
|
|
9
|
-
private attachments: AttachmentFile[] = []
|
|
10
|
-
) {}
|
|
11
10
|
|
|
12
11
|
static async load(
|
|
13
12
|
reader: ReadableStream | LineStream | string,
|
|
@@ -36,7 +35,7 @@ export default class MimeMessage {
|
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
get name(): string {
|
|
39
|
-
return this.node.
|
|
38
|
+
return this.node.firstHeaderString("x-name") || "";
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
get html(): string {
|
|
@@ -44,6 +43,10 @@ export default class MimeMessage {
|
|
|
44
43
|
return alternative?.getFirstChild("text/html")?.text || "";
|
|
45
44
|
}
|
|
46
45
|
|
|
46
|
+
get listUnsubscribe() {
|
|
47
|
+
return this.node.firstHeaderObject("list-unsubscribe")?.as(HeaderLinks);
|
|
48
|
+
}
|
|
49
|
+
|
|
47
50
|
set html(v: string) {
|
|
48
51
|
const alternative = this.node.getFirstChild("multipart/alternative", true);
|
|
49
52
|
const htmlMime = alternative.getFirstChild("text/html", true);
|
|
@@ -66,12 +69,49 @@ export default class MimeMessage {
|
|
|
66
69
|
}
|
|
67
70
|
|
|
68
71
|
get subject(): string {
|
|
69
|
-
return this.node.
|
|
72
|
+
return this.node.firstHeaderString("subject") || "";
|
|
70
73
|
}
|
|
71
74
|
|
|
72
75
|
set subject(v: string) {
|
|
73
|
-
this.node.setHeader("
|
|
76
|
+
this.node.setHeader("Subject", v);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get from(): HeaderEmailAddress {
|
|
80
|
+
return this.node.firstHeaderObject("from")?.as(HeaderEmailAddress);
|
|
81
|
+
}
|
|
82
|
+
set from(v: string | { name: string, emailAddress: string }) {
|
|
83
|
+
if (typeof v === "string") {
|
|
84
|
+
this.node.setHeader("From", v);
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
this.node.setHeader("From", new HeaderEmailAddress(v.emailAddress, v.name).toString());
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
get to(): HeaderContactList {
|
|
91
|
+
return this.node.firstHeaderObject("to")?.as(HeaderContactList);
|
|
74
92
|
}
|
|
93
|
+
set to(v: string | { name: string, emailAddress: string } | { name: string, emailAddress: string }[]) {
|
|
94
|
+
this.node.setHeader("To", HeaderContactList.from(v).toString());
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
get cc(): HeaderContactList {
|
|
98
|
+
return this.node.firstHeaderObject("cc")?.as(HeaderContactList);
|
|
99
|
+
}
|
|
100
|
+
set cc(v: string | { name: string, emailAddress: string } | { name: string, emailAddress: string }[]) {
|
|
101
|
+
this.node.setHeader("Cc", HeaderContactList.from(v).toString());
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
get bcc(): HeaderContactList {
|
|
105
|
+
return this.node.firstHeaderObject("bcc")?.as(HeaderContactList);
|
|
106
|
+
}
|
|
107
|
+
set bcc(v: string | { name: string, emailAddress: string } | { name: string, emailAddress: string }[]) {
|
|
108
|
+
this.node.setHeader("Bcc", HeaderContactList.from(v).toString());
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
constructor(
|
|
112
|
+
private node: MimeNode = new MimeNode("multipart/mixed"),
|
|
113
|
+
private attachments: AttachmentFile[] = []
|
|
114
|
+
) {}
|
|
75
115
|
|
|
76
116
|
async save(): Promise<Blob> {
|
|
77
117
|
const blobWriter = new BlobWriter();
|
|
@@ -8,6 +8,7 @@ import { quotedPrintable } from "./encoder/quoted-printable.js";
|
|
|
8
8
|
import { wordEncoding } from "./encoder/word-encoding.js";
|
|
9
9
|
import LineStream from "./stream/LineStream.js";
|
|
10
10
|
import TextWriter from "./stream/TextWriter.js";
|
|
11
|
+
import MimeHeaders from "./MimeHeaders.js";
|
|
11
12
|
|
|
12
13
|
export class CIMap extends Map<string, any> {
|
|
13
14
|
private ci = new Map<string, string>();
|
|
@@ -93,16 +94,24 @@ export class MimeNode {
|
|
|
93
94
|
|
|
94
95
|
encoded?: string;
|
|
95
96
|
children?: MimeNode[];
|
|
96
|
-
contentType
|
|
97
|
-
|
|
97
|
+
get contentType() {
|
|
98
|
+
return this.headers.first("content-type").as(HeaderContentType);
|
|
99
|
+
}
|
|
100
|
+
get contentDisposition() {
|
|
101
|
+
return this.headers.first("content-disposition")?.as(HeaderContentDisposition);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
set contentDisposition(v: HeaderContentDisposition) {
|
|
105
|
+
this.headers.set("content-disposition", v.toString());
|
|
106
|
+
}
|
|
107
|
+
|
|
98
108
|
textContent?: string;
|
|
99
109
|
blobData?: Blob;
|
|
100
|
-
headers:
|
|
110
|
+
headers: MimeHeaders;
|
|
101
111
|
|
|
102
112
|
constructor(type?: string) {
|
|
103
|
-
this.headers = new
|
|
104
|
-
this.
|
|
105
|
-
this.headers.set("Content-Type", this.contentType);
|
|
113
|
+
this.headers = new MimeHeaders();
|
|
114
|
+
this.headers.append("Content-Type", type);
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
get text(): string {
|
|
@@ -153,11 +162,11 @@ export class MimeNode {
|
|
|
153
162
|
}
|
|
154
163
|
|
|
155
164
|
get contentTransferEncoding(): string | undefined {
|
|
156
|
-
return this.
|
|
165
|
+
return this.firstHeaderString("Content-Transfer-Encoding");
|
|
157
166
|
}
|
|
158
167
|
|
|
159
168
|
set contentTransferEncoding(v: string | undefined) {
|
|
160
|
-
this.
|
|
169
|
+
this.headers.set("Content-Transfer-Encoding", v);
|
|
161
170
|
}
|
|
162
171
|
|
|
163
172
|
get boundary(): string | undefined {
|
|
@@ -168,8 +177,12 @@ export class MimeNode {
|
|
|
168
177
|
return this.enumerate();
|
|
169
178
|
}
|
|
170
179
|
|
|
171
|
-
|
|
172
|
-
return this.headers.
|
|
180
|
+
firstHeaderString(name: string): any {
|
|
181
|
+
return this.headers.first(name)?.value;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
firstHeaderObject(name: string) {
|
|
185
|
+
return this.headers.first(name);
|
|
173
186
|
}
|
|
174
187
|
|
|
175
188
|
setHeader(name: string, value: any): void {
|
|
@@ -185,7 +198,7 @@ export class MimeNode {
|
|
|
185
198
|
this.contentDisposition!.filename!,
|
|
186
199
|
{
|
|
187
200
|
type: this.contentType.type,
|
|
188
|
-
contentId: this.
|
|
201
|
+
contentId: this.firstHeaderString("Content-Id"),
|
|
189
202
|
disposition: this.contentDisposition!.type as any
|
|
190
203
|
});
|
|
191
204
|
af.node = this;
|
|
@@ -283,17 +296,6 @@ export class MimeNode {
|
|
|
283
296
|
} else {
|
|
284
297
|
return;
|
|
285
298
|
}
|
|
286
|
-
|
|
287
|
-
const ct = this.header("Content-Type");
|
|
288
|
-
this.contentType.parse(ct);
|
|
289
|
-
this.setHeader("Content-Type", ct);
|
|
290
|
-
|
|
291
|
-
const cd = this.header("Content-Disposition");
|
|
292
|
-
if (cd) {
|
|
293
|
-
this.contentDisposition ??= new HeaderContentDisposition();
|
|
294
|
-
this.contentDisposition.parse(cd);
|
|
295
|
-
this.setHeader("Content-Disposition", cd);
|
|
296
|
-
}
|
|
297
299
|
}
|
|
298
300
|
|
|
299
301
|
async save(writer: TextWriter): Promise<void> {
|
|
@@ -305,19 +307,8 @@ export class MimeNode {
|
|
|
305
307
|
this.contentType.boundary ||= `${Date.now()}-${Date.now()}-${Date.now()}`;
|
|
306
308
|
}
|
|
307
309
|
|
|
308
|
-
for (const
|
|
309
|
-
|
|
310
|
-
case "content-disposition":
|
|
311
|
-
writer.writeLine(`Content-Disposition: ${this.contentDisposition}`);
|
|
312
|
-
continue;
|
|
313
|
-
case "content-type":
|
|
314
|
-
writer.writeLine(`Content-Type:${this.contentType.toString()}`);
|
|
315
|
-
continue;
|
|
316
|
-
case "subject":
|
|
317
|
-
writer.writeLine(`${key}: ${wordEncoding.encode(element, true)}`);
|
|
318
|
-
continue;
|
|
319
|
-
}
|
|
320
|
-
writer.writeLine(`${key}: ${element}`);
|
|
310
|
+
for (const h of this.headers.entries()) {
|
|
311
|
+
writer.writeLineMax80Chars(`${h.key}: ${wordEncoding.encode(h.value, true)}`);
|
|
321
312
|
}
|
|
322
313
|
|
|
323
314
|
writer.writeLine("");
|
|
@@ -360,10 +351,10 @@ export class MimeNode {
|
|
|
360
351
|
break;
|
|
361
352
|
case "base64":
|
|
362
353
|
text = btoa(RawBuffer.encode(text, this.contentType.charset));
|
|
363
|
-
writer.
|
|
354
|
+
writer.writeLineMax80Chars(text);
|
|
364
355
|
break;
|
|
365
356
|
default:
|
|
366
|
-
writer.
|
|
357
|
+
writer.writeLineMax80Chars(text);
|
|
367
358
|
break;
|
|
368
359
|
}
|
|
369
360
|
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export default class RegexGenerator{
|
|
2
|
+
|
|
3
|
+
constructor(public readonly regex: RegExp) {
|
|
4
|
+
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
*groups(text: string) {
|
|
8
|
+
if (!text) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const { regex } = this;
|
|
12
|
+
regex.lastIndex = 0;
|
|
13
|
+
let m;
|
|
14
|
+
while((m = regex.exec(text)) !== null) {
|
|
15
|
+
if (m.index === regex.lastIndex) {
|
|
16
|
+
regex.lastIndex++;
|
|
17
|
+
}
|
|
18
|
+
if (m?.length) {
|
|
19
|
+
yield m as RegExpExecArray;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
*all(text: string) {
|
|
25
|
+
if (!text) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const { regex } = this;
|
|
29
|
+
regex.lastIndex = 0;
|
|
30
|
+
let m;
|
|
31
|
+
while((m = regex.exec(text)) !== null) {
|
|
32
|
+
if (m.index === regex.lastIndex) {
|
|
33
|
+
regex.lastIndex++;
|
|
34
|
+
}
|
|
35
|
+
if (m?.length) {
|
|
36
|
+
yield { matched: m as RegExpExecArray };
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
};
|
|
@@ -2,6 +2,20 @@ export default class TextWriter {
|
|
|
2
2
|
writeLine(line: string): void {
|
|
3
3
|
throw new Error("Not Implemented");
|
|
4
4
|
}
|
|
5
|
+
|
|
6
|
+
writeLineMax80Chars(line: string, padPrefix = "") {
|
|
7
|
+
let prefix = "";
|
|
8
|
+
for(;;) {
|
|
9
|
+
if (line.length <= 80) {
|
|
10
|
+
this.writeLine(prefix + line);
|
|
11
|
+
break;
|
|
12
|
+
}
|
|
13
|
+
const firstSegment = line.substring(0, 80);
|
|
14
|
+
this.writeLine(prefix + firstSegment);
|
|
15
|
+
line = line.substring(80);
|
|
16
|
+
prefix = padPrefix;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
5
19
|
}
|
|
6
20
|
|
|
7
21
|
export class BlobWriter extends TextWriter {
|