@ladderkitlab/hub-kb 0.4.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/main.js CHANGED
@@ -297,6 +297,7 @@ function continuationStarts(t, n, pos) {
297
297
  if (isWs(c) || CJK_STOPS.includes(c) || c === "&" || c === "#" || c === ";") return false;
298
298
  return !isCloser(c) || isAsciiAlnum(t[pos + 1]);
299
299
  }
300
+ var isWrapQuote = (c) => c === '"' || c === "'";
300
301
  function readWrapped(s, open) {
301
302
  const t = s.text;
302
303
  const close = t[open] === "<" ? ">" : t[open];
@@ -352,19 +353,19 @@ function readAdjacent(s, pos, continuation = false) {
352
353
  }
353
354
  function readSegmentAt(s, pos, continuation = false) {
354
355
  const c = s.text[pos];
355
- return c === '"' || c === "'" || c === "<" ? readWrapped(s, pos) : readAdjacent(s, pos, continuation);
356
+ return isWrapQuote(c) || c === "<" ? readWrapped(s, pos) : readAdjacent(s, pos, continuation);
356
357
  }
357
- function isContent(s, raw) {
358
+ function isContent(s, raw, depth = 1) {
358
359
  s.budget.tick(raw.length + 1);
359
- let decoded;
360
+ let decoded = raw.replace(/\+/g, " ");
360
361
  try {
361
- decoded = decodeURIComponent(raw.replace(/\+/g, " "));
362
+ for (let d = 0; d < depth; d++) decoded = decodeURIComponent(decoded);
362
363
  } catch {
363
364
  return true;
364
365
  }
365
366
  return decoded.trim() !== "";
366
367
  }
367
- function hasRawValueContent(s, start, end) {
368
+ function hasRawValueContent(s, start, end, depth = 1) {
368
369
  let raw = "";
369
370
  for (let k = start; k < end; ) {
370
371
  s.budget.tick();
@@ -372,7 +373,7 @@ function hasRawValueContent(s, start, end) {
372
373
  if (a > 0 && k + a <= end) k += a;
373
374
  else raw += s.text[k++];
374
375
  }
375
- return s.budget.exhausted || isContent(s, raw);
376
+ return s.budget.exhausted || isContent(s, raw, depth);
376
377
  }
377
378
  function interpretUrlValue(s, start) {
378
379
  const cached = s.values.get(start);
@@ -387,7 +388,7 @@ function interpretUrlValue(s, start) {
387
388
  let j = start;
388
389
  while (j < n && isInlineWs(t[j])) j++;
389
390
  s.budget.tick(j - start + 1);
390
- if (j < n && (t[j] === '"' || t[j] === "'")) {
391
+ if (j < n && isWrapQuote(t[j])) {
391
392
  seg = readWrapped(s, j);
392
393
  wide = true;
393
394
  } else if (t[start] === "<") seg = readWrapped(s, start);
@@ -506,6 +507,30 @@ function compoundNameStart(text, nameStart, s) {
506
507
  }
507
508
  return start;
508
509
  }
510
+ function compoundNameEnd(text, from, s) {
511
+ let end = from;
512
+ for (; ; ) {
513
+ let j = end;
514
+ while (j < text.length && isConnector(text[j])) j++;
515
+ let k = j;
516
+ while (k < text.length && isAlnumAt(text, k)) k++;
517
+ s?.budget.tick(k - end + 1);
518
+ if (k === j) return end;
519
+ end = k;
520
+ }
521
+ }
522
+ function separatorEndAfterName(s, nameEnd) {
523
+ const t = s.text;
524
+ const n = s.n;
525
+ let j = nameEnd;
526
+ while (j < n && isWs(t[j])) j++;
527
+ if (j < n && (t[j] === '"' || t[j] === "'")) {
528
+ j++;
529
+ while (j < n && isWs(t[j])) j++;
530
+ }
531
+ s.budget.tick(j - nameEnd + 1);
532
+ return j < n && SEPARATORS.includes(t[j]) ? j + 1 : -1;
533
+ }
509
534
  var URL_NAME_SET = new Set(URL_CREDENTIAL_NAMES);
510
535
  var COMPOUND_URL_NAME = new RegExp(`^_*(?:[a-z0-9]+[_.\\-]+)*(${[...PROSE_CREDENTIAL_NAMES, PROSE_KEY_NAME].join("|")})$`);
511
536
  function decodeOnce(s) {
@@ -593,18 +618,11 @@ function proseNamedHits(s) {
593
618
  i = end2;
594
619
  continue;
595
620
  }
596
- let j = nameEnd;
597
- while (j < n && isWs(t[j])) j++;
598
- if (j < n && (t[j] === '"' || t[j] === "'")) {
599
- j++;
600
- while (j < n && isWs(t[j])) j++;
601
- }
602
- s.budget.tick(j - nameEnd + 1);
603
- if (j >= n || !SEPARATORS.includes(t[j])) {
621
+ const sepEnd = separatorEndAfterName(s, nameEnd);
622
+ if (sepEnd < 0) {
604
623
  i++;
605
624
  continue;
606
625
  }
607
- const sepEnd = j + 1;
608
626
  let headEnd;
609
627
  if (name === PROSE_KEY_NAME) {
610
628
  headEnd = keyThresholdEnd(s, sepEnd);
@@ -659,7 +677,7 @@ function proseNamedHits(s) {
659
677
  }
660
678
  const start = compoundNameStart(t, runStart, s);
661
679
  let urlEnd = -1;
662
- if (t[j] === "=" && j === nameEnd && inUrlParamName(s, start)) {
680
+ if (sepEnd === nameEnd + 1 && t[nameEnd] === "=" && inUrlParamName(s, start)) {
663
681
  const v = interpretUrlValue(s, sepEnd);
664
682
  if (!hasCredentialValue(v) && v.state !== "none") {
665
683
  i = nameEnd;
@@ -691,6 +709,13 @@ function paramNameEnd(t, at) {
691
709
  }
692
710
  return j > at && t[j] === "=" ? j : -1;
693
711
  }
712
+ function encodedSep(t, i) {
713
+ if (t[i] !== "%") return null;
714
+ const dbl = t[i + 1] === "2" && t[i + 2] === "5";
715
+ const code = t.slice(dbl ? i + 3 : i + 1, dbl ? i + 5 : i + 3).toUpperCase();
716
+ const ch = code === "3F" ? "?" : code === "26" ? "&" : code === "23" ? "#" : code === "3D" ? "=" : "";
717
+ return ch ? { ch, len: dbl ? 5 : 3 } : null;
718
+ }
694
719
  function urlCredentialSpans(s) {
695
720
  if (s.urlcred) return s.urlcred;
696
721
  const t = s.text;
@@ -704,33 +729,34 @@ function urlCredentialSpans(s) {
704
729
  }
705
730
  const c = t[i];
706
731
  if (c === "%" && i + 2 < n) {
707
- const head = t.slice(i, i + 3).toUpperCase();
708
- if (head === "%3F" || head === "%26" || head === "%23") {
709
- let j = i + 3;
732
+ const head = encodedSep(t, i);
733
+ if (head && head.ch !== "=") {
734
+ let j = i + head.len;
710
735
  while (j < n) {
711
736
  if (PARAM_NAME_CHAR(t, j)) {
712
737
  j++;
713
738
  continue;
714
739
  }
715
- if (t[j] === "%" && isHex(t[j + 1]) && isHex(t[j + 2]) && !["%3D", "%26", "%3F", "%23"].includes(t.slice(j, j + 3).toUpperCase())) {
740
+ if (t[j] === "%" && isHex(t[j + 1]) && isHex(t[j + 2]) && encodedSep(t, j)?.len !== head.len) {
716
741
  j += 3;
717
742
  continue;
718
743
  }
719
744
  break;
720
745
  }
721
746
  s.budget.tick(j - i + 1);
722
- if (j > i + 3 && t.slice(j, j + 3).toUpperCase() === "%3D") {
723
- const cls2 = urlNameClass(t.slice(i + 3, j).replace(/%25/gi, "%"));
747
+ const eq2 = encodedSep(t, j);
748
+ if (j > i + head.len && eq2?.ch === "=") {
749
+ const cls2 = urlNameClass(t.slice(i + head.len, j).replace(/%25/gi, "%"));
724
750
  if (!cls2) {
725
751
  i = j;
726
752
  continue;
727
753
  }
728
- const vStart = j + 3;
754
+ const vStart = j + eq2.len;
729
755
  let k = vStart;
730
- while (k < n && t[k] !== "&" && t[k] !== "#" && !isWs(t[k]) && !(t[k] === "%" && t.slice(k, k + 3).toUpperCase() === "%26")) k++;
756
+ while (k < n && t[k] !== "&" && t[k] !== "#" && !isWs(t[k]) && !(encodedSep(t, k)?.ch === "&" && encodedSep(t, k).len === head.len)) k++;
731
757
  s.budget.tick(k - vStart + 1);
732
- if (k > vStart && hasRawValueContent(s, vStart, k)) {
733
- out.push({ index: cls2 === "url" ? vStart : i + 3, end: k });
758
+ if (k > vStart && hasRawValueContent(s, vStart, k, eq2.len === 5 ? 2 : 1)) {
759
+ out.push({ index: cls2 === "url" ? vStart : i + head.len, end: k });
734
760
  i = k;
735
761
  continue;
736
762
  }
@@ -783,7 +809,8 @@ function httpHeadAt(t, i) {
783
809
  const head = t.slice(i, i + 8).toLowerCase();
784
810
  return head.startsWith("https://") ? 8 : head.startsWith("http://") ? 7 : 0;
785
811
  }
786
- var ENCODED_HEAD = /https?(?::|%(?:25)?3a)(?:\/|%(?:25)?2f)(?:\/|%(?:25)?2f)/iy;
812
+ var SCHEME_SEPARATOR_SRC = String.raw`(?::|%(?:25)?3a)(?:\/|%(?:25)?2f)(?:\/|%(?:25)?2f)`;
813
+ var ENCODED_HEAD = new RegExp(`https?${SCHEME_SEPARATOR_SRC}`, "iy");
787
814
  function encodedHeadAt(t, i) {
788
815
  if (t[i] !== "h" && t[i] !== "H") return 0;
789
816
  ENCODED_HEAD.lastIndex = i;
@@ -798,8 +825,9 @@ function socksHeadAt(t, i) {
798
825
  for (const p of SOCKS_HEADS) if (head.startsWith(p)) return p.length;
799
826
  return 0;
800
827
  }
828
+ var SCHEME_SEPARATOR = new RegExp(SCHEME_SEPARATOR_SRC, "i");
801
829
  function hasSchemeSeparator(text) {
802
- return text.includes("://");
830
+ return SCHEME_SEPARATOR.test(text);
803
831
  }
804
832
  var ENCODED_UNIT = { "3A": ":", "2F": "/", "3F": "?", "23": "#", "40": "@", "5B": "[", "5D": "]", "28": "(", "29": ")" };
805
833
  var ONCE = 1;
@@ -1165,17 +1193,17 @@ function subscriptionSpans(s) {
1165
1193
  continue;
1166
1194
  }
1167
1195
  const end = endAt(i);
1168
- let q = kw;
1196
+ const sep = separatorEndAfterName(s, compoundNameEnd(t, kw, s));
1197
+ let q = sep >= 0 ? sep : kw;
1169
1198
  while (q < end && isInlineWs(t[q])) q++;
1170
- if (q < end && (t[q] === "=" || t[q] === ":" || t[q] === "\uFF1A")) {
1171
- q++;
1172
- while (q < end && isInlineWs(t[q])) q++;
1173
- }
1199
+ const open = q < end && isWrapQuote(t[q]) ? q : -1;
1174
1200
  s.budget.tick(q - kw + 1);
1175
- const link = linkStartingAt(s, q);
1201
+ const link = linkStartingAt(s, open >= 0 ? q + 1 : q);
1176
1202
  let j;
1177
- if (link) j = Math.min(link.displayEnd, end);
1178
- else {
1203
+ if (link) {
1204
+ j = Math.min(link.displayEnd, end);
1205
+ if (open >= 0 && j < end && t[j] === t[open]) j++;
1206
+ } else {
1179
1207
  j = kw;
1180
1208
  while (j < end && !KEYWORD_VALUE_END(t[j]) && !stops.has(j)) j++;
1181
1209
  s.budget.tick(j - kw + 1);
@@ -1270,7 +1298,7 @@ function regexInRuns(isClass, re, minLen) {
1270
1298
  }
1271
1299
  var EMAIL = regexInRuns((x) => isAlnumCode2(x) || x === 46 || x === 95 || x === 37 || x === 43 || x === 45 || x === 64, /(?<![A-Za-z0-9._%+-])[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]+/g, 5);
1272
1300
  var UUID = regexInRuns((x) => isHexCode2(x) || x === 45, /\b[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\b/gi, 36);
1273
- var IPV6 = regexInRuns((x) => isHexCode2(x) || x === 58 || x === 46, /(?<![0-9a-f:])(?:[0-9a-f]{0,4}:){2,}(?:\d{1,3}\.){3}\d{1,3}|(?<![0-9a-f:])(?!(?<=[a-z])::[0-9a-f:]*[g-z])(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}(?![0-9a-f:])/gi, 2);
1301
+ var IPV6 = regexInRuns((x) => isHexCode2(x) || x === 58 || x === 46, /(?<![0-9a-f:])(?:[0-9a-f]{0,4}:){2,}(?:\d{1,3}\.){3}\d{1,3}|(?<![0-9a-f:])(?!(?<=[a-z])::[0-9a-f:]*[g-z])(?:(?=[0-9a-f:]*::)(?:[0-9a-f]{0,4}:){2,7}[0-9a-f]{0,4}|::(?:[0-9a-f]{1,4}:){6}[0-9a-f]{1,4}|(?:[0-9a-f]{1,4}:){7}:|(?:[0-9a-f]{1,4}:){7}[0-9a-f]{1,4})(?![0-9a-f:])/gi, 2);
1274
1302
  var IPV4 = regexInRuns((x) => x >= 48 && x <= 57 || x === 46, /(?<!\d)(?:\d{1,3}\.){3}\d{1,3}(?!\d)/g, 7);
1275
1303
  function inviteSpans(s) {
1276
1304
  if (s.invites) return s.invites;
@@ -1406,8 +1434,8 @@ function isKnownFalsePositive(m) {
1406
1434
  function credentialRules(text) {
1407
1435
  return [...new Set(sensitiveMatches(text).filter((m) => SECRET_RULES.has(m.rule) && !isKnownFalsePositive(m)).map((m) => m.rule))];
1408
1436
  }
1409
- function maskSensitive(text, policy, opts = {}) {
1410
- const ms = sensitiveMatches(text, opts).filter((m) => policy === "exception" || SECRET_RULES.has(m.rule) && !isKnownFalsePositive(m));
1437
+ function maskSensitive(text, policy, opts = {}, keep) {
1438
+ const ms = sensitiveMatches(text, opts).filter((m) => policy === "exception" || SECRET_RULES.has(m.rule) && !isKnownFalsePositive(m) && !keep?.(m));
1411
1439
  return render(text, ms, () => CREDENTIAL_MASK);
1412
1440
  }
1413
1441
 
@@ -1588,6 +1616,7 @@ function partHeadings(segs) {
1588
1616
  starts.push(off);
1589
1617
  off += l.line.length;
1590
1618
  }
1619
+ let lastH1 = "";
1591
1620
  let lastH2 = "";
1592
1621
  let segStart = 0;
1593
1622
  let i = 0;
@@ -1600,8 +1629,18 @@ function partHeadings(segs) {
1600
1629
  while (k < lines.length && starts[k] < segEnd && lines[k].line.trim() === "") k++;
1601
1630
  if (k < lines.length && starts[k] < segEnd && !lines[k].inFence && /^#{1,2} /.test(lines[k].line)) heading = lines[k].line.trim();
1602
1631
  }
1603
- const label = heading ?? (lastH2 ? `${lastH2}\uFF08\u7EED\uFF09` : seg.split("\n").find((l) => l.trim() !== "")?.trim() ?? "");
1604
- for (let k = i; k < lines.length && starts[k] < segEnd; k++) if (starts[k] >= segStart && !lines[k].inFence && lines[k].line.startsWith("## ")) lastH2 = lines[k].line.trim();
1632
+ const firstLine = () => {
1633
+ for (let k = i; k < lines.length && starts[k] < segEnd; k++) {
1634
+ const t = lines[k].line.slice(Math.max(0, segStart - starts[k]), segEnd - starts[k]).trim();
1635
+ if (!lines[k].inFence && !FENCE_OPEN.test(lines[k].line) && t !== "") return t;
1636
+ }
1637
+ return "";
1638
+ };
1639
+ const label = heading ?? (lastH2 ? `${lastH2}\uFF08\u7EED\uFF09` : lastH1 ? `${lastH1}\uFF08\u7EED\uFF09` : firstLine());
1640
+ for (let k = i; k < lines.length && starts[k] < segEnd; k++) if (starts[k] >= segStart && !lines[k].inFence) {
1641
+ if (lines[k].line.startsWith("## ")) lastH2 = lines[k].line.trim();
1642
+ else if (lines[k].line.startsWith("# ")) lastH1 = lines[k].line.trim();
1643
+ }
1605
1644
  segStart = segEnd;
1606
1645
  return clip(label);
1607
1646
  });
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "name": "@ladderkitlab/hub-kb", "version": "0.4.0", "type": "module",
2
+ "name": "@ladderkitlab/hub-kb", "version": "1.0.0", "type": "module",
3
3
  "license": "(MIT AND CC-BY-NC-SA-4.0)", "engines": { "node": ">=22" },
4
4
  "publishConfig": { "access": "public" },
5
5
  "bin": { "hub-kb": "./dist/main.js" }, "files": ["dist/", "snapshot/", "README.md", "LICENSE", "LICENSE-CONTENT"],