@sema-agent/client-core 0.83.4 → 0.83.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/CHANGELOG.md +97 -0
- package/README.md +16 -15
- package/dist/adapter/activeRunSelfHeal.d.ts +3 -0
- package/dist/adapter/activeRunSelfHeal.js +39 -12
- package/dist/compensations.js +1 -1
- package/dist/displayUntrusted.d.ts +13 -0
- package/dist/displayUntrusted.js +751 -125
- package/dist/hooksWireCaps.js +78 -8
- package/dist/host.d.ts +2 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/pluginHooksWire.d.ts +9 -0
- package/dist/pluginHooksWire.js +119 -0
- package/dist/request/taskRequest.js +1 -1
- package/docs/INTEGRATION-CLIENTS.md +280 -66
- package/package.json +1 -1
package/dist/displayUntrusted.js
CHANGED
|
@@ -37,13 +37,51 @@ function applyEdits(text, edits) {
|
|
|
37
37
|
}
|
|
38
38
|
class PlainTarget {
|
|
39
39
|
text;
|
|
40
|
-
|
|
40
|
+
origin;
|
|
41
|
+
trace;
|
|
42
|
+
constructor(text, trace = false) {
|
|
41
43
|
this.text = text;
|
|
44
|
+
this.trace = trace;
|
|
42
45
|
}
|
|
43
46
|
apply(edits) {
|
|
47
|
+
if (this.trace && edits.length > 0)
|
|
48
|
+
this.origin = editedOrigin(this.origin, this.text.length, edits);
|
|
44
49
|
this.text = applyEdits(this.text, edits);
|
|
45
50
|
}
|
|
46
51
|
}
|
|
52
|
+
const traced = (text, origin) => (origin === undefined ? { text } : { text, origin });
|
|
53
|
+
function editedOrigin(prev, length, edits) {
|
|
54
|
+
let n = length;
|
|
55
|
+
for (const e of edits)
|
|
56
|
+
n += e.text.length - (e.end - e.start);
|
|
57
|
+
const out = new Int32Array(n);
|
|
58
|
+
let w = 0;
|
|
59
|
+
let cursor = 0;
|
|
60
|
+
const copy = (a, b) => {
|
|
61
|
+
for (let k = a; k < b; k++)
|
|
62
|
+
out[w++] = prev === undefined ? k : (prev[k] ?? -1);
|
|
63
|
+
};
|
|
64
|
+
for (const e of edits) {
|
|
65
|
+
copy(cursor, e.start);
|
|
66
|
+
out.fill(-1, w, w + e.text.length);
|
|
67
|
+
w += e.text.length;
|
|
68
|
+
cursor = e.end;
|
|
69
|
+
}
|
|
70
|
+
copy(cursor, length);
|
|
71
|
+
return out;
|
|
72
|
+
}
|
|
73
|
+
function composeOrigin(outer, inner) {
|
|
74
|
+
if (inner === undefined)
|
|
75
|
+
return outer;
|
|
76
|
+
if (outer === undefined)
|
|
77
|
+
return inner;
|
|
78
|
+
const out = new Int32Array(inner.length);
|
|
79
|
+
for (let k = 0; k < inner.length; k++) {
|
|
80
|
+
const s = inner[k] ?? -1;
|
|
81
|
+
out[k] = s < 0 ? -1 : (outer[s] ?? -1);
|
|
82
|
+
}
|
|
83
|
+
return out;
|
|
84
|
+
}
|
|
47
85
|
class TrackedTarget {
|
|
48
86
|
text;
|
|
49
87
|
src;
|
|
@@ -272,9 +310,10 @@ function urlTokenEdits(text) {
|
|
|
272
310
|
const ch = text.charAt(end);
|
|
273
311
|
if (isWhitespaceOrControl(ch) || TOKEN_STOP.has(ch))
|
|
274
312
|
break;
|
|
275
|
-
|
|
313
|
+
const c = ch.charCodeAt(0);
|
|
314
|
+
if (ch === VIEW_SEPARATOR || isUnicodeBlank(c)) {
|
|
276
315
|
let p = end;
|
|
277
|
-
while (p < text.length && text[p] === VIEW_SEPARATOR)
|
|
316
|
+
while (p < text.length && (text[p] === VIEW_SEPARATOR || isUnicodeBlank(text.charCodeAt(p))))
|
|
278
317
|
p++;
|
|
279
318
|
if (credentialWordStartsNear(text, p))
|
|
280
319
|
break;
|
|
@@ -317,6 +356,10 @@ function schemelessUserinfoEdits(text) {
|
|
|
317
356
|
let runStart = at;
|
|
318
357
|
while (runStart > floor && !breaksUserinfo(text.charCodeAt(runStart - 1)))
|
|
319
358
|
runStart--;
|
|
359
|
+
if (text.charCodeAt(runStart) === 0x3a && runStart > floor && text[runStart - 1] === VIEW_SEPARATOR) {
|
|
360
|
+
while (runStart > floor && text[runStart - 1] === VIEW_SEPARATOR)
|
|
361
|
+
runStart--;
|
|
362
|
+
}
|
|
320
363
|
let found = -1;
|
|
321
364
|
let colon = -1;
|
|
322
365
|
let s = runStart;
|
|
@@ -363,8 +406,23 @@ function washUrlNets(target) {
|
|
|
363
406
|
return;
|
|
364
407
|
}
|
|
365
408
|
}
|
|
366
|
-
const
|
|
367
|
-
|
|
409
|
+
const NAME_BLANK_RANGES = [
|
|
410
|
+
[0x00a0, 0x00a0],
|
|
411
|
+
[0x1680, 0x1680],
|
|
412
|
+
[0x2000, 0x200a],
|
|
413
|
+
[0x202f, 0x202f],
|
|
414
|
+
[0x205f, 0x205f],
|
|
415
|
+
[0x3000, 0x3000],
|
|
416
|
+
[0x115f, 0x1160],
|
|
417
|
+
[0x3164, 0x3164],
|
|
418
|
+
[0xffa0, 0xffa0],
|
|
419
|
+
[0x2800, 0x2800],
|
|
420
|
+
];
|
|
421
|
+
const isNameBlank = (cp) => NAME_BLANK_RANGES.some(([a, b]) => cp >= a && cp <= b);
|
|
422
|
+
const isFoldedBlank = (c) => isNameBlank(c) && isRegexSpace(c);
|
|
423
|
+
const isUnicodeBlank = (c) => (c > 0x7f && isRegexSpace(c) && c !== 0xfeff) || isNameBlank(c);
|
|
424
|
+
const SECRET_SCHEME_WORD = /(?:\b|(?<=\\[A-Za-z"']))(bearer|basic)[\s:=\uFF1A\uFF1D\u115F\u1160\u3164\uFFA0\u2800]+/gi;
|
|
425
|
+
const SECRET_LABELLED_WORD = /(?:\b|(?<=_)|(?<=\\[A-Za-z"']))((?:access[-_ ]?|refresh[-_ ]?|id[-_ ]?|client[-_ ]?|api[-_ ]?|x[-_]api[-_ ]?|session[-_ ]?|auth[-_ ]?)?(?:token|key|secret|password|authorization|credential)s?)(?:\\*["'])?[\s\u115F\u1160\u3164\uFFA0\u2800]*[:=\uFF1A\uFF1D][\s\u115F\u1160\u3164\uFFA0\u2800]*/gi;
|
|
368
426
|
const DIAGNOSTIC_VALUE_WORDS = new Set([
|
|
369
427
|
'missing', 'required', 'invalid', 'expired', 'unauthorized', 'unauthenticated', 'authentication',
|
|
370
428
|
'authorization', 'credential', 'credentials', 'unsupported', 'malformed', 'rejected', 'absent',
|
|
@@ -566,6 +624,18 @@ function labelStartsAt(text, at) {
|
|
|
566
624
|
return re.test(text);
|
|
567
625
|
}
|
|
568
626
|
function credentialWordStartsAt(text, at) {
|
|
627
|
+
if (at >= text.length)
|
|
628
|
+
return false;
|
|
629
|
+
if (credentialWordStartsExactlyAt(text, at))
|
|
630
|
+
return true;
|
|
631
|
+
let k = at;
|
|
632
|
+
while (k < text.length && text.charCodeAt(k) === 0x5c)
|
|
633
|
+
k++;
|
|
634
|
+
if (text[k] === '"' || text[k] === "'")
|
|
635
|
+
k++;
|
|
636
|
+
return k > at && credentialWordStartsExactlyAt(text, k);
|
|
637
|
+
}
|
|
638
|
+
function credentialWordStartsExactlyAt(text, at) {
|
|
569
639
|
if (at >= text.length)
|
|
570
640
|
return false;
|
|
571
641
|
if (labelStartsAt(text, at))
|
|
@@ -730,7 +800,7 @@ function trailingLabelStart(text, from, end) {
|
|
|
730
800
|
}
|
|
731
801
|
const withoutSeparators = (s) => (s.includes(VIEW_SEPARATOR) ? s.split(VIEW_SEPARATOR).join('') : s);
|
|
732
802
|
const isHorizontalSpace = (ch) => ch === ' ' || ch === '\t' || ch === '\u00a0' || ch === VIEW_SEPARATOR;
|
|
733
|
-
function labelEdits(text, re) {
|
|
803
|
+
function labelEdits(text, re, literalBlanks = false) {
|
|
734
804
|
const edits = [];
|
|
735
805
|
const schemePass = re === SECRET_SCHEME_WORD;
|
|
736
806
|
const idx = new InnerWordIndex(text);
|
|
@@ -751,6 +821,12 @@ function labelEdits(text, re) {
|
|
|
751
821
|
re.lastIndex = v.end;
|
|
752
822
|
continue;
|
|
753
823
|
}
|
|
824
|
+
if (literalBlanks) {
|
|
825
|
+
if (literalBlankLeadJudgedAsBlank(text, v, label, schemePass)) {
|
|
826
|
+
re.lastIndex = v.end;
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
}
|
|
754
830
|
const rawValue = text.slice(v.start, v.end);
|
|
755
831
|
const value = withoutSeparators(rawValue);
|
|
756
832
|
const quoted = v.start > 0 && (text[v.start - 1] === '"' || text[v.start - 1] === "'");
|
|
@@ -800,6 +876,34 @@ function labelEdits(text, re) {
|
|
|
800
876
|
}
|
|
801
877
|
return edits;
|
|
802
878
|
}
|
|
879
|
+
function literalBlankLeadJudgedAsBlank(text, v, label, schemePass) {
|
|
880
|
+
let restStart;
|
|
881
|
+
if (v.start > 0 && text.charCodeAt(v.start - 1) === 0x5c && nameBlankTokenAt(text, v.start - 1) !== -1)
|
|
882
|
+
restStart = v.start + 5;
|
|
883
|
+
else if (nameBlankTokenAt(text, v.start) !== -1)
|
|
884
|
+
restStart = v.start + 6;
|
|
885
|
+
else if (eatenBlankTokenTailAt(text, v.start))
|
|
886
|
+
restStart = v.start + 5;
|
|
887
|
+
else
|
|
888
|
+
return false;
|
|
889
|
+
const rest = withoutSeparators(text.slice(restStart, v.end));
|
|
890
|
+
const bare = rest.replace(/[.,;:!?)\]}'"]+$/, '');
|
|
891
|
+
if (bare === '')
|
|
892
|
+
return true;
|
|
893
|
+
if (credentialWordStartsAt(' ' + text.slice(restStart, Math.min(text.length, v.end + 64)), 1))
|
|
894
|
+
return true;
|
|
895
|
+
if (schemePass && SCHEME_FOLLOWER_WORDS.has(bare.toLowerCase()))
|
|
896
|
+
return true;
|
|
897
|
+
if (!schemePass && isNamedNotValue(label, rest))
|
|
898
|
+
return true;
|
|
899
|
+
if (!schemePass && /authorizations?$/i.test(label) && isAuthScheme(bare))
|
|
900
|
+
return true;
|
|
901
|
+
return !isAddressShaped(rest) && isDiagnosticValue(rest);
|
|
902
|
+
}
|
|
903
|
+
function eatenBlankTokenTailAt(text, at) {
|
|
904
|
+
const hit = /^u([0-9A-Fa-f]{4})/.exec(text.slice(at, at + 5));
|
|
905
|
+
return hit !== null && isNameBlank(Number.parseInt(hit[1] ?? '', 16));
|
|
906
|
+
}
|
|
803
907
|
function isAuthParamAt(text, j, scheme) {
|
|
804
908
|
const p = /^[A-Za-z][A-Za-z0-9_-]*([ \t\ufeff]*)=([ \t\ufeff]*)("|[^\s=,"]+)([ \t\ufeff]*,)?/.exec(text.slice(j, j + 512));
|
|
805
909
|
if (p === null)
|
|
@@ -991,49 +1095,75 @@ function proseLiteralEdits(text) {
|
|
|
991
1095
|
}
|
|
992
1096
|
return edits;
|
|
993
1097
|
}
|
|
994
|
-
function washWordNets(target) {
|
|
1098
|
+
function washWordNets(target, literalBlanks = false) {
|
|
995
1099
|
target.apply(pemEdits(target.text));
|
|
996
|
-
target.apply(labelEdits(target.text, SECRET_SCHEME_WORD));
|
|
997
|
-
target.apply(labelEdits(target.text, SECRET_LABELLED_WORD));
|
|
1100
|
+
target.apply(labelEdits(target.text, SECRET_SCHEME_WORD, literalBlanks));
|
|
1101
|
+
target.apply(labelEdits(target.text, SECRET_LABELLED_WORD, literalBlanks));
|
|
998
1102
|
target.apply(authParamEdits(target.text));
|
|
999
1103
|
target.apply(proseLiteralEdits(target.text));
|
|
1000
1104
|
}
|
|
1001
|
-
function runCredentialNets(target, urls, words) {
|
|
1105
|
+
function runCredentialNets(target, urls, words, literalBlanks = false) {
|
|
1002
1106
|
if (urls)
|
|
1003
1107
|
washUrlNets(target);
|
|
1004
1108
|
if (words)
|
|
1005
|
-
washWordNets(target);
|
|
1109
|
+
washWordNets(target, literalBlanks);
|
|
1006
1110
|
}
|
|
1007
|
-
const NEEDS_VIEW = /[\u0000-\u0008\u000E-\u001F\u007F-\u009F]|\p{Cf}|\\u/u;
|
|
1111
|
+
const NEEDS_VIEW = /[\u0000-\u0008\u000E-\u001F\u007F-\u009F\uD800-\uDFFF]|\p{Cf}|\\u/u;
|
|
1008
1112
|
const FORMAT_CHAR = /^\p{Cf}$/u;
|
|
1009
1113
|
const isStrippableControl = (c) => (c <= 0x1f && (c < 0x09 || c > 0x0d)) || (c >= 0x7f && c <= 0x9f);
|
|
1010
|
-
|
|
1011
|
-
let j = from;
|
|
1012
|
-
while (j < text.length && text.charCodeAt(j) >= 0x30 && text.charCodeAt(j) <= 0x3f)
|
|
1013
|
-
j++;
|
|
1014
|
-
while (j < text.length && text.charCodeAt(j) >= 0x20 && text.charCodeAt(j) <= 0x2f)
|
|
1015
|
-
j++;
|
|
1016
|
-
const fin = text.charCodeAt(j);
|
|
1017
|
-
return fin >= 0x40 && fin <= 0x7e ? j + 1 : -1;
|
|
1018
|
-
}
|
|
1019
|
-
const MAX_CONTROL_STRING = 4096;
|
|
1020
|
-
class ControlStringEnds {
|
|
1114
|
+
class SequenceReader {
|
|
1021
1115
|
text;
|
|
1116
|
+
atomicBlanks;
|
|
1117
|
+
foldBlanks;
|
|
1022
1118
|
terminator;
|
|
1023
1119
|
lineBreak;
|
|
1024
1120
|
termFrom = -1;
|
|
1025
1121
|
termHit = null;
|
|
1026
1122
|
breakFrom = -1;
|
|
1027
1123
|
breakAt = -1;
|
|
1028
|
-
constructor(text) {
|
|
1124
|
+
constructor(text, atomicBlanks, foldBlanks = false) {
|
|
1029
1125
|
this.text = text;
|
|
1030
|
-
this.
|
|
1126
|
+
this.atomicBlanks = atomicBlanks;
|
|
1127
|
+
this.foldBlanks = foldBlanks;
|
|
1128
|
+
this.terminator = /[\u0007\u009c]|\u001b\\|\\u(?:0007|009[cC]|001[bB]\\)/g;
|
|
1031
1129
|
this.lineBreak = /[\n\r]/g;
|
|
1032
1130
|
}
|
|
1033
|
-
|
|
1131
|
+
atomicTokenAt(at) {
|
|
1132
|
+
if (this.text.charCodeAt(at) !== 0x5c)
|
|
1133
|
+
return false;
|
|
1134
|
+
if (escapeTokenAt(this.text, at) !== null)
|
|
1135
|
+
return true;
|
|
1136
|
+
if (this.atomicBlanks === 'none')
|
|
1137
|
+
return false;
|
|
1138
|
+
const cp = nameBlankTokenAt(this.text, at);
|
|
1139
|
+
return this.atomicBlanks === 'all' ? cp !== -1 : isFoldedBlank(cp);
|
|
1140
|
+
}
|
|
1141
|
+
intermediateAt(at) {
|
|
1142
|
+
const c = this.text.charCodeAt(at);
|
|
1143
|
+
if (c >= 0x20 && c <= 0x2f)
|
|
1144
|
+
return 1;
|
|
1145
|
+
if (!this.foldBlanks)
|
|
1146
|
+
return 0;
|
|
1147
|
+
if (isFoldedBlank(c))
|
|
1148
|
+
return 1;
|
|
1149
|
+
return c === 0x5c && isFoldedBlank(nameBlankTokenAt(this.text, at)) ? 6 : 0;
|
|
1150
|
+
}
|
|
1151
|
+
interruptsAt(at) {
|
|
1152
|
+
if (at >= this.text.length)
|
|
1153
|
+
return false;
|
|
1154
|
+
const c = this.text.charCodeAt(at);
|
|
1155
|
+
if (isStrippableControl(c))
|
|
1156
|
+
return true;
|
|
1157
|
+
if (c === 0x5c)
|
|
1158
|
+
return escapeTokenAt(this.text, at) !== null;
|
|
1159
|
+
if (c < 0xad)
|
|
1160
|
+
return false;
|
|
1161
|
+
const cp = this.text.codePointAt(at) ?? c;
|
|
1162
|
+
return (cp >= 0xd800 && cp <= 0xdfff) || FORMAT_CHAR.test(String.fromCodePoint(cp));
|
|
1163
|
+
}
|
|
1164
|
+
findControlStringEnd(from) {
|
|
1034
1165
|
if (this.termFrom === -1 || from < this.termFrom || (this.termHit !== null && from > this.termHit.index)) {
|
|
1035
|
-
this.
|
|
1036
|
-
this.termHit = this.terminator.exec(this.text);
|
|
1166
|
+
this.termHit = this.nextTerminator(from);
|
|
1037
1167
|
this.termFrom = from;
|
|
1038
1168
|
}
|
|
1039
1169
|
if (this.breakFrom === -1 || from < this.breakFrom || (this.breakAt !== -1 && from > this.breakAt)) {
|
|
@@ -1049,34 +1179,57 @@ class ControlStringEnds {
|
|
|
1049
1179
|
return null;
|
|
1050
1180
|
return { contentEnd: hit.index, end: hit.index + hit[0].length };
|
|
1051
1181
|
}
|
|
1182
|
+
nextTerminator(from) {
|
|
1183
|
+
this.terminator.lastIndex = from;
|
|
1184
|
+
for (let hit = this.terminator.exec(this.text); hit !== null; hit = this.terminator.exec(this.text)) {
|
|
1185
|
+
const last = hit.index + hit[0].length - 1;
|
|
1186
|
+
if (this.text.charCodeAt(last) !== 0x5c || !this.atomicTokenAt(last))
|
|
1187
|
+
return hit;
|
|
1188
|
+
this.terminator.lastIndex = last;
|
|
1189
|
+
}
|
|
1190
|
+
return null;
|
|
1191
|
+
}
|
|
1192
|
+
}
|
|
1193
|
+
function csiEnd(text, from, seq) {
|
|
1194
|
+
let j = from;
|
|
1195
|
+
while (j < text.length && text.charCodeAt(j) >= 0x30 && text.charCodeAt(j) <= 0x3f)
|
|
1196
|
+
j++;
|
|
1197
|
+
for (let w = seq.intermediateAt(j); w > 0 && j < text.length; w = seq.intermediateAt(j))
|
|
1198
|
+
j += w;
|
|
1199
|
+
if (seq.interruptsAt(j))
|
|
1200
|
+
return j;
|
|
1201
|
+
const fin = text.charCodeAt(j);
|
|
1202
|
+
if (fin === 0x5c && seq.atomicTokenAt(j))
|
|
1203
|
+
return j;
|
|
1204
|
+
return fin > 0x40 && fin <= 0x7e ? j + 1 : j;
|
|
1052
1205
|
}
|
|
1053
|
-
|
|
1054
|
-
|
|
1206
|
+
const MAX_CONTROL_STRING = 4096;
|
|
1207
|
+
function controlString(from, seq) {
|
|
1208
|
+
const s = seq.findControlStringEnd(from);
|
|
1055
1209
|
return s === null ? { end: from } : { end: s.end, content: [from, s.contentEnd] };
|
|
1056
1210
|
}
|
|
1057
|
-
function escapeSequenceAfterEsc(text, at,
|
|
1211
|
+
function escapeSequenceAfterEsc(text, at, seq) {
|
|
1058
1212
|
const n = text.charCodeAt(at);
|
|
1059
|
-
if (n === 0x5b)
|
|
1060
|
-
|
|
1061
|
-
return e === -1 ? null : { end: e };
|
|
1062
|
-
}
|
|
1213
|
+
if (n === 0x5b)
|
|
1214
|
+
return { end: csiEnd(text, at + 1, seq) };
|
|
1063
1215
|
if (n === 0x5d || n === 0x50 || n === 0x58 || n === 0x5e || n === 0x5f)
|
|
1064
|
-
return controlString(at + 1,
|
|
1065
|
-
if (n === 0x3a || n === 0x3d || (n >= 0x60 && n <= 0x7e))
|
|
1216
|
+
return controlString(at + 1, seq);
|
|
1217
|
+
if (n === 0x3a || n === 0x3d || n === 0x40 || (n >= 0x60 && n <= 0x7e))
|
|
1066
1218
|
return null;
|
|
1067
1219
|
let j = at;
|
|
1068
|
-
|
|
1069
|
-
j
|
|
1220
|
+
for (let w = seq.intermediateAt(j); w > 0 && j < text.length; w = seq.intermediateAt(j))
|
|
1221
|
+
j += w;
|
|
1070
1222
|
const f = text.charCodeAt(j);
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1223
|
+
const complete = !seq.interruptsAt(j) && !(f === 0x5c && seq.atomicTokenAt(j)) && f >= 0x30 && f <= 0x7e && f !== 0x40;
|
|
1224
|
+
if (complete)
|
|
1225
|
+
return { end: j + 1 };
|
|
1226
|
+
return j > at ? { end: j } : null;
|
|
1227
|
+
}
|
|
1228
|
+
function c1SequenceAfter(text, c1, at, seq) {
|
|
1229
|
+
if (c1 === 0x9b)
|
|
1230
|
+
return { end: csiEnd(text, at, seq) };
|
|
1078
1231
|
if (c1 === 0x9d || c1 === 0x90 || c1 === 0x98 || c1 === 0x9e || c1 === 0x9f)
|
|
1079
|
-
return controlString(at,
|
|
1232
|
+
return controlString(at, seq);
|
|
1080
1233
|
return null;
|
|
1081
1234
|
}
|
|
1082
1235
|
function isFaceMarkedCodePoint(cp) {
|
|
@@ -1097,15 +1250,23 @@ function escapeTokenAt(text, at) {
|
|
|
1097
1250
|
const cp = Number.parseInt(hit[1] ?? '', 16);
|
|
1098
1251
|
return cp <= 0x10ffff && isFaceMarkedCodePoint(cp) ? { end: at + hit[0].length, cp } : null;
|
|
1099
1252
|
}
|
|
1100
|
-
function
|
|
1101
|
-
|
|
1253
|
+
function nameBlankTokenAt(text, at) {
|
|
1254
|
+
const hit = /^\\u([0-9A-Fa-f]{4})/.exec(text.slice(at, at + 6));
|
|
1255
|
+
if (hit === null)
|
|
1256
|
+
return -1;
|
|
1257
|
+
const cp = Number.parseInt(hit[1] ?? '', 16);
|
|
1258
|
+
return isNameBlank(cp) ? cp : -1;
|
|
1259
|
+
}
|
|
1260
|
+
function buildCredentialView(text, separated, wordLetters = false, blankTokens = 'off') {
|
|
1261
|
+
const folding = blankTokens === 'fold' || blankTokens === 'foldlit';
|
|
1262
|
+
if (!NEEDS_VIEW.test(text) && !(folding && hasRawFoldedBlank(text)))
|
|
1102
1263
|
return null;
|
|
1103
1264
|
const chars = [];
|
|
1104
1265
|
const starts = [];
|
|
1105
1266
|
const ends = [];
|
|
1106
1267
|
let changed = false;
|
|
1107
1268
|
let splitAny = false;
|
|
1108
|
-
const
|
|
1269
|
+
const seq = new SequenceReader(text, blankTokens === 'blank' || blankTokens === 'fold' ? 'all' : blankTokens === 'foldlit' ? 'folded' : 'none', folding);
|
|
1109
1270
|
const keep = (ch, s, e) => {
|
|
1110
1271
|
chars.push(ch);
|
|
1111
1272
|
starts.push(s);
|
|
@@ -1118,22 +1279,35 @@ function buildCredentialView(text, separated, wordLetters = false) {
|
|
|
1118
1279
|
};
|
|
1119
1280
|
const dropUnit = (s, unit) => {
|
|
1120
1281
|
const last = unit.end - 1;
|
|
1121
|
-
if (
|
|
1282
|
+
if ((blankTokens === 'literal' || blankTokens === 'foldlit') && last > s && text.charCodeAt(last) === 0x5c && nameBlankTokenAt(text, last) !== -1) {
|
|
1283
|
+
drop(s, last);
|
|
1284
|
+
keep(text.charAt(last + 1), last, last + 6);
|
|
1285
|
+
for (let u = 2; u < 6; u++)
|
|
1286
|
+
keep(text.charAt(last + u), last + 6, last + 6);
|
|
1287
|
+
return last + 6;
|
|
1288
|
+
}
|
|
1289
|
+
if (wordLetters && unit.content === undefined && last > s && (credentialWordBeginsWith(text, last) || finalIsStructural(text, last))) {
|
|
1122
1290
|
drop(s, last);
|
|
1123
1291
|
keep(text.charAt(last), last, unit.end);
|
|
1124
1292
|
splitAny = true;
|
|
1125
|
-
return;
|
|
1293
|
+
return unit.end;
|
|
1126
1294
|
}
|
|
1127
1295
|
drop(s, unit.end);
|
|
1296
|
+
return unit.end;
|
|
1128
1297
|
};
|
|
1129
1298
|
let i = 0;
|
|
1130
1299
|
while (i < text.length) {
|
|
1131
1300
|
const c = text.charCodeAt(i);
|
|
1301
|
+
if (folding && isFoldedBlank(c)) {
|
|
1302
|
+
changed = true;
|
|
1303
|
+
keep(' ', i, i + 1);
|
|
1304
|
+
i++;
|
|
1305
|
+
continue;
|
|
1306
|
+
}
|
|
1132
1307
|
if (c === 0x1b || c === 0x9b || c === 0x9d || c === 0x90 || c === 0x98 || c === 0x9e || c === 0x9f) {
|
|
1133
|
-
const unit = c === 0x1b ? escapeSequenceAfterEsc(text, i + 1,
|
|
1308
|
+
const unit = c === 0x1b ? escapeSequenceAfterEsc(text, i + 1, seq) : c1SequenceAfter(text, c, i + 1, seq);
|
|
1134
1309
|
if (unit !== null) {
|
|
1135
|
-
dropUnit(i, unit);
|
|
1136
|
-
i = unit.end;
|
|
1310
|
+
i = dropUnit(i, unit);
|
|
1137
1311
|
continue;
|
|
1138
1312
|
}
|
|
1139
1313
|
drop(i, i + 1);
|
|
@@ -1144,19 +1318,37 @@ function buildCredentialView(text, separated, wordLetters = false) {
|
|
|
1144
1318
|
const tok = escapeTokenAt(text, i);
|
|
1145
1319
|
if (tok !== null) {
|
|
1146
1320
|
const unit = tok.cp === 0x1b
|
|
1147
|
-
? escapeSequenceAfterEsc(text, tok.end,
|
|
1321
|
+
? escapeSequenceAfterEsc(text, tok.end, seq)
|
|
1148
1322
|
: tok.cp >= 0x90 && tok.cp <= 0x9f
|
|
1149
|
-
? c1SequenceAfter(text, tok.cp, tok.end,
|
|
1323
|
+
? c1SequenceAfter(text, tok.cp, tok.end, seq)
|
|
1150
1324
|
: null;
|
|
1151
1325
|
if (unit !== null) {
|
|
1152
|
-
dropUnit(i, unit);
|
|
1153
|
-
i = unit.end;
|
|
1326
|
+
i = dropUnit(i, unit);
|
|
1154
1327
|
continue;
|
|
1155
1328
|
}
|
|
1156
1329
|
drop(i, tok.end, ' ');
|
|
1157
1330
|
i = tok.end;
|
|
1158
1331
|
continue;
|
|
1159
1332
|
}
|
|
1333
|
+
if (blankTokens !== 'off') {
|
|
1334
|
+
const cp = nameBlankTokenAt(text, i);
|
|
1335
|
+
if (cp !== -1) {
|
|
1336
|
+
changed = true;
|
|
1337
|
+
if (blankTokens === 'blank')
|
|
1338
|
+
keep(String.fromCharCode(cp), i, i + 6);
|
|
1339
|
+
else if (blankTokens === 'fold')
|
|
1340
|
+
keep(isFoldedBlank(cp) ? ' ' : String.fromCharCode(cp), i, i + 6);
|
|
1341
|
+
else if (blankTokens === 'foldlit' && isFoldedBlank(cp))
|
|
1342
|
+
keep(' ', i, i + 6);
|
|
1343
|
+
else {
|
|
1344
|
+
keep(text.charAt(i), i, i + 6);
|
|
1345
|
+
for (let u = 1; u < 6; u++)
|
|
1346
|
+
keep(text.charAt(i + u), i + 6, i + 6);
|
|
1347
|
+
}
|
|
1348
|
+
i += 6;
|
|
1349
|
+
continue;
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1160
1352
|
}
|
|
1161
1353
|
if (isStrippableControl(c)) {
|
|
1162
1354
|
drop(i, i + 1);
|
|
@@ -1166,7 +1358,7 @@ function buildCredentialView(text, separated, wordLetters = false) {
|
|
|
1166
1358
|
if (c >= 0xad) {
|
|
1167
1359
|
const cp = text.codePointAt(i) ?? c;
|
|
1168
1360
|
const width = cp > 0xffff ? 2 : 1;
|
|
1169
|
-
if (FORMAT_CHAR.test(String.fromCodePoint(cp))) {
|
|
1361
|
+
if (FORMAT_CHAR.test(String.fromCodePoint(cp)) || (cp >= 0xd800 && cp <= 0xdfff)) {
|
|
1170
1362
|
drop(i, i + width);
|
|
1171
1363
|
i += width;
|
|
1172
1364
|
continue;
|
|
@@ -1183,7 +1375,13 @@ function buildCredentialView(text, separated, wordLetters = false) {
|
|
|
1183
1375
|
return null;
|
|
1184
1376
|
return { view: chars.join(''), starts: Int32Array.from(starts), ends: Int32Array.from(ends) };
|
|
1185
1377
|
}
|
|
1186
|
-
|
|
1378
|
+
function finalIsStructural(text, at) {
|
|
1379
|
+
if (text.charCodeAt(at) !== 0x5c)
|
|
1380
|
+
return false;
|
|
1381
|
+
const n = text.charCodeAt(at + 1);
|
|
1382
|
+
return n === 0x22 || n === 0x27;
|
|
1383
|
+
}
|
|
1384
|
+
const isProbeFiller = (c) => c === 0x5c || c === 0x22 || c === 0x27 || isRegexSpace(c) || isNameBlank(c);
|
|
1187
1385
|
function credentialWordBeginsWith(text, at) {
|
|
1188
1386
|
const c = text.charCodeAt(at);
|
|
1189
1387
|
const letter = (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a);
|
|
@@ -1193,6 +1391,13 @@ function credentialWordBeginsWith(text, at) {
|
|
|
1193
1391
|
const nextOk = (n >= 0x41 && n <= 0x5a) || (n >= 0x61 && n <= 0x7a) || (!letter && (n === 0x22 || n === 0x27));
|
|
1194
1392
|
if (!nextOk)
|
|
1195
1393
|
return false;
|
|
1394
|
+
if (letter) {
|
|
1395
|
+
let e = at;
|
|
1396
|
+
while (e < text.length && isKeyWordChar(text.charCodeAt(e)))
|
|
1397
|
+
e++;
|
|
1398
|
+
if (SECRET_LITERAL_RE.test(text.slice(at, e + 1)))
|
|
1399
|
+
return true;
|
|
1400
|
+
}
|
|
1196
1401
|
let end = Math.min(text.length, at + 64);
|
|
1197
1402
|
while (end < text.length && isProbeFiller(text.charCodeAt(end - 1)))
|
|
1198
1403
|
end++;
|
|
@@ -1201,8 +1406,21 @@ function credentialWordBeginsWith(text, at) {
|
|
|
1201
1406
|
return credentialWordStartsAt(probe, 1);
|
|
1202
1407
|
return credentialWordStartsAt(probe, 2) || credentialWordStartsAt(probe, 3);
|
|
1203
1408
|
}
|
|
1204
|
-
function rebuildFromView(original, view, target) {
|
|
1409
|
+
function rebuildFromView(original, view, target, trace = false) {
|
|
1205
1410
|
const out = [];
|
|
1411
|
+
const origin = trace ? [] : null;
|
|
1412
|
+
const fromOriginal = (a, b) => {
|
|
1413
|
+
out.push(original.slice(a, b));
|
|
1414
|
+
if (origin !== null)
|
|
1415
|
+
for (let x = a; x < b; x++)
|
|
1416
|
+
origin.push(x);
|
|
1417
|
+
};
|
|
1418
|
+
const inserted = (s) => {
|
|
1419
|
+
out.push(s);
|
|
1420
|
+
if (origin !== null)
|
|
1421
|
+
for (let x = 0; x < s.length; x++)
|
|
1422
|
+
origin.push(-1);
|
|
1423
|
+
};
|
|
1206
1424
|
const viewLength = view.view.length;
|
|
1207
1425
|
let cursor = 0;
|
|
1208
1426
|
let k = 0;
|
|
@@ -1212,11 +1430,11 @@ function rebuildFromView(original, view, target) {
|
|
|
1212
1430
|
if (s >= 0) {
|
|
1213
1431
|
const end = view.ends[s] ?? cursor;
|
|
1214
1432
|
if ((view.starts[s] ?? 0) >= cursor) {
|
|
1215
|
-
|
|
1433
|
+
fromOriginal(cursor, end);
|
|
1216
1434
|
cursor = end;
|
|
1217
1435
|
}
|
|
1218
1436
|
else
|
|
1219
|
-
|
|
1437
|
+
inserted(text.charAt(k));
|
|
1220
1438
|
k++;
|
|
1221
1439
|
continue;
|
|
1222
1440
|
}
|
|
@@ -1232,38 +1450,144 @@ function rebuildFromView(original, view, target) {
|
|
|
1232
1450
|
const from = a < viewLength ? (view.starts[a] ?? original.length) : original.length;
|
|
1233
1451
|
const to = b > a ? (view.ends[b - 1] ?? from) : from;
|
|
1234
1452
|
if (from > cursor)
|
|
1235
|
-
|
|
1236
|
-
|
|
1453
|
+
fromOriginal(cursor, from);
|
|
1454
|
+
inserted(text.slice(k, j));
|
|
1237
1455
|
cursor = Math.max(cursor, to);
|
|
1238
1456
|
k = j;
|
|
1239
1457
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1458
|
+
fromOriginal(cursor, original.length);
|
|
1459
|
+
const rebuilt = out.join('');
|
|
1460
|
+
return origin === null ? { text: rebuilt } : { text: rebuilt, origin: Int32Array.from(origin) };
|
|
1242
1461
|
}
|
|
1243
|
-
function washThroughView(original, view, urls, words) {
|
|
1462
|
+
function washThroughView(original, view, urls, words, trace = false, literalBlanks = false) {
|
|
1244
1463
|
const target = new TrackedTarget(view.view, view.view.length);
|
|
1245
|
-
runCredentialNets(target, urls, words);
|
|
1246
|
-
return target.text === view.view ? original : rebuildFromView(original, view, target);
|
|
1247
|
-
}
|
|
1248
|
-
function washCredentialNetsOnce(text, urls, words, nested) {
|
|
1249
|
-
const
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
const
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1464
|
+
runCredentialNets(target, urls, words, literalBlanks);
|
|
1465
|
+
return target.text === view.view ? { text: original } : rebuildFromView(original, view, target, trace);
|
|
1466
|
+
}
|
|
1467
|
+
function washCredentialNetsOnce(text, urls, words, nested, blankTokens, trace) {
|
|
1468
|
+
const contents = nested ? { text } : washControlStringContents(text, urls, words, blankTokens, trace);
|
|
1469
|
+
if (blankTokens === 'off')
|
|
1470
|
+
return washThroughViews(contents, urls, words, 'off', trace);
|
|
1471
|
+
const tokens = hasNameBlankToken(contents.text);
|
|
1472
|
+
const readings = [];
|
|
1473
|
+
if (tokens)
|
|
1474
|
+
readings.push('literal');
|
|
1475
|
+
const rawFolded = blankTokens === 'fold' && hasRawFoldedBlank(contents.text);
|
|
1476
|
+
if (blankTokens === 'fold' && (tokens || rawFolded))
|
|
1477
|
+
readings.push('fold');
|
|
1478
|
+
if (blankTokens === 'fold' && tokens && hasNameBlankToken(contents.text, false) && (rawFolded || hasNameBlankToken(contents.text, true)))
|
|
1479
|
+
readings.push('foldlit');
|
|
1480
|
+
readings.push('blank');
|
|
1481
|
+
if (readings.length === 1)
|
|
1482
|
+
return washThroughViews(contents, urls, words, 'blank', trace);
|
|
1483
|
+
const base = { text: contents.text };
|
|
1484
|
+
const union = unionMaskedRanges(base.text, readings.map((r) => washThroughViews(base, urls, words, r, true)));
|
|
1485
|
+
return traced(union.text, composeOrigin(contents.origin, union.origin));
|
|
1486
|
+
}
|
|
1487
|
+
function unionMaskedRanges(text, results) {
|
|
1488
|
+
const ranges = [];
|
|
1489
|
+
for (const r of results) {
|
|
1490
|
+
if (r.text === text || r.origin === undefined)
|
|
1491
|
+
continue;
|
|
1492
|
+
const origin = r.origin;
|
|
1493
|
+
let lastKept = -1;
|
|
1494
|
+
let k = 0;
|
|
1495
|
+
while (k < origin.length) {
|
|
1496
|
+
if ((origin[k] ?? -1) >= 0) {
|
|
1497
|
+
lastKept = origin[k] ?? lastKept;
|
|
1498
|
+
k++;
|
|
1499
|
+
continue;
|
|
1500
|
+
}
|
|
1501
|
+
let j = k;
|
|
1502
|
+
while (j < origin.length && (origin[j] ?? -1) < 0)
|
|
1503
|
+
j++;
|
|
1504
|
+
const next = j < origin.length ? (origin[j] ?? text.length) : text.length;
|
|
1505
|
+
ranges.push({ start: lastKept + 1, end: next, text: r.text.slice(k, j) });
|
|
1506
|
+
k = j;
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1509
|
+
if (ranges.length === 0)
|
|
1510
|
+
return { text };
|
|
1511
|
+
ranges.sort((a, b) => (a.start !== b.start ? a.start - b.start : b.end - a.end));
|
|
1512
|
+
const merged = [];
|
|
1513
|
+
for (const m of ranges) {
|
|
1514
|
+
const last = merged[merged.length - 1];
|
|
1515
|
+
if (last !== undefined && (m.start < last.end || (m.start === last.start && m.end === last.end))) {
|
|
1516
|
+
if (m.end > last.end) {
|
|
1517
|
+
if (m.end - m.start > last.end - last.start)
|
|
1518
|
+
last.text = m.text;
|
|
1519
|
+
last.end = m.end;
|
|
1520
|
+
}
|
|
1521
|
+
continue;
|
|
1522
|
+
}
|
|
1523
|
+
if (last !== undefined && m.start === m.end && m.start > last.start && m.start < last.end)
|
|
1524
|
+
continue;
|
|
1525
|
+
merged.push({ ...m });
|
|
1526
|
+
}
|
|
1527
|
+
const parts = [];
|
|
1528
|
+
const origin = [];
|
|
1529
|
+
let cursor = 0;
|
|
1530
|
+
for (const m of merged) {
|
|
1531
|
+
parts.push(text.slice(cursor, m.start), m.text);
|
|
1532
|
+
for (let x = cursor; x < m.start; x++)
|
|
1533
|
+
origin.push(x);
|
|
1534
|
+
for (let x = 0; x < m.text.length; x++)
|
|
1535
|
+
origin.push(-1);
|
|
1536
|
+
cursor = m.end;
|
|
1257
1537
|
}
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1538
|
+
parts.push(text.slice(cursor));
|
|
1539
|
+
for (let x = cursor; x < text.length; x++)
|
|
1540
|
+
origin.push(x);
|
|
1541
|
+
return { text: parts.join(''), origin: Int32Array.from(origin) };
|
|
1261
1542
|
}
|
|
1262
|
-
function
|
|
1543
|
+
function hasRawFoldedBlank(text) {
|
|
1544
|
+
for (let k = 0; k < text.length; k++)
|
|
1545
|
+
if (isFoldedBlank(text.charCodeAt(k)))
|
|
1546
|
+
return true;
|
|
1547
|
+
return false;
|
|
1548
|
+
}
|
|
1549
|
+
function hasNameBlankToken(text, folded) {
|
|
1550
|
+
if (!text.includes('\\u'))
|
|
1551
|
+
return false;
|
|
1552
|
+
for (const hit of text.matchAll(/\\u([0-9A-Fa-f]{4})/g)) {
|
|
1553
|
+
const cp = Number.parseInt(hit[1] ?? '', 16);
|
|
1554
|
+
if (isNameBlank(cp) && (folded === undefined || isFoldedBlank(cp) === folded))
|
|
1555
|
+
return true;
|
|
1556
|
+
}
|
|
1557
|
+
return false;
|
|
1558
|
+
}
|
|
1559
|
+
function washThroughViews(input, urls, words, blankRead, trace) {
|
|
1560
|
+
const start = input.text;
|
|
1561
|
+
const literal = blankRead === 'literal' || blankRead === 'foldlit';
|
|
1562
|
+
const lettered = buildCredentialView(start, true, true, blankRead);
|
|
1563
|
+
const letteredWashed = lettered === null ? { text: start } : washThroughView(start, lettered, urls, words, trace, literal);
|
|
1564
|
+
const pre = letteredWashed.text;
|
|
1565
|
+
let origin = composeOrigin(input.origin, letteredWashed.origin);
|
|
1566
|
+
const separated = buildCredentialView(pre, true, false, blankRead);
|
|
1567
|
+
if (separated === null) {
|
|
1568
|
+
const target = new PlainTarget(pre, trace);
|
|
1569
|
+
runCredentialNets(target, urls, words, literal);
|
|
1570
|
+
return traced(target.text, composeOrigin(origin, target.origin));
|
|
1571
|
+
}
|
|
1572
|
+
const afterSeparated = washThroughView(pre, separated, urls, words, trace, literal);
|
|
1573
|
+
origin = composeOrigin(origin, afterSeparated.origin);
|
|
1574
|
+
const joined = buildCredentialView(afterSeparated.text, false, false, blankRead);
|
|
1575
|
+
if (joined === null)
|
|
1576
|
+
return traced(afterSeparated.text, origin);
|
|
1577
|
+
const afterJoined = washThroughView(afterSeparated.text, joined, urls, words, trace, literal);
|
|
1578
|
+
return traced(afterJoined.text, composeOrigin(origin, afterJoined.origin));
|
|
1579
|
+
}
|
|
1580
|
+
function washControlStringContents(text, urls, words, blankTokens, trace) {
|
|
1263
1581
|
if (!/[\u001b\u0090\u0098\u009d-\u009f]|\\u/.test(text))
|
|
1264
|
-
return text;
|
|
1265
|
-
const ends = new
|
|
1582
|
+
return { text };
|
|
1583
|
+
const ends = new SequenceReader(text, 'none');
|
|
1266
1584
|
const parts = [];
|
|
1585
|
+
const origin = trace ? [] : null;
|
|
1586
|
+
const keptUpTo = (a, b) => {
|
|
1587
|
+
if (origin !== null)
|
|
1588
|
+
for (let x = a; x < b; x++)
|
|
1589
|
+
origin.push(x);
|
|
1590
|
+
};
|
|
1267
1591
|
let cursor = 0;
|
|
1268
1592
|
let i = 0;
|
|
1269
1593
|
while (i < text.length) {
|
|
@@ -1288,32 +1612,45 @@ function washControlStringContents(text, urls, words) {
|
|
|
1288
1612
|
if (unit.content !== undefined) {
|
|
1289
1613
|
const [a, b] = unit.content;
|
|
1290
1614
|
const inner = text.slice(a, b);
|
|
1291
|
-
const washed =
|
|
1292
|
-
if (washed !== inner) {
|
|
1293
|
-
parts.push(text.slice(cursor, a), washed);
|
|
1615
|
+
const washed = washNets(inner, urls, words, true, blankTokens, trace);
|
|
1616
|
+
if (washed.text !== inner) {
|
|
1617
|
+
parts.push(text.slice(cursor, a), washed.text);
|
|
1618
|
+
keptUpTo(cursor, a);
|
|
1619
|
+
if (origin !== null) {
|
|
1620
|
+
const w = washed.origin;
|
|
1621
|
+
for (let x = 0; x < washed.text.length; x++) {
|
|
1622
|
+
const s = w === undefined ? x : (w[x] ?? -1);
|
|
1623
|
+
origin.push(s < 0 ? -1 : a + s);
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1294
1626
|
cursor = b;
|
|
1295
1627
|
}
|
|
1296
1628
|
}
|
|
1297
1629
|
i = Math.max(unit.end, i + 1);
|
|
1298
1630
|
}
|
|
1299
1631
|
if (cursor === 0)
|
|
1300
|
-
return text;
|
|
1632
|
+
return { text };
|
|
1301
1633
|
parts.push(text.slice(cursor));
|
|
1302
|
-
|
|
1634
|
+
keptUpTo(cursor, text.length);
|
|
1635
|
+
const joined = parts.join('');
|
|
1636
|
+
return origin === null ? { text: joined } : { text: joined, origin: Int32Array.from(origin) };
|
|
1303
1637
|
}
|
|
1304
1638
|
const MAX_SETTLE_ROUNDS = 4;
|
|
1305
|
-
function
|
|
1639
|
+
function washNets(text, urls, words, nested, blankTokens, trace) {
|
|
1306
1640
|
if (!urls && !words)
|
|
1307
|
-
return text;
|
|
1308
|
-
let current = text;
|
|
1641
|
+
return { text };
|
|
1642
|
+
let current = { text };
|
|
1309
1643
|
for (let round = 0; round < MAX_SETTLE_ROUNDS; round++) {
|
|
1310
|
-
const next = washCredentialNetsOnce(current, urls, words, nested);
|
|
1311
|
-
if (next === current)
|
|
1644
|
+
const next = washCredentialNetsOnce(current.text, urls, words, nested, blankTokens, trace);
|
|
1645
|
+
if (next.text === current.text)
|
|
1312
1646
|
return current;
|
|
1313
|
-
current = next;
|
|
1647
|
+
current = traced(next.text, composeOrigin(current.origin, next.origin));
|
|
1314
1648
|
}
|
|
1315
1649
|
return current;
|
|
1316
1650
|
}
|
|
1651
|
+
function washCredentialNets(text, urls, words, nested = false, blankTokens = 'off') {
|
|
1652
|
+
return washNets(text, urls, words, nested, blankTokens, false).text;
|
|
1653
|
+
}
|
|
1317
1654
|
export function washCredentials(text) {
|
|
1318
1655
|
if (typeof text !== 'string')
|
|
1319
1656
|
return text;
|
|
@@ -1324,7 +1661,111 @@ const C0_KEEP_LAYOUT = '\\u0000-\\u0008\\u000B-\\u001F';
|
|
|
1324
1661
|
const DEL_C1 = '\\u007F-\\u009F';
|
|
1325
1662
|
const LONE_SURROGATE = '\\uD800-\\uDFFF';
|
|
1326
1663
|
const LINE_SEPARATORS = '\\u2028\\u2029';
|
|
1327
|
-
const
|
|
1664
|
+
const NAME_BLANKS = NAME_BLANK_RANGES.map(([a, b]) => (a === b ? classEscape(a) : `${classEscape(a)}-${classEscape(b)}`)).join('');
|
|
1665
|
+
function classEscape(cp) {
|
|
1666
|
+
return `\\u${cp.toString(16).toUpperCase().padStart(4, '0')}`;
|
|
1667
|
+
}
|
|
1668
|
+
function compilesAsUnicodeRegExp(source) {
|
|
1669
|
+
try {
|
|
1670
|
+
return new RegExp(source, 'u').unicode;
|
|
1671
|
+
}
|
|
1672
|
+
catch {
|
|
1673
|
+
return false;
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
const JOINING_SCRIPT_CANDIDATES = [
|
|
1677
|
+
'Arab', 'Syrc', 'Mand', 'Mani', 'Phlp', 'Sogd', 'Ougr', 'Chrs', 'Mong', 'Phag', 'Nkoo', 'Adlm', 'Rohg',
|
|
1678
|
+
'Deva', 'Beng', 'Guru', 'Gujr', 'Orya', 'Taml', 'Telu', 'Knda', 'Mlym', 'Sinh',
|
|
1679
|
+
'Brah', 'Kthi', 'Shrd', 'Sidd', 'Tirh', 'Gran', 'Newa', 'Takr', 'Modi', 'Mahj', 'Khoj', 'Sind', 'Dogr', 'Nand',
|
|
1680
|
+
'Gonm', 'Gong', 'Diak', 'Bhks', 'Zanb', 'Soyo', 'Ahom', 'Cakm', 'Sylo', 'Saur', 'Mtei', 'Limb', 'Lepc', 'Kawi',
|
|
1681
|
+
'Mymr', 'Khmr', 'Thai', 'Laoo', 'Tibt', 'Bali', 'Java', 'Sund', 'Lana', 'Tavt', 'Talu', 'Tale', 'Cham', 'Batk',
|
|
1682
|
+
'Bugi', 'Maka', 'Rjng', 'Kali', 'Tglg', 'Hano', 'Buhd', 'Tagb',
|
|
1683
|
+
];
|
|
1684
|
+
const JOINING_SCRIPTS = JOINING_SCRIPT_CANDIDATES.filter((sc) => compilesAsUnicodeRegExp(`\\p{scx=${sc}}`));
|
|
1685
|
+
function codePointTest(source) {
|
|
1686
|
+
if (!compilesAsUnicodeRegExp(source))
|
|
1687
|
+
return () => false;
|
|
1688
|
+
const re = new RegExp(`^${source}$`, 'u');
|
|
1689
|
+
return (cp) => re.test(String.fromCodePoint(cp));
|
|
1690
|
+
}
|
|
1691
|
+
const isClusterExtender = codePointTest('[\\p{M}\\p{Emoji_Modifier}]');
|
|
1692
|
+
const isPictograph = codePointTest('\\p{Extended_Pictographic}');
|
|
1693
|
+
const isEmojiPresentation = codePointTest('\\p{Emoji_Presentation}');
|
|
1694
|
+
const isEmojiModifier = codePointTest('\\p{Emoji_Modifier}');
|
|
1695
|
+
const isJoiningLetter = JOINING_SCRIPTS.length === 0
|
|
1696
|
+
? () => false
|
|
1697
|
+
: codePointTest('(?=\\p{L})(?!(?=\\p{Lm})[\\p{sc=Zyyy}\\p{sc=Zinh}])[' + JOINING_SCRIPTS.map((sc) => '\\p{scx=' + sc + '}').join('') + ']');
|
|
1698
|
+
const VIRAMA_CODE_POINTS = [
|
|
1699
|
+
0x094d, 0x09cd, 0x0a4d, 0x0acd, 0x0b4d, 0x0bcd, 0x0c4d, 0x0ccd, 0x0d3b, 0x0d3c, 0x0d4d, 0x0dca,
|
|
1700
|
+
0x0e3a, 0x0eba, 0x0f84, 0x1039, 0x103a, 0x1714, 0x1715, 0x1734, 0x17d2, 0x1a60, 0x1b44, 0x1baa, 0x1bab, 0x1bf2, 0x1bf3,
|
|
1701
|
+
0xa806, 0xa82c, 0xa8c4, 0xa953, 0xa9c0, 0xaaf6, 0xabed,
|
|
1702
|
+
0x11046, 0x11070, 0x1107f, 0x110b9, 0x11133, 0x11134, 0x111c0, 0x11235, 0x112ea, 0x1134d, 0x11442, 0x114c2, 0x115bf,
|
|
1703
|
+
0x1163f, 0x116b6, 0x1172b, 0x11839, 0x1193d, 0x1193e, 0x119e0, 0x11a34, 0x11a47, 0x11a99, 0x11c3f, 0x11d44, 0x11d45,
|
|
1704
|
+
0x11d97, 0x11f41, 0x11f42,
|
|
1705
|
+
];
|
|
1706
|
+
const VIRAMAS = new Set(VIRAMA_CODE_POINTS);
|
|
1707
|
+
function joinerKept(text, at, zwj) {
|
|
1708
|
+
let p = at;
|
|
1709
|
+
let left = -1;
|
|
1710
|
+
let base = -1;
|
|
1711
|
+
let afterBase = -1;
|
|
1712
|
+
let visited = -1;
|
|
1713
|
+
while (p > 0) {
|
|
1714
|
+
let q = p - 1;
|
|
1715
|
+
const unit = text.charCodeAt(q);
|
|
1716
|
+
if (unit >= 0xdc00 && unit <= 0xdfff && q > 0) {
|
|
1717
|
+
const hi = text.charCodeAt(q - 1);
|
|
1718
|
+
if (hi >= 0xd800 && hi <= 0xdbff)
|
|
1719
|
+
q -= 1;
|
|
1720
|
+
}
|
|
1721
|
+
const cp = text.codePointAt(q) ?? unit;
|
|
1722
|
+
if (left === -1)
|
|
1723
|
+
left = cp;
|
|
1724
|
+
p = q;
|
|
1725
|
+
if (!isClusterExtender(cp)) {
|
|
1726
|
+
base = cp;
|
|
1727
|
+
afterBase = visited;
|
|
1728
|
+
break;
|
|
1729
|
+
}
|
|
1730
|
+
visited = cp;
|
|
1731
|
+
}
|
|
1732
|
+
if (base === -1)
|
|
1733
|
+
return false;
|
|
1734
|
+
let r = at + 1;
|
|
1735
|
+
let next = -1;
|
|
1736
|
+
let rightBase = -1;
|
|
1737
|
+
while (r < text.length) {
|
|
1738
|
+
const cp = text.codePointAt(r) ?? 0;
|
|
1739
|
+
if (next === -1)
|
|
1740
|
+
next = cp;
|
|
1741
|
+
r += cp > 0xffff ? 2 : 1;
|
|
1742
|
+
if (!isClusterExtender(cp)) {
|
|
1743
|
+
rightBase = cp;
|
|
1744
|
+
break;
|
|
1745
|
+
}
|
|
1746
|
+
}
|
|
1747
|
+
if (zwj && next !== -1) {
|
|
1748
|
+
const nextEnd = at + 1 + (next > 0xffff ? 2 : 1);
|
|
1749
|
+
const afterNext = nextEnd < text.length ? (text.codePointAt(nextEnd) ?? -1) : -1;
|
|
1750
|
+
if (emojiPresented(base, afterBase) && emojiPresented(next, afterNext))
|
|
1751
|
+
return true;
|
|
1752
|
+
}
|
|
1753
|
+
if (!isJoiningLetter(base))
|
|
1754
|
+
return false;
|
|
1755
|
+
if (rightBase !== -1 && isJoiningLetter(rightBase))
|
|
1756
|
+
return true;
|
|
1757
|
+
return zwj && VIRAMAS.has(left);
|
|
1758
|
+
}
|
|
1759
|
+
function emojiPresented(cp, after) {
|
|
1760
|
+
return isPictograph(cp) && (isEmojiPresentation(cp) || after === 0xfe0f || (after !== -1 && isEmojiModifier(after)));
|
|
1761
|
+
}
|
|
1762
|
+
function faceHitKept(ch, at, text) {
|
|
1763
|
+
if (ch.length > 2)
|
|
1764
|
+
return true;
|
|
1765
|
+
const c = ch.charCodeAt(0);
|
|
1766
|
+
return ch.length === 1 && (c === 0x200d || c === 0x200c) && joinerKept(text, at, c === 0x200d);
|
|
1767
|
+
}
|
|
1768
|
+
const FORMAT_CLASS = '\\p{Cf}';
|
|
1328
1769
|
const RGI_TAG_FLAG = '\\u{1F3F4}\\u{E0067}\\u{E0062}(?:\\u{E0065}\\u{E006E}\\u{E0067}|\\u{E0073}\\u{E0063}\\u{E0074}|\\u{E0077}\\u{E006C}\\u{E0073})\\u{E007F}';
|
|
1329
1770
|
const BIDI_REORDER = '\\u061C\\u200E\\u200F\\u2028\\u2029\\u202A-\\u202E\\u2066-\\u2069';
|
|
1330
1771
|
function unsafeClass(members, withFormatClass) {
|
|
@@ -1357,27 +1798,173 @@ function markOf(ch, mark) {
|
|
|
1357
1798
|
const hex = code.toString(16).toUpperCase();
|
|
1358
1799
|
return code > 0xffff ? `\\u{${hex}}` : `\\u${hex.padStart(4, '0')}`;
|
|
1359
1800
|
}
|
|
1360
|
-
const
|
|
1801
|
+
const FOLD_RUN = /\s+/g;
|
|
1802
|
+
const FOLD_RUN_KEEPING_BLANKS = new RegExp(`[^\\S${NAME_BLANKS}]+`, 'g');
|
|
1803
|
+
const EDGE_SPACE = /^ | $/g;
|
|
1804
|
+
const foldWhitespace = (s, keepBlanks = false) => keepBlanks ? s.replace(FOLD_RUN_KEEPING_BLANKS, ' ').replace(EDGE_SPACE, '') : s.replace(FOLD_RUN, ' ').trim();
|
|
1361
1805
|
export function applyDisplayFace(text, face) {
|
|
1362
|
-
const markAll = (s) => face.unsafe === null ? s : s.replace(face.unsafe, ch => (ch
|
|
1806
|
+
const markAll = (s) => face.unsafe === null ? s : s.replace(face.unsafe, (ch, at, whole) => (faceHitKept(ch, at, whole) ? ch : markOf(ch, face.mark)));
|
|
1807
|
+
const keepBlanks = face.keepBlanks === true;
|
|
1363
1808
|
if (face.mark === 'space') {
|
|
1364
1809
|
const marked = markAll(text);
|
|
1365
|
-
return face.fold ? foldWhitespace(marked) : marked;
|
|
1810
|
+
return face.fold ? foldWhitespace(marked, keepBlanks) : marked;
|
|
1811
|
+
}
|
|
1812
|
+
return markAll(face.fold ? foldWhitespace(text, keepBlanks) : text);
|
|
1813
|
+
}
|
|
1814
|
+
function markTracked(t, re, mark, ledger) {
|
|
1815
|
+
const parts = [];
|
|
1816
|
+
const ids = [];
|
|
1817
|
+
const keep = (a, b) => {
|
|
1818
|
+
parts.push(t.text.slice(a, b));
|
|
1819
|
+
for (let k = a; k < b; k++)
|
|
1820
|
+
ids.push(t.ids[k] ?? -1);
|
|
1821
|
+
};
|
|
1822
|
+
let cursor = 0;
|
|
1823
|
+
for (const hit of t.text.matchAll(re)) {
|
|
1824
|
+
const at = hit.index ?? 0;
|
|
1825
|
+
const ch = hit[0];
|
|
1826
|
+
keep(cursor, at);
|
|
1827
|
+
if (faceHitKept(ch, at, t.text))
|
|
1828
|
+
keep(at, at + ch.length);
|
|
1829
|
+
else {
|
|
1830
|
+
const form = markOf(ch, mark);
|
|
1831
|
+
const id = ledger.cps.length;
|
|
1832
|
+
ledger.cps.push(ch.codePointAt(0) ?? 0);
|
|
1833
|
+
ledger.lens.push(form.length);
|
|
1834
|
+
parts.push(form);
|
|
1835
|
+
for (let x = 0; x < form.length; x++)
|
|
1836
|
+
ids.push(id);
|
|
1837
|
+
}
|
|
1838
|
+
cursor = at + ch.length;
|
|
1366
1839
|
}
|
|
1367
|
-
|
|
1840
|
+
if (cursor === 0)
|
|
1841
|
+
return t;
|
|
1842
|
+
keep(cursor, t.text.length);
|
|
1843
|
+
return { text: parts.join(''), ids: Int32Array.from(ids) };
|
|
1368
1844
|
}
|
|
1369
|
-
|
|
1370
|
-
const
|
|
1371
|
-
const
|
|
1372
|
-
|
|
1373
|
-
const
|
|
1845
|
+
function foldTracked(t, keepBlanks) {
|
|
1846
|
+
const parts = [];
|
|
1847
|
+
const ids = [];
|
|
1848
|
+
let cursor = 0;
|
|
1849
|
+
for (const run of t.text.matchAll(keepBlanks ? FOLD_RUN_KEEPING_BLANKS : FOLD_RUN)) {
|
|
1850
|
+
const at = run.index ?? 0;
|
|
1851
|
+
parts.push(t.text.slice(cursor, at));
|
|
1852
|
+
for (let k = cursor; k < at; k++)
|
|
1853
|
+
ids.push(t.ids[k] ?? -1);
|
|
1854
|
+
let id = -1;
|
|
1855
|
+
for (let k = at; k < at + run[0].length && id < 0; k++)
|
|
1856
|
+
id = t.ids[k] ?? -1;
|
|
1857
|
+
parts.push(' ');
|
|
1858
|
+
ids.push(id);
|
|
1859
|
+
cursor = at + run[0].length;
|
|
1860
|
+
}
|
|
1861
|
+
parts.push(t.text.slice(cursor));
|
|
1862
|
+
for (let k = cursor; k < t.text.length; k++)
|
|
1863
|
+
ids.push(t.ids[k] ?? -1);
|
|
1864
|
+
const folded = parts.join('');
|
|
1865
|
+
let from = 0;
|
|
1866
|
+
let to = folded.length;
|
|
1867
|
+
if (folded.charCodeAt(0) === 0x20)
|
|
1868
|
+
from = 1;
|
|
1869
|
+
if (to > from && folded.charCodeAt(to - 1) === 0x20)
|
|
1870
|
+
to -= 1;
|
|
1871
|
+
return { text: folded.slice(from, to), ids: Int32Array.from(ids.slice(from, to)) };
|
|
1872
|
+
}
|
|
1873
|
+
function faceTracked(t, face, ledger) {
|
|
1874
|
+
const keepBlanks = face.keepBlanks === true;
|
|
1875
|
+
const unsafe = face.unsafe;
|
|
1876
|
+
const markAll = (x) => (unsafe === null ? x : markTracked(x, unsafe, face.mark, ledger));
|
|
1877
|
+
if (face.mark === 'space') {
|
|
1878
|
+
const marked = markAll(t);
|
|
1879
|
+
return face.fold ? foldTracked(marked, keepBlanks) : marked;
|
|
1880
|
+
}
|
|
1881
|
+
return markAll(face.fold ? foldTracked(t, keepBlanks) : t);
|
|
1882
|
+
}
|
|
1883
|
+
function carryIds(ids, origin) {
|
|
1884
|
+
if (origin === undefined)
|
|
1885
|
+
return ids;
|
|
1886
|
+
const out = new Int32Array(origin.length);
|
|
1887
|
+
for (let k = 0; k < origin.length; k++) {
|
|
1888
|
+
const s = origin[k] ?? -1;
|
|
1889
|
+
out[k] = s < 0 ? -1 : (ids[s] ?? -1);
|
|
1890
|
+
}
|
|
1891
|
+
return out;
|
|
1892
|
+
}
|
|
1893
|
+
function pairLiteralBackslashes(t) {
|
|
1894
|
+
if (!t.text.includes('\\'))
|
|
1895
|
+
return t;
|
|
1896
|
+
const parts = [];
|
|
1897
|
+
const ids = [];
|
|
1898
|
+
for (let k = 0; k < t.text.length; k++) {
|
|
1899
|
+
const ch = t.text.charAt(k);
|
|
1900
|
+
const id = t.ids[k] ?? -1;
|
|
1901
|
+
parts.push(ch);
|
|
1902
|
+
ids.push(id);
|
|
1903
|
+
if (ch === '\\' && id < 0) {
|
|
1904
|
+
parts.push('\\');
|
|
1905
|
+
ids.push(-1);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
return { text: parts.join(''), ids: Int32Array.from(ids) };
|
|
1909
|
+
}
|
|
1910
|
+
function capPaired(t, max) {
|
|
1911
|
+
if (typeof max !== 'number' || !Number.isFinite(max))
|
|
1912
|
+
return t;
|
|
1913
|
+
const limit = Math.max(0, Math.floor(max));
|
|
1914
|
+
let cost = 0;
|
|
1915
|
+
let cut = 0;
|
|
1916
|
+
while (cut < t.text.length) {
|
|
1917
|
+
const width = t.text.charCodeAt(cut) === 0x5c && (t.ids[cut] ?? -1) < 0 ? 2 : 1;
|
|
1918
|
+
if (cost + width > limit)
|
|
1919
|
+
break;
|
|
1920
|
+
cost += width;
|
|
1921
|
+
cut++;
|
|
1922
|
+
}
|
|
1923
|
+
if (cut === t.text.length)
|
|
1924
|
+
return t;
|
|
1925
|
+
const id = t.ids[cut] ?? -1;
|
|
1926
|
+
if (id >= 0)
|
|
1927
|
+
while (cut > 0 && t.ids[cut - 1] === id)
|
|
1928
|
+
cut--;
|
|
1929
|
+
if (cut > 0) {
|
|
1930
|
+
const code = t.text.charCodeAt(cut - 1);
|
|
1931
|
+
if (code >= 0xd800 && code <= 0xdbff)
|
|
1932
|
+
cut -= 1;
|
|
1933
|
+
}
|
|
1934
|
+
return { text: t.text.slice(0, cut), ids: t.ids.slice(0, cut) };
|
|
1935
|
+
}
|
|
1936
|
+
function collectMarks(t, ledger) {
|
|
1937
|
+
const out = [];
|
|
1938
|
+
let k = 0;
|
|
1939
|
+
while (k < t.ids.length) {
|
|
1940
|
+
const id = t.ids[k] ?? -1;
|
|
1941
|
+
if (id < 0) {
|
|
1942
|
+
k++;
|
|
1943
|
+
continue;
|
|
1944
|
+
}
|
|
1945
|
+
let j = k + 1;
|
|
1946
|
+
while (j < t.ids.length && t.ids[j] === id)
|
|
1947
|
+
j++;
|
|
1948
|
+
if (j - k === ledger.lens[id])
|
|
1949
|
+
out.push({ start: k, end: j, codePoint: ledger.cps[id] ?? 0 });
|
|
1950
|
+
k = j;
|
|
1951
|
+
}
|
|
1952
|
+
return out;
|
|
1953
|
+
}
|
|
1954
|
+
const PUBLIC_UNSAFE_BY_NETS = Object.freeze(Array.from({ length: 16 }, (_, i) => {
|
|
1955
|
+
const controls = (i & 8) !== 0;
|
|
1956
|
+
const bidi = (i & 4) !== 0;
|
|
1957
|
+
const keep = (i & 2) !== 0;
|
|
1958
|
+
const blanks = (i & 1) !== 0;
|
|
1959
|
+
const members = `${controls ? `${keep ? C0_KEEP_LAYOUT : C0_ALL}${DEL_C1}${LONE_SURROGATE}` : ''}${bidi ? LINE_SEPARATORS : ''}${blanks ? NAME_BLANKS : ''}`;
|
|
1374
1960
|
return unsafeClass(members, bidi);
|
|
1375
1961
|
}));
|
|
1376
1962
|
function publicFace(opts) {
|
|
1377
1963
|
const fold = opts.foldLines !== false;
|
|
1378
1964
|
const keep = !fold && opts.keepLayout === true;
|
|
1379
|
-
const
|
|
1380
|
-
|
|
1965
|
+
const blanks = opts.blanks === true;
|
|
1966
|
+
const index = (opts.controls !== false ? 8 : 0) + (opts.bidi !== false ? 4 : 0) + (keep ? 2 : 0) + (blanks ? 1 : 0);
|
|
1967
|
+
return { unsafe: PUBLIC_UNSAFE_BY_NETS[index] ?? null, fold, mark: opts.mark ?? 'escape', keepBlanks: blanks };
|
|
1381
1968
|
}
|
|
1382
1969
|
const ESCAPE_TOKEN_AT = /^\\u(?:[0-9A-F]{4}|\{[0-9A-F]{1,6}\})/;
|
|
1383
1970
|
function capDisplay(text, max, mark) {
|
|
@@ -1405,33 +1992,72 @@ function capDisplay(text, max, mark) {
|
|
|
1405
1992
|
return text.slice(0, cut);
|
|
1406
1993
|
}
|
|
1407
1994
|
const MAX_CAP_SETTLE_ROUNDS = 4;
|
|
1408
|
-
|
|
1409
|
-
const input = typeof text === 'string' ? text : String(text ?? '');
|
|
1410
|
-
const o = opts ?? {};
|
|
1411
|
-
const urls = o.credentialUrls !== false;
|
|
1412
|
-
const words = o.credentialWords !== false;
|
|
1413
|
-
const face = publicFace(o);
|
|
1995
|
+
function runDisplaySteps(start, steps) {
|
|
1414
1996
|
const settle = (s) => {
|
|
1415
|
-
let current =
|
|
1416
|
-
if (current === s)
|
|
1997
|
+
let current = steps.paint(steps.wash(s));
|
|
1998
|
+
if (steps.text(current) === steps.text(s))
|
|
1417
1999
|
return current;
|
|
1418
2000
|
for (let round = 1; round < MAX_SETTLE_ROUNDS; round++) {
|
|
1419
|
-
const next =
|
|
1420
|
-
if (next === current)
|
|
2001
|
+
const next = steps.paint(steps.wash(current));
|
|
2002
|
+
if (steps.text(next) === steps.text(current))
|
|
1421
2003
|
return current;
|
|
1422
2004
|
current = next;
|
|
1423
2005
|
}
|
|
1424
2006
|
return current;
|
|
1425
2007
|
};
|
|
1426
|
-
const settled = settle(
|
|
1427
|
-
let out =
|
|
1428
|
-
if (out === settled)
|
|
2008
|
+
const settled = settle(start);
|
|
2009
|
+
let out = steps.cap(settled);
|
|
2010
|
+
if (steps.text(out) === steps.text(settled))
|
|
1429
2011
|
return out;
|
|
1430
2012
|
for (let round = 0; round < MAX_CAP_SETTLE_ROUNDS; round++) {
|
|
1431
|
-
const again =
|
|
1432
|
-
if (again === out)
|
|
2013
|
+
const again = steps.cap(settle(out));
|
|
2014
|
+
if (steps.text(again) === steps.text(out))
|
|
1433
2015
|
break;
|
|
1434
2016
|
out = again;
|
|
1435
2017
|
}
|
|
1436
2018
|
return out;
|
|
1437
2019
|
}
|
|
2020
|
+
function renderUntrusted(text, opts, ledger) {
|
|
2021
|
+
const input = typeof text === 'string' ? text : String(text ?? '');
|
|
2022
|
+
const o = opts ?? {};
|
|
2023
|
+
const urls = o.credentialUrls !== false;
|
|
2024
|
+
const words = o.credentialWords !== false;
|
|
2025
|
+
const face = publicFace(o);
|
|
2026
|
+
const blankTokens = face.keepBlanks !== true ? 'off' : face.fold ? 'fold' : 'keep';
|
|
2027
|
+
const pairBackslashes = o.escapeBackslashes === true && face.mark !== 'dot' && face.mark !== 'space';
|
|
2028
|
+
const book = ledger ?? (pairBackslashes ? { cps: [], lens: [] } : null);
|
|
2029
|
+
if (book === null) {
|
|
2030
|
+
return runDisplaySteps(input, {
|
|
2031
|
+
wash: (s) => washCredentialNets(s, urls, words, false, blankTokens),
|
|
2032
|
+
paint: (s) => applyDisplayFace(s, face),
|
|
2033
|
+
cap: (s) => capDisplay(s, o.max, face.mark),
|
|
2034
|
+
text: (s) => s,
|
|
2035
|
+
});
|
|
2036
|
+
}
|
|
2037
|
+
const out = runDisplaySteps({ text: input, ids: new Int32Array(input.length).fill(-1) }, {
|
|
2038
|
+
wash: (t) => {
|
|
2039
|
+
const r = washNets(t.text, urls, words, false, blankTokens, true);
|
|
2040
|
+
return r.text === t.text ? t : { text: r.text, ids: carryIds(t.ids, r.origin) };
|
|
2041
|
+
},
|
|
2042
|
+
paint: (t) => faceTracked(t, face, book),
|
|
2043
|
+
cap: (t) => {
|
|
2044
|
+
if (pairBackslashes)
|
|
2045
|
+
return capPaired(t, o.max);
|
|
2046
|
+
const cut = capDisplay(t.text, o.max, face.mark);
|
|
2047
|
+
return cut === t.text ? t : { text: cut, ids: t.ids.slice(0, cut.length) };
|
|
2048
|
+
},
|
|
2049
|
+
text: (t) => t.text,
|
|
2050
|
+
});
|
|
2051
|
+
return pairBackslashes ? pairLiteralBackslashes(out) : out;
|
|
2052
|
+
}
|
|
2053
|
+
export function displayUntrusted(text, opts = {}) {
|
|
2054
|
+
const out = renderUntrusted(text, opts, null);
|
|
2055
|
+
return typeof out === 'string' ? out : out.text;
|
|
2056
|
+
}
|
|
2057
|
+
export function displayUntrustedMarks(text, opts = {}) {
|
|
2058
|
+
const ledger = { cps: [], lens: [] };
|
|
2059
|
+
const out = renderUntrusted(text, opts, ledger);
|
|
2060
|
+
if (typeof out === 'string')
|
|
2061
|
+
return { text: out, marks: [] };
|
|
2062
|
+
return { text: out.text, marks: collectMarks(out, ledger) };
|
|
2063
|
+
}
|