@gscdump/engine-gsc-api 0.33.0 → 0.33.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.
- package/dist/index.d.mts +12 -0
- package/dist/index.mjs +53 -16
- package/package.json +3 -3
package/dist/index.d.mts
CHANGED
|
@@ -175,11 +175,23 @@ interface RunGscSearchAppearanceContextSliceOptions {
|
|
|
175
175
|
searchType: SearchType$1;
|
|
176
176
|
rowsThisPage: number;
|
|
177
177
|
}) => void;
|
|
178
|
+
continuation?: SearchAppearanceContinuation;
|
|
178
179
|
}
|
|
180
|
+
type SearchAppearanceContinuation = {
|
|
181
|
+
phase: 'discovery';
|
|
182
|
+
appearances: string[];
|
|
183
|
+
nextStartRow: number;
|
|
184
|
+
} | {
|
|
185
|
+
phase: 'context';
|
|
186
|
+
appearances: string[];
|
|
187
|
+
appearanceIndex: number;
|
|
188
|
+
nextStartRow: number;
|
|
189
|
+
};
|
|
179
190
|
interface RunGscSearchAppearanceContextSliceResult {
|
|
180
191
|
appearances: string[];
|
|
181
192
|
totalRows: number;
|
|
182
193
|
hasMore: boolean;
|
|
194
|
+
continuation?: SearchAppearanceContinuation;
|
|
183
195
|
}
|
|
184
196
|
declare function runGscSyncSlice(opts: RunGscSyncSliceOptions): Promise<RunGscSyncSliceResult>;
|
|
185
197
|
/**
|
package/dist/index.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { googleSearchConsole } from "gscdump/api";
|
|
2
2
|
import { between, clicks, date, extractMetricFilters, extractSpecialOperatorFilters, gsc, normalizeFilter } from "gscdump/query";
|
|
3
|
-
import { assertDimensionsSupported,
|
|
3
|
+
import { assertDimensionsSupported, getFilterDimensions, matchesDimensionFilter, matchesMetricFilter, matchesTopLevelPage, metricValue } from "@gscdump/engine/resolver";
|
|
4
4
|
import { buildLogicalPlan } from "gscdump/query/plan";
|
|
5
5
|
const METRIC_NAMES = [
|
|
6
6
|
"clicks",
|
|
@@ -11,12 +11,20 @@ const METRIC_NAMES = [
|
|
|
11
11
|
function isMetricDimension$1(dim) {
|
|
12
12
|
return METRIC_NAMES.includes(dim);
|
|
13
13
|
}
|
|
14
|
+
function isLocalDimensionFilter(filter) {
|
|
15
|
+
return filter.dimension !== "date" && !isMetricDimension$1(filter.dimension) && filter.operator !== "topLevel" && !filter.operator.startsWith("metric");
|
|
16
|
+
}
|
|
17
|
+
function matchesDimensionFilterTree(row, filter) {
|
|
18
|
+
if (!filter || !("_filters" in filter)) return true;
|
|
19
|
+
const checks = [...filter._filters.filter(isLocalDimensionFilter).map((f) => matchesDimensionFilter(row, f)), ...(filter._nestedGroups ?? []).map((group) => matchesDimensionFilterTree(row, group))];
|
|
20
|
+
if (checks.length === 0) return true;
|
|
21
|
+
return filter._groupType === "or" ? checks.some(Boolean) : checks.every(Boolean);
|
|
22
|
+
}
|
|
14
23
|
function applyBuilderStatePostProcessing(rows, state) {
|
|
15
|
-
const dimensionFilters = getDimensionFilters(state.filter, isMetricDimension$1);
|
|
16
24
|
const metricFilters = extractMetricFilters(state.filter);
|
|
17
25
|
const specialFilters = extractSpecialOperatorFilters(state.filter);
|
|
18
26
|
const ordered = [...rows.filter((row) => {
|
|
19
|
-
if (!
|
|
27
|
+
if (!matchesDimensionFilterTree(row, state.filter)) return false;
|
|
20
28
|
if (!metricFilters.every((filter) => matchesMetricFilter(row, filter))) return false;
|
|
21
29
|
if (specialFilters.some((filter) => filter.operator === "topLevel") && !matchesTopLevelPage(row)) return false;
|
|
22
30
|
return true;
|
|
@@ -49,12 +57,12 @@ async function fetchGscTopN(opts) {
|
|
|
49
57
|
const key = row[dimension.dimension];
|
|
50
58
|
if (typeof key !== "string" || !key) return null;
|
|
51
59
|
const impressions = Number(row.impressions ?? 0);
|
|
52
|
-
const position = Number(row.position ?? 0);
|
|
60
|
+
const position = Math.max(1, Number(row.position ?? 0));
|
|
53
61
|
return {
|
|
54
62
|
key,
|
|
55
63
|
clicks: Number(row.clicks ?? 0),
|
|
56
64
|
impressions,
|
|
57
|
-
sum_position: position * impressions
|
|
65
|
+
sum_position: (position - 1) * impressions
|
|
58
66
|
};
|
|
59
67
|
}).filter((x) => x != null);
|
|
60
68
|
if (!orderByClicksDesc) mapped.sort((a, b) => b.clicks - a.clicks);
|
|
@@ -73,7 +81,7 @@ async function fetchGscDaily(opts) {
|
|
|
73
81
|
date: Date.parse(`${row.date}T00:00:00Z`),
|
|
74
82
|
clicks: row.clicks ?? 0,
|
|
75
83
|
impressions,
|
|
76
|
-
sum_position: (row.position ?? 0) * impressions,
|
|
84
|
+
sum_position: (Math.max(1, row.position ?? 0) - 1) * impressions,
|
|
77
85
|
anonymizedImpressionsPct: 0
|
|
78
86
|
};
|
|
79
87
|
}).filter((x) => x != null).sort((a, b) => a.date - b.date);
|
|
@@ -105,15 +113,18 @@ function createGscApiQuerySource(options) {
|
|
|
105
113
|
buildLogicalPlan(state, GSC_API_CAPABILITIES);
|
|
106
114
|
const filterDims = getFilterDimensions(state.filter, isMetricDimension);
|
|
107
115
|
assertDimensionsSupported([...state.dimensions, ...filterDims], "api", "gsc-api query source");
|
|
108
|
-
|
|
116
|
+
const apiState = {
|
|
117
|
+
...state,
|
|
118
|
+
rowLimit: void 0,
|
|
119
|
+
startRow: void 0
|
|
120
|
+
};
|
|
121
|
+
return applyBuilderStatePostProcessing(await collectRows(client.query(siteUrl, builderFromState(apiState))), state);
|
|
109
122
|
}
|
|
110
123
|
};
|
|
111
124
|
}
|
|
112
125
|
const PRO_ONLY_DIMENSIONS = /* @__PURE__ */ new Set(["queryCanonical", "page_keywords"]);
|
|
113
126
|
function canProxyToGsc(state) {
|
|
114
127
|
if (state.dimensions.some((d) => PRO_ONLY_DIMENSIONS.has(d))) return false;
|
|
115
|
-
if (extractMetricFilters(state.filter).length > 0) return false;
|
|
116
|
-
if (extractSpecialOperatorFilters(state.filter).length > 0) return false;
|
|
117
128
|
return true;
|
|
118
129
|
}
|
|
119
130
|
function withSearchType(state, searchType) {
|
|
@@ -211,7 +222,7 @@ async function runGscSyncSlice(opts) {
|
|
|
211
222
|
const maxPages = opts.maxPages ?? Infinity;
|
|
212
223
|
const searchType = opts.searchType ?? "web";
|
|
213
224
|
const dimensions = opts.dimensions ? [...opts.dimensions] : [...DIMENSIONS_BY_TABLE[opts.table]];
|
|
214
|
-
const dataState = opts.dataState ?? "all";
|
|
225
|
+
const dataState = opts.dataState ?? (dimensions.includes("hour") ? "hourly_all" : "all");
|
|
215
226
|
const dimensionFilterGroups = buildDimensionFilterGroups(opts.domainFilter, opts.dimensionFilters);
|
|
216
227
|
const loopStart = Date.now();
|
|
217
228
|
let startRow = opts.initialStartRow ?? 0;
|
|
@@ -278,7 +289,7 @@ async function runGscSyncSlice(opts) {
|
|
|
278
289
|
searchType,
|
|
279
290
|
rowsThisPage: rows.length
|
|
280
291
|
});
|
|
281
|
-
const isLastPage = rows.length
|
|
292
|
+
const isLastPage = rows.length === 0;
|
|
282
293
|
const nextStartRow = page.startRow + rows.length;
|
|
283
294
|
const prefetch = isLastPage ? null : startFetchIfAllowed(nextStartRow);
|
|
284
295
|
if (rows.length > 0) {
|
|
@@ -323,11 +334,11 @@ function contextTableForGrain(grain) {
|
|
|
323
334
|
}
|
|
324
335
|
async function runGscSearchAppearanceContextSlice(opts) {
|
|
325
336
|
const table = opts.table ?? contextTableForGrain(opts.grain ?? "page_query");
|
|
326
|
-
const appearances = opts.appearances?.slice() ?? [];
|
|
337
|
+
const appearances = opts.continuation?.appearances?.slice() ?? opts.appearances?.slice() ?? [];
|
|
327
338
|
let totalRows = 0;
|
|
328
339
|
let hasMore = false;
|
|
329
|
-
if (!opts.appearances) {
|
|
330
|
-
const discovered =
|
|
340
|
+
if (!opts.appearances && opts.continuation?.phase !== "context") {
|
|
341
|
+
const discovered = new Set(appearances);
|
|
331
342
|
const discovery = await runGscSyncSlice({
|
|
332
343
|
client: opts.client,
|
|
333
344
|
siteUrl: opts.siteUrl,
|
|
@@ -340,6 +351,7 @@ async function runGscSearchAppearanceContextSlice(opts) {
|
|
|
340
351
|
maxPages: opts.maxPages,
|
|
341
352
|
cpuBudgetMs: opts.cpuBudgetMs,
|
|
342
353
|
searchType: opts.searchType,
|
|
354
|
+
initialStartRow: opts.continuation?.phase === "discovery" ? opts.continuation.nextStartRow : void 0,
|
|
343
355
|
onPage: opts.onPage,
|
|
344
356
|
onBatch: async (rows) => {
|
|
345
357
|
for (const row of rows) {
|
|
@@ -350,10 +362,23 @@ async function runGscSearchAppearanceContextSlice(opts) {
|
|
|
350
362
|
}
|
|
351
363
|
});
|
|
352
364
|
totalRows += discovery.totalRows;
|
|
365
|
+
if (discovery.hasMore) return {
|
|
366
|
+
appearances: [...discovered],
|
|
367
|
+
totalRows,
|
|
368
|
+
hasMore: true,
|
|
369
|
+
continuation: {
|
|
370
|
+
phase: "discovery",
|
|
371
|
+
appearances: [...discovered],
|
|
372
|
+
nextStartRow: discovery.nextStartRow
|
|
373
|
+
}
|
|
374
|
+
};
|
|
353
375
|
hasMore ||= discovery.hasMore;
|
|
354
|
-
appearances.
|
|
376
|
+
appearances.splice(0, appearances.length, ...discovered);
|
|
355
377
|
}
|
|
356
|
-
|
|
378
|
+
const startIndex = opts.continuation?.phase === "context" ? opts.continuation.appearanceIndex : 0;
|
|
379
|
+
const startRow = opts.continuation?.phase === "context" ? opts.continuation.nextStartRow : 0;
|
|
380
|
+
for (let i = startIndex; i < appearances.length; i++) {
|
|
381
|
+
const searchAppearance = appearances[i];
|
|
357
382
|
const context = await runGscSyncSlice({
|
|
358
383
|
client: opts.client,
|
|
359
384
|
siteUrl: opts.siteUrl,
|
|
@@ -370,6 +395,7 @@ async function runGscSearchAppearanceContextSlice(opts) {
|
|
|
370
395
|
maxPages: opts.maxPages,
|
|
371
396
|
cpuBudgetMs: opts.cpuBudgetMs,
|
|
372
397
|
searchType: opts.searchType,
|
|
398
|
+
initialStartRow: i === startIndex ? startRow : void 0,
|
|
373
399
|
onPage: opts.onPage,
|
|
374
400
|
onBatch: (rows) => opts.onContextBatch({
|
|
375
401
|
searchAppearance,
|
|
@@ -378,6 +404,17 @@ async function runGscSearchAppearanceContextSlice(opts) {
|
|
|
378
404
|
})
|
|
379
405
|
});
|
|
380
406
|
totalRows += context.totalRows;
|
|
407
|
+
if (context.hasMore) return {
|
|
408
|
+
appearances,
|
|
409
|
+
totalRows,
|
|
410
|
+
hasMore: true,
|
|
411
|
+
continuation: {
|
|
412
|
+
phase: "context",
|
|
413
|
+
appearances,
|
|
414
|
+
appearanceIndex: i,
|
|
415
|
+
nextStartRow: context.nextStartRow
|
|
416
|
+
}
|
|
417
|
+
};
|
|
381
418
|
hasMore ||= context.hasMore;
|
|
382
419
|
}
|
|
383
420
|
return {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/engine-gsc-api",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.33.
|
|
4
|
+
"version": "0.33.3",
|
|
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": "0.33.
|
|
40
|
-
"@gscdump/engine": "0.33.
|
|
39
|
+
"gscdump": "0.33.3",
|
|
40
|
+
"@gscdump/engine": "0.33.3"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"vitest": "^4.1.9"
|