@ansi-tools/parser 0.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.
@@ -0,0 +1,485 @@
1
+ //#region src/constants.ts
2
+ const BELL = String.fromCharCode(7);
3
+ const ESC = String.fromCharCode(27);
4
+ const BACKSLASH = String.fromCharCode(92);
5
+ const DCS = String.fromCharCode(144);
6
+ const SOS = String.fromCharCode(152);
7
+ const CSI = String.fromCharCode(155);
8
+ const ST = String.fromCharCode(156);
9
+ const OSC = String.fromCharCode(157);
10
+ const PM = String.fromCharCode(158);
11
+ const APC = String.fromCharCode(159);
12
+ const CSI_OPEN = "[";
13
+ const OSC_OPEN = "]";
14
+ const DEC_OPEN = "?";
15
+ const PRIVATE_OPENERS = new Set([
16
+ "<",
17
+ "=",
18
+ ">"
19
+ ]);
20
+ const DCS_OPEN = "P";
21
+ const APC_OPEN = "_";
22
+ const SOS_OPEN = "^";
23
+ const PM_OPEN = "X";
24
+ const STRING_OPENERS = new Set([
25
+ DCS_OPEN,
26
+ APC_OPEN,
27
+ SOS_OPEN,
28
+ PM_OPEN
29
+ ]);
30
+ const TOKEN_TYPES = {
31
+ TEXT: "TEXT",
32
+ INTRODUCER: "INTRODUCER",
33
+ DATA: "DATA",
34
+ FINAL: "FINAL"
35
+ };
36
+ const CODE_TYPES = {
37
+ CSI: "CSI",
38
+ DCS: "DCS",
39
+ DEC: "DEC",
40
+ ESC: "ESC",
41
+ OSC: "OSC",
42
+ PRIVATE: "PRIVATE",
43
+ SGR: "SGR",
44
+ STRING: "STRING",
45
+ TEXT: "TEXT"
46
+ };
47
+
48
+ //#endregion
49
+ //#region src/parsers/csi.ts
50
+ function parseCSI(pos, raw, data, final) {
51
+ const params = [];
52
+ let intermediates = "";
53
+ if (data) {
54
+ let i = 0;
55
+ let paramSection = "";
56
+ while (i < data.length && data.charCodeAt(i) >= 48 && data.charCodeAt(i) <= 63) {
57
+ paramSection += data[i];
58
+ i++;
59
+ }
60
+ intermediates = data.slice(i);
61
+ if (paramSection) {
62
+ let current = "";
63
+ for (let j = 0; j < paramSection.length; j++) if (paramSection[j] === ";") {
64
+ params.push(current || "-1");
65
+ current = "";
66
+ } else current += paramSection[j];
67
+ params.push(current || "-1");
68
+ }
69
+ }
70
+ const command = intermediates + final;
71
+ return {
72
+ type: CODE_TYPES.CSI,
73
+ pos,
74
+ raw,
75
+ command,
76
+ params
77
+ };
78
+ }
79
+ function parsePrivateCSI(pos, raw, data, final) {
80
+ const privateIndicator = data[0];
81
+ const withoutIndicator = data.slice(1);
82
+ const match = withoutIndicator.match(/^([\d;]*)(.*)/);
83
+ const paramsRaw = match?.[1] ?? "";
84
+ const intermediates = match?.[2] ?? "";
85
+ const command = `${privateIndicator}${intermediates}${final}`;
86
+ const params = [];
87
+ if (paramsRaw) {
88
+ let current = "";
89
+ for (let i = 0; i < paramsRaw.length; i++) if (paramsRaw[i] === ";") {
90
+ params.push(current || "-1");
91
+ current = "";
92
+ } else current += paramsRaw[i];
93
+ params.push(current || "-1");
94
+ }
95
+ return {
96
+ type: CODE_TYPES.PRIVATE,
97
+ pos,
98
+ raw,
99
+ command,
100
+ params
101
+ };
102
+ }
103
+
104
+ //#endregion
105
+ //#region src/parsers/dcs.ts
106
+ const DCS_PATTERNS = new Map([
107
+ ["$q", 2],
108
+ ["+q", 2],
109
+ ["+p", 2],
110
+ ["|", 1],
111
+ ["{", 1]
112
+ ]);
113
+ function parseDCS(pos, raw, data) {
114
+ if (!data) return {
115
+ type: CODE_TYPES.DCS,
116
+ pos,
117
+ raw,
118
+ command: "",
119
+ params: []
120
+ };
121
+ for (const [pattern, length] of DCS_PATTERNS) if (data.startsWith(pattern)) {
122
+ const remainder = data.slice(length);
123
+ const params = [];
124
+ if (remainder) {
125
+ let current = "";
126
+ for (let i = 0; i < remainder.length; i++) if (remainder[i] === ";") {
127
+ params.push(current || "-1");
128
+ current = "";
129
+ } else current += remainder[i];
130
+ params.push(current || "-1");
131
+ }
132
+ return {
133
+ type: CODE_TYPES.DCS,
134
+ pos,
135
+ raw,
136
+ command: pattern,
137
+ params
138
+ };
139
+ }
140
+ return {
141
+ type: CODE_TYPES.DCS,
142
+ pos,
143
+ raw,
144
+ command: "",
145
+ params: [data]
146
+ };
147
+ }
148
+
149
+ //#endregion
150
+ //#region src/parsers/dec.ts
151
+ function parseDEC(pos, raw, data, final) {
152
+ const withoutPrefix = data.slice(1);
153
+ let i = 0;
154
+ let paramsRaw = "";
155
+ while (i < withoutPrefix.length && (withoutPrefix.charCodeAt(i) >= 48 && withoutPrefix.charCodeAt(i) <= 57 || withoutPrefix[i] === ";")) {
156
+ paramsRaw += withoutPrefix[i];
157
+ i++;
158
+ }
159
+ const command = withoutPrefix.slice(i) + final;
160
+ const params = [];
161
+ if (paramsRaw) {
162
+ let current = "";
163
+ for (let j = 0; j < paramsRaw.length; j++) if (paramsRaw[j] === ";") {
164
+ params.push(current || "-1");
165
+ current = "";
166
+ } else current += paramsRaw[j];
167
+ params.push(current || "-1");
168
+ }
169
+ return {
170
+ type: CODE_TYPES.DEC,
171
+ pos,
172
+ raw,
173
+ command,
174
+ params
175
+ };
176
+ }
177
+
178
+ //#endregion
179
+ //#region src/parsers/esc.ts
180
+ function parseESC(pos, raw, command, data) {
181
+ return {
182
+ type: CODE_TYPES.ESC,
183
+ pos,
184
+ raw,
185
+ command,
186
+ params: data ? [data] : []
187
+ };
188
+ }
189
+
190
+ //#endregion
191
+ //#region src/parsers/osc.ts
192
+ function parseOSC(pos, raw, data) {
193
+ const semicolonIndex = data.indexOf(";");
194
+ if (semicolonIndex === -1) return {
195
+ type: CODE_TYPES.OSC,
196
+ pos,
197
+ raw,
198
+ command: data,
199
+ params: []
200
+ };
201
+ const command = data.slice(0, semicolonIndex);
202
+ const remainder = data.slice(semicolonIndex + 1);
203
+ if (command === "1337") return {
204
+ type: CODE_TYPES.OSC,
205
+ pos,
206
+ raw,
207
+ command,
208
+ params: [remainder]
209
+ };
210
+ const params = [];
211
+ if (remainder) {
212
+ let current = "";
213
+ for (let i = 0; i < remainder.length; i++) if (remainder[i] === ";") {
214
+ params.push(current);
215
+ current = "";
216
+ } else current += remainder[i];
217
+ params.push(current);
218
+ }
219
+ return {
220
+ type: CODE_TYPES.OSC,
221
+ pos,
222
+ raw,
223
+ command,
224
+ params
225
+ };
226
+ }
227
+
228
+ //#endregion
229
+ //#region src/tokenize.ts
230
+ const INTRODUCERS = new Set([
231
+ ESC,
232
+ CSI,
233
+ OSC,
234
+ DCS,
235
+ APC,
236
+ PM,
237
+ SOS
238
+ ]);
239
+ function emit$1(token) {
240
+ return token;
241
+ }
242
+ function* tokenizer(input) {
243
+ let i = 0;
244
+ let state = "GROUND";
245
+ let currentCode;
246
+ function setState(next, code) {
247
+ state = next;
248
+ currentCode = code;
249
+ }
250
+ while (i < input.length) if (state === "GROUND") {
251
+ const textStart = i;
252
+ while (i < input.length) {
253
+ const char = input[i];
254
+ if (INTRODUCERS.has(char)) break;
255
+ i++;
256
+ }
257
+ if (i > textStart) yield emit$1({
258
+ type: TOKEN_TYPES.TEXT,
259
+ pos: textStart,
260
+ raw: input.substring(textStart, i)
261
+ });
262
+ if (i < input.length) {
263
+ const char = input[i];
264
+ if (char === CSI || char === OSC || char === DCS || char === APC || char === PM || char === SOS) {
265
+ yield emit$1({
266
+ type: TOKEN_TYPES.INTRODUCER,
267
+ pos: i,
268
+ raw: char,
269
+ code: char
270
+ });
271
+ i++;
272
+ setState("SEQUENCE", char);
273
+ } else if (char === ESC) {
274
+ const next = input[i + 1];
275
+ if (next === CSI_OPEN) {
276
+ yield emit$1({
277
+ type: TOKEN_TYPES.INTRODUCER,
278
+ pos: i,
279
+ raw: char + next,
280
+ code: CSI
281
+ });
282
+ i += 2;
283
+ setState("SEQUENCE", CSI);
284
+ } else if (next === OSC_OPEN) {
285
+ yield emit$1({
286
+ type: TOKEN_TYPES.INTRODUCER,
287
+ pos: i,
288
+ raw: char + next,
289
+ code: OSC
290
+ });
291
+ i += 2;
292
+ setState("SEQUENCE", OSC);
293
+ } else if (STRING_OPENERS.has(next)) {
294
+ yield emit$1({
295
+ type: TOKEN_TYPES.INTRODUCER,
296
+ pos: i,
297
+ raw: char + next,
298
+ code: next
299
+ });
300
+ i += 2;
301
+ setState("SEQUENCE", next);
302
+ } else if (next && next.charCodeAt(0) >= 32 && next.charCodeAt(0) <= 47) {
303
+ yield emit$1({
304
+ type: TOKEN_TYPES.INTRODUCER,
305
+ pos: i,
306
+ raw: char,
307
+ code: ESC
308
+ });
309
+ i += 1;
310
+ yield emit$1({
311
+ type: TOKEN_TYPES.FINAL,
312
+ pos: i,
313
+ raw: next
314
+ });
315
+ i++;
316
+ } else if (next) {
317
+ yield emit$1({
318
+ type: TOKEN_TYPES.INTRODUCER,
319
+ pos: i,
320
+ raw: char,
321
+ code: ESC
322
+ });
323
+ yield emit$1({
324
+ type: TOKEN_TYPES.FINAL,
325
+ pos: i + 1,
326
+ raw: next
327
+ });
328
+ i += 2;
329
+ } else {
330
+ yield emit$1({
331
+ type: TOKEN_TYPES.INTRODUCER,
332
+ pos: i,
333
+ raw: char,
334
+ code: ESC
335
+ });
336
+ i++;
337
+ }
338
+ }
339
+ }
340
+ } else {
341
+ const pos = i;
342
+ const code = currentCode;
343
+ let data = "";
344
+ if (code === CSI) while (i < input.length) {
345
+ const char = input[i];
346
+ const charCode = char.charCodeAt(0);
347
+ if (charCode >= 64 && charCode < 126) {
348
+ if (data) yield emit$1({
349
+ type: TOKEN_TYPES.DATA,
350
+ pos,
351
+ raw: data
352
+ });
353
+ yield emit$1({
354
+ type: TOKEN_TYPES.FINAL,
355
+ pos: i,
356
+ raw: char
357
+ });
358
+ i++;
359
+ break;
360
+ }
361
+ data += char;
362
+ i++;
363
+ }
364
+ else if (code) while (i < input.length) {
365
+ const char = input[i];
366
+ let terminator;
367
+ if (char === ST) terminator = ST;
368
+ else if (char === BELL && code === OSC) terminator = BELL;
369
+ else if (char === ESC && input[i + 1] === BACKSLASH) terminator = ESC + BACKSLASH;
370
+ if (terminator) {
371
+ if (data) yield emit$1({
372
+ type: TOKEN_TYPES.DATA,
373
+ pos,
374
+ raw: data
375
+ });
376
+ yield emit$1({
377
+ type: TOKEN_TYPES.FINAL,
378
+ pos: i,
379
+ raw: terminator
380
+ });
381
+ i += terminator.length;
382
+ break;
383
+ }
384
+ data += char;
385
+ i++;
386
+ }
387
+ setState("GROUND");
388
+ }
389
+ }
390
+ function tokenize(input) {
391
+ return Array.from(tokenizer(input));
392
+ }
393
+
394
+ //#endregion
395
+ //#region src/parse.ts
396
+ function emit(token) {
397
+ return token;
398
+ }
399
+ function* parser(tokens) {
400
+ let current = tokens.next();
401
+ while (!current.done) {
402
+ const token = current.value;
403
+ if (token.type === TOKEN_TYPES.TEXT) {
404
+ yield emit({
405
+ type: CODE_TYPES.TEXT,
406
+ pos: token.pos,
407
+ raw: token.raw
408
+ });
409
+ current = tokens.next();
410
+ continue;
411
+ }
412
+ if (token.type === TOKEN_TYPES.INTRODUCER) {
413
+ const pos = token.pos;
414
+ let raw = token.raw;
415
+ let data = "";
416
+ let finalToken;
417
+ current = tokens.next();
418
+ while (!current.done && !finalToken) {
419
+ const nextToken = current.value;
420
+ if (nextToken.type === TOKEN_TYPES.DATA) {
421
+ data += nextToken.raw;
422
+ raw += nextToken.raw;
423
+ } else if (nextToken.type === TOKEN_TYPES.FINAL) {
424
+ finalToken = nextToken;
425
+ raw += nextToken.raw;
426
+ }
427
+ current = tokens.next();
428
+ }
429
+ if (finalToken) switch (token.code) {
430
+ case CSI:
431
+ if (data.startsWith(DEC_OPEN)) yield emit(parseDEC(pos, raw, data, finalToken.raw));
432
+ else if (PRIVATE_OPENERS.has(data[0])) yield emit(parsePrivateCSI(pos, raw, data, finalToken.raw));
433
+ else yield emit(parseCSI(pos, raw, data, finalToken.raw));
434
+ break;
435
+ case OSC:
436
+ yield emit(parseOSC(pos, raw, data));
437
+ break;
438
+ case DCS:
439
+ case DCS_OPEN:
440
+ yield emit(parseDCS(pos, raw, data));
441
+ break;
442
+ case APC:
443
+ case APC_OPEN:
444
+ yield emit({
445
+ type: CODE_TYPES.STRING,
446
+ pos,
447
+ raw,
448
+ command: "APC",
449
+ params: data ? [data] : []
450
+ });
451
+ break;
452
+ case PM:
453
+ case PM_OPEN:
454
+ yield emit({
455
+ type: CODE_TYPES.STRING,
456
+ pos,
457
+ raw,
458
+ command: "PM",
459
+ params: data ? [data] : []
460
+ });
461
+ break;
462
+ case SOS:
463
+ case SOS_OPEN:
464
+ yield emit({
465
+ type: CODE_TYPES.STRING,
466
+ pos,
467
+ raw,
468
+ command: "SOS",
469
+ params: data ? [data] : []
470
+ });
471
+ break;
472
+ case ESC:
473
+ yield emit(parseESC(pos, raw, finalToken.raw, data));
474
+ break;
475
+ }
476
+ else if (token.code === ESC) yield emit(parseESC(pos, raw, "", ""));
477
+ } else current = tokens.next();
478
+ }
479
+ }
480
+ function parse(input) {
481
+ return Array.from(parser(tokenizer(input)));
482
+ }
483
+
484
+ //#endregion
485
+ export { APC, APC_OPEN, BACKSLASH, BELL, CODE_TYPES, CSI, CSI_OPEN, DCS, DCS_OPEN, DEC_OPEN, ESC, OSC, OSC_OPEN, PM, PM_OPEN, PRIVATE_OPENERS, SOS, SOS_OPEN, ST, STRING_OPENERS, TOKEN_TYPES, parse, parser, tokenize, tokenizer };
package/package.json ADDED
@@ -0,0 +1,40 @@
1
+ {
2
+ "name": "@ansi-tools/parser",
3
+ "version": "0.0.0",
4
+ "description": "Tokenize and parse strings containing ANSI escape sequences and control codes",
5
+ "main": "./dist/index.js",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "import": "./dist/index.js",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "./escaped": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/escaped.js",
16
+ "default": "./dist/escaped.js"
17
+ }
18
+ },
19
+ "keywords": [
20
+ "ansi",
21
+ "escape codes",
22
+ "web application"
23
+ ],
24
+ "author": "Lars Kappert <lars@webpro.nl>",
25
+ "license": "ISC",
26
+ "publishConfig": {
27
+ "access": "public"
28
+ },
29
+ "devDependencies": {
30
+ "@types/node": "^24.0.13",
31
+ "tsdown": "^0.12.9",
32
+ "typescript": "^5.8.3"
33
+ },
34
+ "scripts": {
35
+ "prebuild": "pnpm type-check && pnpm test",
36
+ "build": "tsdown --dts src/index.ts src/escaped.ts",
37
+ "test": "node --test",
38
+ "type-check": "tsc"
39
+ }
40
+ }
@@ -0,0 +1,40 @@
1
+ export const BELL = String.fromCharCode(7);
2
+ export const ESC = String.fromCharCode(27);
3
+ export const BACKSLASH = String.fromCharCode(92);
4
+ export const DCS = String.fromCharCode(144);
5
+ export const SOS = String.fromCharCode(152);
6
+ export const CSI = String.fromCharCode(155);
7
+ export const ST = String.fromCharCode(156);
8
+ export const OSC = String.fromCharCode(157);
9
+ export const PM = String.fromCharCode(158);
10
+ export const APC = String.fromCharCode(159);
11
+
12
+ export const CSI_OPEN = "[";
13
+ export const OSC_OPEN = "]";
14
+ export const DEC_OPEN = "?";
15
+ export const PRIVATE_OPENERS = new Set(["<", "=", ">"]);
16
+
17
+ export const DCS_OPEN = "P";
18
+ export const APC_OPEN = "_";
19
+ export const SOS_OPEN = "^";
20
+ export const PM_OPEN = "X";
21
+ export const STRING_OPENERS = new Set([DCS_OPEN, APC_OPEN, SOS_OPEN, PM_OPEN]);
22
+
23
+ export const TOKEN_TYPES = {
24
+ TEXT: "TEXT",
25
+ INTRODUCER: "INTRODUCER",
26
+ DATA: "DATA",
27
+ FINAL: "FINAL",
28
+ } as const;
29
+
30
+ export const CODE_TYPES = {
31
+ CSI: "CSI",
32
+ DCS: "DCS",
33
+ DEC: "DEC",
34
+ ESC: "ESC",
35
+ OSC: "OSC",
36
+ PRIVATE: "PRIVATE",
37
+ SGR: "SGR",
38
+ STRING: "STRING",
39
+ TEXT: "TEXT",
40
+ } as const;
package/src/escaped.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./constants.ts";
2
+ export { parse, parser } from "./parse.ts";
3
+ export { tokenize, tokenizer } from "./tokenize.escaped.ts";
4
+ export type { CODE, CONTROL_CODE, CONTROL_CODE_TEXT, TOKEN } from "./types.ts";
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from "./constants.ts";
2
+ export { parse, parser } from "./parse.ts";
3
+ export { tokenize, tokenizer } from "./tokenize.ts";
4
+ export type { CODE, CONTROL_CODE, CONTROL_CODE_TEXT, TOKEN } from "./types.ts";
@@ -0,0 +1,86 @@
1
+ import { test } from "node:test";
2
+ import assert from "node:assert/strict";
3
+ import { tokenizer } from "./tokenize.escaped.ts";
4
+ import { parser } from "./parse.ts";
5
+ import { CODE_TYPES } from "./constants.ts";
6
+
7
+ test("parse simple text", () => {
8
+ const input = "hello world";
9
+ const tokens = tokenizer(input);
10
+ const codes = [...parser(tokens)];
11
+ assert.deepEqual(codes, [{ type: CODE_TYPES.TEXT, pos: 0, raw: "hello world" }]);
12
+ });
13
+
14
+ test("parse mixed text and csi", () => {
15
+ const input = String.raw`hello \e[31mworld\e[0m`;
16
+ const tokens = tokenizer(input);
17
+ const codes = [...parser(tokens)];
18
+ assert.deepEqual(codes, [
19
+ { type: CODE_TYPES.TEXT, pos: 0, raw: "hello " },
20
+ { type: CODE_TYPES.CSI, pos: 6, raw: "\\e[31m", params: ["31"], command: "m" },
21
+ { type: CODE_TYPES.TEXT, pos: 12, raw: "world" },
22
+ { type: CODE_TYPES.CSI, pos: 17, raw: "\\e[0m", params: ["0"], command: "m" },
23
+ ]);
24
+ });
25
+
26
+ test("subsequent escape sequences", () => {
27
+ const input = String.raw`\e[31m\e[32m\e[33m`;
28
+ const tokens = tokenizer(input);
29
+ const codes = [...parser(tokens)];
30
+ assert.deepEqual(codes, [
31
+ { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[31m", params: ["31"], command: "m" },
32
+ { type: CODE_TYPES.CSI, pos: 6, raw: "\\e[32m", params: ["32"], command: "m" },
33
+ { type: CODE_TYPES.CSI, pos: 12, raw: "\\e[33m", params: ["33"], command: "m" },
34
+ ]);
35
+ });
36
+
37
+ test("parse multiple different sequence types", () => {
38
+ const input = String.raw`\e[1;32m\e]0;title\a\e=\ePdata\e\\`;
39
+ const tokens = tokenizer(input);
40
+ const codes = [...parser(tokens)];
41
+ assert.deepEqual(codes, [
42
+ { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[1;32m", params: ["1", "32"], command: "m" },
43
+ { type: CODE_TYPES.OSC, pos: 8, raw: "\\e]0;title\\a", params: ["title"], command: "0" },
44
+ { type: CODE_TYPES.ESC, pos: 20, raw: "\\e=", command: "=", params: [] },
45
+ { type: CODE_TYPES.DCS, pos: 23, raw: "\\ePdata\\e\\\\", params: ["data"], command: "" },
46
+ ]);
47
+ });
48
+
49
+ test("parse mixed standard and private sequences", () => {
50
+ const input = String.raw`\e[31m\e[<5h\e[?25l\e[>c`;
51
+ const tokens = tokenizer(input);
52
+ const codes = [...parser(tokens)];
53
+ assert.deepEqual(codes, [
54
+ { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[31m", params: ["31"], command: "m" },
55
+ { type: CODE_TYPES.PRIVATE, pos: 6, raw: "\\e[<5h", params: ["5"], command: "<h" },
56
+ { type: CODE_TYPES.DEC, pos: 12, raw: "\\e[?25l", params: ["25"], command: "l" },
57
+ { type: CODE_TYPES.PRIVATE, pos: 19, raw: "\\e[>c", params: [], command: ">c" },
58
+ ]);
59
+ });
60
+
61
+ test("parse complex missing parameter scenarios", () => {
62
+ const input = String.raw`\e[;5;m\e[?;h\eP$q;;\e\\`;
63
+ const tokens = tokenizer(input);
64
+ const codes = [...parser(tokens)];
65
+ assert.deepEqual(codes, [
66
+ { type: CODE_TYPES.CSI, pos: 0, raw: "\\e[;5;m", params: ["-1", "5", "-1"], command: "m" },
67
+ { type: CODE_TYPES.DEC, pos: 7, raw: "\\e[?;h", params: ["-1", "-1"], command: "h" },
68
+ { type: CODE_TYPES.DCS, pos: 13, raw: "\\eP$q;;\\e\\\\", params: ["-1", "-1", "-1"], command: "$q" },
69
+ ]);
70
+ });
71
+
72
+ test("parse iTerm2 image sequence", () => {
73
+ const input = String.raw`\e]1337;File=inline=1;width=1;height=1:R0lG=\a`;
74
+ const tokens = tokenizer(input);
75
+ const codes = [...parser(tokens)];
76
+ assert.deepEqual(codes, [
77
+ { type: CODE_TYPES.OSC, pos: 0, raw: input, command: "1337", params: ["File=inline=1;width=1;height=1:R0lG="] },
78
+ ]);
79
+ });
80
+
81
+ test("parse DECUDK sequence", () => {
82
+ const input = String.raw`\eP0|23/68656c6c6f\e\\`;
83
+ const tokens = tokenizer(input);
84
+ const codes = [...parser(tokens)];
85
+ assert.deepEqual(codes, [{ type: CODE_TYPES.DCS, pos: 0, raw: input, command: "", params: ["0|23/68656c6c6f"] }]);
86
+ });