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 +7 -5
- package/dist/contracts.d.mts +136 -0
- package/dist/contracts.mjs +1 -0
- package/dist/driver.d.mts +78 -0
- package/dist/driver.mjs +1 -0
- package/dist/index.d.mts +158 -89
- package/dist/index.mjs +389 -257
- package/dist/normalize.d.mts +2 -0
- package/dist/normalize.mjs +16 -0
- package/dist/query/index.d.mts +73 -33
- package/dist/query/index.mjs +238 -190
- package/dist/query/plan.d.mts +130 -0
- package/dist/query/plan.mjs +296 -0
- package/dist/sitemap.d.mts +13 -0
- package/dist/sitemap.mjs +31 -0
- package/dist/tenant.d.mts +18 -0
- package/dist/tenant.mjs +18 -0
- package/dist/url.d.mts +9 -0
- package/dist/url.mjs +6 -0
- package/package.json +43 -9
- package/dist/analysis/index.d.mts +0 -513
- package/dist/analysis/index.mjs +0 -872
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
|
|
159
|
+
## Related
|
|
158
160
|
|
|
159
|
-
- [`@gscdump/cli`](../cli)
|
|
160
|
-
- [`@gscdump/
|
|
161
|
-
- [`@gscdump/
|
|
162
|
-
- [`@gscdump/
|
|
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 };
|
package/dist/driver.mjs
ADDED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
*
|
|
344
|
-
*
|
|
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
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
354
|
-
}
|
|
355
|
-
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
|
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
|
-
*
|
|
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
|
|
407
|
+
declare function generateGscDateRange(startDate: string, endDate: string): string[];
|
|
384
408
|
/**
|
|
385
|
-
*
|
|
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
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
*
|
|
435
|
+
* Backfill progress (0-1) given the oldest + newest dates synced.
|
|
436
|
+
* Returns null when no sync data exists.
|
|
394
437
|
*/
|
|
395
|
-
declare function
|
|
396
|
-
|
|
397
|
-
|
|
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 };
|