turva-llms-txt-validator 0.3.21 → 0.4.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/package.json CHANGED
@@ -1,22 +1,32 @@
1
1
  {
2
2
  "name": "turva-llms-txt-validator",
3
- "version": "0.3.21",
3
+ "version": "0.4.0",
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
- "main": "./src/index.mjs",
7
- "exports": "./src/index.mjs",
6
+ "main": "./dist/src/index.js",
7
+ "types": "./dist/src/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./dist/src/index.d.ts",
11
+ "default": "./dist/src/index.js"
12
+ }
13
+ },
8
14
  "bin": {
9
- "llms-txt-validate": "bin/cli.mjs"
15
+ "llms-txt-validate": "dist/bin/cli.js"
10
16
  },
11
17
  "files": [
12
- "src",
13
- "bin",
18
+ "dist/src/index.js",
19
+ "dist/src/index.d.ts",
20
+ "dist/bin/cli.js",
14
21
  "README.md",
15
22
  "LICENSE",
16
23
  "THIRD-PARTY-NOTICES.md"
17
24
  ],
18
25
  "scripts": {
19
- "test": "node --test"
26
+ "build": "node -e \"import('node:fs').then(function(f){f.rmSync('dist',{recursive:true,force:true})})\" && node node_modules/typescript/bin/tsc -p tsconfig.json",
27
+ "typecheck": "node node_modules/typescript/bin/tsc -p tsconfig.json --noEmit",
28
+ "test": "npm run build && node --test \"dist/test/**/*.test.js\"",
29
+ "prepack": "npm run build"
20
30
  },
21
31
  "engines": {
22
32
  "node": ">=22"
@@ -38,5 +48,9 @@
38
48
  "homepage": "https://turva.dev/llms-txt-validator",
39
49
  "bugs": {
40
50
  "url": "https://github.com/erekola/llms-txt-validator/issues"
51
+ },
52
+ "devDependencies": {
53
+ "@types/node": "^22.20.5",
54
+ "typescript": "^7.0.2"
41
55
  }
42
56
  }
package/bin/cli.mjs DELETED
@@ -1,95 +0,0 @@
1
- #!/usr/bin/env node
2
- // CLI for turva-llms-txt-validator. Exit codes: 0 = valid (or valid with
3
- // warnings), 1 = not valid (or warnings with --strict), 2 = could not fetch
4
- // or bad input. The two v2 discovery checks and the input-path note carry status
5
- // "info" and move no exit code, by design. Same checks as the hosted validator, and the same
6
- // JSON shape for a completed validation:
7
- // curl -H "Accept: application/json" "https://turva.dev/llms-txt-validator?url=example.com"
8
- import { readFileSync } from "node:fs";
9
- import { fileURLToPath } from "node:url";
10
- import { validateHost, stripControls, maskLocation } from "../src/index.mjs";
11
-
12
- const USAGE = [
13
- "usage: llms-txt-validate <domain-or-url> [--json] [--strict]",
14
- " --json print the result as JSON (same shape as the hosted validator for a completed validation)",
15
- " --strict exit 1 on warnings too, for CI gates",
16
- " --help print this help",
17
- " --version print the installed version and exit"
18
- ];
19
- const KNOWN = new Set(["--json", "--strict", "--help", "-h", "--version", "-V"]);
20
-
21
- // The arguments are read strictly since 0.3.8 (Tek-496). An unknown flag such as a misspelled
22
- // --strcit used to be ignored, so a CI step ran without the gate it asked for, and a second
23
- // target was dropped without a word. Both now stop with exit 2, and every input error is JSON
24
- // when --json was given, the missing target included.
25
- const args = process.argv.slice(2);
26
- const json = args.includes("--json");
27
- const flags = args.filter((a) => a.startsWith("-") && a !== "-");
28
- const targets = args.filter((a) => !(a.startsWith("-") && a !== "-"));
29
-
30
- function fail(message) {
31
- if (json) console.log(JSON.stringify({ error: message }, null, 2));
32
- else {
33
- console.error("error: " + message);
34
- console.error(USAGE[0]);
35
- }
36
- process.exit(2);
37
- }
38
-
39
- if (flags.includes("--help") || flags.includes("-h")) {
40
- for (const line of USAGE) console.log(line);
41
- process.exit(0);
42
- }
43
- if (flags.includes("--version") || flags.includes("-V")) {
44
- // Read package.json relative to this file, not the caller's cwd, so this works the same
45
- // whether the CLI runs installed, linked or through npx (V-04, outside audit 2026-09-26).
46
- // No network read: the version is the one actually installed, not the npm registry's latest.
47
- const pkgUrl = new URL("../package.json", import.meta.url);
48
- const pkg = JSON.parse(readFileSync(fileURLToPath(pkgUrl), "utf8"));
49
- console.log(pkg.version);
50
- process.exit(0);
51
- }
52
- const unknown = flags.find((f) => !KNOWN.has(f));
53
- if (unknown) fail("unknown option " + JSON.stringify(stripControls(unknown).slice(0, 40)));
54
- if (targets.length === 0 || targets[0].trim() === "") fail("no domain or URL given");
55
- if (targets.length > 1) fail("one domain or URL at a time, got " + targets.length);
56
-
57
- const mark = { pass: "ok ", warn: "warn", fail: "FAIL", info: "info" };
58
- try {
59
- const result = await validateHost(targets[0]);
60
- if (json) {
61
- console.log(JSON.stringify(result, null, 2));
62
- } else {
63
- // Details reach this point without control characters (validateHost strips them), so a
64
- // published link cannot write an escape sequence to the terminal. A check's reflected
65
- // value, when it has one, is printed ahead of the sentence, the same position the old
66
- // concatenated string put it in, so text output reads the same while the JSON field stays
67
- // separate (round 3 Tek-542, mirrored from worker.js decision 19, Tek-526).
68
- console.log(result.target);
69
- for (const c of result.checks) console.log(" " + mark[c.status] + " " + c.label + " (" + (c.value !== undefined ? c.value + " " : "") + c.detail + ")");
70
- console.log("result: " + result.summary);
71
- }
72
- if (result.summary === "not valid") process.exit(1);
73
- if (result.summary === "valid with warnings" && flags.includes("--strict")) process.exit(1);
74
- process.exit(0);
75
- } catch (err) {
76
- const message = stripControls(err && err.message ? err.message : String(err));
77
- // The hosted validator answers every failure as JSON when JSON was asked for, so a CI
78
- // step that parses --json output gets {"error": ...} here too instead of an empty stdout
79
- // and a plain-text stderr (round 16 S3-3). The exit code is unchanged.
80
- //
81
- // target and code are added as separate fields (V-05, outside audit 2026-09-26): a bare
82
- // {"error": message} left a CI step unable to tell which target failed or why without
83
- // parsing free text. code reads a network error's cause first (undici wraps the real
84
- // errno code, such as EAI_AGAIN or ECONNREFUSED, in err.cause.code) and falls back to
85
- // err.code for an error that sets it directly; it is omitted, not null, when neither is set.
86
- // target goes through maskLocation like every other address the tool prints, so a user name,
87
- // a password or a query value typed into the command line is never echoed back.
88
- const code = (err && err.cause && err.cause.code) || (err && err.code);
89
- if (json) {
90
- const report = { error: message, target: stripControls(maskLocation(targets[0])) };
91
- if (code) report.code = stripControls(String(code));
92
- console.log(JSON.stringify(report, null, 2));
93
- } else console.error("error: " + message);
94
- process.exit(2);
95
- }