gscdump 0.4.0 → 0.5.2

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
@@ -20,6 +20,8 @@
20
20
  npm install gscdump
21
21
  ```
22
22
 
23
+ This package is the edge-safe surface: REST client + query builder + cross-package contracts. For the storage engine (Parquet/DuckDB, planner, adapters), install [`@gscdump/engine`](../engine). For analyzers, install [`@gscdump/analysis`](../analysis).
24
+
23
25
  ## Usage
24
26
 
25
27
  ```ts
@@ -154,12 +156,12 @@ const cannibalization = analyzeCannibalization(keywordPageData)
154
156
 
155
157
  **Utils:** `formatDateGsc`, `percentDifference`
156
158
 
157
- ## Related Packages
159
+ ## Related
158
160
 
159
- - [`@gscdump/cli`](../cli) - CLI for dump, sync, compare, analyze
160
- - [`@gscdump/mcp`](../mcp) - MCP server for AI agents
161
- - [`@gscdump/db`](../db) - SQLite persistence
162
- - [`@gscdump/query`](../query) - Unified data provider
161
+ - [`@gscdump/cli`](../cli) CLI: `sync`, `query`, `dump`, `analyze`, `mcp`, `store` admin.
162
+ - [`@gscdump/engine`](../engine) — Append-only Parquet/DuckDB storage engine.
163
+ - [`@gscdump/analysis`](../analysis) — SEO analyzers (row-based + DuckDB-native + D1-ready).
164
+ - [`@gscdump/nuxt-analytics`](../nuxt-analytics) Nuxt layer wrapping the full stack.
163
165
 
164
166
  ## License
165
167
 
