@sema-agent/client-core 0.101.0 → 0.102.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +57 -0
  2. package/README.md +12 -7
  3. package/dist/adapt/toolCards.d.ts +2 -1
  4. package/dist/adapt/toolCards.js +20 -6
  5. package/dist/adapt.d.ts +1 -1
  6. package/dist/adapt.js +1 -1
  7. package/dist/adapter/downstream/eventToSdkMessage.js +6 -68
  8. package/dist/adapter/downstream/wiringManifestView.d.ts +1 -0
  9. package/dist/adapter/downstream/wiringManifestView.js +138 -0
  10. package/dist/compensations.js +1 -1
  11. package/dist/displayUntrusted.d.ts +8 -4
  12. package/dist/displayUntrusted.js +769 -96
  13. package/dist/engineRowRef.js +1 -0
  14. package/dist/fleet/fleetProjection.js +1 -1
  15. package/dist/i18n/messages.en.d.ts +38 -0
  16. package/dist/i18n/messages.en.js +38 -0
  17. package/dist/i18n/messages.zh.js +40 -0
  18. package/dist/index.d.ts +8 -0
  19. package/dist/index.js +5 -0
  20. package/dist/interactiveHalt.d.ts +3 -1
  21. package/dist/interactiveHalt.js +2 -0
  22. package/dist/interactiveInterrupt.d.ts +2 -0
  23. package/dist/interactiveInterrupt.js +15 -0
  24. package/dist/modsFace.d.ts +8 -0
  25. package/dist/modsFace.js +59 -0
  26. package/dist/ownKey.d.ts +1 -0
  27. package/dist/ownKey.js +15 -0
  28. package/dist/printToolResultFrame.js +1 -1
  29. package/dist/request/printNotification.js +1 -1
  30. package/dist/request/taskRequest.d.ts +21 -0
  31. package/dist/request/taskRequest.js +123 -7
  32. package/dist/seam.js +1 -1
  33. package/dist/seatContract.d.ts +22 -6
  34. package/dist/seatContract.js +35 -6
  35. package/dist/sessionBackground.d.ts +165 -0
  36. package/dist/sessionBackground.js +781 -0
  37. package/dist/sessionBackgroundHandoff.d.ts +54 -0
  38. package/dist/sessionBackgroundHandoff.js +321 -0
  39. package/dist/sessionMap.js +4 -1
  40. package/dist/settingsCredentialKeyPaths.js +2 -17
  41. package/dist/thinkingLatch.d.ts +69 -0
  42. package/dist/thinkingLatch.js +255 -0
  43. package/dist/thinkingLatchWords.d.ts +7 -0
  44. package/dist/thinkingLatchWords.js +4 -0
  45. package/dist/toolResult.js +2 -24
  46. package/docs/INTEGRATION-CLIENTS.md +309 -10
  47. package/package.json +1 -1
  48. package/dist/argvFlagValue.d.ts +0 -8
  49. package/dist/argvFlagValue.js +0 -37
@@ -11,7 +11,7 @@ function isMarker(v) {
11
11
  return core === RETIRED_MARKER || MARKER_RE.test(core);
12
12
  }
13
13
  const VIEW_SEPARATOR = '\ufeff';
14
- function pushEdit(edits, src, start, end, text) {
14
+ function pushEdit(edits, src, start, end, text, phases) {
15
15
  let a = start;
16
16
  let b = end;
17
17
  while (a < b && src[a] === VIEW_SEPARATOR)
@@ -22,7 +22,7 @@ function pushEdit(edits, src, start, end, text) {
22
22
  a = b = start;
23
23
  if (src.slice(a, b) === text)
24
24
  return;
25
- edits.push({ start: a, end: b, text });
25
+ edits.push(phases === undefined ? { start: a, end: b, text } : { start: a, end: b, text, phases });
26
26
  }
27
27
  const UNIT_NONE = 0;
28
28
  const UNIT_SEQUENCE = 1;
@@ -220,7 +220,7 @@ const isHostChar = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) |
220
220
  const isIpv6Char = (c) => (c >= 0x30 && c <= 0x3a) || (c >= 0x41 && c <= 0x46) || (c >= 0x61 && c <= 0x66) || c === 0x2e;
221
221
  const endsSchemelessHost = (c) => Number.isNaN(c) || c === 0x2f || c === 0x3f || c === 0x23 || c === 0x22 || c === 0x3c || c === 0x3e || c === 0x60 ||
222
222
  c === 0x7c || c === 0x5c || c === 0x5e || c === 0x7b || c === 0x7d || isRegexSpace(c);
223
- function schemelessHostEnd(text, from) {
223
+ function schemelessHostEnd(text, from, wide = false) {
224
224
  let k = from;
225
225
  if (text.charCodeAt(k) === 0x5b) {
226
226
  k++;
@@ -238,14 +238,25 @@ function schemelessHostEnd(text, from) {
238
238
  }
239
239
  if (text.charCodeAt(k) === 0x3a) {
240
240
  let d = k + 1;
241
- while (d < text.length && text.charCodeAt(d) >= 0x30 && text.charCodeAt(d) <= 0x39)
242
- d++;
243
- if (d > k + 1)
241
+ if (wide) {
242
+ while (d < text.length && isSchemelessPortChar(text.charCodeAt(d)))
243
+ d++;
244
244
  k = d;
245
+ }
246
+ else {
247
+ while (d < text.length && text.charCodeAt(d) >= 0x30 && text.charCodeAt(d) <= 0x39)
248
+ d++;
249
+ if (d > k + 1)
250
+ k = d;
251
+ }
245
252
  }
246
253
  const c = text.charCodeAt(k);
247
- return endsSchemelessHost(c) || c === 0x27 ? k : -1;
254
+ return endsSchemelessHost(c) || c === 0x27 || (wide && endsSchemelessHostInProse(c)) ? k : -1;
248
255
  }
256
+ const isSchemelessPortChar = (c) => (c >= 0x30 && c <= 0x39) || (c >= 0x41 && c <= 0x5a) || (c >= 0x61 && c <= 0x7a) || c === 0x2e || c === 0x5f || c === 0x7e || c === 0x25 || c === 0x2d;
257
+ const endsSchemelessHostInProse = (c) => c === 0x29 || c === 0x2c || c === 0x3b || c === 0x21 || c === 0x5d;
258
+ const SCHEMELESS_USERINFO_WIDE_SPAN = 64 + 1 + 256;
259
+ const isUserinfoWideDelimiter = (ch) => ch === '?' || ch === '#' || ch === '@';
249
260
  const MAX_PORT = 65535;
250
261
  function authorityIsHostPort(text, from, to) {
251
262
  if (to <= from || schemelessHostEnd(text.slice(0, to), from) !== to)
@@ -282,32 +293,32 @@ function queryCandidates(src, base, query, edits, soft, byShape = false) {
282
293
  const eq = part.indexOf('=');
283
294
  if (eq === -1) {
284
295
  const at = pos;
285
- if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i))) && (!byShape || queryPartCarriesCredential(null, part, relax)))
286
- pushEdit(edits, src, pos, pos + part.length, REDACTED_QUERY);
296
+ if (!slotIsWholeMarker(part, soft && ((i) => soft(at + i)))) {
297
+ const phases = byShape ? queryPartPhases(null, part, relax) : phaseRange(0, scanUpto);
298
+ if (phases !== 0)
299
+ pushEdit(edits, src, pos, pos + part.length, REDACTED_QUERY, phasesOrAll(phases));
300
+ }
287
301
  }
288
302
  else {
289
303
  const value = part.slice(eq + 1);
290
304
  const at = pos + eq + 1;
291
- if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i))) && (!byShape || queryPartCarriesCredential(part.slice(0, eq), value, relax))) {
292
- pushEdit(edits, src, pos + eq + 1, pos + part.length, REDACTED_QUERY);
305
+ if (value.length > 0 && !slotIsWholeMarker(value, soft && ((i) => soft(at + i)))) {
306
+ const phases = byShape ? queryPartPhases(part.slice(0, eq), value, relax) : phaseRange(0, scanUpto);
307
+ if (phases !== 0)
308
+ pushEdit(edits, src, pos + eq + 1, pos + part.length, REDACTED_QUERY, phasesOrAll(phases));
293
309
  }
294
310
  }
