turva-llms-txt-validator 0.3.19 → 0.3.20

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -137,10 +137,10 @@ For vulnerability reporting and supported versions, see [SECURITY.md](https://gi
137
137
  GitHub Actions publishes every version from 0.1.3 on with npm trusted publishing, and each one carries a provenance attestation. To check one, install it in an empty directory and ask npm to verify the signatures. Replace the version with the one you want to check.
138
138
 
139
139
  ```sh
140
- mkdir verify-llms && cd verify-llms && npm init -y && npm install turva-llms-txt-validator@0.3.19 --ignore-scripts && npm audit signatures
140
+ mkdir verify-llms && cd verify-llms && npm init -y && npm install turva-llms-txt-validator@0.3.20 --ignore-scripts && npm audit signatures
141
141
  ```
142
142
 
143
- `npm audit signatures` checks the registry signature and the provenance attestation of each installed package that has one. The attestation of this package names three things to compare with what you expect: the repository `github.com/erekola/llms-txt-validator`, the workflow file `.github/workflows/publish.yml` and the commit that produced the tarball. The npm version page shows them under Provenance. Tag `v0.3.19` must point at that same commit, and `gh api repos/erekola/llms-txt-validator/commits/v0.3.19 --jq .sha` prints it. A mismatch is a reason not to use that version. Provenance proves where a release was built and from which commit. It does not prove that the code is safe, so reading the source and the dependencies stays your job.
143
+ `npm audit signatures` checks the registry signature and the provenance attestation of each installed package that has one. The attestation of this package names three things to compare with what you expect: the repository `github.com/erekola/llms-txt-validator`, the workflow file `.github/workflows/publish.yml` and the commit that produced the tarball. The npm version page shows them under Provenance. Tag `v0.3.20` must point at that same commit, and `gh api repos/erekola/llms-txt-validator/commits/v0.3.20 --jq .sha` prints it. A mismatch is a reason not to use that version. Provenance proves where a release was built and from which commit. It does not prove that the code is safe, so reading the source and the dependencies stays your job.
144
144
 
145
145
  ## Hosted version and source
146
146
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "turva-llms-txt-validator",
3
- "version": "0.3.19",
3
+ "version": "0.3.20",
4
4
  "description": "Validate a site's llms.txt structure: eight checks as pass/warn/fail plus two llms.txt v2 discovery checks that read pass or info and never move the summary, JSON output. Same checks as the hosted validator at turva.dev/llms-txt-validator.",
5
5
  "type": "module",
6
6
  "main": "./src/index.mjs",
package/src/index.mjs CHANGED
@@ -544,10 +544,22 @@ function isSetextH1(line, next) {
544
544
  // one, it stays paragraph text) and a link reference definition. Only the later-H1 reading skips
545
545
  // them; the title reading keeps its Tek-560 predicate. QA 2 of Tek-564 measured "---", "<div>" and
546
546
  // "[a]: url" over a run of "=" warning as a second H1 although 0.3.18 passed them.
547
+ const HTML_BLOCK_START = /^ {0,3}<(?:[A-Za-z][A-Za-z0-9-]*(?:[\s\/>]|$)|\/[A-Za-z][A-Za-z0-9-]*(?:[\s>]|$)|!--|\?|![A-Za-z]|!\[CDATA\[)/;
548
+ const LINK_REF_DEF = /^ {0,3}\[[^\]]+\]:/;
549
+ // HTML blocks of CommonMark types 1 to 5 end at a marker, not at a blank line: <pre>, <script>,
550
+ // <style> and <textarea> at "</...>", "<!--" at "-->", "<?" at "?>", "<!X" at ">" and "<![CDATA[" at
551
+ // "]]>". Each entry is [start, end]; the end marker is looked for after the start on the same line.
552
+ const RAW_HTML_BLOCKS = [
553
+ [/^ {0,3}<(?:pre|script|style|textarea)(?:[ \t>]|$)/i, /<\/(?:pre|script|style|textarea)>/i],
554
+ [/^ {0,3}<!--/, /-->/],
555
+ [/^ {0,3}<\?/, /\?>/],
556
+ [/^ {0,3}<!\[CDATA\[/, /\]\]>/],
557
+ [/^ {0,3}<![A-Za-z]/, />/]
558
+ ];
547
559
  function startsOtherBlock(line) {
548
560
  return /^ {0,3}([-*_])(?:[ \t]*\1){2,}[ \t]*$/.test(line)
549
- || /^ {0,3}<(?:[A-Za-z][A-Za-z0-9-]*(?:[\s\/>]|$)|\/[A-Za-z][A-Za-z0-9-]*(?:[\s>]|$)|!--|\?|![A-Za-z]|!\[CDATA\[)/.test(line)
550
- || /^ {0,3}\[[^\]]+\]:/.test(line);
561
+ || HTML_BLOCK_START.test(line)
562
+ || LINK_REF_DEF.test(line);
551
563
  }
552
564
 
553
565
  // Setext H1 lines after the title (Tek-564). A second H1 written as one text line over a run of "="
@@ -557,13 +569,49 @@ function startsOtherBlock(line) {
557
569
  // blank line is a continuation line, so a title over several lines is not read as a heading, and
558
570
  // text that starts with "#" is not an H1 (isSetextText). The underline is returned separately
559
571
  // because it is not a heading and not content.
572
+ // An indented code block is not a paragraph (Tek-565, outside review W46): a line indented four
573
+ // columns or more that does not continue a paragraph is code, so the text after it starts a new
574
+ // paragraph and "Title" over "===" there is a second H1. A four-space line right after paragraph
575
+ // text stays a continuation line (the accepted Tek-564 limit). The exception is a list item: its
576
+ // blank lines and indented lines belong to the item as continuation paragraphs, not as code, so
577
+ // while a list item is open (listCtx) an indented line is read as before. The list context ends at
578
+ // a thematic break or an ATX heading, or at a line with no indent after a blank line. When unsure
579
+ // the function stays in the list context, which is the 0.3.19 reading. A block quote, an HTML block
580
+ // and a link reference definition keep the old reading too, until the next blank line, and an HTML
581
+ // block of type 1 to 5 (<pre>, a comment, <?, <!X, CDATA) until its end marker: an indented line
582
+ // there continues the quote paragraph or the HTML block, or follows a "=" line that is itself
583
+ // paragraph text, and is not code.
560
584
  function laterSetextH1(lines, fenced) {
561
585
  const text = new Array(lines.length).fill(false);
562
586
  const under = new Array(lines.length).fill(false);
563
587
  let open = false;
588
+ let listCtx = false;
589
+ let blankBefore = false;
590
+ let quoteCtx = false;
591
+ let htmlCtx = false;
592
+ let rawEnd = null;
593
+ let rawDone = false;
564
594
  for (let i = 0; i < lines.length; i++) {
565
595
  const l = lines[i];
566
- if (fenced[i] || l.trim() === "") { open = false; continue; }
596
+ if (rawDone) { htmlCtx = false; rawDone = false; }
597
+ if (fenced[i]) { open = false; continue; }
598
+ if (l.trim() === "") { open = false; blankBefore = true; quoteCtx = false; if (!rawEnd) htmlCtx = false; continue; }
599
+ const indented = /^(?: {4}| {0,3}\t)/.test(l);
600
+ if (/^ ?#{1,6}(?:[ \t]|$)/.test(l) || /^ ?([-*_])(?:[ \t]*\1){2,}[ \t]*$/.test(l)) listCtx = false;
601
+ else if (/^ {0,3}(?:[-*+]|\d{1,9}[.)])(?:[ \t]|$)/.test(l)) listCtx = true;
602
+ else if (blankBefore && /^ ?[^ \t]/.test(l)) listCtx = false;
603
+ blankBefore = false;
604
+ if (/^ {0,3}>/.test(l)) quoteCtx = true;
605
+ if (rawEnd) {
606
+ if (rawEnd.test(l)) { rawEnd = null; rawDone = true; }
607
+ } else {
608
+ if (HTML_BLOCK_START.test(l) || LINK_REF_DEF.test(l)) htmlCtx = true;
609
+ for (const [start, end] of RAW_HTML_BLOCKS) {
610
+ const m = start.exec(l);
611
+ if (m) { if (!end.test(l.slice(m[0].length))) rawEnd = end; break; }
612
+ }
613
+ }
614
+ if (!open && !listCtx && !quoteCtx && !htmlCtx && indented) continue;
567
615
  if (!open && !fenced[i + 1] && !startsOtherBlock(l) && isSetextH1(l, lines[i + 1])) { text[i] = true; under[i + 1] = true; i++; continue; }
568
616
  if (open && /^ {0,3}=+[ \t]*$/.test(l)) { open = false; continue; }
569
617
  open = !(/^ {0,3}#{1,6}(?:[ \t]|$)/.test(l) || /^ {0,3}(?:-+|([-*_])(?: *\1){2,})[ \t]*$/.test(l));