@gscdump/cli 3.8.0 → 4.0.1

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.
Files changed (73) hide show
  1. package/README.md +115 -30
  2. package/bin/gscdump.mjs +21 -1
  3. package/dist/analysis-local.mjs +172 -53
  4. package/dist/auth-state.mjs +21 -6
  5. package/dist/auth.mjs +27 -18
  6. package/dist/bing-data.mjs +6 -2
  7. package/dist/bing-hosted.mjs +1 -1
  8. package/dist/cli-args.mjs +8 -0
  9. package/dist/cli.d.mts +5 -1
  10. package/dist/cli.mjs +41 -10
  11. package/dist/cloud-google.mjs +8 -1
  12. package/dist/command-meta.mjs +4 -4
  13. package/dist/command-registry.mjs +86 -10
  14. package/dist/commands/analyze.mjs +84 -43
  15. package/dist/commands/auth.mjs +46 -37
  16. package/dist/commands/bing.mjs +3 -3
  17. package/dist/commands/compact.mjs +12 -8
  18. package/dist/commands/config.mjs +14 -14
  19. package/dist/commands/doctor.mjs +26 -26
  20. package/dist/commands/dump.mjs +203 -105
  21. package/dist/commands/entities.mjs +10 -113
  22. package/dist/commands/gc.mjs +4 -3
  23. package/dist/commands/indexing-urls.mjs +185 -0
  24. package/dist/commands/indexing.mjs +12 -8
  25. package/dist/commands/init.mjs +77 -18
  26. package/dist/commands/inspect.mjs +97 -110
  27. package/dist/commands/mcp.mjs +37 -57
  28. package/dist/commands/papercut.mjs +1 -1
  29. package/dist/commands/profile.mjs +9 -17
  30. package/dist/commands/query.mjs +219 -210
  31. package/dist/commands/report.mjs +68 -85
  32. package/dist/commands/rollups.mjs +9 -6
  33. package/dist/commands/sitemaps.mjs +51 -104
  34. package/dist/commands/sites.mjs +40 -24
  35. package/dist/commands/stats.mjs +14 -17
  36. package/dist/commands/store-purge.mjs +9 -5
  37. package/dist/commands/store.mjs +0 -12
  38. package/dist/commands/sync.mjs +774 -296
  39. package/dist/config.mjs +7 -4
  40. package/dist/context.mjs +51 -21
  41. package/dist/coverage.mjs +251 -0
  42. package/dist/dump-bing.mjs +71 -0
  43. package/dist/dump-writers.mjs +246 -0
  44. package/dist/error-handler.mjs +108 -38
  45. package/dist/filters.mjs +125 -0
  46. package/dist/hosted-site.mjs +91 -0
  47. package/dist/inspect-urls.mjs +76 -0
  48. package/dist/inspection-record.mjs +93 -0
  49. package/dist/local-entities.mjs +658 -0
  50. package/dist/mcp/errors.mjs +71 -18
  51. package/dist/mcp/handlers/diagnostics.mjs +15 -12
  52. package/dist/mcp/handlers/reports.mjs +33 -42
  53. package/dist/mcp/server/index.mjs +80 -38
  54. package/dist/mcp/types.mjs +7 -7
  55. package/dist/package.mjs +1 -1
  56. package/dist/quota-ledger.mjs +370 -0
  57. package/dist/render/analysis.mjs +8 -1
  58. package/dist/render/report.mjs +8 -1
  59. package/dist/request-pacer.mjs +29 -0
  60. package/dist/route.mjs +301 -0
  61. package/dist/sitemap.mjs +4 -0
  62. package/dist/sql-views.mjs +117 -0
  63. package/dist/store-sites.mjs +134 -0
  64. package/dist/sync-plan.mjs +129 -0
  65. package/dist/sync-run.mjs +96 -0
  66. package/dist/table-sources.mjs +57 -0
  67. package/dist/token-info.mjs +54 -0
  68. package/dist/utils.mjs +28 -19
  69. package/dist/window.mjs +159 -0
  70. package/package.json +17 -14
  71. package/skills/gscdump/SKILL.md +172 -37
  72. package/dist/commands/export.mjs +0 -76
  73. package/dist/native-duckdb.mjs +0 -46