295
311
  }
296
312
  pos += part.length + 1;
297
313
  }
298
314
  }
299
- function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
315
+ function pathCandidates(src, base, path, opaqueSegments, edits, soft, whole = false) {
300
316
  let pos = base;
301
317
  for (const segment of path.split('/')) {
302
318
  const semi = segment.indexOf(';');
303
319
  const head = semi === -1 ? segment : segment.slice(0, semi);
304
320
  if (opaqueSegments) {
305
- let a = 0;
306
- let b = head.length;
307
- while (a < b && !isKeyWordChar(head.charCodeAt(a)) && !(head.charCodeAt(a) === 0x25 && hasNonAsciiPercentEscape(head.slice(a, a + 3))))
308
- a++;
309
- while (b > a && !isKeyWordChar(head.charCodeAt(b - 1)))
310
- b--;
321
+ const [a, b] = markerAtomicCut(head, ...segmentWordCut(head));
311
322
  if (isKnownSecretSegment(head)) {
312
323
  if (b > a)
313
324
  pushEdit(edits, src, pos + a, pos + b, REDACTED_SECRET);
@@ -317,6 +328,10 @@ function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
317
328
  if (glued > 0)
318
329
  pushEdit(edits, src, pos + a + glued, pos + b, REDACTED_SECRET);
319
330
  }
331
+ if (whole && b > a) {
332
+ const at = pos;
333
+ emitRow('path-slot', (phases) => pathSlotEdits(src, at, head, a, b, phases, edits, soft));
334
+ }
320
335
  }
321
336
  if (semi !== -1) {
322
337
  let ppos = pos + semi + 1;
@@ -334,9 +349,76 @@ function pathCandidates(src, base, path, opaqueSegments, edits, soft) {
334
349
  pos += segment.length + 1;
335
350
  }
336
351
  }
337
- function addressCandidates(src, from, end, opaqueSegments, edits, regions, soft, byShape = false, whole = false, hostRegion = whole) {
352
+ function segmentWordCut(head) {
353
+ let a = 0;
354
+ let b = head.length;
355
+ while (a < b && !isKeyWordChar(head.charCodeAt(a)) && !(head.charCodeAt(a) === 0x25 && hasNonAsciiPercentEscape(head.slice(a, a + 3))))
356
+ a++;
357
+ while (b > a && !isKeyWordChar(head.charCodeAt(b - 1)))
358
+ b--;
359
+ return [a, b];
360
+ }
361
+ function markerAtomicCut(head, a, b) {
362
+ if (!head.includes(REDACTION_MARKER_PREFIX))
363
+ return [a, b];
364
+ let s = a;
365
+ let e = b;
366
+ const re = new RegExp(MARKER_ANYWHERE.source, MARKER_ANYWHERE.flags);
367
+ for (let m = re.exec(head); m !== null; m = re.exec(head)) {
368
+ const ms = m.index;
369
+ const me = ms + m[0].length;
370
+ if (ms < s && s < me)
371
+ s = ms;
372
+ if (ms < e && e < me)
373
+ e = me;
374
+ }
375
+ return [s, e];
376
+ }
377
+ function pathSlotEdits(src, pos, head, a, b, rowPhases, edits, soft) {
378
+ const softFrom = (at) => (soft === undefined ? undefined : (i) => soft(pos + at + i));
379
+ const eq = head.indexOf('=', a);
380
+ let named = 0;
381
+ if (eq > a && eq < b) {
382
+ const name = head.slice(a, eq);
383
+ if (!slotIsWholeMarker(name, softFrom(a))) {
384
+ named = queryPartPhases(null, name, false) & rowPhases;
385
+ if (named !== 0)
386
+ pushEdit(edits, src, pos + a, pos + b, REDACTED_SECRET, named);
387
+ if (named === rowPhases)
388
+ return;
389
+ }
390
+ }
391
+ const [s, e] = markerAtomicCut(head, eq !== -1 && eq < b ? eq + 1 : a, b);
392
+ if (s >= e)
393
+ return;
394
+ const slot = head.slice(s, e);
395
+ if (slotIsWholeMarker(slot, softFrom(s)))
396
+ return;
397
+ const valued = queryPartPhases(null, slot, false) & rowPhases & ~named;
398
+ if (valued !== 0)
399
+ pushEdit(edits, src, pos + s, pos + e, REDACTED_SECRET, valued);
400
+ }
401
+ const anchoredFormPattern = (form) => new RegExp(`(?:https?:\/\/)?${form.host.split('.').join('\\.')}${form.trailingDot ? '\\.?' : ''}\/(?:${form.pathKinds.join('|')})\/((?:[${form.tailClass}]|${MARKER_ANYWHERE.source})+)`, 'gi');
402
+ function anchoredFormCandidates(text, out) {
403
+ for (const { id, form, probe, pattern } of ANCHORED_FORM_ROWS) {
404
+ if (!probe.test(text))
405
+ continue;
406
+ emitRow(id, (phases) => {
407
+ const re = new RegExp(pattern.source, pattern.flags);
408
+ for (let m = re.exec(text); m !== null; m = re.exec(text)) {
409
+ const tail = m[1] ?? '';
410
+ const end = m.index + m[0].length;
411
+ if (tail.length > 0 && (tail.includes(REDACTION_MARKER_PREFIX) || tail.length >= form.minTailLength))
412
+ out.push({ a: end - tail.length, b: end, kind: 'secret', cls: 'url', te: -1, phases });
413
+ if (m[0].length === 0)
414
+ re.lastIndex++;
415
+ }
416
+ });
417
+ }
418
+ }
419
+ function addressCandidates(src, from, end, opaqueSegments, edits, regions, soft, byShape = false, whole = false, hostRegion = whole, pathOnly = false) {
338
420
  const rest = src.slice(from, end);
339
- const authorityEnd = rest.search(/[/?#]/);
421
+ const authorityEnd = pathOnly ? 0 : rest.search(/[/?#]/);
340
422
  const authority = authorityEnd === -1 ? rest : rest.slice(0, authorityEnd);
341
423
  const tailStart = from + authority.length;
342
424
  const tail = src.slice(tailStart, end);
@@ -347,7 +429,7 @@ function addressCandidates(src, from, end, opaqueSegments, edits, regions, soft,
347
429
  const beforeHash = hash === -1 ? tail : tail.slice(0, hash);
348
430
  const question = beforeHash.indexOf('?');
349
431
  const path = question === -1 ? beforeHash : beforeHash.slice(0, question);
350
- pathCandidates(src, tailStart, path, opaqueSegments, edits, soft);
432
+ pathCandidates(src, tailStart, path, opaqueSegments, edits, soft, whole);
351
433
  if (whole) {
352
434
  const cut = pathCutAfterFirstLabel(path, true);
353
435
  if (cut !== -1 && fragmentTerminatorFrom(path, cut))
@@ -376,7 +458,8 @@ function relativeAddressCandidates(text, out, regions, unitPos, soft, hostRegion
376
458
  while (p0 < text.length && (isWhitespaceOrControl(text.charAt(p0)) || text.charAt(p0) === VIEW_SEPARATOR))
377
459
  p0++;
378
460
  const c0 = text.charAt(p0);
379
- if (c0 !== '/' && c0 !== '?' && c0 !== '#')
461
+ const pathOnly = c0 !== '/' && c0 !== '?' && c0 !== '#' && isRelativeRequestAt(text, p0);
462
+ if (c0 !== '/' && c0 !== '?' && c0 !== '#' && !pathOnly)
380
463
  return false;
381
464
  let end = text.length;
382
465
  while (end > p0 && (text.charAt(end - 1) === VIEW_SEPARATOR || isWhitespaceOrControl(text.charAt(end - 1)) || isUnicodeBlank(text.charCodeAt(end - 1))))
@@ -384,10 +467,19 @@ function relativeAddressCandidates(text, out, regions, unitPos, soft, hostRegion
384
467
  end = trailingCloserEnd(text, p0, p0 + 1, end, unitPos);
385
468
  const from = text.startsWith('//', p0) ? p0 + 2 : p0;
386
469
  const tokenEdits = [];
387
- addressCandidates(text, from, end, true, tokenEdits, regions, soft, false, true, hostRegion);
470
+ addressCandidates(text, from, end, true, tokenEdits, regions, soft, false, true, hostRegion, pathOnly);
388
471
  editsToCandidates(tokenEdits, 'url', out, end);
389
472
  return true;
390
473
  }
474
+ function isRelativeRequestAt(text, p0) {
475
+ const rest = text.slice(p0);
476
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:/.test(rest))
477
+ return false;
478
+ const q = rest.search(/[?#]/);
479
+ if (q <= 0)
480
+ return false;
481
+ return !/[\s=\ufeff]/.test(rest.slice(0, q)) && rest.indexOf('=', q + 1) !== -1;
482
+ }
391
483
  function urlCandidates(text, out, regions, unitPos = () => false, soft, byShape = false, whole = false, hostRegion = whole) {
392
484
  if (whole && relativeAddressCandidates(text, out, regions, unitPos, soft, hostRegion))
393
485
  return;
@@ -457,6 +549,8 @@ function urlCandidates(text, out, regions, unitPos = () => false, soft, byShape
457
549
  end = runStart;
458
550
  end = trailingCloserEnd(text, start, mark + 3, end, unitPos);
459
551
  const tokenEdits = [];
552
+ if (userinfoBoundaryAmbiguous(text.slice(start, end)))
553
+ pushEdit(tokenEdits, text, start, end, REDACTED_SECRET);
460
554
  addressCandidates(text, mark + 3, end, !/^file:/i.test(text.slice(start, mark + 3)), tokenEdits, regions, soft, byShape, whole, hostRegion);
461
555
  editsToCandidates(tokenEdits, 'url', out, end);
462
556
  mark = text.indexOf('://', end > mark + 3 ? end : mark + 3);
@@ -635,13 +729,7 @@ function userinfoCandidates(S, out, words = true) {
635
729
  return nextBareStop(from) >= at;
636
730
  };
637
731
  let floor = 0;
638
- for (let at = text.indexOf('@'); at !== -1; at = text.indexOf('@', at + 1)) {
639
- let hostEnd = schemelessHostEnd(text, at + 1);
640
- const hidden = hostEnd === -1;
641
- if (hidden)
642
- hostEnd = hiddenHostEnd(text, at + 1, unitAt);
643
- if (hostEnd === -1)
644
- continue;
732
+ const readAt = (at, hostEnd, hidden, runStartIn) => {
645
733
  let shaped = null;
646
734
  const hostShaped = () => {
647
735
  if (shaped === null) {
@@ -673,9 +761,7 @@ function userinfoCandidates(S, out, words = true) {
673
761
  return hostShaped() ? 'bare' : null;
674
762
  };
675
763
  const refused = [];
676
- let runStart = at;
677
- while (runStart > floor && (!breaksUserinfo(text.charCodeAt(runStart - 1)) || (hidden && unitAt(runStart - 1) !== UNIT_NONE)))
678
- runStart--;
764
+ let runStart = runStartIn;
679
765
  if (text.charCodeAt(runStart) === 0x3a && runStart > floor && text[runStart - 1] === VIEW_SEPARATOR) {
680
766
  while (runStart > floor && text[runStart - 1] === VIEW_SEPARATOR)
681
767
  runStart--;
@@ -723,7 +809,7 @@ function userinfoCandidates(S, out, words = true) {
723
809
  continue;
724
810
  }
725
811
  }
726
- if (USERNAME_DELIMITERS.has(ch)) {
812
+ if (USERNAME_DELIMITERS.has(ch) || isUserinfoWideDelimiter(ch)) {
727
813
  lo = colon + 1;
728
814
  continue;
729
815
  }
@@ -791,10 +877,50 @@ function userinfoCandidates(S, out, words = true) {
791
877
  }
792
878
  }
793
879
  }
794
- if (found === -1)
795
- continue;
796
- out.push({ a: found, b: at, kind: 'userinfo', cls: hidden ? 'userinfo-hidden' : 'structural', te: hostEnd });
797
- floor = hostEnd;
880
+ return found;
881
+ };
882
+ const walkFrom = (at, hidden) => {
883
+ let r = at;
884
+ while (r > floor && (!breaksUserinfo(text.charCodeAt(r - 1)) || (hidden && unitAt(r - 1) !== UNIT_NONE)))
885
+ r--;
886
+ return r;
887
+ };
888
+ const widen = (at, r) => {
889
+ const wideFloor = Math.max(floor, at - SCHEMELESS_USERINFO_WIDE_SPAN);
890
+ let w = r;
891
+ while (w > wideFloor && isUserinfoWideDelimiter(text.charAt(w - 1))) {
892
+ w--;
893
+ while (w > wideFloor && !breaksUserinfo(text.charCodeAt(w - 1)))
894
+ w--;
895
+ }
896
+ return w;
897
+ };
898
+ for (let at = text.indexOf('@'); at !== -1; at = text.indexOf('@', at + 1)) {
899
+ let hostEnd = schemelessHostEnd(text, at + 1);
900
+ const hidden = hostEnd === -1;
901
+ if (hidden)
902
+ hostEnd = hiddenHostEnd(text, at + 1, unitAt);
903
+ const cursor = markerFrom;
904
+ const runStart = hostEnd === -1 ? -1 : walkFrom(at, hidden);
905
+ const found = runStart === -1 ? -1 : readAt(at, hostEnd, hidden, runStart);
906
+ if (found !== -1)
907
+ out.push({ a: found, b: at, kind: 'userinfo', cls: hidden ? 'userinfo-hidden' : 'structural', te: hostEnd });
908
+ const wideEnd = schemelessHostEnd(text, at + 1, true);
909
+ const wHidden = wideEnd === -1 ? hidden : false;
910
+ const wHostEnd = wideEnd === -1 ? hostEnd : wideEnd;
911
+ if (wHostEnd !== -1) {
912
+ const wRunStart = widen(at, wHostEnd === hostEnd && wHidden === hidden && runStart !== -1 ? runStart : walkFrom(at, wHidden));
913
+ if (wHostEnd !== hostEnd || wHidden !== hidden || wRunStart !== runStart) {
914
+ const after = markerFrom;
915
+ markerFrom = cursor;
916
+ const wFound = readAt(at, wHostEnd, wHidden, wRunStart);
917
+ markerFrom = Math.max(after, markerFrom);
918
+ if (wFound !== -1 && (wFound !== found || wHostEnd !== hostEnd))
919
+ out.push({ a: wFound, b: at, kind: 'userinfo', cls: wHidden ? 'userinfo-hidden' : 'structural', te: wHostEnd });
920
+ }
921
+ }
922
+ if (found !== -1)
923
+ floor = hostEnd;
798
924
  }
799
925
  }
800
926
  function labelWashedBefore(text, a, floor) {
@@ -4040,7 +4166,7 @@ function editsToCandidates(edits, cls, out, te = -1) {
4040
4166
  const kind = KIND_BY_TEXT.get(e.text);
4041
4167
  if (kind === undefined || e.end <= e.start)
4042
4168
  continue;
4043
- out.push({ a: e.start, b: e.end, kind, cls, te });
4169
+ out.push(e.phases === undefined ? { a: e.start, b: e.end, kind, cls, te } : { a: e.start, b: e.end, kind, cls, te, phases: e.phases });
4044
4170
  }
4045
4171
  }
4046
4172
  function pemRegions(recognized) {
@@ -4258,7 +4384,8 @@ function toRaw(S, c) {
4258
4384
  if (a >= b)
4259
4385
  return null;
4260
4386
  const te = c.te > b ? S.rawEnd(c.te - 1) : S.rawEnd(b - 1);
4261
- return { start: S.rawStart(a), end: S.rawEnd(b - 1), kind: c.kind, cls: c.cls, tokenEnd: te };
4387
+ const r = { start: S.rawStart(a), end: S.rawEnd(b - 1), kind: c.kind, cls: c.cls, tokenEnd: te };
4388
+ return c.phases === undefined ? r : { ...r, phases: c.phases };
4262
4389
  }
4263
4390
  function runLabelPasses(S, regions, cands, blocks) {
4264
4391
  labelCandidates(S, 'scheme', regions, cands, blocks);
@@ -4307,6 +4434,7 @@ function collectCandidates(text, o, nested) {
4307
4434
  if (r[2] === 'host')
4308
4435
  regions.push(r);
4309
4436
  }
4437
+ anchoredFormCandidates(S.text, cands);
4310
4438
  if (o.words) {
4311
4439
  editsToCandidates(pemEdits(S, (blk) => { if (S === regionSurface)
4312
4440
  pemBlocksSeen.push(pemBlockToRaw(S, blk)); }), 'pem', cands);
@@ -4327,26 +4455,100 @@ function collectCandidates(text, o, nested) {
4327
4455
  for (const [a, b] of V.contents) {
4328
4456
  const sub = text.slice(a, b);
4329
4457
  const inner = collectCandidates(sub, o, true);
4330
- for (const iv of resolveCandidates(sub, inner.cands, inner.rawUnit, inner.rawDot, o.words ? pemRegions(inner.pemBlocksSeen) : [], inner.rawSoft, inner.rawPlain)) {
4331
- raw.push({ start: a + iv.start, end: a + iv.end, kind: iv.kind, cls: 'nested', tokenEnd: a + iv.end, pretrimmed: true });
4458
+ for (const [iv, phases] of resolveEachPhase(sub, inner, o)) {
4459
+ const c = { start: a + iv.start, end: a + iv.end, kind: iv.kind, cls: 'nested', tokenEnd: a + iv.end, pretrimmed: true };
4460
+ raw.push(phases === undefined ? c : { ...c, phases });
4332
4461
  }
4333
4462
  }
4334
4463
  for (const c of [...screen.contents, ...decodedContents]) {
4335
4464
  const inner = collectCandidates(c.sub, o, true);
4336
- for (const iv of resolveCandidates(c.sub, inner.cands, inner.rawUnit, inner.rawDot, o.words ? pemRegions(inner.pemBlocksSeen) : [], inner.rawSoft, inner.rawPlain)) {
4465
+ for (const [iv, phases] of resolveEachPhase(c.sub, inner, o)) {
4337
4466
  const start = c.rawAt(iv.start);
4338
4467
  const end = c.rawEndAt(iv.end - 1);
4339
- if (end > start)
4340
- raw.push({ start, end, kind: iv.kind, cls: 'nested', tokenEnd: end, pretrimmed: true });
4468
+ if (end <= start)
4469
+ continue;
4470
+ const r = { start, end, kind: iv.kind, cls: 'nested', tokenEnd: end, pretrimmed: true };
4471
+ raw.push(phases === undefined ? r : { ...r, phases });
4341
4472
  }
4342
4473
  }
4343
4474
  }
4344
4475
  return { cands: raw, rawUnit: V.rawUnit, rawDot: screen.surfaces[0]?.rawDot ?? null, rawSoft: screen.rawSoft, rawPlain: screen.rawPlain, pemBlocksSeen };
4345
4476
  }
4346
- function scanOnce(text, o, trace) {
4347
- const { cands, rawUnit, rawDot, rawSoft, rawPlain, pemBlocksSeen } = collectCandidates(text, o, false);
4348
- const intervals = resolveCandidates(text, cands, rawUnit, rawDot, o.words ? pemRegions(pemBlocksSeen) : [], rawSoft, rawPlain);
4349
- return materialize(text, intervals, trace);
4477
+ const RULES_BY_VERSION = {
4478
+ '0.102.0': [
4479
+ { id: 'path-slot', ticket: 'CC-849', kind: 'generator' },
4480
+ {
4481
+ id: 'anchored-form:slack-webhook-url',
4482
+ ticket: 'CC-849',
4483
+ kind: 'generator',
4484
+ form: { host: 'hooks.slack.com', trailingDot: true, pathKinds: ['services', 'workflows', 'triggers'], tailClass: 'A-Za-z0-9+/_-', minTailLength: 40 },
4485
+ },
4486
+ ],
4487
+ };
4488
+ const RULE_ROWS_ARE_GENERATORS = true;
4489
+ void RULE_ROWS_ARE_GENERATORS;
4490
+ function rowPhaseTable() {
4491
+ const m = new Map();
4492
+ const keys = Object.keys(RULES_BY_VERSION);
4493
+ keys.forEach((k, j) => {
4494
+ for (const row of RULES_BY_VERSION[k])
4495
+ m.set(row.id, j + 1);
4496
+ });
4497
+ return m;
4498
+ }
4499
+ const ROW_PHASE = rowPhaseTable();
4500
+ const LAST_PHASE = Object.keys(RULES_BY_VERSION).length;
4501
+ const ANCHORED_FORM_ROWS = Object.values(RULES_BY_VERSION)
4502
+ .flatMap((rows) => rows.filter((r) => 'form' in r))
4503
+ .map((r) => ({ id: r.id, form: r.form, probe: new RegExp(r.form.host.split('.').join('\\.'), 'i'), pattern: anchoredFormPattern(r.form) }));
4504
+ let scanUpto = LAST_PHASE;
4505
+ const phaseRange = (from, to) => (to < from ? 0 : ((1 << (to + 1)) - 1) & ~((1 << from) - 1));
4506
+ const phasesOrAll = (mask) => (mask === phaseRange(0, scanUpto) ? undefined : mask);
4507
+ const inPhase = (phases, k) => phases === undefined || ((phases >> k) & 1) === 1;
4508
+ function emitRow(rowId, emit) {
4509
+ const p = ROW_PHASE.get(rowId) ?? Number.POSITIVE_INFINITY;
4510
+ if (p > scanUpto)
4511
+ return;
4512
+ emit(phaseRange(p, scanUpto));
4513
+ }
4514
+ function resolveEachPhase(text, c, o) {
4515
+ const pems = o.words ? pemRegions(c.pemBlocksSeen) : [];
4516
+ const run = (cs) => resolveCandidates(text, cs, c.rawUnit, c.rawDot, pems, c.rawSoft, c.rawPlain);
4517
+ if (!c.cands.some((x) => x.phases !== undefined))
4518
+ return run(c.cands).map((iv) => [iv, undefined]);
4519
+ const per = [];
4520
+ for (let k = 0; k <= scanUpto; k++)
4521
+ per.push(run(c.cands.filter((x) => inPhase(x.phases, k))));
4522
+ const key = (ivs) => ivs.map((iv) => `${iv.start}:${iv.end}:${iv.kind}`).join(',');
4523
+ const k0 = key(per[0] ?? []);
4524
+ if (per.every((ivs) => key(ivs) === k0))
4525
+ return (per[0] ?? []).map((iv) => [iv, undefined]);
4526
+ const out = [];
4527
+ per.forEach((ivs, k) => {
4528
+ for (const iv of ivs)
4529
+ out.push([iv, 1 << k]);
4530
+ });
4531
+ return out;
4532
+ }
4533
+ function scanPhases(text, o, trace, upto) {
4534
+ const saved = scanUpto;
4535
+ scanUpto = upto;
4536
+ try {
4537
+ const c = collectCandidates(text, o, false);
4538
+ const pems = o.words ? pemRegions(c.pemBlocksSeen) : [];
4539
+ const run = (cs) => materialize(text, resolveCandidates(text, cs, c.rawUnit, c.rawDot, pems, c.rawSoft, c.rawPlain), trace);
4540
+ if (!c.cands.some((x) => x.phases !== undefined)) {
4541
+ const one = run(c.cands);
4542
+ return Array.from({ length: upto + 1 }, () => one);
4543
+ }
4544
+ const outs = [];
4545
+ for (let k = 0; k <= upto; k++)
4546
+ outs.push(run(c.cands.filter((x) => inPhase(x.phases, k))));
4547
+ return outs;
4548
+ }
4549
+ finally {
4550
+ scanUpto = saved;
4551
+ }
4350
4552
  }
4351
4553
  const MAX_WASH_REWRITES = 8;
4352
4554
  function composeOrigin(first, second) {
@@ -4361,36 +4563,76 @@ function composeOrigin(first, second) {
4361
4563
  }
4362
4564
  return out;
4363
4565
  }
4364
- function scanAndMask(text, o, trace, stats) {
4566
+ function scanAndMask(text, o, trace, stats, phaseOut) {
4365
4567
  if (!o.urls && !o.words) {
4366
4568
  if (stats !== undefined) {
4367
4569
  stats.passes = 0;
4368
4570
  stats.capped = false;
4571
+ stats.collections = 0;
4369
4572
  }
4573
+ phaseOut?.push({ text });
4370
4574
  return { text };
4371
4575
  }
4372
- let cur = scanOnce(text, o, trace);
4373
- let passes = 1;
4576
+ const upto = scanUpto;
4577
+ let memoText = null;
4578
+ let memo = [];
4579
+ let collections = 0;
4580
+ let passes = 0;
4581
+ const op = (t, k) => {
4582
+ if (t !== memoText) {
4583
+ memo = scanPhases(t, o, trace, upto);
4584
+ memoText = t;
4585
+ collections++;
4586
+ }
4587
+ passes++;
4588
+ return memo[k] ?? { text: t };
4589
+ };
4590
+ const advance = (cur, next) => {
4591
+ const origin = trace ? composeOrigin(cur.origin, next.origin) : undefined;
4592
+ return origin === undefined ? { text: next.text } : { text: next.text, origin };
4593
+ };
4594
+ let cur = op(text, 0);
4374
4595
  let rewrites = cur.text !== text ? 1 : 0;
4375
4596
  let changed = rewrites > 0;
4376
4597
  while (changed && rewrites < MAX_WASH_REWRITES) {
4377
- const next = scanOnce(cur.text, o, trace);
4378
- passes++;
4598
+ const next = op(cur.text, 0);
4379
4599
  changed = next.text !== cur.text;
4380
4600
  if (!changed)
4381
4601
  break;
4382
4602
  rewrites++;
4383
- const origin = trace ? composeOrigin(cur.origin, next.origin) : undefined;
4384
- cur = origin === undefined ? { text: next.text } : { text: next.text, origin };
4603
+ cur = advance(cur, next);
4604
+ }
4605
+ const stable = [!changed];
4606
+ phaseOut?.push(cur);
4607
+ for (let j = 1; j <= upto; j++) {
4608
+ stable.push(false);
4609
+ let rw = 0;
4610
+ let k = j;
4611
+ while (rw < MAX_WASH_REWRITES && !stable.every(Boolean)) {
4612
+ if (stable[k] === true) {
4613
+ k = (k + j) % (j + 1);
4614
+ continue;
4615
+ }
4616
+ const next = op(cur.text, k);
4617
+ if (next.text === cur.text)
4618
+ stable[k] = true;
4619
+ else {
4620
+ rw++;
4621
+ cur = advance(cur, next);
4622
+ stable.fill(false);
4623
+ }
4624
+ k = (k + j) % (j + 1);
4625
+ }
4626
+ phaseOut?.push(cur);
4385
4627
  }
4386
4628
  if (stats !== undefined) {
4387
4629
  let capped = false;
4388
- if (changed) {
4389
- passes++;
4390
- capped = scanOnce(cur.text, o, false).text !== cur.text;
4391
- }
4630
+ for (let k = 0; k < stable.length; k++)
4631
+ if (stable[k] !== true && op(cur.text, k).text !== cur.text)
4632
+ capped = true;
4392
4633
  stats.passes = passes;
4393
4634
  stats.capped = capped;
4635
+ stats.collections = collections;
4394
4636
  }
4395
4637
  return cur;
4396
4638
  }
@@ -4789,14 +5031,28 @@ function scanOptionsOf(o, face, queryByShape = false, wholeAddress = false) {
4789
5031
  };
4790
5032
  }
4791
5033
  export function credentialWashPasses(text, opts = {}) {
4792
- const input = typeof text === 'string' ? text : String(text ?? '');
4793
- const stats = { passes: 0, capped: false };
5034
+ const input = displayInputText(text);
5035
+ const stats = { passes: 0, capped: false, collections: 0 };
4794
5036
  const scan = opts === null ? WASH_OPTIONS : scanOptionsOf(opts, publicFace(opts));
4795
5037
  scanAndMask(input, scan, false, stats);
4796
- return { passes: stats.passes, capped: stats.capped };
5038
+ return { collections: stats.collections, passes: stats.passes, capped: stats.capped };
5039
+ }
5040
+ function displayInputText(text) {
5041
+ switch (typeof text) {
5042
+ case 'string':
5043
+ return text;
5044
+ case 'number':
5045
+ case 'bigint':
5046
+ case 'boolean':
5047
+ return String(text);
5048
+ case 'undefined':
5049
+ return '';
5050
+ default:
5051
+ return text === null ? '' : REDACTED_SECRET;
5052
+ }
4797
5053
  }
4798
5054
  function renderUntrusted(text, opts, ledger, queryByShape = false, wholeAddress = false) {
4799
- const input = typeof text === 'string' ? text : String(text ?? '');
5055
+ const input = displayInputText(text);
4800
5056
  const o = opts ?? {};
4801
5057
  const face = publicFace(o);
4802
5058
  const scan = scanOptionsOf(o, face, queryByShape, wholeAddress);
@@ -4995,51 +5251,74 @@ function hasOpaquePiece(text) {
4995
5251
  }
4996
5252
  return false;
4997
5253
  }
4998
- function carriesCredentialStructure(value) {
4999
- const netChanges = (v) => {
5000
- if (washCredentials(v) !== v || hasOpaquePiece(v))
5001
- return true;
5002
- if (!v.includes('+'))
5003
- return false;
5004
- const spaced = v.split('+').join(' ');
5005
- return washCredentials(spaced) !== spaced;
5254
+ function carriesCredentialStructurePhases(value) {
5255
+ const all = phaseRange(0, scanUpto);
5256
+ let hit = 0;
5257
+ const net = (v) => {
5258
+ if (hasOpaquePiece(v)) {
5259
+ hit = all;
5260
+ return;
5261
+ }
5262
+ hit |= washChangesPhases(v);
5263
+ if (hit === all || !v.includes('+'))
5264
+ return;
5265
+ hit |= washChangesPhases(v.split('+').join(' '));
5006
5266
  };
5007
5267
  let view = value;
5008
5268
  for (let layer = 0; layer < MAX_PERCENT_VIEW_LAYERS; layer++) {
5009
- if (netChanges(view))
5010
- return true;
5269
+ net(view);
5270
+ if (hit === all)
5271
+ return all;
5011
5272
  const next = percentDecodeForMatching(view).decoded;
5012
5273
  if (next === view)
5013
- return false;
5274
+ return hit;
5014
5275
  view = next;
5015
5276
  }
5016
- if (netChanges(view))
5017
- return true;
5018
- return percentDecodeForMatching(view).decoded !== view;
5277
+ net(view);
5278
+ if (hit === all)
5279
+ return all;
5280
+ return percentDecodeForMatching(view).decoded !== view ? all : hit;
5281
+ }
5282
+ function washChangesPhases(v) {
5283
+ const ends = [];
5284
+ scanAndMask(v, WASH_OPTIONS, false, undefined, ends);
5285
+ let mask = 0;
5286
+ ends.forEach((t, k) => {
5287
+ if (t.text !== v)
5288
+ mask |= 1 << k;
5289
+ });
5290
+ return mask;
5019
5291
  }
5020
- function queryPartCarriesCredential(key, value, relax) {
5292
+ function queryPartPhases(key, value, relax) {
5293
+ const all = phaseRange(0, scanUpto);
5021
5294
  if (value.length === 0 || isMarker(value))
5022
- return false;
5295
+ return 0;
5023
5296
  if (key !== null) {
5024
5297
  if (isCredentialLabelName(key))
5025
- return true;
5298
+ return all;
5026
5299
  if (relax && isNonSecretFieldName(key) && isReadableWordChain(value))
5027
- return isKnownSecretSegment(value);
5300
+ return isKnownSecretSegment(value) ? all : 0;
5028
5301
  }
5029
- if (isOpaqueSecretValue(value) || carriesCredentialStructure(value))
5030
- return true;
5302
+ if (isOpaqueSecretValue(value))
5303
+ return all;
5304
+ let hit = carriesCredentialStructurePhases(value);
5305
+ if (hit === all)
5306
+ return all;
5031
5307
  let from = 0;
5032
5308
  for (let i = 0; i <= value.length; i++) {
5033
5309
  if (i < value.length && !endsUrlFragmentAt(value, i))
5034
5310
  continue;
5035
5311
  if (i > from && (from > 0 || i < value.length)) {
5036
5312
  const chunk = value.slice(from, i);
5037
- if (isOpaqueSecretValue(chunk) || carriesCredentialStructure(chunk))
5038
- return true;
5313
+ if (isOpaqueSecretValue(chunk))
5314
+ return all;
5315
+ hit |= carriesCredentialStructurePhases(chunk);
5316
+ if (hit === all)
5317
+ return all;
5039
5318
  }
5040
5319
  from = i + 1;
5041
5320
  }
5042
- return false;
5321
+ return hit;
5043
5322
  }
5044
5323
  const UPLOAD_PROBE_PREFIX = 'https://q.invalid/?';
5045
5324
  const UPLOAD_SCAN_OPTIONS = Object.freeze({ urls: true, queryByShape: 'strict', wholeAddress: true, words: true, blanks: false, fold: false, face: null, escapeSpaces: null });
@@ -5047,27 +5326,421 @@ export function queryCarriesCredential(query) {
5047
5326
  if (typeof query !== 'string' || query.length === 0)
5048
5327
  return false;
5049
5328
  const probe = `${UPLOAD_PROBE_PREFIX}${query}`;
5050
- return scanOnce(probe, UPLOAD_SCAN_OPTIONS, false).text !== probe;
5329
+ return scanAndMask(probe, UPLOAD_SCAN_OPTIONS, false).text !== probe;
5051
5330
  }
5052
5331
  export function displayConfiguredUrl(url, opts = {}) {
5053
- const out = renderUntrusted(typeof url === 'string' ? configuredUrlScannable(url) : url, opts, null, 'relaxed', true);
5332
+ const reading = typeof url === 'string' ? configuredAddressReading(url) : null;
5333
+ const out = renderUntrusted(reading === null ? url : reading.withheld ? REDACTED_SECRET : reading.text, opts, null, 'relaxed', true);
5054
5334
  return typeof out === 'string' ? out : out.text;
5055
5335
  }
5056
- function configuredUrlScannable(url) {
5057
- const view = url.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '');
5058
- if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(view) || !/^[A-Za-z][A-Za-z0-9+.-]*:/.test(view))
5059
- return url;
5336
+ function parserViewOf(url) {
5337
+ return url.replace(/[\t\n\r]/g, '').replace(/^[\u0000-\u0020]+/, '');
5338
+ }
5339
+ const AMBIGUOUS_USERINFO_BOUNDARY = /^(?:[A-Za-z][A-Za-z0-9+.-]*:\/\/|(?:https?|wss?|ftp):[\\/]*)[^/\\?#@]*[?#][^@]*@/i;
5340
+ function userinfoBoundaryAmbiguous(view) {
5341
+ return AMBIGUOUS_USERINFO_BOUNDARY.test(view);
5342
+ }
5343
+ function authorityHasBackslash(view) {
5344
+ const scheme = /^(?:https?|wss?|ftp|file):\/\//i.exec(view);
5345
+ if (scheme === null)
5346
+ return false;
5347
+ const from = scheme[0].length;
5348
+ let end = from;
5349
+ while (end < view.length && !'/?#'.includes(view.charAt(end)))
5350
+ end++;
5351
+ const marks = markerSpans(view);
5352
+ for (let s = 0; s < marks.length; s += 2)
5353
+ if ((marks[s] ?? end) >= from && (marks[s] ?? end) < end)
5354
+ return false;
5355
+ let backslash = false;
5356
+ for (let k = from; k < end; k++) {
5357
+ if (view.charAt(k) !== '\\')
5358
+ continue;
5359
+ if (outletAtomEnd(view, k) > k)
5360
+ return false;
5361
+ backslash = true;
5362
+ }
5363
+ return backslash;
5364
+ }
5365
+ function outletAtomEnd(text, at) {
5366
+ return escapeTokenAt(text, at)?.end ?? (nameBlankTokenAt(text, at) !== -1 ? at + 6 : -1);
5367
+ }
5368
+ function rebuiltAddress(url) {
5060
5369
  let u;
5061
5370
  try {
5062
5371
  u = new URL(url);
5063
5372
  }
5064
5373
  catch {
5065
- return url;
5374
+ return null;
5066
5375
  }
5067
5376
  if (u.host === '')
5068
- return url;
5377
+ return null;
5069
5378
  const userinfo = u.username !== '' || u.password !== '' ? `${u.username}${u.password !== '' ? `:${u.password}` : ''}@` : '';
5070
- return `${u.protocol}//${userinfo}${u.host}${u.pathname}${u.search}${u.hash}`;
5379
+ const text = `${u.protocol}//${userinfo}${u.host}${u.pathname}${u.search}${u.hash}`;
5380
+ const fwdA = new Int32Array(url.length).fill(-2);
5381
+ const fwdB = new Int32Array(url.length).fill(-2);
5382
+ const backA = new Int32Array(text.length).fill(-2);
5383
+ const backB = new Int32Array(text.length).fill(-2);
5384
+ const parts = new Int8Array(url.length);
5385
+ const out = { text, toRebuilt: [fwdA, fwdB], toOriginal: [backA, backB], parts };
5386
+ const encoder = new TextEncoder();
5387
+ const at = [];
5388
+ for (let i = 0; i < url.length; i++) {
5389
+ const c = url.charCodeAt(i);
5390
+ if (c === 0x09 || c === 0x0a || c === 0x0d)
5391
+ fwdA[i] = -1;
5392
+ else
5393
+ at.push(i);
5394
+ }
5395
+ let a = 0;
5396
+ let b = at.length;
5397
+ while (a < b && url.charCodeAt(at[a] ?? 0) <= 0x20)
5398
+ fwdA[at[a++] ?? 0] = -1;
5399
+ while (b > a && url.charCodeAt(at[b - 1] ?? 0) <= 0x20)
5400
+ fwdA[at[--b] ?? 0] = -1;
5401
+ const ch = (k) => url.charAt(at[k] ?? -1);
5402
+ let e = a;
5403
+ while (e < b && ch(e) !== ':')
5404
+ e++;
5405
+ const scheme = at.slice(a, e).map((i) => url.charAt(i)).join('');
5406
+ if (e === b || !/^(?:https?|wss?|ftp)$/i.test(scheme))
5407
+ return out;
5408
+ const align = (from, to, rFrom, rTo, mode, part) => {
5409
+ const pairs = [];
5410
+ let r = rFrom;
5411
+ for (let k = from; k < to;) {
5412
+ const i = at[k] ?? -1;
5413
+ const cp = url.codePointAt(i) ?? 0;
5414
+ const wide = cp > 0xffff && k + 1 < to && at[k + 1] === i + 1;
5415
+ const c = wide ? String.fromCodePoint(cp) : url.charAt(i);
5416
+ const step = c.length;
5417
+ let len = -1;
5418
+ if (text.startsWith(c, r))
5419
+ len = c.length;
5420
+ else if (mode === 'fold' && c.length === 1 && /[A-Z]/.test(c) && text.charAt(r) === c.toLowerCase())
5421
+ len = 1;
5422
+ else if (mode === 'path' && c === '\\' && text.charAt(r) === '/')
5423
+ len = 1;
5424
+ else if (mode === 'encoded' || mode === 'path') {
5425
+ const enc = [...encoder.encode(c)].map((x) => `%${x.toString(16).toUpperCase().padStart(2, '0')}`).join('');
5426
+ if (text.startsWith(enc, r))
5427
+ len = enc.length;
5428
+ }
5429
+ if (len < 0 || r + len > rTo)
5430
+ return;
5431
+ pairs.push(i, step, r, r + len);
5432
+ r += len;
5433
+ k += step;
5434
+ }
5435
+ if (r !== rTo)
5436
+ return;
5437
+ for (let p = 0; p < pairs.length; p += 4) {
5438
+ const i = pairs[p] ?? -1;
5439
+ const n = pairs[p + 1] ?? 1;
5440
+ const s = pairs[p + 2] ?? 0;
5441
+ const t = pairs[p + 3] ?? 0;
5442
+ for (let j = i; j < i + n; j++) {
5443
+ fwdA[j] = s;
5444
+ fwdB[j] = t;
5445
+ parts[j] = part;
5446
+ }
5447
+ backA.fill(i, s, t);
5448
+ backB.fill(i + n, s, t);
5449
+ }
5450
+ };
5451
+ const drop = (from, to) => {
5452
+ for (let k = from; k < to; k++)
5453
+ fwdA[at[k] ?? -1] = -1;
5454
+ };
5455
+ const insert = (rFrom, rTo) => {
5456
+ backA.fill(-1, rFrom, rTo);
5457
+ };
5458
+ align(a, e + 1, 0, u.protocol.length, 'fold', 1);
5459
+ let k = e + 1;
5460
+ while (k < b && (ch(k) === '/' || ch(k) === '\\'))
5461
+ k++;
5462
+ drop(e + 1, k);
5463
+ insert(u.protocol.length, u.protocol.length + 2);
5464
+ let r = u.protocol.length + 2;
5465
+ let end = k;
5466
+ while (end < b && !'/\\?#'.includes(ch(end)))
5467
+ end++;
5468
+ let lastAt = -1;
5469
+ for (let j = k; j < end; j++)
5470
+ if (ch(j) === '@')
5471
+ lastAt = j;
5472
+ if (lastAt >= 0) {
5473
+ let colon = lastAt;
5474
+ for (let j = k; j < lastAt; j++)
5475
+ if (ch(j) === ':') {
5476
+ colon = j;
5477
+ break;
5478
+ }
5479
+ align(k, colon, r, r + u.username.length, 'encoded', 2);
5480
+ r += u.username.length;
5481
+ if (colon < lastAt) {
5482
+ if (u.password !== '') {
5483
+ align(colon, colon + 1, r, r + 1, 'exact', 2);
5484
+ r += 1;
5485
+ }
5486
+ else
5487
+ drop(colon, colon + 1);
5488
+ align(colon + 1, lastAt, r, r + u.password.length, 'encoded', 2);
5489
+ r += u.password.length;
5490
+ }
5491
+ if (userinfo !== '') {
5492
+ align(lastAt, lastAt + 1, r, r + 1, 'exact', 3);
5493
+ r += 1;
5494
+ }
5495
+ else
5496
+ drop(lastAt, lastAt + 1);
5497
+ k = lastAt + 1;
5498
+ }
5499
+ let portColon = end;
5500
+ let bracket = false;
5501
+ for (let j = k; j < end; j++) {
5502
+ const c = ch(j);
5503
+ if (c === '[')
5504
+ bracket = true;
5505
+ else if (c === ']')
5506
+ bracket = false;
5507
+ else if (c === ':' && !bracket) {
5508
+ portColon = j;
5509
+ break;
5510
+ }
5511
+ }
5512
+ align(k, portColon, r, r + u.hostname.length, 'fold', 4);
5513
+ r += u.hostname.length;
5514
+ if (portColon < end) {
5515
+ if (u.port !== '') {
5516
+ align(portColon, end, r, r + 1 + u.port.length, 'exact', 5);
5517
+ r += 1 + u.port.length;
5518
+ }
5519
+ else
5520
+ drop(portColon, end);
5521
+ }
5522
+ let q = end;
5523
+ if (q < b && (ch(q) === '/' || ch(q) === '\\'))
5524
+ while (q < b && ch(q) !== '?' && ch(q) !== '#')
5525
+ q++;
5526
+ if (q === end)
5527
+ insert(r, r + u.pathname.length);
5528
+ else
5529
+ align(end, q, r, r + u.pathname.length, 'path', 6);
5530
+ r += u.pathname.length;
5531
+ let h = q;
5532
+ if (h < b && ch(h) === '?')
5533
+ while (h < b && ch(h) !== '#')
5534
+ h++;
5535
+ if (h - q === 1)
5536
+ drop(q, h);
5537
+ else if (h > q)
5538
+ align(q, h, r, r + u.search.length, 'encoded', 7);
5539
+ r += u.search.length;
5540
+ if (b - h === 1)
5541
+ drop(h, b);
5542
+ else if (b > h)
5543
+ align(h, b, r, r + u.hash.length, 'encoded', 8);
5544
+ return out;
5545
+ }
5546
+ function restoreAtoms(url, c) {
5547
+ const atoms = [];
5548
+ const marks = markerSpans(url);
5549
+ for (let s = 0; s + 1 < marks.length; s += 2)
5550
+ atoms.push([marks[s] ?? 0, marks[s + 1] ?? 0]);
5551
+ for (let i = url.indexOf('\\'); i >= 0; i = url.indexOf('\\', i + 1)) {
5552
+ const end = outletAtomEnd(url, i);
5553
+ if (end > i)
5554
+ atoms.push([i, end]);
5555
+ }
5556
+ atoms.sort((x, y) => x[0] - y[0]);
5557
+ const [fwdA, fwdB] = c.toRebuilt;
5558
+ const [backA, backB] = c.toOriginal;
5559
+ const swaps = [];
5560
+ for (const [from, to] of atoms) {
5561
+ let ok = (fwdA[from] ?? -1) >= 0;
5562
+ for (let i = from; ok && i + 1 < to; i++)
5563
+ ok = (fwdA[i + 1] ?? -1) >= 0 && fwdB[i] === fwdA[i + 1];
5564
+ if (ok)
5565
+ swaps.push(fwdA[from] ?? 0, fwdB[to - 1] ?? 0, from, to);
5566
+ }
5567
+ if (swaps.length === 0)
5568
+ return c;
5569
+ const old = c.text;
5570
+ const shift = new Int32Array(old.length + 1);
5571
+ let text = '';
5572
+ const nA = [];
5573
+ const nB = [];
5574
+ let w = 0;
5575
+ for (let k = 0; k <= old.length;) {
5576
+ if (w < swaps.length && k === swaps[w]) {
5577
+ const rTo = swaps[w + 1] ?? k;
5578
+ const from = swaps[w + 2] ?? 0;
5579
+ const to = swaps[w + 3] ?? 0;
5580
+ shift[k] = text.length;
5581
+ for (let j = k + 1; j < rTo; j++)
5582
+ shift[j] = -1;
5583
+ for (let i = from; i < to; i++) {
5584
+ fwdA[i] = text.length;
5585
+ fwdB[i] = text.length + 1;
5586
+ nA.push(i);
5587
+ nB.push(i + 1);
5588
+ text += url.charAt(i);
5589
+ }
5590
+ w += 4;
5591
+ k = rTo;
5592
+ continue;
5593
+ }
5594
+ shift[k] = text.length;
5595
+ if (k < old.length) {
5596
+ nA.push(backA[k] ?? -2);
5597
+ nB.push(backB[k] ?? -2);
5598
+ text += old.charAt(k);
5599
+ }
5600
+ k++;
5601
+ }
5602
+ const inSwap = new Uint8Array(url.length);
5603
+ for (let s = 0; s < swaps.length; s += 4)
5604
+ inSwap.fill(1, swaps[s + 2], swaps[s + 3]);
5605
+ for (let i = 0; i < url.length; i++) {
5606
+ if (inSwap[i] === 1 || (fwdA[i] ?? -2) < 0)
5607
+ continue;
5608
+ fwdA[i] = shift[fwdA[i] ?? 0] ?? -2;
5609
+ fwdB[i] = shift[fwdB[i] ?? 0] ?? -2;
5610
+ }
5611
+ return { text, toRebuilt: [fwdA, fwdB], toOriginal: [Int32Array.from(nA), Int32Array.from(nB)], parts: c.parts };
5612
+ }
5613
+ function hiddenUnits(text, queryByShape) {
5614
+ const r = scanAndMask(text, scanOptionsOf({}, publicFace({}), queryByShape, true), true);
5615
+ const hidden = new Uint8Array(text.length);
5616
+ if (r.text !== text) {
5617
+ if (r.origin === undefined)
5618
+ return null;
5619
+ hidden.fill(1);
5620
+ for (const k of r.origin)
5621
+ if (k >= 0)
5622
+ hidden[k] = 0;
5623
+ }
5624
+ return hidden;
5625
+ }
5626
+ function coversHidden(from, to, image) {
5627
+ for (let i = 0; i < from.length; i++) {
5628
+ if (from[i] !== 1)
5629
+ continue;
5630
+ const s = image[0][i] ?? -2;
5631
+ if (s === -1)
5632
+ continue;
5633
+ if (s === -2)
5634
+ return false;
5635
+ for (let k = s; k < (image[1][i] ?? s); k++)
5636
+ if (to[k] !== 1)
5637
+ return false;
5638
+ }
5639
+ return true;
5640
+ }
5641
+ const CONFIGURED_ADDRESS_WITHHELD = Object.freeze({ withheld: true });
5642
+ function rebuiltKeepsRuns(url, rebuilt, hidden) {
5643
+ const [fwdA, fwdB] = rebuilt.toRebuilt;
5644
+ for (let i = 0; i < url.length;) {
5645
+ if (hidden[i] !== 1) {
5646
+ i++;
5647
+ continue;
5648
+ }
5649
+ let j = i;
5650
+ while (j < url.length && hidden[j] === 1)
5651
+ j++;
5652
+ const s = fwdA[i] ?? -2;
5653
+ const t = fwdB[j - 1] ?? -2;
5654
+ if (s < 0 || t - s !== j - i || rebuilt.text.slice(s, t) !== url.slice(i, j))
5655
+ return false;
5656
+ for (let k = i; k < j; k++)
5657
+ if (rebuilt.parts[k] !== rebuilt.parts[i])
5658
+ return false;
5659
+ i = j;
5660
+ }
5661
+ return true;
5662
+ }
5663
+ function readingOf(url, rebuilt, n1) {
5664
+ if (!n1) {
5665
+ const hidden = hiddenUnits(url, 'strict');
5666
+ return hidden !== null && rebuiltKeepsRuns(url, rebuilt, hidden) ? { withheld: false, text: rebuilt.text } : CONFIGURED_ADDRESS_WITHHELD;
5667
+ }
5668
+ let original = true;
5669
+ let viaRebuilt = true;
5670
+ for (const mode of ['relaxed', 'strict']) {
5671
+ const hiddenOriginal = hiddenUnits(url, mode);
5672
+ const hiddenRebuilt = hiddenUnits(rebuilt.text, mode);
5673
+ if (hiddenOriginal === null || hiddenRebuilt === null)
5674
+ return CONFIGURED_ADDRESS_WITHHELD;
5675
+ original &&= coversHidden(hiddenRebuilt, hiddenOriginal, rebuilt.toOriginal);
5676
+ viaRebuilt &&= coversHidden(hiddenOriginal, hiddenRebuilt, rebuilt.toRebuilt);
5677
+ }
5678
+ return original ? { withheld: false, text: url } : viaRebuilt ? { withheld: false, text: rebuilt.text } : CONFIGURED_ADDRESS_WITHHELD;
5679
+ }
5680
+ function configuredAddressReading(url) {
5681
+ const view = parserViewOf(url);
5682
+ if (userinfoBoundaryAmbiguous(view))
5683
+ return CONFIGURED_ADDRESS_WITHHELD;
5684
+ if (!/^[A-Za-z][A-Za-z0-9+.-]*:/.test(view))
5685
+ return { withheld: false, text: url };
5686
+ if (/^[A-Za-z][A-Za-z0-9+.-]*:\/\//.test(view)) {
5687
+ const n1 = authorityHasBackslash(view) ? rebuiltAddress(url) : null;
5688
+ return n1 === null ? { withheld: false, text: url } : readingOf(url, restoreAtoms(url, n1), true);
5689
+ }
5690
+ const rebuilt = rebuiltAddress(url);
5691
+ if (rebuilt !== null)
5692
+ return readingOf(url, rebuilt, false);
5693
+ if (/^(?:https?|wss?|ftp):/i.test(view) && /[@?#]/.test(view) && !schemelessUserinfoLeads(view))
5694
+ return CONFIGURED_ADDRESS_WITHHELD;
5695
+ return { withheld: false, text: url };
5696
+ }
5697
+ function schemelessUserinfoLeads(view) {
5698
+ const V = buildScanView(view, WASH_OPTIONS);
5699
+ const S = separatedSurface(view, V);
5700
+ const out = [];
5701
+ userinfoCandidates(S, out, true);
5702
+ return out.some((c) => c.kind === 'userinfo' && S.rawStart(c.a) === 0);
5703
+ }
5704
+ export function configuredValueCarriesCredential(value) {
5705
+ if (typeof value !== 'string' || value.length === 0)
5706
+ return false;
5707
+ const reading = configuredAddressReading(value);
5708
+ if (reading.withheld)
5709
+ return true;
5710
+ const text = reading.text;
5711
+ const r = scanAndMask(text, scanOptionsOf({}, publicFace({}), 'strict', true), true);
5712
+ const origin = r.origin;
5713
+ if (r.text === text || origin === undefined)
5714
+ return false;
5715
+ let prevVisible = -1;
5716
+ for (let k = 0; k < r.text.length;) {
5717
+ const o = origin[k] ?? -1;
5718
+ if (o >= 0) {
5719
+ prevVisible = o;
5720
+ k++;
5721
+ continue;
5722
+ }
5723
+ let e = k;
5724
+ while (e < r.text.length && (origin[e] ?? -1) < 0)
5725
+ e++;
5726
+ const hiddenStart = prevVisible + 1;
5727
+ const hiddenEnd = e < r.text.length ? (origin[e] ?? text.length) : text.length;
5728
+ const shown = r.text.slice(k, e);
5729
+ const re = new RegExp(MARKER_ANYWHERE.source, MARKER_ANYWHERE.flags);
5730
+ for (let m = re.exec(shown); m !== null; m = re.exec(shown)) {
5731
+ const kind = KIND_BY_TEXT.get(m[0]);
5732
+ if (kind === 'userinfo' || kind === 'query' || kind === 'secret')
5733
+ return true;
5734
+ if (kind === 'fragment') {
5735
+ if (hiddenStart === 0 || text[hiddenStart - 1] !== '#')
5736
+ return true;
5737
+ if (queryCarriesCredential(text.slice(hiddenStart, hiddenEnd)))
5738
+ return true;
5739
+ }
5740
+ }
5741
+ k = e;
5742
+ }
5743
+ return false;
5071
5744
  }
5072
5745
  export function quoteUntrustedForSentence(text, max) {
5073
5746
  const shown = displayUntrusted(text, { max, blanks: true, escapeBackslashes: true });