@gscdump/engine-gsc-api 0.17.2 → 0.17.4

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/dist/index.d.mts CHANGED
@@ -64,4 +64,58 @@ interface GscApiQuerySourceOptions {
64
64
  siteUrl: string;
65
65
  }
66
66
  declare function createGscApiQuerySource(options: GscApiQuerySourceOptions): AnalysisQuerySource;
67
- export { type CreateLiveGscSourceOptions, type FetchTopNOptions, type GscApiQuerySourceOptions, type GscDailyRow, type GscRange, type GscTopNRow, canProxyToGsc, createGscApiQuerySource, createLiveGscSource, fetchGscDaily, fetchGscTopN };
67
+ interface GscApiRow {
68
+ keys: string[];
69
+ clicks: number;
70
+ impressions: number;
71
+ ctr: number;
72
+ position: number;
73
+ }
74
+ interface SyncSliceDomainFilter {
75
+ /**
76
+ * Domain (eTLD+1 + subdomain) to scope the slice to — matches both
77
+ * `www.` and bare variants. Strip the protocol; the regex is built here.
78
+ */
79
+ domain?: string;
80
+ }
81
+ interface RunGscSyncSliceOptions {
82
+ client: GoogleSearchConsoleClient;
83
+ siteUrl: string;
84
+ /** One of the engine sync-fan tables. Drives the dimension list. */
85
+ table: 'pages' | 'keywords' | 'countries' | 'devices' | 'page_keywords';
86
+ startDate: string;
87
+ endDate: string;
88
+ domainFilter?: SyncSliceDomainFilter | null;
89
+ /**
90
+ * Invoked per GSC API page with the rows fetched. Return a promise; the
91
+ * loop awaits it before paging further. Throw inside to abort; AbortError /
92
+ * timeout messages are swallowed so the continuation can re-process.
93
+ */
94
+ onBatch: (rows: GscApiRow[]) => Promise<void>;
95
+ initialStartRow?: number;
96
+ /**
97
+ * Max GSC API pages per call. `Infinity` for unbounded; hosts cap this
98
+ * based on path (D1 vs R2) and table.
99
+ */
100
+ maxPages?: number;
101
+ /** GSC page size. 500 is the D1-safe default; 10k is the R2 path. */
102
+ rowLimit?: number;
103
+ /**
104
+ * Soft CPU budget for the loop itself. Returns `hasMore` when crossed so
105
+ * the continuation resumes from `nextStartRow`.
106
+ */
107
+ cpuBudgetMs?: number;
108
+ searchType?: SearchType;
109
+ /** Invoked once per successful GSC API page. Hosts wire telemetry here. */
110
+ onPage?: (info: {
111
+ searchType: SearchType;
112
+ rowsThisPage: number;
113
+ }) => void;
114
+ }
115
+ interface RunGscSyncSliceResult {
116
+ totalRows: number;
117
+ hasMore: boolean;
118
+ nextStartRow: number;
119
+ }
120
+ declare function runGscSyncSlice(opts: RunGscSyncSliceOptions): Promise<RunGscSyncSliceResult>;
121
+ export { type CreateLiveGscSourceOptions, type FetchTopNOptions, type GscApiQuerySourceOptions, type GscApiRow, type GscDailyRow, type GscRange, type GscTopNRow, type RunGscSyncSliceOptions, type RunGscSyncSliceResult, type SyncSliceDomainFilter, canProxyToGsc, createGscApiQuerySource, createLiveGscSource, fetchGscDaily, fetchGscTopN, runGscSyncSlice };
package/dist/index.mjs CHANGED
@@ -159,4 +159,88 @@ function createLiveGscSource(opts) {
159
159
  }
160
160
  };
161
161
  }