@@ -0,0 +1,76 @@
1
+ import { formatQuotaStop, quotaStopOf } from "./quota-ledger.mjs";
2
+ import { classifyError } from "gscdump/errors";
3
+ import { URL_INSPECTION_QUOTA, describeInspectionError } from "gscdump/indexing";
4
+ function checkInspectionBatch(urls) {
5
+ if (urls.length <= URL_INSPECTION_QUOTA.perDay) return { kind: "ok" };
6
+ const perDay = URL_INSPECTION_QUOTA.perDay.toLocaleString("en-US");
7
+ return {
8
+ kind: "too-many",
9
+ message: `You passed ${urls.length.toLocaleString("en-US")} URLs. Google allows ${perDay} URL inspections per day for each property. Inspect at most ${perDay} URLs in one run.`
10
+ };
11
+ }
12
+ async function inspectUrls(input) {
13
+ const intervalMs = input.intervalMs ?? 120;
14
+ const sleep = input.sleep ?? ((ms) => new Promise((resolve) => setTimeout(resolve, ms)));
15
+ const now = input.now ?? Date.now;
16
+ const outcomes = [];
17
+ let lastStart = null;
18
+ for (let i = 0; i < input.urls.length; i++) {
19
+ const url = input.urls[i];
20
+ if (!input.inProperty(url)) {
21
+ const outcome = {
22
+ kind: "failed",
23
+ url,
24
+ error: "The URL is outside this Site."
25
+ };
26
+ outcomes.push(outcome);
27
+ await input.onOutcome(outcome);
28
+ continue;
29
+ }
30
+ if (lastStart !== null) {
31
+ const wait = lastStart + intervalMs - now();
32
+ if (wait > 0) await sleep(wait);
33
+ }
34
+ lastStart = now();
35
+ const settled = await input.inspect(url).then((result) => ({
36
+ ok: true,
37
+ result
38
+ }), (error) => ({
39
+ ok: false,
40
+ error
41
+ }));
42
+ if (!settled.ok) {
43
+ const quota = quotaStopOf(settled.error);
44
+ if (quota) return {
45
+ outcomes,
46
+ stopped: {
47
+ reason: formatQuotaStop(quota),
48
+ remaining: input.urls.length - i
49
+ }
50
+ };
51
+ if (classifyError(settled.error).kind === "rate-limited") return {
52
+ outcomes,
53
+ stopped: {
54
+ reason: describeInspectionError(settled.error),
55
+ remaining: input.urls.length - i
56
+ }
57
+ };
58
+ }
59
+ const outcome = settled.ok ? {
60
+ kind: "inspected",
61
+ url,
62
+ result: settled.result
63
+ } : {
64
+ kind: "failed",
65
+ url,
66
+ error: describeInspectionError(settled.error)
67
+ };
68
+ outcomes.push(outcome);
69
+ await input.onOutcome(outcome);
70
+ }
71
+ return {
72
+ outcomes,
73
+ stopped: null
74
+ };
75
+ }
76
+ export { checkInspectionBatch, inspectUrls };
@@ -0,0 +1,93 @@
1
+ import { inspectionPolicy } from "@gscdump/engine";
2
+ const INSPECTION_QPD_PER_PROPERTY = 2e3;
3
+ const DAY_MS = 864e5;
4
+ const STATE_FIELDS = [
5
+ "indexStatus",
6
+ "coverageState",
7
+ "robotsTxtState",
8
+ "indexingState",
9
+ "pageFetchState",
10
+ "googleCanonical"
11
+ ];
12
+ function verdictOf(indexStatus) {
13
+ return indexStatus === "PASS" || indexStatus === "FAIL" ? indexStatus : "NEUTRAL";
14
+ }
15
+ function scheduleOf(record) {
16
+ if (record.raw?.schedule) return record.raw.schedule;
17
+ const at = Date.parse(record.inspectedAt);
18
+ return inspectionPolicy(verdictOf(record.indexStatus)).initial(Number.isFinite(at) ? at : 0);
19
+ }
20
+ function toInspectionRecord(input) {
21
+ const { url, result, inspectedAt, previous } = input;
22
+ const indexStatus = result?.indexStatusResult;
23
+ const record = {
24
+ url,
25
+ inspectedAt: inspectedAt.toISOString(),
26
+ indexStatus: indexStatus?.verdict ?? void 0,
27
+ lastCrawlTime: indexStatus?.lastCrawlTime ?? void 0,
28
+ googleCanonical: indexStatus?.googleCanonical ?? void 0,
29
+ userCanonical: indexStatus?.userCanonical ?? void 0,
30
+ coverageState: indexStatus?.coverageState ?? void 0,
31
+ robotsTxtState: indexStatus?.robotsTxtState ?? void 0,
32
+ indexingState: indexStatus?.indexingState ?? void 0,
33
+ pageFetchState: indexStatus?.pageFetchState ?? void 0,
34
+ mobileUsabilityVerdict: result?.mobileUsabilityResult?.verdict ?? void 0,
35
+ richResultsVerdict: result?.richResultsResult?.verdict ?? void 0
36
+ };
37
+ const policy = inspectionPolicy(verdictOf(record.indexStatus));
38
+ const at = inspectedAt.getTime();
39
+ const schedule = previous ? policy.observe(scheduleOf(previous), {
40
+ changed: STATE_FIELDS.some((field) => (previous[field] ?? null) !== (record[field] ?? null)),
41
+ at
42
+ }) : policy.initial(at);
43
+ record.raw = {
44
+ ...result,
45
+ schedule
46
+ };
47
+ return record;
48
+ }
49
+ function recentMonths(now) {
50
+ const month = (at) => new Date(at).toISOString().slice(0, 7);
51
+ return [.../* @__PURE__ */ new Set([month(now.getTime() - DAY_MS), month(now.getTime())])];
52
+ }
53
+ function latestByUrl(records) {
54
+ const latest = /* @__PURE__ */ new Map();
55
+ for (const record of records) {
56
+ const prior = latest.get(record.url);
57
+ if (!prior || record.inspectedAt > prior.inspectedAt) latest.set(record.url, record);
58
+ }
59
+ return latest;
60
+ }
61
+ function planInspections(input) {
62
+ const now = input.now.getTime();
63
+ const since = now - DAY_MS;
64
+ const usedToday = input.recent.filter((record) => {
65
+ const at = Date.parse(record.inspectedAt);
66
+ return at > since && at <= now;
67
+ }).length;
68
+ const quotaLeft = Math.max(0, INSPECTION_QPD_PER_PROPERTY - usedToday);
69
+ const latest = input.latest;
70
+ const fresh = [];
71
+ const due = [];
72
+ for (const url of new Set(input.candidates)) {
73
+ const record = latest.get(url);
74
+ if (!record) {
75
+ fresh.push(url);
76
+ continue;
77
+ }
78
+ const schedule = scheduleOf(record);
79
+ if (inspectionPolicy(verdictOf(record.indexStatus)).isDue(schedule, now)) due.push({
80
+ url,
81
+ nextAt: schedule.nextAt
82
+ });
83
+ }
84
+ due.sort((a, b) => a.nextAt - b.nextAt);
85
+ const ordered = [...fresh, ...due.map((entry) => entry.url)];
86
+ const budget = Math.max(0, Math.min(input.limit, quotaLeft));
87
+ return {
88
+ urls: ordered.slice(0, budget),
89
+ deferred: Math.max(0, ordered.length - budget),
90
+ quotaLeft
91
+ };
92
+ }
93
+ export { INSPECTION_QPD_PER_PROPERTY, latestByUrl, planInspections, recentMonths, scheduleOf, toInspectionRecord };