@gscdump/cli 1.5.0 → 1.5.3

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.
@@ -1,9 +1,10 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger, readUrlList } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { gscErrorHandler } from "../error-handler.mjs";
4
+ import { loadSitemapUrls } from "../sitemap.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
- import { batchRequestIndexing, fetchSitemapUrls, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump";
7
+ import { batchRequestIndexing, getIndexingMetadata, requestIndexing, runSequentialBatch } from "gscdump";
7
8
  const RETRIES_ARG = { retries: {
8
9
  type: "string",
9
10
  description: "Override per-call retry count (default: 3)"
@@ -15,10 +16,15 @@ function parseRetries(v) {
15
16
  }
16
17
  async function resolveUrlSource(args) {
17
18
  const fromSitemap = args["from-sitemap"];
18
- if (fromSitemap) return fetchSitemapUrls(String(fromSitemap)).catch((e) => {
19
- logger.error(`Sitemap fetch failed: ${e.message}`);
20
- process.exit(1);
21
- });
19
+ if (fromSitemap) {
20
+ const result = await loadSitemapUrls(String(fromSitemap));
21
+ if (result._tag === "error") {
22
+ logger.error(`Sitemap fetch failed: ${result.message}`);
23
+ process.exit(1);
24
+ }
25
+ if (!result.value.complete) logger.warn("Sitemap walk was incomplete; indexing only the URLs that were read");
26
+ return result.value.urls;
27
+ }
22
28
  return readUrlList(args);
23
29
  }
24
30
  const submitCommand = defineCommand({
@@ -1,9 +1,10 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger, readUrlList } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { gscErrorHandler } from "../error-handler.mjs";
4
+ import { loadSitemapUrls } from "../sitemap.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
- import { batchInspectUrls, fetchSitemapUrls } from "gscdump";
7
+ import { batchInspectUrls } from "gscdump";
7
8
  function verdictTone(verdict) {
8
9
  if (verdict === "PASS") return "\x1B[32m";
9
10
  if (verdict === "NEUTRAL" || verdict === "PARTIAL") return "\x1B[33m";
@@ -121,10 +122,16 @@ const batchCommand = defineCommand({
121
122
  },
122
123
  async run({ args }) {
123
124
  const { json, quiet } = applyOutputMode(args);
124
- const urls = args["from-sitemap"] ? await fetchSitemapUrls(String(args["from-sitemap"])).catch((e) => {
125
- logger.error(`Sitemap fetch failed: ${e.message}`);
126
- process.exit(1);
127
- }) : await readUrlList(args);
125
+ let urls;
126
+ if (args["from-sitemap"]) {
127
+ const result = await loadSitemapUrls(String(args["from-sitemap"]));
128
+ if (result._tag === "error") {
129
+ logger.error(`Sitemap fetch failed: ${result.message}`);
130
+ process.exit(1);
131
+ }
132
+ if (!result.value.complete) logger.warn("Sitemap walk was incomplete; inspecting only the URLs that were read");
133
+ urls = result.value.urls;
134
+ } else urls = await readUrlList(args);
128
135
  if (urls.length === 0) {
129
136
  logger.error("No URLs provided. Pass URLs as args, --file, --from-sitemap, or stdin.");
130
137
  process.exit(1);
@@ -1,9 +1,11 @@
1
1
  import { OUTPUT_ARGS, applyOutputMode, logger, noSubcommandSelected } from "../utils.mjs";
2
2
  import { createCommandContext } from "../context.mjs";
3
3
  import { gscErrorHandler } from "../error-handler.mjs";
4
+ import { loadSitemapUrls } from "../sitemap.mjs";
4
5
  import process from "node:process";
5
6
  import { defineCommand } from "citty";
6
- import { discoverSitemap, fetchSitemap, fetchSitemapUrls } from "gscdump";
7
+ import { fetchSitemap } from "gscdump";
8
+ import { discoverSitemapResult } from "gscdump/sitemap";
7
9
  const listCommand = defineCommand({
8
10
  meta: {
9
11
  name: "list",
@@ -195,14 +197,20 @@ const sitemapsCommand = defineCommand({
195
197
  async run({ args }) {
196
198
  const { json } = applyOutputMode(args);
197
199
  const domain = String(args.domain).replace(/^https?:\/\//, "").replace(/\/.*$/, "");
198
- const url = await discoverSitemap(domain).catch(() => null);
200
+ const discovery = await discoverSitemapResult(domain);
201
+ const url = discovery._tag === "found" ? discovery.url : null;
199
202
  if (json) {
200
203
  console.log(JSON.stringify({
201
204
  domain,
202
- sitemap: url
205
+ sitemap: url,
206
+ status: discovery._tag
203
207
  }, null, 2));
204
208
  return;
205
209
  }
210
+ if (discovery._tag === "incomplete") {
211
+ logger.error(`Sitemap discovery was incomplete: ${discovery.failures[0]?.detail ?? "unknown failure"}`);
212
+ process.exit(1);
213
+ }
206
214
  if (!url) {
207
215
  logger.warn(`No sitemap discovered for ${domain}`);
208
216
  process.exit(1);
@@ -236,21 +244,26 @@ const sitemapsCommand = defineCommand({
236
244
  const { json } = applyOutputMode(args);
237
245
  const limit = args.limit ? Number.parseInt(String(args.limit), 10) : void 0;
238
246
  const maxDepth = args["max-depth"] ? Number.parseInt(String(args["max-depth"]), 10) : void 0;
239
- const urls = await fetchSitemapUrls(String(args.url), {
240
- limit,
247
+ const result = await loadSitemapUrls(String(args.url), {
248
+ maxUrls: limit,
241
249
  maxDepth
242
- }).catch((e) => {
243
- logger.error(`Sitemap fetch failed: ${e.message}`);
244
- process.exit(1);
245
250
  });
251
+ if (result._tag === "error") {
252
+ logger.error(`Sitemap fetch failed: ${result.message}`);
253
+ process.exit(1);
254
+ }
255
+ const { urls, complete, documentsRead } = result.value;
246
256
  if (json) {
247
257
  console.log(JSON.stringify({
248
258
  sitemap: args.url,
249
259
  count: urls.length,
260
+ complete,
261
+ documentsRead,
250
262
  urls
251
263
  }, null, 2));
252
264
  return;
253
265
  }
266
+ if (!complete) logger.warn("Sitemap walk was truncated or a child document failed");
254
267
  for (const u of urls) console.log(u);
255
268
  }
256
269
  })
@@ -4,7 +4,8 @@ import { listReports, runReportHandler } from "../handlers/reports.mjs";
4
4
  import { getSitemap, listSitesWithSitemaps } from "../handlers/sites.mjs";
5
5
  import "../handlers/index.mjs";
6
6
  import { batchInspectUrlsInput, batchRequestIndexingInput, getIndexingStatusInput, inspectUrlInput, listReportsInput, listSitemapsInput, listSitesInput, requestIndexingInput, runReportInput, sitemapInput } from "../types.mjs";
7
- import { addSite, deleteSite, discoverSitemap, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, runSequentialBatch, unverifySite, verifySite } from "gscdump";
7
+ import { addSite, deleteSite, getIndexingMetadata, getVerificationToken, getVerifiedSite, googleSearchConsole, listVerifiedSites, runSequentialBatch, unverifySite, verifySite } from "gscdump";
8
+ import { discoverSitemapResult } from "gscdump/sitemap";
8
9
  import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
10
  import { z } from "zod";
10
11
  async function runMcpSearchAnalyticsQuery(client, args) {
@@ -385,12 +386,14 @@ function createGscMcpServer(options) {
385
386
  inputSchema: z.object({ domain: z.string().describe("Domain (e.g., example.com)") }).shape
386
387
  }, async ({ domain }) => {
387
388
  const cleaned = String(domain).replace(/^https?:\/\//, "").replace(/\/.*$/, "");
388
- const url = await discoverSitemap(cleaned).catch(() => null);
389
+ const discovery = await discoverSitemapResult(cleaned);
389
390
  return { content: [{
390
391
  type: "text",
391
392
  text: JSON.stringify({
392
393
  domain: cleaned,
393
- sitemap: url
394
+ sitemap: discovery._tag === "found" ? discovery.url : null,
395
+ status: discovery._tag,
396
+ ...discovery._tag === "incomplete" ? { failures: discovery.failures } : {}
394
397
  }, null, 2)
395
398
  }] };
396
399
  });
package/dist/package.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var version = "1.5.0";
1
+ var version = "1.5.3";
2
2
  export { version };
@@ -0,0 +1,27 @@
1
+ import { walkSitemaps } from "gscdump/sitemap";
2
+ function failureMessage(result) {
3
+ if (result._tag === "not_found") return "No sitemap document was found";
4
+ const first = result.failures[0];
5
+ if (!first) return "Sitemap walk failed before reading a document";
6
+ const error = first.error;
7
+ if (error._tag === "network_error") return `${first.url}: ${error.error}`;
8
+ if (error._tag === "http_error") return `${first.url}: HTTP ${error.status} ${error.statusText}`;
9
+ if (error._tag === "not_found") return `${first.url}: HTTP ${error.status}`;
10
+ return `${first.url}: ${error.error._tag}`;
11
+ }
12
+ async function loadSitemapUrls(url, options = {}) {
13
+ const result = await walkSitemaps(url, options);
14
+ if (result._tag !== "ok") return {
15
+ _tag: "error",
16
+ message: failureMessage(result)
17
+ };
18
+ return {
19
+ _tag: "ok",
20
+ value: {
21
+ urls: result.entries.map((entry) => entry.loc),
22
+ complete: result.complete,
23
+ documentsRead: result.documentsRead
24
+ }
25
+ };
26
+ }
27
+ export { loadSitemapUrls };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/cli",
3
3
  "type": "module",
4
- "version": "1.5.0",
4
+ "version": "1.5.3",
5
5
  "description": "CLI for Google Search Console - dump, query, and run MCP server",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -50,10 +50,10 @@
50
50
  "ofetch": "^1.5.1",
51
51
  "open": "^11.0.0",
52
52
  "zod": "^4.4.3",
53
- "@gscdump/analysis": "^1.5.0",
54
- "@gscdump/engine": "^1.5.0",
55
- "@gscdump/engine-gsc-api": "^1.5.0",
56
- "gscdump": "^1.5.0"
53
+ "@gscdump/analysis": "^1.5.3",
54
+ "@gscdump/engine-gsc-api": "^1.5.3",
55
+ "gscdump": "^1.5.3",
56
+ "@gscdump/engine": "^1.5.3"
57
57
  },
58
58
  "devDependencies": {
59
59
  "vitest": "^4.1.10"