@@ -0,0 +1,136 @@
1
+ declare const _default: {
2
+ name: string;
3
+ 'alpha-2': string;
4
+ 'alpha-3': string;
5
+ 'country-code': string;
6
+ }[];
7
+ declare const Devices: {
8
+ readonly MOBILE: "MOBILE";
9
+ readonly DESKTOP: "DESKTOP";
10
+ readonly TABLET: "TABLET";
11
+ };
12
+ type Device = typeof Devices[keyof typeof Devices];
13
+ declare const SearchTypes: {
14
+ readonly WEB: "web";
15
+ readonly IMAGE: "image";
16
+ readonly VIDEO: "video";
17
+ readonly NEWS: "news";
18
+ readonly DISCOVER: "discover";
19
+ readonly GOOGLE_NEWS: "googleNews";
20
+ };
21
+ type SearchType = typeof SearchTypes[keyof typeof SearchTypes];
22
+ declare const Countries: { [K in (typeof _default)[number]["alpha-3"]]: Lowercase<K> };
23
+ type Country = typeof Countries[keyof typeof Countries];
24
+ interface DimensionValueMap {
25
+ query: string;
26
+ queryCanonical: string;
27
+ page: string;
28
+ country: Country;
29
+ device: Device;
30
+ searchAppearance: string;
31
+ date: string;
32
+ }
33
+ type Dimension = keyof DimensionValueMap;
34
+ interface QueryParamValueMap {
35
+ searchType: SearchType;
36
+ }
37
+ type QueryParamName = keyof QueryParamValueMap;
38
+ type FilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'includingRegex' | 'excludingRegex';
39
+ type DateOperator = 'gte' | 'gt' | 'lte' | 'lt' | 'between';
40
+ type MetricOperator = 'metricGte' | 'metricGt' | 'metricLte' | 'metricLt' | 'metricBetween';
41
+ type SpecialOperator = 'topLevel';
42
+ interface InternalFilter {
43
+ dimension: Dimension | QueryParamName | Metric;
44
+ operator: FilterOperator | DateOperator | MetricOperator | SpecialOperator;
45
+ expression: string;
46
+ expression2?: string;
47
+ }
48
+ interface Filter<C = object> {
49
+ readonly __filterBrand: 'gscdump.Filter';
50
+ readonly _constraints: C;
51
+ readonly _filters: InternalFilter[];
52
+ readonly _nestedGroups?: Filter<any>[];
53
+ readonly _groupType?: 'and' | 'or';
54
+ }
55
+ type Metric = 'clicks' | 'impressions' | 'ctr' | 'position';
56
+ interface BuilderState {
57
+ dimensions: Dimension[];
58
+ metrics?: Metric[];
59
+ filter?: Filter<any>;
60
+ orderBy?: {
61
+ column: Metric | 'date';
62
+ dir: 'asc' | 'desc';
63
+ };
64
+ rowLimit?: number;
65
+ startRow?: number;
66
+ }
67
+ /** Logical table / dataset identifier. Canonical across query builder + storage engine. */
68
+ type TableName = 'pages' | 'keywords' | 'countries' | 'devices' | 'page_keywords' | 'search_appearance';
69
+ /** Untyped row shape crossing storage/query boundaries. */
70
+ type Row = Record<string, unknown>;
71
+ type ColumnType = 'DATE' | 'VARCHAR' | 'INTEGER' | 'BIGINT' | 'DOUBLE';
72
+ interface ColumnDef {
73
+ name: string;
74
+ type: ColumnType;
75
+ nullable: boolean;
76
+ }
77
+ interface TableSchema {
78
+ name: TableName;
79
+ columns: ColumnDef[];
80
+ sortKey: string[];
81
+ /**
82
+ * Monotonically increasing version. Tagged onto every manifest entry written
83
+ * for this table; bump when columns are added/removed/retyped so readers can
84
+ * detect stale on-disk data.
85
+ */
86
+ version: number;
87
+ }
88
+ /** Tenant identity for multi-user / multi-site storage. */
89
+ interface TenantCtx {
90
+ userId: string;
91
+ siteId?: string;
92
+ }
93
+ type AnalysisTool = 'striking-distance' | 'opportunity' | 'movers' | 'decay' | 'zero-click' | 'brand' | 'cannibalization' | 'clustering' | 'concentration' | 'seasonality' | 'trends' | 'ctr-anomaly' | 'position-volatility' | 'long-tail' | 'intent-atlas' | 'query-migration' | 'bayesian-ctr' | 'stl-decompose' | 'change-point' | 'bipartite-pagerank' | 'survival' | 'position-distribution' | 'ctr-curve' | 'dark-traffic' | 'content-velocity' | 'keyword-breadth' | 'device-gap' | 'data-query' | 'data-detail';
94
+ interface AnalysisParams {
95
+ type: AnalysisTool;
96
+ startDate?: string;
97
+ endDate?: string;
98
+ prevStartDate?: string;
99
+ prevEndDate?: string;
100
+ brandTerms?: string[];
101
+ limit?: number;
102
+ offset?: number;
103
+ /** Sort column. Each analyzer enforces its own whitelist. */
104
+ sortBy?: string;
105
+ /** Sort direction. Default per-analyzer. */
106
+ sortDir?: 'asc' | 'desc';
107
+ minPosition?: number;
108
+ maxPosition?: number;
109
+ minImpressions?: number;
110
+ maxCtr?: number;
111
+ minPages?: number;
112
+ maxPositionSpread?: number;
113
+ minClusterSize?: number;
114
+ clusterBy?: 'prefix' | 'intent' | 'both';
115
+ dimension?: 'pages' | 'keywords';
116
+ topN?: number;
117
+ metric?: 'clicks' | 'impressions';
118
+ changeThreshold?: number;
119
+ minPreviousClicks?: number;
120
+ threshold?: number;
121
+ weeks?: number;
122
+ minWeeksWithData?: number;
123
+ /** content-velocity lookback window in days (max 365, default 90). */
124
+ days?: number;
125
+ /** data-query / data-detail primary BuilderState. */
126
+ q?: BuilderState;
127
+ /** data-query / data-detail optional comparison-period BuilderState. */
128
+ qc?: BuilderState;
129
+ /** data-query comparison filter applied to joined current/previous rows. */
130
+ comparisonFilter?: 'new' | 'lost' | 'improving' | 'declining';
131
+ }
132
+ interface AnalysisResult {
133
+ results: Record<string, unknown>[];
134
+ meta: Record<string, unknown>;
135
+ }
136
+ export { AnalysisParams, AnalysisResult, AnalysisTool, ColumnDef, ColumnType, Row, type TableName, TableSchema, TenantCtx };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Driver contracts: cross-package shapes returned by GSC API drivers
3
+ * (live REST, cloud-bridged, in-memory test doubles). Consumers (cloud SDK,
4
+ * MCP server, test fixtures) bind against these contracts so a driver swap
5
+ * doesn't ripple through call sites.
6
+ *
7
+ * Edge-safe: no engine imports, no `node:*`. Driver shapes describe data
8
+ * returned over the wire, so they belong with the REST client surface, not
9
+ * the storage engine.
10
+ */
11
+ interface DriverSite {
12
+ siteUrl: string;
13
+ permissionLevel: string;
14
+ }
15
+ interface DriverSiteWithSync extends DriverSite {
16
+ siteId: string;
17
+ syncStatus: string | null;
18
+ syncProgress: {
19
+ total: number;
20
+ completed: number;
21
+ percent: number;
22
+ };
23
+ lastSyncAt: number | null;
24
+ newestDateSynced: string | null;
25
+ oldestDateSynced: string | null;
26
+ }
27
+ interface DriverQueryParams {
28
+ startDate: string;
29
+ endDate: string;
30
+ dimensions?: string[];
31
+ rowLimit?: number;
32
+ startRow?: number;
33
+ searchType?: string;
34
+ filters?: Array<{
35
+ dimension: string;
36
+ operator: string;
37
+ expression: string;
38
+ }>;
39
+ }
40
+ interface DriverQueryRow {
41
+ clicks: number;
42
+ impressions: number;
43
+ ctr: number;
44
+ position: number;
45
+ [key: string]: unknown;
46
+ }
47
+ interface DriverQueryResult {
48
+ rows: DriverQueryRow[];
49
+ meta: {
50
+ siteUrl: string;
51
+ dimensions: string[];
52
+ dateRange: {
53
+ startDate: string;
54
+ endDate: string;
55
+ };
56
+ rowCount: number;
57
+ hasMore: boolean;
58
+ };
59
+ }
60
+ interface DriverSitemap {
61
+ path: string;
62
+ type?: string;
63
+ isPending?: boolean;
64
+ errors?: number;
65
+ warnings?: number;
66
+ urlCount?: number;
67
+ lastDownloaded?: string | null;
68
+ }
69
+ interface DriverInspectResult {
70
+ url: string;
71
+ verdict: string | null;
72
+ coverageState: string | null;
73
+ indexingState: string | null;
74
+ lastCrawlTime: string | null;
75
+ isIndexed: boolean;
76
+ raw: unknown;
77
+ }
78
+ export { DriverInspectResult, DriverQueryParams, DriverQueryResult, DriverQueryRow, DriverSite, DriverSiteWithSync, DriverSitemap };
@@ -0,0 +1 @@
1
+ export {};
package/dist/index.d.mts CHANGED
@@ -1,8 +1,6 @@
1
1
  import { $Fetch, FetchOptions } from "ofetch";
