@graphty/graph-io 0.3.40 → 0.3.42
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/README.md +2 -0
- package/dist/chunks/{children-CL3Cy0ez.js → children-CA4coBi0.js} +36 -27
- package/dist/chunks/children-CA4coBi0.js.map +1 -0
- package/dist/chunks/{choose-CS3fvGT1.js → choose-DvpbdRuz.js} +2 -2
- package/dist/chunks/{choose-CS3fvGT1.js.map → choose-DvpbdRuz.js.map} +1 -1
- package/dist/chunks/{constants-DqKZT6cH.js → constants-fT3E8JYY.js} +2 -2
- package/dist/chunks/{constants-DqKZT6cH.js.map → constants-fT3E8JYY.js.map} +1 -1
- package/dist/chunks/{escape-BJRLd3vJ.js → escape-B62ZGM_a.js} +2 -2
- package/dist/chunks/{escape-BJRLd3vJ.js.map → escape-B62ZGM_a.js.map} +1 -1
- package/dist/chunks/{export-B-aVCwyG.js → export-rKLeGQMo.js} +404 -243
- package/dist/chunks/export-rKLeGQMo.js.map +1 -0
- package/dist/chunks/{exporter-6ocL_9x_.js → exporter-8hLNemFk.js} +4 -4
- package/dist/chunks/{exporter-6ocL_9x_.js.map → exporter-8hLNemFk.js.map} +1 -1
- package/dist/chunks/{exporter-BVXnV9-9.js → exporter-BWwEuOPT.js} +29 -17
- package/dist/chunks/exporter-BWwEuOPT.js.map +1 -0
- package/dist/chunks/{exporter-Bcp-l4Bo.js → exporter-CpRyHZ3H.js} +574 -475
- package/dist/chunks/exporter-CpRyHZ3H.js.map +1 -0
- package/dist/chunks/{exporter-SFYkDzD0.js → exporter-yLxKBvLE.js} +23 -20
- package/dist/chunks/{exporter-SFYkDzD0.js.map → exporter-yLxKBvLE.js.map} +1 -1
- package/dist/chunks/{importer-DcKB97kW.js → importer-BbNb7eNg.js} +111 -71
- package/dist/chunks/importer-BbNb7eNg.js.map +1 -0
- package/dist/chunks/{importer-D_srHyj6.js → importer-C98dpSH4.js} +5 -5
- package/dist/chunks/{importer-D_srHyj6.js.map → importer-C98dpSH4.js.map} +1 -1
- package/dist/chunks/{importer-BJlGAOht.js → importer-D794XUTe.js} +3 -3
- package/dist/chunks/{importer-BJlGAOht.js.map → importer-D794XUTe.js.map} +1 -1
- package/dist/chunks/{importer-AjwOJrn2.js → importer-Dz7tI8Sa.js} +202 -181
- package/dist/chunks/importer-Dz7tI8Sa.js.map +1 -0
- package/dist/chunks/{importer-DEp_fYjf.js → importer-Ht7LNg8L.js} +92 -64
- package/dist/chunks/importer-Ht7LNg8L.js.map +1 -0
- package/dist/chunks/{importer-CPqnIY8m.js → importer-WrGRZb0N.js} +42 -29
- package/dist/chunks/importer-WrGRZb0N.js.map +1 -0
- package/dist/chunks/{importer-BpuS3J8C.js → importer-xZ-3suY1.js} +74 -61
- package/dist/chunks/importer-xZ-3suY1.js.map +1 -0
- package/dist/chunks/{json-elements-DpO6AXIc.js → json-elements-DKSqpNW1.js} +3 -3
- package/dist/chunks/{json-elements-DpO6AXIc.js.map → json-elements-DKSqpNW1.js.map} +1 -1
- package/dist/chunks/{ontology-export-50L9DOV4.js → ontology-export-CtRXjg_i.js} +2 -2
- package/dist/chunks/{ontology-export-50L9DOV4.js.map → ontology-export-CtRXjg_i.js.map} +1 -1
- package/dist/chunks/{records-r3SFHz11.js → records-Cu4bxYOm.js} +69 -36
- package/dist/chunks/records-Cu4bxYOm.js.map +1 -0
- package/dist/chunks/{text-Bjfa1xYL.js → text-DHOJXnED.js} +20 -11
- package/dist/chunks/text-DHOJXnED.js.map +1 -0
- package/dist/csv.js +323 -241
- package/dist/csv.js.map +1 -1
- package/dist/cx.js +1 -1
- package/dist/cx2.js +2 -2
- package/dist/cys.js +1 -1
- package/dist/dot.js +1 -1
- package/dist/gexf.js +195 -97
- package/dist/gexf.js.map +1 -1
- package/dist/gml.js +170 -149
- package/dist/gml.js.map +1 -1
- package/dist/graph-io.js +27 -27
- package/dist/graphml.js +2 -2
- package/dist/json.js +1 -1
- package/dist/neo4j.js +103 -65
- package/dist/neo4j.js.map +1 -1
- package/dist/obo.js +1 -1
- package/dist/pajek.js +1 -1
- package/dist/src/children.d.ts.map +1 -1
- package/dist/src/children.js +50 -27
- package/dist/src/children.js.map +1 -1
- package/dist/src/common/attributes.d.ts.map +1 -1
- package/dist/src/common/attributes.js +86 -42
- package/dist/src/common/attributes.js.map +1 -1
- package/dist/src/common/input.d.ts.map +1 -1
- package/dist/src/common/input.js +358 -184
- package/dist/src/common/input.js.map +1 -1
- package/dist/src/common/lists.js +30 -15
- package/dist/src/common/lists.js.map +1 -1
- package/dist/src/common/text.d.ts +7 -0
- package/dist/src/common/text.d.ts.map +1 -1
- package/dist/src/common/text.js +15 -7
- package/dist/src/common/text.js.map +1 -1
- package/dist/src/common/zip.d.ts.map +1 -1
- package/dist/src/common/zip.js +36 -19
- package/dist/src/common/zip.js.map +1 -1
- package/dist/src/formats/csv/exporter.d.ts.map +1 -1
- package/dist/src/formats/csv/exporter.js +288 -146
- package/dist/src/formats/csv/exporter.js.map +1 -1
- package/dist/src/formats/csv/importer.d.ts.map +1 -1
- package/dist/src/formats/csv/importer.js +129 -80
- package/dist/src/formats/csv/importer.js.map +1 -1
- package/dist/src/formats/csv/records.d.ts.map +1 -1
- package/dist/src/formats/csv/records.js +78 -36
- package/dist/src/formats/csv/records.js.map +1 -1
- package/dist/src/formats/cys/tables.d.ts.map +1 -1
- package/dist/src/formats/cys/tables.js +11 -8
- package/dist/src/formats/cys/tables.js.map +1 -1
- package/dist/src/formats/dot/tokenizer.d.ts +39 -0
- package/dist/src/formats/dot/tokenizer.d.ts.map +1 -1
- package/dist/src/formats/dot/tokenizer.js +112 -66
- package/dist/src/formats/dot/tokenizer.js.map +1 -1
- package/dist/src/formats/gexf/exporter.d.ts.map +1 -1
- package/dist/src/formats/gexf/exporter.js +65 -37
- package/dist/src/formats/gexf/exporter.js.map +1 -1
- package/dist/src/formats/gexf/importer.d.ts.map +1 -1
- package/dist/src/formats/gexf/importer.js +138 -50
- package/dist/src/formats/gexf/importer.js.map +1 -1
- package/dist/src/formats/gml/exporter.d.ts.map +1 -1
- package/dist/src/formats/gml/exporter.js +51 -35
- package/dist/src/formats/gml/exporter.js.map +1 -1
- package/dist/src/formats/gml/importer.d.ts.map +1 -1
- package/dist/src/formats/gml/importer.js +103 -58
- package/dist/src/formats/gml/importer.js.map +1 -1
- package/dist/src/formats/gml/syntax.d.ts.map +1 -1
- package/dist/src/formats/gml/syntax.js +17 -8
- package/dist/src/formats/gml/syntax.js.map +1 -1
- package/dist/src/formats/graphml/exporter.d.ts.map +1 -1
- package/dist/src/formats/graphml/exporter.js +34 -24
- package/dist/src/formats/graphml/exporter.js.map +1 -1
- package/dist/src/formats/graphml/importer.d.ts.map +1 -1
- package/dist/src/formats/graphml/importer.js +17 -13
- package/dist/src/formats/graphml/importer.js.map +1 -1
- package/dist/src/formats/graphml/tree.js +23 -14
- package/dist/src/formats/graphml/tree.js.map +1 -1
- package/dist/src/formats/graphml/yfiles.d.ts.map +1 -1
- package/dist/src/formats/graphml/yfiles.js +22 -10
- package/dist/src/formats/graphml/yfiles.js.map +1 -1
- package/dist/src/formats/json/dialect.d.ts.map +1 -1
- package/dist/src/formats/json/dialect.js +54 -20
- package/dist/src/formats/json/dialect.js.map +1 -1
- package/dist/src/formats/json/exporter.d.ts.map +1 -1
- package/dist/src/formats/json/exporter.js +38 -21
- package/dist/src/formats/json/exporter.js.map +1 -1
- package/dist/src/formats/json/importer.d.ts.map +1 -1
- package/dist/src/formats/json/importer.js +536 -343
- package/dist/src/formats/json/importer.js.map +1 -1
- package/dist/src/formats/json/obographs.js +29 -17
- package/dist/src/formats/json/obographs.js.map +1 -1
- package/dist/src/formats/neo4j/exporter.d.ts.map +1 -1
- package/dist/src/formats/neo4j/exporter.js +73 -43
- package/dist/src/formats/neo4j/exporter.js.map +1 -1
- package/dist/src/formats/neo4j/header.d.ts.map +1 -1
- package/dist/src/formats/neo4j/header.js +37 -16
- package/dist/src/formats/neo4j/header.js.map +1 -1
- package/dist/src/formats/neo4j/importer.d.ts.map +1 -1
- package/dist/src/formats/neo4j/importer.js +21 -11
- package/dist/src/formats/neo4j/importer.js.map +1 -1
- package/dist/src/formats/obo/exporter.d.ts.map +1 -1
- package/dist/src/formats/obo/exporter.js +18 -7
- package/dist/src/formats/obo/exporter.js.map +1 -1
- package/dist/src/formats/pajek/exporter.d.ts.map +1 -1
- package/dist/src/formats/pajek/exporter.js +142 -73
- package/dist/src/formats/pajek/exporter.js.map +1 -1
- package/dist/src/formats/pajek/importer.d.ts.map +1 -1
- package/dist/src/formats/pajek/importer.js +26 -15
- package/dist/src/formats/pajek/importer.js.map +1 -1
- package/dist/src/formats/pajek/syntax.d.ts.map +1 -1
- package/dist/src/formats/pajek/syntax.js +85 -58
- package/dist/src/formats/pajek/syntax.js.map +1 -1
- package/dist/src/formats/xgmml/emit.d.ts +7 -0
- package/dist/src/formats/xgmml/emit.d.ts.map +1 -1
- package/dist/src/formats/xgmml/emit.js +79 -40
- package/dist/src/formats/xgmml/emit.js.map +1 -1
- package/dist/src/formats/xgmml/exporter.d.ts.map +1 -1
- package/dist/src/formats/xgmml/exporter.js +38 -12
- package/dist/src/formats/xgmml/exporter.js.map +1 -1
- package/dist/src/formats/xgmml/importer.d.ts.map +1 -1
- package/dist/src/formats/xgmml/importer.js +38 -18
- package/dist/src/formats/xgmml/importer.js.map +1 -1
- package/dist/src/sniff.js +109 -57
- package/dist/src/sniff.js.map +1 -1
- package/dist/xgmml.js +2 -2
- package/package.json +2 -2
- package/src/children.ts +54 -27
- package/src/common/attributes.ts +118 -49
- package/src/common/input.ts +439 -197
- package/src/common/lists.ts +29 -15
- package/src/common/text.ts +24 -15
- package/src/common/zip.ts +47 -18
- package/src/formats/csv/exporter.ts +377 -175
- package/src/formats/csv/importer.ts +155 -84
- package/src/formats/csv/records.ts +90 -35
- package/src/formats/cys/tables.ts +11 -10
- package/src/formats/dot/tokenizer.ts +118 -68
- package/src/formats/gexf/exporter.ts +67 -36
- package/src/formats/gexf/importer.ts +173 -63
- package/src/formats/gml/exporter.ts +78 -82
- package/src/formats/gml/importer.ts +119 -68
- package/src/formats/gml/syntax.ts +18 -8
- package/src/formats/graphml/exporter.ts +44 -24
- package/src/formats/graphml/importer.ts +18 -11
- package/src/formats/graphml/tree.ts +22 -12
- package/src/formats/graphml/yfiles.ts +23 -10
- package/src/formats/json/dialect.ts +61 -22
- package/src/formats/json/exporter.ts +40 -21
- package/src/formats/json/importer.ts +701 -385
- package/src/formats/json/obographs.ts +35 -15
- package/src/formats/neo4j/exporter.ts +79 -41
- package/src/formats/neo4j/header.ts +50 -16
- package/src/formats/neo4j/importer.ts +21 -10
- package/src/formats/obo/exporter.ts +34 -17
- package/src/formats/pajek/exporter.ts +180 -117
- package/src/formats/pajek/importer.ts +27 -15
- package/src/formats/pajek/syntax.ts +99 -58
- package/src/formats/xgmml/emit.ts +88 -40
- package/src/formats/xgmml/exporter.ts +40 -12
- package/src/formats/xgmml/importer.ts +45 -23
- package/src/sniff.ts +115 -59
- package/dist/chunks/children-CL3Cy0ez.js.map +0 -1
- package/dist/chunks/export-B-aVCwyG.js.map +0 -1
- package/dist/chunks/exporter-BVXnV9-9.js.map +0 -1
- package/dist/chunks/exporter-Bcp-l4Bo.js.map +0 -1
- package/dist/chunks/importer-AjwOJrn2.js.map +0 -1
- package/dist/chunks/importer-BpuS3J8C.js.map +0 -1
- package/dist/chunks/importer-CPqnIY8m.js.map +0 -1
- package/dist/chunks/importer-DEp_fYjf.js.map +0 -1
- package/dist/chunks/importer-DcKB97kW.js.map +0 -1
- package/dist/chunks/records-r3SFHz11.js.map +0 -1
- package/dist/chunks/text-Bjfa1xYL.js.map +0 -1
package/dist/src/common/input.js
CHANGED
|
@@ -405,31 +405,58 @@ class ByteDecoder {
|
|
|
405
405
|
const bad = this.encoding === "utf-8" ? invalidUtf8(all) : null;
|
|
406
406
|
const at = base + (bad === null ? 0 : bad.index);
|
|
407
407
|
if (this.mayFallBack && this.asciiSoFar) {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
return this.binary(this.control);
|
|
412
|
-
}
|
|
413
|
-
if (this.textFollows && bad?.cut === true) {
|
|
414
|
-
return this.report.fail(INVALID_UTF8_CODE, `invalid UTF-8 at byte ${at}: a byte chunk ends inside a multi-byte sequence and a text chunk follows`, undefined, { byteOffset: at });
|
|
415
|
-
}
|
|
416
|
-
if (!stream && bad?.truncated === true) {
|
|
417
|
-
return this.report.fail(INVALID_UTF8_CODE, `the input ends in the middle of a UTF-8 sequence at byte ${at} (it may be truncated)`, undefined, { byteOffset: at });
|
|
418
|
-
}
|
|
419
|
-
if (!startsWithUtf8(all)) {
|
|
420
|
-
const cutNote = !stream && bad?.cut === true
|
|
421
|
-
? ": it ends inside a multi-byte UTF-8 sequence, so it may be truncated"
|
|
422
|
-
: "";
|
|
423
|
-
this.report.warning("coercion", ENCODING_FALLBACK_CODE, this.declaredUtf8
|
|
424
|
-
? `the input declares UTF-8 but is not valid UTF-8 (at byte ${at}); everything before was ASCII, so it is read as windows-1252 (pass the encoding option to choose another)`
|
|
425
|
-
: `the input is not valid UTF-8 (at byte ${at}${cutNote}) and declares no encoding; read as windows-1252 (pass the encoding option to choose another)`);
|
|
426
|
-
this.use("windows-1252", false);
|
|
427
|
-
this.fellBack = true;
|
|
428
|
-
this.mixedPending = true;
|
|
429
|
-
this.reportUtf8After(all, base);
|
|
430
|
-
return this.decoder.decode(all, { stream });
|
|
408
|
+
const text = this.fallBack(all, bad, at, base, stream);
|
|
409
|
+
if (text !== null) {
|
|
410
|
+
return text;
|
|
431
411
|
}
|
|
432
412
|
}
|
|
413
|
+
return this.failDecode(bytes, at, base, stream);
|
|
414
|
+
}
|
|
415
|
+
/**
|
|
416
|
+
* Undeclared input that was ASCII so far failed to decode: fail on binary data or a cut
|
|
417
|
+
* sequence, else switch to windows-1252 with a warning unless the bytes start with valid
|
|
418
|
+
* non-ASCII UTF-8.
|
|
419
|
+
* @param all - the held-back bytes followed by the bytes that failed
|
|
420
|
+
* @param bad - the first invalid UTF-8 sequence, or null when the encoding is not UTF-8
|
|
421
|
+
* @param at - the byte offset of the first invalid byte
|
|
422
|
+
* @param base - the byte offset of `all`
|
|
423
|
+
* @param stream - whether more bytes follow
|
|
424
|
+
* @returns the bytes decoded as windows-1252, or null when they do not fall back
|
|
425
|
+
*/
|
|
426
|
+
fallBack(all, bad, at, base, stream) {
|
|
427
|
+
// windows-1252 text holds no control bytes but TAB, LF, FF and CR: one anywhere in
|
|
428
|
+
// undeclared input that is not UTF-8 marks binary data (watch() saw every byte so far)
|
|
429
|
+
if (this.control !== null) {
|
|
430
|
+
return this.binary(this.control);
|
|
431
|
+
}
|
|
432
|
+
if (this.textFollows && bad?.cut === true) {
|
|
433
|
+
return this.report.fail(INVALID_UTF8_CODE, `invalid UTF-8 at byte ${at}: a byte chunk ends inside a multi-byte sequence and a text chunk follows`, undefined, { byteOffset: at });
|
|
434
|
+
}
|
|
435
|
+
if (!stream && bad?.truncated === true) {
|
|
436
|
+
return this.report.fail(INVALID_UTF8_CODE, `the input ends in the middle of a UTF-8 sequence at byte ${at} (it may be truncated)`, undefined, { byteOffset: at });
|
|
437
|
+
}
|
|
438
|
+
if (startsWithUtf8(all)) {
|
|
439
|
+
return null;
|
|
440
|
+
}
|
|
441
|
+
const cutNote = !stream && bad?.cut === true ? ": it ends inside a multi-byte UTF-8 sequence, so it may be truncated" : "";
|
|
442
|
+
this.report.warning("coercion", ENCODING_FALLBACK_CODE, this.declaredUtf8
|
|
443
|
+
? `the input declares UTF-8 but is not valid UTF-8 (at byte ${at}); everything before was ASCII, so it is read as windows-1252 (pass the encoding option to choose another)`
|
|
444
|
+
: `the input is not valid UTF-8 (at byte ${at}${cutNote}) and declares no encoding; read as windows-1252 (pass the encoding option to choose another)`);
|
|
445
|
+
this.use("windows-1252", false);
|
|
446
|
+
this.fellBack = true;
|
|
447
|
+
this.mixedPending = true;
|
|
448
|
+
this.reportUtf8After(all, base);
|
|
449
|
+
return this.decoder.decode(all, { stream });
|
|
450
|
+
}
|
|
451
|
+
/**
|
|
452
|
+
* Fail the import on bytes that are not valid in the encoding.
|
|
453
|
+
* @param bytes - the bytes that failed
|
|
454
|
+
* @param at - the byte offset of the first invalid UTF-8 byte
|
|
455
|
+
* @param base - the byte offset of the held-back bytes and these
|
|
456
|
+
* @param stream - whether more bytes follow
|
|
457
|
+
* @returns never: it throws
|
|
458
|
+
*/
|
|
459
|
+
failDecode(bytes, at, base, stream) {
|
|
433
460
|
if (this.encoding === "utf-8") {
|
|
434
461
|
const after = this.mayFallBack ? " after valid non-ASCII UTF-8 text; pass the encoding option" : "";
|
|
435
462
|
return this.report.fail(INVALID_UTF8_CODE, `invalid UTF-8 at byte ${at}${after}`, undefined, {
|
|
@@ -1018,132 +1045,210 @@ function reportTextEncoding(report, encoding) {
|
|
|
1018
1045
|
export async function* textChunks(rawInput, report, options = {}) {
|
|
1019
1046
|
const input = normalizeInput(rawInput);
|
|
1020
1047
|
const signal = options.signal ?? null;
|
|
1021
|
-
const onProgress = options.onProgress ?? null;
|
|
1022
1048
|
const filter = new TextFilter(report, options);
|
|
1023
1049
|
throwIfAborted(signal);
|
|
1024
1050
|
if (typeof input === "string") {
|
|
1025
1051
|
reportTextEncoding(report, options.encoding);
|
|
1026
|
-
|
|
1027
|
-
let done = 0;
|
|
1028
|
-
let start = 0;
|
|
1029
|
-
while (start < input.length) {
|
|
1030
|
-
throwIfAborted(signal);
|
|
1031
|
-
let end = Math.min(start + DECODE_SLICE, input.length);
|
|
1032
|
-
const highSurrogate = (input.charCodeAt(end - 1) & 0xfc00) === 0xd800; // NOSONAR(S7758): reads UTF-16 code units on purpose
|
|
1033
|
-
if (end < input.length && highSurrogate) {
|
|
1034
|
-
end++; // never split a surrogate pair
|
|
1035
|
-
}
|
|
1036
|
-
const piece = start === 0 && end === input.length ? input : input.slice(start, end);
|
|
1037
|
-
start = end;
|
|
1038
|
-
const text = filter.push(piece);
|
|
1039
|
-
if (text.length > 0) {
|
|
1040
|
-
yield text;
|
|
1041
|
-
}
|
|
1042
|
-
if (onProgress !== null) {
|
|
1043
|
-
done += utf8Length(piece);
|
|
1044
|
-
onProgress(done, total);
|
|
1045
|
-
}
|
|
1046
|
-
}
|
|
1047
|
-
if (input.length === 0) {
|
|
1048
|
-
onProgress?.(0, 0);
|
|
1049
|
-
}
|
|
1050
|
-
filter.end();
|
|
1052
|
+
yield* stringTextChunks(input, filter, options);
|
|
1051
1053
|
return;
|
|
1052
1054
|
}
|
|
1053
1055
|
const decoder = new ByteDecoder(report, options);
|
|
1054
|
-
const headBytes = Math.max(options.declarationBytes ?? HEAD_BYTES, BOM_BYTES);
|
|
1055
1056
|
if (input instanceof Uint8Array) {
|
|
1056
|
-
|
|
1057
|
-
decoder.start(input.subarray(0, headBytes));
|
|
1058
|
-
for (let offset = 0; offset < length; offset += DECODE_SLICE) {
|
|
1059
|
-
throwIfAborted(signal);
|
|
1060
|
-
if (input.byteLength !== length) {
|
|
1061
|
-
throw new GraphFormatError("E_UNSUPPORTED", "the input's buffer was detached (transferred) or resized during the import", { reason: "detached" });
|
|
1062
|
-
}
|
|
1063
|
-
const slice = input.subarray(offset, Math.min(offset + DECODE_SLICE, length));
|
|
1064
|
-
const text = filter.push(decoder.decode(slice, offset + DECODE_SLICE < length));
|
|
1065
|
-
if (text.length > 0) {
|
|
1066
|
-
yield text;
|
|
1067
|
-
}
|
|
1068
|
-
onProgress?.(Math.min(offset + DECODE_SLICE, length), length);
|
|
1069
|
-
}
|
|
1070
|
-
if (length === 0) {
|
|
1071
|
-
onProgress?.(0, 0);
|
|
1072
|
-
}
|
|
1073
|
-
filter.end();
|
|
1057
|
+
yield* byteTextChunks(input, filter, decoder, options);
|
|
1074
1058
|
return;
|
|
1075
1059
|
}
|
|
1076
|
-
// the
|
|
1077
|
-
//
|
|
1078
|
-
|
|
1079
|
-
|
|
1060
|
+
// the stream's loop stays here rather than in a delegated generator: a yield* layer costs
|
|
1061
|
+
// time on every chunk, and a stream may arrive in many small ones
|
|
1062
|
+
const stream = new StreamText(filter, decoder, options);
|
|
1063
|
+
for await (const chunk of isReadableStream(input) ? streamChunks(input, signal) : input) {
|
|
1064
|
+
throwIfAborted(signal);
|
|
1065
|
+
const text = stream.push(chunk);
|
|
1066
|
+
if (text.length > 0) {
|
|
1067
|
+
yield text;
|
|
1068
|
+
}
|
|
1069
|
+
options.onProgress?.(stream.done);
|
|
1070
|
+
}
|
|
1071
|
+
const tail = stream.end();
|
|
1072
|
+
if (tail.length > 0) {
|
|
1073
|
+
yield tail;
|
|
1074
|
+
}
|
|
1075
|
+
filter.end();
|
|
1076
|
+
options.onProgress?.(stream.done, stream.done);
|
|
1077
|
+
}
|
|
1078
|
+
/**
|
|
1079
|
+
* The end of the slice of a string input that starts at start: DECODE_SLICE code units, one more
|
|
1080
|
+
* when the slice would end between the two halves of a surrogate pair.
|
|
1081
|
+
* @param input - the string
|
|
1082
|
+
* @param start - where the slice starts
|
|
1083
|
+
* @returns where it ends
|
|
1084
|
+
*/
|
|
1085
|
+
function stringSliceEnd(input, start) {
|
|
1086
|
+
const end = Math.min(start + DECODE_SLICE, input.length);
|
|
1087
|
+
const highSurrogate = (input.charCodeAt(end - 1) & 0xfc00) === 0xd800; // NOSONAR(S7758): reads UTF-16 code units on purpose
|
|
1088
|
+
return end < input.length && highSurrogate ? end + 1 : end;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* textChunks() of a string input: slices of it, through the text filter.
|
|
1092
|
+
* @param input - the string
|
|
1093
|
+
* @param filter - the text checks
|
|
1094
|
+
* @param options - cancellation and progress
|
|
1095
|
+
* @yields the filtered slices
|
|
1096
|
+
* @returns nothing
|
|
1097
|
+
*/
|
|
1098
|
+
function* stringTextChunks(input, filter, options) {
|
|
1099
|
+
const signal = options.signal ?? null;
|
|
1100
|
+
const onProgress = options.onProgress ?? null;
|
|
1101
|
+
const total = onProgress === null ? 0 : utf8Length(input);
|
|
1080
1102
|
let done = 0;
|
|
1081
|
-
let
|
|
1082
|
-
|
|
1083
|
-
const flushHead = (stream) => {
|
|
1084
|
-
const bytes = concatBytes(head);
|
|
1085
|
-
head = [];
|
|
1086
|
-
headLength = 0;
|
|
1087
|
-
decoder.start(bytes.subarray(0, headBytes));
|
|
1088
|
-
return decoder.decode(bytes, stream);
|
|
1089
|
-
};
|
|
1090
|
-
const chunks = isReadableStream(input) ? streamChunks(input, signal) : input;
|
|
1091
|
-
for await (const chunk of chunks) {
|
|
1103
|
+
let start = 0;
|
|
1104
|
+
while (start < input.length) {
|
|
1092
1105
|
throwIfAborted(signal);
|
|
1093
|
-
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
let pending = "";
|
|
1100
|
-
if (headLength > 0) {
|
|
1101
|
-
pending = decoder.flushBeforeText(() => flushHead(false));
|
|
1102
|
-
}
|
|
1103
|
-
else if (decoder.started) {
|
|
1104
|
-
pending = decoder.flushBeforeText(() => decoder.decode(new Uint8Array(0), false));
|
|
1105
|
-
}
|
|
1106
|
-
else {
|
|
1107
|
-
decoder.start(new Uint8Array(0));
|
|
1108
|
-
}
|
|
1109
|
-
text = filter.push(pending + chunk);
|
|
1110
|
-
done += onProgress === null ? 0 : utf8Length(chunk);
|
|
1106
|
+
const end = stringSliceEnd(input, start);
|
|
1107
|
+
const piece = start === 0 && end === input.length ? input : input.slice(start, end);
|
|
1108
|
+
start = end;
|
|
1109
|
+
const text = filter.push(piece);
|
|
1110
|
+
if (text.length > 0) {
|
|
1111
|
+
yield text;
|
|
1111
1112
|
}
|
|
1112
|
-
|
|
1113
|
-
done +=
|
|
1114
|
-
|
|
1115
|
-
text = filter.push(decoder.decode(bytes, true));
|
|
1116
|
-
}
|
|
1117
|
-
else {
|
|
1118
|
-
head.push(bytes);
|
|
1119
|
-
headLength += bytes.byteLength;
|
|
1120
|
-
text = headLength >= needed ? filter.push(flushHead(true)) : "";
|
|
1121
|
-
}
|
|
1113
|
+
if (onProgress !== null) {
|
|
1114
|
+
done += utf8Length(piece);
|
|
1115
|
+
onProgress(done, total);
|
|
1122
1116
|
}
|
|
1123
|
-
|
|
1117
|
+
}
|
|
1118
|
+
if (input.length === 0) {
|
|
1119
|
+
onProgress?.(0, 0);
|
|
1120
|
+
}
|
|
1121
|
+
filter.end();
|
|
1122
|
+
}
|
|
1123
|
+
/**
|
|
1124
|
+
* textChunks() of a Uint8Array input: slices of it, decoded, through the text filter.
|
|
1125
|
+
* @param input - the bytes
|
|
1126
|
+
* @param filter - the text checks
|
|
1127
|
+
* @param decoder - the byte decoder
|
|
1128
|
+
* @param options - cancellation, progress and the declaration size
|
|
1129
|
+
* @yields the decoded, filtered slices
|
|
1130
|
+
* @returns nothing
|
|
1131
|
+
*/
|
|
1132
|
+
function* byteTextChunks(input, filter, decoder, options) {
|
|
1133
|
+
const signal = options.signal ?? null;
|
|
1134
|
+
const length = input.byteLength;
|
|
1135
|
+
decoder.start(input.subarray(0, headByteCount(options)));
|
|
1136
|
+
for (let offset = 0; offset < length; offset += DECODE_SLICE) {
|
|
1137
|
+
throwIfAborted(signal);
|
|
1138
|
+
if (input.byteLength !== length) {
|
|
1139
|
+
throw new GraphFormatError("E_UNSUPPORTED", "the input's buffer was detached (transferred) or resized during the import", { reason: "detached" });
|
|
1140
|
+
}
|
|
1141
|
+
const slice = input.subarray(offset, Math.min(offset + DECODE_SLICE, length));
|
|
1142
|
+
const text = filter.push(decoder.decode(slice, offset + DECODE_SLICE < length));
|
|
1143
|
+
if (text.length > 0) {
|
|
1144
|
+
yield text;
|
|
1145
|
+
}
|
|
1146
|
+
options.onProgress?.(Math.min(offset + DECODE_SLICE, length), length);
|
|
1147
|
+
}
|
|
1148
|
+
if (length === 0) {
|
|
1149
|
+
options.onProgress?.(0, 0);
|
|
1150
|
+
}
|
|
1151
|
+
filter.end();
|
|
1152
|
+
}
|
|
1153
|
+
/**
|
|
1154
|
+
* How many leading bytes the encoding is chosen from: the format's declaration size, at least a BOM.
|
|
1155
|
+
* @param options - the declaration size
|
|
1156
|
+
* @returns the byte count
|
|
1157
|
+
*/
|
|
1158
|
+
function headByteCount(options) {
|
|
1159
|
+
return Math.max(options.declarationBytes ?? HEAD_BYTES, BOM_BYTES);
|
|
1160
|
+
}
|
|
1161
|
+
/**
|
|
1162
|
+
* The chunks of a stream input (a ReadableStream or an async iterable of strings and bytes) turned
|
|
1163
|
+
* into filtered text. The first bytes are held back until the BOM check (and the declaration
|
|
1164
|
+
* check, when the format has one) can see enough of them, or the input ended, or text arrived, so
|
|
1165
|
+
* a BOM or a declaration split across small chunks is still seen.
|
|
1166
|
+
*/
|
|
1167
|
+
class StreamText {
|
|
1168
|
+
/**
|
|
1169
|
+
* Create the reader of one stream input.
|
|
1170
|
+
* @param filter - the text checks
|
|
1171
|
+
* @param decoder - the byte decoder
|
|
1172
|
+
* @param options - progress, the declaration reader and its size
|
|
1173
|
+
*/
|
|
1174
|
+
constructor(filter, decoder, options) {
|
|
1175
|
+
this.filter = filter;
|
|
1176
|
+
this.decoder = decoder;
|
|
1177
|
+
/** Bytes read so far (text counted as UTF-8 when progress is reported), for progress. */
|
|
1178
|
+
this.done = 0;
|
|
1179
|
+
this.head = [];
|
|
1180
|
+
this.headLength = 0;
|
|
1181
|
+
this.headBytes = headByteCount(options);
|
|
1182
|
+
this.needed = options.declaredEncoding === undefined ? BOM_BYTES : this.headBytes;
|
|
1183
|
+
this.counting = (options.onProgress ?? null) !== null;
|
|
1184
|
+
}
|
|
1185
|
+
/**
|
|
1186
|
+
* Read the next chunk.
|
|
1187
|
+
* @param chunk - a string or a Uint8Array (anything else is E_UNSUPPORTED)
|
|
1188
|
+
* @returns its filtered text; empty while bytes are held back
|
|
1189
|
+
*/
|
|
1190
|
+
push(chunk) {
|
|
1191
|
+
if (typeof chunk === "string") {
|
|
1192
|
+
this.done += this.counting ? utf8Length(chunk) : 0;
|
|
1193
|
+
return this.filter.push(this.beforeText() + chunk);
|
|
1194
|
+
}
|
|
1195
|
+
const bytes = asBytes(chunk);
|
|
1196
|
+
if (bytes === null) {
|
|
1124
1197
|
throw new GraphFormatError("E_UNSUPPORTED", "an input chunk must be a string or a Uint8Array", {
|
|
1125
1198
|
reason: "chunk type",
|
|
1126
1199
|
found: typeof chunk,
|
|
1127
1200
|
});
|
|
1128
1201
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1202
|
+
this.done += bytes.byteLength;
|
|
1203
|
+
if (this.decoder.started) {
|
|
1204
|
+
return this.filter.push(this.decoder.decode(bytes, true));
|
|
1131
1205
|
}
|
|
1132
|
-
|
|
1206
|
+
this.head.push(bytes);
|
|
1207
|
+
this.headLength += bytes.byteLength;
|
|
1208
|
+
return this.headLength >= this.needed ? this.filter.push(this.flushHead(true)) : "";
|
|
1133
1209
|
}
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1210
|
+
/**
|
|
1211
|
+
* The input ended: the filtered text of whatever is still held or pending.
|
|
1212
|
+
* @returns the text
|
|
1213
|
+
*/
|
|
1214
|
+
end() {
|
|
1215
|
+
let tail = "";
|
|
1216
|
+
if (this.headLength > 0) {
|
|
1217
|
+
tail = this.flushHead(false);
|
|
1218
|
+
}
|
|
1219
|
+
else if (this.decoder.started) {
|
|
1220
|
+
tail = this.decoder.decode(new Uint8Array(0), false);
|
|
1221
|
+
}
|
|
1222
|
+
return this.filter.push(tail);
|
|
1137
1223
|
}
|
|
1138
|
-
|
|
1139
|
-
|
|
1224
|
+
/**
|
|
1225
|
+
* Start the decoder on the held bytes and decode them.
|
|
1226
|
+
* @param stream - whether more bytes follow
|
|
1227
|
+
* @returns their text
|
|
1228
|
+
*/
|
|
1229
|
+
flushHead(stream) {
|
|
1230
|
+
const bytes = concatBytes(this.head);
|
|
1231
|
+
this.head = [];
|
|
1232
|
+
this.headLength = 0;
|
|
1233
|
+
this.decoder.start(bytes.subarray(0, this.headBytes));
|
|
1234
|
+
return this.decoder.decode(bytes, stream);
|
|
1140
1235
|
}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1236
|
+
/**
|
|
1237
|
+
* A text chunk follows: finish any byte sequence still pending in the decoder before switching
|
|
1238
|
+
* to text. The BOM and declaration checks apply to the start of the input only, so bytes after
|
|
1239
|
+
* text are never taken for a BOM.
|
|
1240
|
+
* @returns the text of the pending bytes
|
|
1241
|
+
*/
|
|
1242
|
+
beforeText() {
|
|
1243
|
+
if (this.headLength > 0) {
|
|
1244
|
+
return this.decoder.flushBeforeText(() => this.flushHead(false));
|
|
1245
|
+
}
|
|
1246
|
+
if (this.decoder.started) {
|
|
1247
|
+
return this.decoder.flushBeforeText(() => this.decoder.decode(new Uint8Array(0), false));
|
|
1248
|
+
}
|
|
1249
|
+
this.decoder.start(new Uint8Array(0));
|
|
1250
|
+
return "";
|
|
1144
1251
|
}
|
|
1145
|
-
filter.end();
|
|
1146
|
-
onProgress?.(done, done);
|
|
1147
1252
|
}
|
|
1148
1253
|
/**
|
|
1149
1254
|
* Whether an input is a ReadableStream (by duck type, so a stream from another realm qualifies).
|
|
@@ -1309,84 +1414,153 @@ export class LineReader {
|
|
|
1309
1414
|
* @returns nothing
|
|
1310
1415
|
*/
|
|
1311
1416
|
async *[Symbol.asyncIterator]() {
|
|
1312
|
-
// The pieces of the line in progress
|
|
1313
|
-
//
|
|
1314
|
-
|
|
1315
|
-
const pending = [];
|
|
1316
|
-
let pendingLength = 0;
|
|
1417
|
+
// The pieces of the line in progress, joined once when the line ends, so a line spanning
|
|
1418
|
+
// many chunks costs its length, not its length times the chunk count.
|
|
1419
|
+
const pending = new PendingLine((line) => tooLarge(this.report, this.lineTooLong(), line));
|
|
1317
1420
|
let trailingCr = false;
|
|
1318
1421
|
for await (const chunk of textChunks(this.input, this.report, this.options)) {
|
|
1319
1422
|
let start = 0;
|
|
1320
|
-
const end = chunk.length;
|
|
1321
1423
|
if (trailingCr) {
|
|
1322
1424
|
// the previous chunk ended with `\r`: that ended a line, and a leading `\n` here is
|
|
1323
1425
|
// the second half of the same terminator
|
|
1324
1426
|
trailingCr = false;
|
|
1325
1427
|
this.lineNumber++;
|
|
1326
|
-
|
|
1327
|
-
pending.length = 0;
|
|
1328
|
-
pendingLength = 0;
|
|
1329
|
-
yield joined.slice(0, -1);
|
|
1428
|
+
yield pending.take().slice(0, -1);
|
|
1330
1429
|
if (chunk.charCodeAt(0) === 10) {
|
|
1331
1430
|
start = 1;
|
|
1332
1431
|
}
|
|
1333
1432
|
}
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
let nl = chunk.indexOf("\n", start);
|
|
1337
|
-
let cr = chunk.indexOf("\r", start);
|
|
1338
|
-
while (nl >= 0 || cr >= 0) {
|
|
1339
|
-
let cut;
|
|
1340
|
-
let next;
|
|
1341
|
-
if (cr >= 0 && (nl < 0 || cr < nl)) {
|
|
1342
|
-
if (cr === end - 1) {
|
|
1343
|
-
// a trailing \r may be the first half of \r\n split across chunks
|
|
1344
|
-
break;
|
|
1345
|
-
}
|
|
1346
|
-
cut = cr;
|
|
1347
|
-
next = chunk.charCodeAt(cr + 1) === 10 ? cr + 2 : cr + 1;
|
|
1348
|
-
}
|
|
1349
|
-
else {
|
|
1350
|
-
cut = nl;
|
|
1351
|
-
next = nl + 1;
|
|
1352
|
-
}
|
|
1433
|
+
const breaks = new LineBreaks(chunk, start);
|
|
1434
|
+
while (breaks.advance()) {
|
|
1353
1435
|
this.lineNumber++;
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
yield piece;
|
|
1357
|
-
}
|
|
1358
|
-
else {
|
|
1359
|
-
if (pendingLength + piece.length > MAX_TEXT_LENGTH) {
|
|
1360
|
-
tooLarge(this.report, this.lineTooLong(), this.lineNumber);
|
|
1361
|
-
}
|
|
1362
|
-
pending.push(piece);
|
|
1363
|
-
const joined = pending.join("");
|
|
1364
|
-
pending.length = 0;
|
|
1365
|
-
pendingLength = 0;
|
|
1366
|
-
yield joined;
|
|
1367
|
-
}
|
|
1368
|
-
start = next;
|
|
1369
|
-
if (nl >= 0 && nl < next) {
|
|
1370
|
-
nl = chunk.indexOf("\n", next);
|
|
1371
|
-
}
|
|
1372
|
-
if (cr >= 0 && cr < next) {
|
|
1373
|
-
cr = chunk.indexOf("\r", next);
|
|
1374
|
-
}
|
|
1436
|
+
yield pending.finish(chunk.slice(start, breaks.cut), this.lineNumber);
|
|
1437
|
+
start = breaks.next;
|
|
1375
1438
|
}
|
|
1376
|
-
if (start <
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
tooLarge(this.report, this.lineTooLong(), this.lineNumber + 1);
|
|
1380
|
-
}
|
|
1381
|
-
pending.push(start === 0 ? chunk : chunk.slice(start));
|
|
1382
|
-
trailingCr = chunk.charCodeAt(end - 1) === 13;
|
|
1439
|
+
if (start < chunk.length) {
|
|
1440
|
+
pending.appendRest(chunk, start, this.lineNumber + 1);
|
|
1441
|
+
trailingCr = chunk.codePointAt(chunk.length - 1) === 13;
|
|
1383
1442
|
}
|
|
1384
1443
|
}
|
|
1385
|
-
if (pending.
|
|
1444
|
+
if (!pending.empty) {
|
|
1386
1445
|
this.lineNumber++;
|
|
1387
|
-
const joined = pending.
|
|
1446
|
+
const joined = pending.take();
|
|
1388
1447
|
yield joined.endsWith("\r") ? joined.slice(0, -1) : joined;
|
|
1389
1448
|
}
|
|
1390
1449
|
}
|
|
1391
1450
|
}
|
|
1451
|
+
/**
|
|
1452
|
+
* The pieces of a line that spans chunks. None of them holds a line break, except that the last
|
|
1453
|
+
* may end with a `\r` whose meaning the next chunk decides.
|
|
1454
|
+
*/
|
|
1455
|
+
class PendingLine {
|
|
1456
|
+
/**
|
|
1457
|
+
* Start an empty line.
|
|
1458
|
+
* @param tooLong - fails the import when the line grows longer than one string can hold
|
|
1459
|
+
*/
|
|
1460
|
+
constructor(tooLong) {
|
|
1461
|
+
this.pieces = [];
|
|
1462
|
+
this.length = 0;
|
|
1463
|
+
this.tooLong = tooLong;
|
|
1464
|
+
}
|
|
1465
|
+
/**
|
|
1466
|
+
* Whether no piece is held.
|
|
1467
|
+
* @returns true when the line in progress is empty
|
|
1468
|
+
*/
|
|
1469
|
+
get empty() {
|
|
1470
|
+
return this.pieces.length === 0;
|
|
1471
|
+
}
|
|
1472
|
+
/**
|
|
1473
|
+
* Hold the rest of a chunk from `start`: the line goes on in the next chunk.
|
|
1474
|
+
* @param chunk - the chunk
|
|
1475
|
+
* @param start - where the unfinished line starts in it
|
|
1476
|
+
* @param line - the 1-based number of the line, for the too-long error
|
|
1477
|
+
*/
|
|
1478
|
+
appendRest(chunk, start, line) {
|
|
1479
|
+
this.append(start === 0 ? chunk : chunk.slice(start), line);
|
|
1480
|
+
}
|
|
1481
|
+
/**
|
|
1482
|
+
* End the line with its last piece.
|
|
1483
|
+
* @param piece - the text from the start of the chunk (or the last break) to the line break
|
|
1484
|
+
* @param line - the 1-based number of the line, for the too-long error
|
|
1485
|
+
* @returns the whole line
|
|
1486
|
+
*/
|
|
1487
|
+
finish(piece, line) {
|
|
1488
|
+
if (this.pieces.length === 0) {
|
|
1489
|
+
return piece;
|
|
1490
|
+
}
|
|
1491
|
+
this.append(piece, line);
|
|
1492
|
+
return this.take();
|
|
1493
|
+
}
|
|
1494
|
+
/**
|
|
1495
|
+
* Join the held pieces and start a new line.
|
|
1496
|
+
* @returns the joined text
|
|
1497
|
+
*/
|
|
1498
|
+
take() {
|
|
1499
|
+
const joined = this.pieces.join("");
|
|
1500
|
+
this.pieces.length = 0;
|
|
1501
|
+
this.length = 0;
|
|
1502
|
+
return joined;
|
|
1503
|
+
}
|
|
1504
|
+
/**
|
|
1505
|
+
* Hold one more piece.
|
|
1506
|
+
* @param piece - the piece
|
|
1507
|
+
* @param line - the 1-based number of the line, for the too-long error
|
|
1508
|
+
*/
|
|
1509
|
+
append(piece, line) {
|
|
1510
|
+
this.length += piece.length;
|
|
1511
|
+
if (this.length > MAX_TEXT_LENGTH) {
|
|
1512
|
+
this.tooLong(line);
|
|
1513
|
+
}
|
|
1514
|
+
this.pieces.push(piece);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
/**
|
|
1518
|
+
* The line breaks of one chunk, in order: `\n`, `\r\n` and a lone `\r`. Each of `\n` and `\r` is
|
|
1519
|
+
* searched for once per chunk and again only after it was consumed, so a chunk is scanned once
|
|
1520
|
+
* whatever its line count.
|
|
1521
|
+
*/
|
|
1522
|
+
class LineBreaks {
|
|
1523
|
+
/**
|
|
1524
|
+
* Scan a chunk from a position.
|
|
1525
|
+
* @param chunk - the chunk
|
|
1526
|
+
* @param start - where scanning starts
|
|
1527
|
+
*/
|
|
1528
|
+
constructor(chunk, start) {
|
|
1529
|
+
/** Where the line just found ends (its terminator starts there). */
|
|
1530
|
+
this.cut = 0;
|
|
1531
|
+
this.chunk = chunk;
|
|
1532
|
+
this.next = start;
|
|
1533
|
+
this.nl = chunk.indexOf("\n", start);
|
|
1534
|
+
this.cr = chunk.indexOf("\r", start);
|
|
1535
|
+
}
|
|
1536
|
+
/**
|
|
1537
|
+
* Find the next line break.
|
|
1538
|
+
* @returns false when none is left, or the only one left is a trailing `\r` (it may be the first
|
|
1539
|
+
* half of a `\r\n` split across chunks)
|
|
1540
|
+
*/
|
|
1541
|
+
advance() {
|
|
1542
|
+
const { chunk, next } = this;
|
|
1543
|
+
if (this.nl >= 0 && this.nl < next) {
|
|
1544
|
+
this.nl = chunk.indexOf("\n", next);
|
|
1545
|
+
}
|
|
1546
|
+
if (this.cr >= 0 && this.cr < next) {
|
|
1547
|
+
this.cr = chunk.indexOf("\r", next);
|
|
1548
|
+
}
|
|
1549
|
+
const { nl, cr } = this;
|
|
1550
|
+
if (cr >= 0 && (nl < 0 || cr < nl)) {
|
|
1551
|
+
if (cr === chunk.length - 1) {
|
|
1552
|
+
return false;
|
|
1553
|
+
}
|
|
1554
|
+
this.cut = cr;
|
|
1555
|
+
this.next = chunk.codePointAt(cr + 1) === 10 ? cr + 2 : cr + 1;
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
if (nl < 0) {
|
|
1559
|
+
return false;
|
|
1560
|
+
}
|
|
1561
|
+
this.cut = nl;
|
|
1562
|
+
this.next = nl + 1;
|
|
1563
|
+
return true;
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1392
1566
|
//# sourceMappingURL=input.js.map
|