pagetrace 0.8.1 → 0.9.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/CHANGELOG.md CHANGED
@@ -6,6 +6,22 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
  While the version is below 1.0.0, breaking changes ship in a minor release.
8
8
 
9
+ ## [0.9.0] - 2026-09-07
10
+
11
+ ### Added
12
+
13
+ - `pagetrace update` asks the npm registry whether a newer version is published
14
+ and installs it. `--check` reports without installing. A project-local install
15
+ is not overwritten with a global one; the command prints the package-manager
16
+ command instead, because installing globally would leave the version the
17
+ project actually runs untouched.
18
+
19
+ ### Fixed
20
+
21
+ - A bare `pagetrace`, or a misspelled command, printed nothing and exited 0,
22
+ which reads as a silent success. Both now print the command list, and an
23
+ unknown command exits 2.
24
+
9
25
  ## [0.8.1] - 2026-09-06
10
26
 
11
27
  ### Fixed
package/README.md CHANGED
@@ -115,6 +115,18 @@ Routes are discovered from `robots.txt` sitemap declarations, falling back to `/
115
115
 
116
116
  URLs pointing at another host are skipped. A page that cannot be fetched stops the run with an error rather than being dropped from the snapshot — a page silently missing from a crawl is indistinguishable from a page you deleted, and reporting a transient outage as a site-wide deletion is worse than failing.
117
117
 
118
+ ### Version and updates
119
+
120
+ ```bash
121
+ pagetrace # command list (same as --help)
122
+ pagetrace <command> --help # flags for one command
123
+ pagetrace --version # installed version
124
+ pagetrace update --check # ask npm whether a newer one exists
125
+ pagetrace update # install it globally
126
+ ```
127
+
128
+ `update` refuses to install globally over a project-local copy and prints the package-manager command instead. Unrelated to `check --update`, which rewrites the lockfile.
129
+
118
130
  ## What it records
119
131
 
120
132
  **Per page** — title, meta description, canonical, robots directives, Open Graph and Twitter Card tags, hreflang alternates, `h1` text, heading outline, every JSON-LD entity with its property list, word count, images missing `alt`, and the length of the first quotable paragraph.
package/dist/cli.cjs CHANGED
@@ -24,6 +24,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
  ));
25
25
 
26
26
  // src/cli.ts
27
+ var import_node_child_process2 = require("child_process");
27
28
  var import_promises2 = require("fs/promises");
28
29
  var import_cac = require("cac");
29
30
  var import_picocolors2 = __toESM(require("picocolors"), 1);
@@ -1516,6 +1517,27 @@ async function snapshotFromOrigin(origin, options = {}) {
1516
1517
  return { schemaVersion: 1, createdAt: (/* @__PURE__ */ new Date()).toISOString(), site, pages };
1517
1518
  }
1518
1519
 
1520
+ // src/update.ts
1521
+ var REGISTRY = "https://registry.npmjs.org";
1522
+ function isNewer(latest, current) {
1523
+ const parts = (v) => v.trim().replace(/^v/, "").split(/[-+]/)[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
1524
+ const [a, b] = [parts(latest), parts(current)];
1525
+ for (let i = 0; i < 3; i += 1) {
1526
+ if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
1527
+ }
1528
+ return false;
1529
+ }
1530
+ async function latestVersion(name, timeoutMs = 5e3) {
1531
+ const response = await fetch(`${REGISTRY}/${name}/latest`, {
1532
+ headers: { accept: "application/json" },
1533
+ signal: AbortSignal.timeout(timeoutMs)
1534
+ });
1535
+ if (!response.ok) throw new Error(`npm registry answered ${response.status} for ${name}.`);
1536
+ const body = await response.json();
1537
+ if (!body.version) throw new Error(`npm registry returned no version for ${name}.`);
1538
+ return body.version;
1539
+ }
1540
+
1519
1541
  // src/cli.ts
1520
1542
  var DEFAULT_LOCKFILE = "pagetrace.lock.json";
1521
1543
  var DEFAULT_CONFIG = "pagetrace.config.json";
@@ -1645,11 +1667,44 @@ cli.command("audit", "Audit a site as it stands, with explanations and fixes").o
1645
1667
  process.exitCode = EXIT_FINDINGS;
1646
1668
  }
1647
1669
  });
1670
+ cli.command("update", "Check npm for a newer pagetrace and install it").option("--check", "Only report whether an update exists").action(async (flags) => {
1671
+ const latest = await latestVersion("pagetrace");
1672
+ if (!isNewer(latest, "0.9.0")) {
1673
+ console.log(import_picocolors2.default.green(`pagetrace ${"0.9.0"} is the latest version.`));
1674
+ return;
1675
+ }
1676
+ console.log(import_picocolors2.default.yellow(`Update available: ${"0.9.0"} \u2192 ${latest}`));
1677
+ if (flags.check) return;
1678
+ if (process.argv[1]?.startsWith(process.cwd())) {
1679
+ console.log(
1680
+ import_picocolors2.default.dim("This is a project-local install. Update it with your package manager, e.g.")
1681
+ );
1682
+ console.log(` npm install -D pagetrace@${latest}`);
1683
+ return;
1684
+ }
1685
+ const { status, error } = (0, import_node_child_process2.spawnSync)("npm", ["install", "-g", `pagetrace@${latest}`], {
1686
+ stdio: "inherit"
1687
+ });
1688
+ if (error || status !== 0) {
1689
+ throw new Error(`npm install failed. Run \`npm install -g pagetrace@${latest}\` yourself.`);
1690
+ }
1691
+ console.log(import_picocolors2.default.green(`Updated to pagetrace ${latest}.`));
1692
+ });
1648
1693
  cli.help();