2
- import { indexing_v3 } from "@googleapis/indexing/v3";
3
- import { searchconsole_v1 } from "@googleapis/searchconsole/v1";
4
-
5
- //#region src/core/types.d.ts
2
+ import { indexing_v3 } from "@googleapis/indexing/build/v3";
3
+ import { searchconsole_v1 } from "@googleapis/searchconsole/build/v1";
6
4
  type ApiSite = searchconsole_v1.Schema$WmxSite;
7
5
  type ApiSitemap = searchconsole_v1.Schema$WmxSitemap;
8
6
  type ApiSitemapContent = searchconsole_v1.Schema$WmxSitemapContent;
@@ -69,16 +67,12 @@ interface SiteAnalytics {
69
67
  impressions: number;
70
68
  }[];
71
69
  }
72
- //#endregion
73
- //#region src/query/utils/countries.d.ts
74
70
  declare const _default: {
75
71
  name: string;
76
72
  'alpha-2': string;
77
73
  'alpha-3': string;
78
74
  'country-code': string;
79
75
  }[];
80
- //#endregion
81
- //#region src/query/constants.d.ts
82
76
  declare const Devices: {
83
77
  readonly MOBILE: "MOBILE";
84
78
  readonly DESKTOP: "DESKTOP";
@@ -90,12 +84,12 @@ declare const SearchTypes: {
90
84
  readonly IMAGE: "image";
91
85
  readonly VIDEO: "video";
92
86
  readonly NEWS: "news";
87
+ readonly DISCOVER: "discover";
88
+ readonly GOOGLE_NEWS: "googleNews";
93
89
  };
94
90
  type SearchType = typeof SearchTypes[keyof typeof SearchTypes];
95
91
  declare const Countries: { [K in (typeof _default)[number]["alpha-3"]]: Lowercase<K> };
96
92
  type Country = typeof Countries[keyof typeof Countries];
97
- //#endregion
98
- //#region src/query/types.d.ts
99
93
  interface DimensionValueMap {
100
94
  query: string;
101
95
  queryCanonical: string;
@@ -110,9 +104,8 @@ interface QueryParamValueMap {
110
104
  searchType: SearchType;
111
105
  }
112
106
  type QueryParamName = keyof QueryParamValueMap;
113
- declare const ColumnBrand: unique symbol;
114
107
  interface Column<D extends Dimension> {
115
- readonly [ColumnBrand]: D;
108
+ readonly __columnBrand: 'gscdump.Column';
116
109
  readonly dimension: D;
117
110
  }
118
111
  type FilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'includingRegex' | 'excludingRegex';
@@ -125,9 +118,8 @@ interface InternalFilter {
125
118
  expression: string;
126
119
  expression2?: string;
127
120
  }
128
- declare const FilterBrand: unique symbol;
129
121
  interface Filter<C = object> {
130
- readonly [FilterBrand]: true;
122
+ readonly __filterBrand: 'gscdump.Filter';
131
123
  readonly _constraints: C;
132
124
  readonly _filters: InternalFilter[];
133
125
  readonly _nestedGroups?: Filter<any>[];
@@ -140,9 +132,8 @@ type GSCRow<D extends Dimension[], C> = { [K in D[number]]: K extends keyof C ?
140
132
  position: number;
141
133
  };
142
134
  type Metric = 'clicks' | 'impressions' | 'ctr' | 'position';
143
- declare const MetricColumnBrand: unique symbol;
144
135
  interface MetricColumn<M extends Metric> {
145
- readonly [MetricColumnBrand]: M;
136
+ readonly __metricColumnBrand: 'gscdump.MetricColumn';
146
137
  readonly metric: M;
147
138
  }
148
139
  interface BuilderState {
@@ -156,8 +147,6 @@ interface BuilderState {
156
147
  rowLimit?: number;
157
148
  startRow?: number;
158
149
  }
159
- //#endregion
160
- //#region src/query/builder.d.ts
161
150
  type SelectableColumn = Column<Dimension> | MetricColumn<Metric>;
162
151
  type OrderableColumn = MetricColumn<Metric> | Column<'date'>;
163
152
  type ExtractDimensions<T extends SelectableColumn[]> = { [K in keyof T]: T[K] extends Column<infer D> ? D : never }[number] extends infer U ? Exclude<U, never>[] : never;
@@ -173,8 +162,6 @@ interface GSCQueryBuilder<D extends Dimension[] = [], C = object> {
173
162
  toBody: () => SearchAnalyticsQuery;
174
163
  getState: () => BuilderState;
175
164
  }
176
- //#endregion
177
- //#region src/core/client.d.ts
178
165
  /**
179
166
  * Compatible interface with OAuth2Client from google-auth-library
180
167
  */
@@ -199,27 +186,31 @@ type Auth = string | {
199
186
  accessToken: string;
200
187
  } | AuthClient | AuthOptions;
201
188
  declare function createFetch(auth: Auth, options?: FetchOptions): $Fetch;
189
+ /** Per-call options. `signal` cancels the in-flight request (and, for `query`, the next page too). */
190
+ interface CallOptions {
191
+ signal?: AbortSignal;
192
+ }
202
193
  interface GoogleSearchConsoleClient {
203
194
  /** Query search analytics with builder, returns async generator yielding typed row batches */
204
- query: <D extends Dimension[], C>(siteUrl: string, builder: GSCQueryBuilder<D, C>) => AsyncGenerator<GSCRow<D, C>[]>;
195
+ query: <D extends Dimension[], C>(siteUrl: string, builder: GSCQueryBuilder<D, C>, opts?: CallOptions) => AsyncGenerator<GSCRow<D, C>[]>;
205
196
  /** List all sites */
206
- sites: () => Promise<ApiSite[]>;
197
+ sites: (opts?: CallOptions) => Promise<ApiSite[]>;
207
198
  /** Inspect a URL */
208
- inspect: (siteUrl: string, url: string) => Promise<InspectUrlIndexResponse>;
199
+ inspect: (siteUrl: string, url: string, opts?: CallOptions) => Promise<InspectUrlIndexResponse>;
209
200
  /** Sitemap operations */
210
201
  sitemaps: {
211
- list: (siteUrl: string) => Promise<ApiSitemap[]>;
212
- get: (siteUrl: string, feedpath: string) => Promise<ApiSitemap>;
213
- submit: (siteUrl: string, feedpath: string) => Promise<void>;
214
- delete: (siteUrl: string, feedpath: string) => Promise<void>;
202
+ list: (siteUrl: string, opts?: CallOptions) => Promise<ApiSitemap[]>;
203
+ get: (siteUrl: string, feedpath: string, opts?: CallOptions) => Promise<ApiSitemap>;
204
+ submit: (siteUrl: string, feedpath: string, opts?: CallOptions) => Promise<void>;
205
+ delete: (siteUrl: string, feedpath: string, opts?: CallOptions) => Promise<void>;
215
206
  };
216
207
  /** Indexing API operations */
217
208
  indexing: {
218
- publish: (url: string, type: 'URL_UPDATED' | 'URL_DELETED') => Promise<PublishUrlNotificationResponse>;
219
- getMetadata: (url: string) => Promise<UrlNotificationMetadata>;
209
+ publish: (url: string, type: 'URL_UPDATED' | 'URL_DELETED', opts?: CallOptions) => Promise<PublishUrlNotificationResponse>;
210
+ getMetadata: (url: string, opts?: CallOptions) => Promise<UrlNotificationMetadata>;
220
211
  };
221
212
  /** @internal */
222
- _rawQuery: (siteUrl: string, body: SearchAnalyticsQuery) => Promise<SearchAnalyticsResponse>;
213
+ _rawQuery: (siteUrl: string, body: SearchAnalyticsQuery, opts?: CallOptions) => Promise<SearchAnalyticsResponse>;
223
214
  }
224
215
  interface GoogleSearchConsoleClientOptions {
225
216
  fetchOptions?: FetchOptions;
@@ -229,8 +220,6 @@ interface GoogleSearchConsoleClientOptions {
229
220
  }) => void | Promise<void>;
230
221
  }
231
222
  declare function googleSearchConsole(auth: Auth, options?: GoogleSearchConsoleClientOptions): GoogleSearchConsoleClient;
232
- //#endregion
233
- //#region src/api/indexing.d.ts
234
223
  type IndexingNotificationType = 'URL_UPDATED' | 'URL_DELETED';
235
224
  interface IndexingResult {
236
225
  url: string;
@@ -264,8 +253,6 @@ declare function batchRequestIndexing(client: GoogleSearchConsoleClient, urls: s
264
253
  delayMs?: number;
265
254
  onProgress?: (result: IndexingResult, index: number, total: number) => void;
266
255
  }): Promise<IndexingResult[]>;
267
- //#endregion
268
- //#region src/api/inspection.d.ts
269
256
  interface InspectUrlResult {
270
257
  url: string;
271
258
  inspection?: UrlInspectionResult;
@@ -286,8 +273,6 @@ declare function batchInspectUrls(client: GoogleSearchConsoleClient, siteUrl: st
286
273
  delayMs?: number;
287
274
  onProgress?: (result: InspectUrlResult, index: number, total: number) => void;
288
275
  }): Promise<InspectUrlResult[]>;
289
- //#endregion
290
- //#region src/api/sites.d.ts
291
276
  /**
292
277
  * Fetches all sites the authenticated user has access to in Google Search Console.
293
278
  */
@@ -314,84 +299,168 @@ declare function submitSitemap(client: GoogleSearchConsoleClient, siteUrl: strin
314
299
  * Deletes a sitemap from Google Search Console.
315
300
  */
316
301
  declare function deleteSitemap(client: GoogleSearchConsoleClient, siteUrl: string, feedpath: string): Promise<void>;
317
- //#endregion
318
- //#region src/core/api-client.d.ts
319
302
  interface GscdumpApiOptions {
320
303
  /** API key (gsd_user_xxx or gsd_prod_xxx) */
321
304
  apiKey: string;
322
305
  /** Base URL (defaults to https://gscdump.com) */
323
306
  baseUrl?: string;
324
307
  }
325
- /**
326
- * Create a client that queries GSC data through gscdump.com API
327
- * instead of directly to Google. Useful when you don't have OAuth credentials.
328
- *
329
- * @example
330
- * ```ts
331
- * const client = gscdumpApi({ apiKey: 'gsd_user_xxx' })
332
- *
333
- * // Same query builder usage as direct client
334
- * for await (const rows of client.query(siteId, gsc.select('page', 'query').where(...))) {
335
- * console.log(rows)
336
- * }
337
- * ```
338
- */
339
308
  declare function gscdumpApi(options: GscdumpApiOptions): GoogleSearchConsoleClient;
340
- //#endregion
341
- //#region src/core/errors.d.ts
342
309
  /**
343
- * GSC API error detection and formatting utilities.
344
- * Provides helpful messages for quota exceeded and rate limit errors.
310
+ * Build a row with default-zeroed metrics. Callers attach dimension keys
311
+ * separately (positional via `row.keys[i]` for the raw GSC API, or named via
312
+ * `row[dim]` for the gscdump.com REST API).
345
313
  */
346
- interface ErrorInfo {
347
- isQuotaError: boolean;
348
- isRateLimitError: boolean;
349
- isAuthError: boolean;
350
- code?: number;
314
+ declare function rowWithMetricDefaults(row: {
315
+ clicks?: number | null;
316
+ impressions?: number | null;
317
+ ctr?: number | null;
318
+ position?: number | null;
319
+ }): {
320
+ clicks: number;
321
+ impressions: number;
322
+ ctr: number;
323
+ position: number;
324
+ };
325
+ declare function progressBar(current: number, total: number, label: string, width?: number): string;
326
+ type GscErrorKind = 'auth-expired' | 'rate-limited' | 'not-found' | 'validation' | 'storage' | 'transport';
327
+ type GscError = {
328
+ kind: 'auth-expired';
329
+ message: string;
330
+ cause: unknown;
331
+ } | {
332
+ kind: 'rate-limited';
351
333
  message: string;
352
334
  retryAfter?: number;
353
- suggestion: string;
354
- }
355
- /** GSC API quota limits (approximate) */
335
+ cause: unknown;
336
+ } | {
337
+ kind: 'not-found';
338
+ message: string;
339
+ cause: unknown;
340
+ } | {
341
+ kind: 'validation';
342
+ message: string;
343
+ cause: unknown;
344
+ } | {
345
+ kind: 'storage';
346
+ message: string;
347
+ cause: unknown;
348
+ } | {
349
+ kind: 'transport';
350
+ message: string;
351
+ status?: number;
352
+ cause: unknown;
353
+ };
354
+ /** Approximate per-day GSC API quotas, used in CLI messaging. */
356
355
  declare const GSC_QUOTAS: {
357
- /** Search Analytics API: ~25,000 requests/day per project */
358
356
  readonly searchAnalytics: 25000;
359
- /** URL Inspection API: ~2,000 requests/day per property */
360
357
  readonly urlInspection: 2000;
361
- /** Indexing API: ~200 requests/day per property */
362
358
  readonly indexing: 200;
363
359
  };
364
360
  /**
365
- * Detects if an error is a quota exceeded error (403 quotaExceeded).
366
- */
367
- declare function isQuotaError(error: unknown): boolean;
368
- /**
369
- * Detects if an error is a rate limit error (429 Too Many Requests).
361
+ * Classify an unknown error into a `GscError` discriminated union.
362
+ * Transport is the catch-all — anything without a recognizable status ends up there.
370
363
  */
371
- declare function isRateLimitError(error: unknown): boolean;
364
+ declare function classifyError(cause: unknown): GscError;
365
+ /** Construct a storage-kind error from inside the analytics engine / adapters. */
366
+ declare function storageError(message: string, cause?: unknown): GscError;
372
367
  /**
373
- * Detects if an error is an authentication error (401/403 without quota).
368
+ * String-matches an error against the signals Google returns when a token
369
+ * has lost access to a property (revoked, downgraded, or never had it).
370
+ * Cheaper than a full {@link classifyError} call when the caller only
371
+ * needs the permission verdict.
374
372
  */
375
- declare function isAuthError(error: unknown): boolean;
373
+ declare function isPermissionDeniedError(err: unknown): boolean;
374
+ /** CLI-facing formatter. Returns an ANSI-colored multi-line string. */
375
+ declare function formatErrorForCli(cause: unknown): string;
376
+ /** Milliseconds in a UTC day. Use for date arithmetic without DST surprises. */
377
+ declare const MS_PER_DAY = 86400000;
378
+ /** Format a Date as YYYY-MM-DD (UTC). */
379
+ declare function toIsoDate(d: Date): string;
380
+ /** Most recent date GSC has fully finalized data for (no further updates). */
381
+ declare const GSC_FINALIZED_LAG_DAYS = 3;
382
+ /** Most recent date GSC will return data for, may still be updating. */
383
+ declare const GSC_FRESHEST_LAG_DAYS = 1;
384
+ /** Approximate historical retention window for Search Analytics. */
385
+ declare const GSC_RETENTION_MONTHS = 16;
386
+ /** Today's date (YYYY-MM-DD) in PST. */
387
+ declare function getPstDate(): string;
388
+ /** YYYY-MM-DD for `now() - n` days, UTC. */
389
+ declare function daysAgo(n: number): string;
390
+ /** Most recent finalized date (3 days ago PST). */
391
+ declare function getLatestGscDate(): string;
392
+ /** Freshest date GSC will return (yesterday PST, may still update). */
393
+ declare function getFreshestGscDate(): string;
376
394
  /**
377
- * Extracts HTTP status code from various error formats.
395
+ * Dates that are still pending (1-3 days ago PST). These need to be re-synced
396
+ * each day until they finalize.
378
397
  */
379
- declare function getErrorCode(error: unknown): number | undefined;
398
+ declare function getPendingDates(): string[];
399
+ /** All dates between two YYYY-MM-DD strings (inclusive, oldest first). */
400
+ declare function getDateRange(startDate: string, endDate: string): string[];
401
+ /** Default span used when chunking long backfills into batches. */
402
+ declare const DAYS_PER_RANGE = 30;
380
403
  /**
381
- * Extracts error message from various error formats.
404
+ * Like {@link getDateRange} but skips dates outside GSC's queryable window
405
+ * (older than retention or newer than the freshest date).
382
406
  */
383
- declare function getErrorMessage(error: unknown): string;
407
+ declare function generateGscDateRange(startDate: string, endDate: string): string[];
384
408
  /**
385
- * Extracts retry-after value from error headers (in seconds).
409
+ * Group a sorted list of dates into contiguous runs, splitting on either a
410
+ * gap or after `daysPerRange` dates accumulate. Useful for bucketing
411
+ * backfill work into bounded jobs.
386
412
  */
387
- declare function getRetryAfter(error: unknown): number | undefined;
388
- /**
389
- * Analyzes an error and returns structured information with suggestions.
390
- */
391
- declare function analyzeError(error: unknown): ErrorInfo;
413
+ declare function groupIntoRanges(dates: string[], daysPerRange?: number): Array<{
414
+ startDate: string;
415
+ endDate: string;
416
+ }>;
417
+ /** Inclusive day count between two YYYY-MM-DD strings. */
418
+ declare function countDays(startDate: string, endDate: string): number;
419
+ /** Oldest date GSC retains (~16 months ago). */
420
+ declare function getOldestGscDate(): string;
421
+ /** Add `n` UTC days to a YYYY-MM-DD string (n may be negative). */
422
+ declare function addDays(dateStr: string, n: number): string;
423
+ declare function getPreviousDate(dateStr: string): string;
424
+ declare function getNextDate(dateStr: string): string;
425
+ /** True if `dateStr` falls within GSC's queryable window. */
426
+ declare function isValidGscDate(dateStr: string): boolean;
427
+ interface BackfillProgress {
428
+ progress: number;
429
+ daysAvailable: number;
430
+ daysSynced: number;
431
+ oldestGscDate: string;
432
+ isComplete: boolean;
433
+ }
392
434
  /**
393
- * Formats an error for CLI display with color codes.
435
+ * Backfill progress (0-1) given the oldest + newest dates synced.
436
+ * Returns null when no sync data exists.
394
437
  */
395
- declare function formatErrorForCli(error: unknown): string;
396
- //#endregion
397
- export { ApiSite, ApiSitemap, ApiSitemapContent, Auth, AuthClient, AuthOptions, DataRow, DimensionFilter, DimensionFilterGroup, ErrorInfo, GSC_QUOTAS, GoogleSearchConsoleClient, GoogleSearchConsoleClientOptions, GscdumpApiOptions, IndexStatusResult, IndexingMetadata, IndexingNotificationType, IndexingResult, InspectUrlIndexResponse, InspectUrlResult, MobileUsabilityResult, Period, PublishUrlNotificationResponse, RequiredNonNullable, ResolvedAnalyticsRange, RichResultsResult, SearchAnalyticsQuery, SearchAnalyticsResponse, Site, SiteAnalytics, UrlInspectionResult, UrlNotificationMetadata, analyzeError, batchInspectUrls, batchRequestIndexing, createAuth, createFetch, deleteSitemap, fetchSitemap, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, formatErrorForCli, getErrorCode, getErrorMessage, getIndexingMetadata, getRetryAfter, googleSearchConsole, gscdumpApi, inspectUrl, isAuthError, isQuotaError, isRateLimitError, requestIndexing, submitSitemap };
438
+ declare function getBackfillProgress(oldestDateSynced: string | null, newestDateSynced: string | null): BackfillProgress | null;
439
+ declare const INDEXING_ISSUE_FILTERS: {
440
+ readonly canonical_mismatch: "user_canonical IS NOT NULL AND google_canonical IS NOT NULL AND user_canonical != google_canonical";
441
+ readonly stale_crawl: "last_crawl_time < datetime('now', '-30 days')";
442
+ readonly very_stale_crawl: "last_crawl_time < datetime('now', '-60 days')";
443
+ readonly not_indexed: "verdict IN ('FAIL', 'PARTIAL', 'NEUTRAL')";
444
+ readonly unknown_to_google: "coverage_state = 'URL is unknown to Google'";
445
+ readonly crawled_not_indexed: "coverage_state LIKE '%Crawled%not indexed%' OR coverage_state LIKE '%Discovered%not indexed%'";
446
+ readonly not_found: "page_fetch_state = 'NOT_FOUND' OR coverage_state = 'Not found (404)'";
447
+ readonly soft_404: "page_fetch_state = 'SOFT_404'";
448
+ readonly server_error: "page_fetch_state = 'SERVER_ERROR'";
449
+ readonly blocked_robots: "robots_txt_state = 'DISALLOWED'";
450
+ readonly noindex: "indexing_state LIKE '%noindex%' OR coverage_state LIKE '%noindex%'";
451
+ readonly redirect: "coverage_state = 'Page with redirect'";
452
+ readonly fragment_url: "url LIKE '%#%'";
453
+ readonly mobile_fail: "mobile_verdict IN ('FAIL', 'PARTIAL')";
454
+ readonly rich_results_fail: "rich_results_verdict = 'FAIL'";
455
+ readonly rich_results_warning: "rich_results_verdict = 'PARTIAL'";
456
+ readonly rich_results_pass: "rich_results_verdict = 'PASS'";
457
+ };
458
+ type IndexingIssueType = keyof typeof INDEXING_ISSUE_FILTERS;
459
+ declare const INDEXING_ISSUE_LABELS: Record<IndexingIssueType, string>;
460
+ declare const INDEXING_ISSUE_SEVERITY: Record<IndexingIssueType, 'error' | 'warning' | 'info'>;
461
+ declare const INDEXING_DAILY_LIMIT = 2000;
462
+ declare const INDEXING_EFFECTIVE_LIMIT = 1800;
463
+ declare function hasGscReadScope(scopes: string | null | undefined): boolean;
464
+ declare function hasGscWriteScope(scopes: string | null | undefined): boolean;
465
+ declare function hasIndexingScope(scopes: string | null | undefined): boolean;
466
+ export { ApiSite, ApiSitemap, ApiSitemapContent, Auth, AuthClient, AuthOptions, BackfillProgress, CallOptions, DAYS_PER_RANGE, DataRow, DimensionFilter, DimensionFilterGroup, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_QUOTAS, GSC_RETENTION_MONTHS, GoogleSearchConsoleClient, GoogleSearchConsoleClientOptions, GscError, GscErrorKind, GscdumpApiOptions, INDEXING_DAILY_LIMIT, INDEXING_EFFECTIVE_LIMIT, INDEXING_ISSUE_FILTERS, INDEXING_ISSUE_LABELS, INDEXING_ISSUE_SEVERITY, IndexStatusResult, IndexingIssueType, IndexingMetadata, IndexingNotificationType, IndexingResult, InspectUrlIndexResponse, InspectUrlResult, MS_PER_DAY, MobileUsabilityResult, Period, PublishUrlNotificationResponse, RequiredNonNullable, ResolvedAnalyticsRange, RichResultsResult, SearchAnalyticsQuery, SearchAnalyticsResponse, Site, SiteAnalytics, UrlInspectionResult, UrlNotificationMetadata, addDays, batchInspectUrls, batchRequestIndexing, classifyError, countDays, createAuth, createFetch, daysAgo, deleteSitemap, fetchSitemap, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, formatErrorForCli, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getIndexingMetadata, getLatestGscDate, getNextDate, getOldestGscDate, getPendingDates, getPreviousDate, getPstDate, googleSearchConsole, groupIntoRanges, gscdumpApi, hasGscReadScope, hasGscWriteScope, hasIndexingScope, inspectUrl, isPermissionDeniedError, isValidGscDate, progressBar, requestIndexing, rowWithMetricDefaults, storageError, submitSitemap, toIsoDate };