162
- export { canProxyToGsc, createGscApiQuerySource, createLiveGscSource, fetchGscDaily, fetchGscTopN };
162
+ const DIMENSIONS_BY_TABLE = {
163
+ pages: ["page", "date"],
164
+ keywords: ["query", "date"],
165
+ countries: ["country", "date"],
166
+ devices: ["device", "date"],
167
+ page_keywords: [
168
+ "page",
169
+ "query",
170
+ "date"
171
+ ]
172
+ };
173
+ function isTimeoutLike(err) {
174
+ if (!(err instanceof Error)) return false;
175
+ return err.name === "AbortError" || err.message?.includes("timeout") || err.message?.includes("aborted");
176
+ }
177
+ function buildDomainFilterGroups(filter) {
178
+ if (!filter?.domain) return void 0;
179
+ return [{ filters: [{
180
+ dimension: "page",
181
+ operator: "includingRegex",
182
+ expression: `^https?://(www\\.)?${filter.domain.replace(/^www\./, "").replace(/\./g, "\\.")}/`
183
+ }] }];
184
+ }
185
+ async function runGscSyncSlice(opts) {
186
+ const rowLimit = opts.rowLimit ?? 500;
187
+ const cpuBudgetMs = opts.cpuBudgetMs ?? 2e4;
188
+ const maxPages = opts.maxPages ?? Infinity;
189
+ const searchType = opts.searchType ?? "web";
190
+ const dimensions = [...DIMENSIONS_BY_TABLE[opts.table]];
191
+ const dimensionFilterGroups = buildDomainFilterGroups(opts.domainFilter);
192
+ const loopStart = Date.now();
193
+ let startRow = opts.initialStartRow ?? 0;
194
+ let totalRows = 0;
195
+ let pageCount = 0;
196
+ while (true) {
197
+ if (pageCount >= maxPages) return {
198
+ totalRows,
199
+ hasMore: true,
200
+ nextStartRow: startRow
201
+ };
202
+ if (Date.now() - loopStart >= cpuBudgetMs) return {
203
+ totalRows,
204
+ hasMore: true,
205
+ nextStartRow: startRow
206
+ };
207
+ const query = {
208
+ startDate: opts.startDate,
209
+ endDate: opts.endDate,
210
+ dimensions,
211
+ rowLimit,
212
+ startRow,
213
+ dataState: "all",
214
+ searchType,
215
+ ...dimensionFilterGroups ? { dimensionFilterGroups } : {}
216
+ };
217
+ const response = await opts.client._rawQuery(opts.siteUrl, query).catch((err) => {
218
+ if (isTimeoutLike(err)) return null;
219
+ throw err;
220
+ });
221
+ if (!response) return {
222
+ totalRows,
223
+ hasMore: true,
224
+ nextStartRow: startRow
225
+ };
226
+ const rows = response.rows ?? [];
227
+ totalRows += rows.length;
228
+ pageCount++;
229
+ opts.onPage?.({
230
+ searchType,
231
+ rowsThisPage: rows.length
232
+ });
233
+ if (rows.length > 0) await opts.onBatch(rows).catch((err) => {
234
+ if (isTimeoutLike(err)) return;
235
+ throw err;
236
+ });
237
+ if (rows.length < rowLimit) break;
238
+ startRow += rows.length;
239
+ }
240
+ return {
241
+ totalRows,
242
+ hasMore: false,
243
+ nextStartRow: startRow
244
+ };
245
+ }
246
+ export { canProxyToGsc, createGscApiQuerySource, createLiveGscSource, fetchGscDaily, fetchGscTopN, runGscSyncSlice };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/engine-gsc-api",
3
3
  "type": "module",
4
- "version": "0.17.2",
4
+ "version": "0.17.4",
5
5
  "description": "GSC live-API engine adapter — wraps the Search Analytics REST API as an AnalysisQuerySource for typed analyzer dispatch.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -36,8 +36,8 @@
36
36
  "node": ">=18"
37
37
  },
38
38
  "dependencies": {
39
- "@gscdump/engine": "0.17.2",
40
- "gscdump": "0.17.2"
39
+ "@gscdump/engine": "0.17.4",
40
+ "gscdump": "0.17.4"
41
41
  },
42
42
  "devDependencies": {
43
43
  "vitest": "^4.1.6"