1649
- cli.version("0.8.1");
1694
+ cli.version("0.9.0");
1650
1695
  async function main() {
1651
1696
  try {
1652
1697
  cli.parse(process.argv, { run: false });
1698
+ if (!cli.matchedCommand) {
1699
+ if (cli.options.help || cli.options.version) return;
1700
+ cli.outputHelp();
1701
+ if (cli.args[0]) {
1702
+ console.error(import_picocolors2.default.red(`
1703
+ Unknown command "${cli.args[0]}".`));
1704
+ process.exitCode = EXIT_FAILURE;
1705
+ }
1706
+ return;
1707
+ }
1653
1708
  await cli.runMatchedCommand();
1654
1709
  } catch (error) {
1655
1710
  console.error(import_picocolors2.default.red(error.message));
package/dist/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/cli.ts
4
+ import { spawnSync } from "child_process";
4
5
  import { readFile as readFile2, writeFile } from "fs/promises";
5
6
  import { cac } from "cac";
6
7
  import pc2 from "picocolors";
@@ -1493,6 +1494,27 @@ async function snapshotFromOrigin(origin, options = {}) {
1493
1494
  return { schemaVersion: 1, createdAt: (/* @__PURE__ */ new Date()).toISOString(), site, pages };
1494
1495
  }
1495
1496
 
1497
+ // src/update.ts
1498
+ var REGISTRY = "https://registry.npmjs.org";
1499
+ function isNewer(latest, current) {
1500
+ const parts = (v) => v.trim().replace(/^v/, "").split(/[-+]/)[0].split(".").map((n) => Number.parseInt(n, 10) || 0);
1501
+ const [a, b] = [parts(latest), parts(current)];
1502
+ for (let i = 0; i < 3; i += 1) {
1503
+ if ((a[i] ?? 0) !== (b[i] ?? 0)) return (a[i] ?? 0) > (b[i] ?? 0);
1504
+ }
1505
+ return false;
1506
+ }
1507
+ async function latestVersion(name, timeoutMs = 5e3) {
1508
+ const response = await fetch(`${REGISTRY}/${name}/latest`, {
1509
+ headers: { accept: "application/json" },
1510
+ signal: AbortSignal.timeout(timeoutMs)
1511
+ });
1512
+ if (!response.ok) throw new Error(`npm registry answered ${response.status} for ${name}.`);
1513
+ const body = await response.json();
1514
+ if (!body.version) throw new Error(`npm registry returned no version for ${name}.`);
1515
+ return body.version;
1516
+ }
1517
+
1496
1518
  // src/cli.ts
1497
1519
  var DEFAULT_LOCKFILE = "pagetrace.lock.json";
1498
1520
  var DEFAULT_CONFIG = "pagetrace.config.json";
@@ -1622,11 +1644,44 @@ cli.command("audit", "Audit a site as it stands, with explanations and fixes").o
1622
1644
  process.exitCode = EXIT_FINDINGS;
1623
1645
  }
1624
1646
  });
1647
+ cli.command("update", "Check npm for a newer pagetrace and install it").option("--check", "Only report whether an update exists").action(async (flags) => {
1648
+ const latest = await latestVersion("pagetrace");
1649
+ if (!isNewer(latest, "0.9.0")) {
1650
+ console.log(pc2.green(`pagetrace ${"0.9.0"} is the latest version.`));
1651
+ return;
1652
+ }
1653
+ console.log(pc2.yellow(`Update available: ${"0.9.0"} \u2192 ${latest}`));
1654
+ if (flags.check) return;
1655
+ if (process.argv[1]?.startsWith(process.cwd())) {
1656
+ console.log(
1657
+ pc2.dim("This is a project-local install. Update it with your package manager, e.g.")
1658
+ );
1659
+ console.log(` npm install -D pagetrace@${latest}`);
1660
+ return;
1661
+ }
1662
+ const { status, error } = spawnSync("npm", ["install", "-g", `pagetrace@${latest}`], {
1663
+ stdio: "inherit"
1664
+ });
1665
+ if (error || status !== 0) {
1666
+ throw new Error(`npm install failed. Run \`npm install -g pagetrace@${latest}\` yourself.`);
1667
+ }
1668
+ console.log(pc2.green(`Updated to pagetrace ${latest}.`));
1669
+ });
1625
1670
  cli.help();
1626
- cli.version("0.8.1");
1671
+ cli.version("0.9.0");
1627
1672
  async function main() {
1628
1673
  try {
1629
1674
  cli.parse(process.argv, { run: false });
1675
+ if (!cli.matchedCommand) {
1676
+ if (cli.options.help || cli.options.version) return;
1677
+ cli.outputHelp();
1678
+ if (cli.args[0]) {
1679
+ console.error(pc2.red(`
1680
+ Unknown command "${cli.args[0]}".`));
1681
+ process.exitCode = EXIT_FAILURE;
1682
+ }
1683
+ return;
1684
+ }
1630
1685
  await cli.runMatchedCommand();
1631
1686
  } catch (error) {
1632
1687
  console.error(pc2.red(error.message));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pagetrace",
3
- "version": "0.8.1",
3
+ "version": "0.9.0",
4
4
  "description": "Baseline your site's SEO and AEO surface, diff every build against it, and fail CI on regressions.",
5
5
  "main": "./dist/index.cjs",
6
6
  "scripts": {