@gscdump/analysis 0.38.2 → 0.40.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.
- package/README.md +9 -5
- package/dist/analyzer/index.d.mts +10 -9
- package/dist/analyzer/index.mjs +154 -92
- package/dist/default-registry.mjs +154 -92
- package/dist/index.d.mts +55 -13
- package/dist/index.mjs +186 -101
- package/package.json +4 -5
|
@@ -6,7 +6,7 @@ import { num } from "@gscdump/engine/analysis-types";
|
|
|
6
6
|
import { err, ok, unwrapResult } from "gscdump/result";
|
|
7
7
|
import { between, date, extractDateRange, gsc, page, query } from "gscdump/query";
|
|
8
8
|
import { MS_PER_DAY, daysAgoUtc, toIsoDate } from "gscdump/dates";
|
|
9
|
-
import { buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL,
|
|
9
|
+
import { buildExtrasQueries, buildTotalsSql, mergeExtras, resolveComparisonSQL, resolveToSQLOptimized } from "@gscdump/engine/resolver";
|
|
10
10
|
function rowNumber(value) {
|
|
11
11
|
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
12
12
|
if (typeof value === "bigint") return Number(value);
|
|
@@ -522,8 +522,7 @@ function analyzeBrandSegmentation(keywords, options) {
|
|
|
522
522
|
const impressions = num(row.impressions);
|
|
523
523
|
if (impressions < minImpressions) continue;
|
|
524
524
|
const clicks = num(row.clicks);
|
|
525
|
-
|
|
526
|
-
if (lowerBrandTerms.some((term) => query.includes(term))) {
|
|
525
|
+
if ((row.variants?.length ? row.variants : [row.query ?? row.keyword ?? ""]).some((query) => lowerBrandTerms.some((term) => query.toLowerCase().includes(term)))) {
|
|
527
526
|
brand.push(row);
|
|
528
527
|
brandClicks += clicks;
|
|
529
528
|
brandImpressions += impressions;
|
|
@@ -665,7 +664,7 @@ function createMetricSorter(defaultMetric, orderByMetric) {
|
|
|
665
664
|
return [...items].sort((a, b) => (a[sortBy] - b[sortBy]) * mult);
|
|
666
665
|
};
|
|
667
666
|
}
|
|
668
|
-
const sortRowResults
|
|
667
|
+
const sortRowResults = createSorter((item, metric) => {
|
|
669
668
|
switch (metric) {
|
|
670
669
|
case "clicks": return item.totalClicks;
|
|
671
670
|
case "impressions": return item.totalImpressions;
|
|
@@ -712,7 +711,7 @@ function analyzeCannibalization(rows, options = {}) {
|
|
|
712
711
|
positionSpread
|
|
713
712
|
});
|
|
714
713
|
}
|
|
715
|
-
return sortRowResults
|
|
714
|
+
return sortRowResults(results, sortBy, sortOrder);
|
|
716
715
|
}
|
|
717
716
|
const cannibalizationAnalyzer = defineAnalyzer({
|
|
718
717
|
id: "cannibalization",
|
|
@@ -2250,14 +2249,9 @@ function shapeDataQueryRows(rows, params, extras) {
|
|
|
2250
2249
|
function buildDataDetailPlan(params, options) {
|
|
2251
2250
|
const state = requireBuilderState(params.q, "data-detail");
|
|
2252
2251
|
if (!state.dimensions.includes("date")) throw new Error("data-detail: `date` dimension is required");
|
|
2253
|
-
const main =
|
|
2254
|
-
const totals = buildTotalsSql(state, options);
|
|
2252
|
+
const main = resolveToSQLOptimized(state, options);
|
|
2255
2253
|
const prev = optionalBuilderState(params.qc, "data-detail", "qc");
|
|
2256
|
-
const extraQueries = [
|
|
2257
|
-
name: "totals",
|
|
2258
|
-
sql: totals.sql,
|
|
2259
|
-
params: totals.params
|
|
2260
|
-
}];
|
|
2254
|
+
const extraQueries = [];
|
|
2261
2255
|
if (prev) {
|
|
2262
2256
|
const previousTotals = buildTotalsSql(prev, options);
|
|
2263
2257
|
extraQueries.push({
|
|
@@ -2275,18 +2269,22 @@ function buildDataDetailPlan(params, options) {
|
|
|
2275
2269
|
}
|
|
2276
2270
|
function shapeDataDetailRows(rows, params, extras) {
|
|
2277
2271
|
const { startDate: rangeStart, endDate: rangeEnd } = extractDateRange(requireBuilderState(params.q, "data-detail").filter);
|
|
2278
|
-
const
|
|
2272
|
+
const first = rows[0];
|
|
2273
|
+
const totals = {
|
|
2274
|
+
clicks: Number(first?.totalClicks ?? 0),
|
|
2275
|
+
impressions: Number(first?.totalImpressions ?? 0),
|
|
2276
|
+
ctr: Number(first?.totalCtr ?? 0),
|
|
2277
|
+
position: Number(first?.totalPosition ?? 0)
|
|
2278
|
+
};
|
|
2279
|
+
const coerced = rows.map((raw) => {
|
|
2280
|
+
const { totalCount: _tc, totalClicks: _tclk, totalImpressions: _timp, totalCtr: _tctr, totalPosition: _tpos, sum_position: _sp, ...rest } = raw;
|
|
2281
|
+
return coerceNumericCols(rest);
|
|
2282
|
+
});
|
|
2279
2283
|
const daily = rangeStart && rangeEnd ? padTimeseries(coerced, {
|
|
2280
2284
|
startDate: rangeStart,
|
|
2281
2285
|
endDate: rangeEnd
|
|
2282
2286
|
}) : coerced;
|
|
2283
|
-
const
|
|
2284
|
-
const meta = { totals: {
|
|
2285
|
-
clicks: Number(totalsRow.clicks ?? 0),
|
|
2286
|
-
impressions: Number(totalsRow.impressions ?? 0),
|
|
2287
|
-
ctr: Number(totalsRow.ctr ?? 0),
|
|
2288
|
-
position: Number(totalsRow.position ?? 0)
|
|
2289
|
-
} };
|
|
2287
|
+
const meta = { totals };
|
|
2290
2288
|
if (extras?.prevTotals) {
|
|
2291
2289
|
const previousTotalsRow = extras.prevTotals[0] ?? {};
|
|
2292
2290
|
meta.previousTotals = {
|
|
@@ -2403,7 +2401,69 @@ function paginateInMemory(rows, input) {
|
|
|
2403
2401
|
const o = clampOffset(input.offset);
|
|
2404
2402
|
return rows.slice(o, o + l);
|
|
2405
2403
|
}
|
|
2406
|
-
|
|
2404
|
+
function paginateSortedInMemory(rows, input, compare) {
|
|
2405
|
+
const limit = clampLimit(input.limit, rows.length);
|
|
2406
|
+
const offset = clampOffset(input.offset);
|
|
2407
|
+
const selectedCount = Math.min(rows.length, offset + limit);
|
|
2408
|
+
if (limit === 0 || offset >= rows.length || selectedCount === 0) return [];
|
|
2409
|
+
const fullSort = () => [...rows].sort(compare).slice(offset, offset + limit);
|
|
2410
|
+
if (selectedCount >= rows.length / 2) return fullSort();
|
|
2411
|
+
let invalidComparison = false;
|
|
2412
|
+
const compareStable = (left, right) => {
|
|
2413
|
+
const order = compare(left.value, right.value);
|
|
2414
|
+
if (Number.isNaN(order)) {
|
|
2415
|
+
invalidComparison = true;
|
|
2416
|
+
return left.index - right.index;
|
|
2417
|
+
}
|
|
2418
|
+
return order || left.index - right.index;
|
|
2419
|
+
};
|
|
2420
|
+
const isWorse = (left, right) => compareStable(left, right) > 0;
|
|
2421
|
+
const heap = [];
|
|
2422
|
+
const siftUp = (start) => {
|
|
2423
|
+
let index = start;
|
|
2424
|
+
while (index > 0) {
|
|
2425
|
+
const parent = index - 1 >>> 1;
|
|
2426
|
+
if (!isWorse(heap[index], heap[parent])) break;
|
|
2427
|
+
const swap = heap[parent];
|
|
2428
|
+
heap[parent] = heap[index];
|
|
2429
|
+
heap[index] = swap;
|
|
2430
|
+
index = parent;
|
|
2431
|
+
}
|
|
2432
|
+
};
|
|
2433
|
+
const siftDown = () => {
|
|
2434
|
+
let index = 0;
|
|
2435
|
+
for (;;) {
|
|
2436
|
+
const left = index * 2 + 1;
|
|
2437
|
+
if (left >= heap.length) return;
|
|
2438
|
+
const right = left + 1;
|
|
2439
|
+
let worse = left;
|
|
2440
|
+
if (right < heap.length && isWorse(heap[right], heap[left])) worse = right;
|
|
2441
|
+
if (!isWorse(heap[worse], heap[index])) return;
|
|
2442
|
+
const swap = heap[index];
|
|
2443
|
+
heap[index] = heap[worse];
|
|
2444
|
+
heap[worse] = swap;
|
|
2445
|
+
index = worse;
|
|
2446
|
+
}
|
|
2447
|
+
};
|
|
2448
|
+
for (let index = 0; index < rows.length; index++) {
|
|
2449
|
+
const candidate = {
|
|
2450
|
+
value: rows[index],
|
|
2451
|
+
index
|
|
2452
|
+
};
|
|
2453
|
+
if (heap.length < selectedCount) {
|
|
2454
|
+
heap.push(candidate);
|
|
2455
|
+
siftUp(heap.length - 1);
|
|
2456
|
+
} else if (compareStable(candidate, heap[0]) < 0) {
|
|
2457
|
+
heap[0] = candidate;
|
|
2458
|
+
siftDown();
|
|
2459
|
+
}
|
|
2460
|
+
}
|
|
2461
|
+
if (invalidComparison) return fullSort();
|
|
2462
|
+
heap.sort(compareStable);
|
|
2463
|
+
if (invalidComparison) return fullSort();
|
|
2464
|
+
return heap.slice(offset, offset + limit).map((entry) => entry.value);
|
|
2465
|
+
}
|
|
2466
|
+
const sortResults = createMetricSorter("lostClicks", {
|
|
2407
2467
|
lostClicks: "desc",
|
|
2408
2468
|
declinePercent: "desc",
|
|
2409
2469
|
currentClicks: "asc"
|
|
@@ -2436,7 +2496,7 @@ function analyzeDecay(input, options = {}) {
|
|
|
2436
2496
|
positionDrop: currentPosition != null ? currentPosition - prev.position : null
|
|
2437
2497
|
});
|
|
2438
2498
|
}
|
|
2439
|
-
return sortResults
|
|
2499
|
+
return sortResults(results, sortBy);
|
|
2440
2500
|
}
|
|
2441
2501
|
const decayAnalyzer = defineAnalyzer({
|
|
2442
2502
|
id: "decay",
|
|
@@ -3435,12 +3495,52 @@ function calculateCtrGapScore(actualCtr, position) {
|
|
|
3435
3495
|
const gap = expectedCtr - actualCtr;
|
|
3436
3496
|
return Math.min(gap / expectedCtr, 1);
|
|
3437
3497
|
}
|
|
3438
|
-
const
|
|
3498
|
+
const SORT_DIR = {
|
|
3439
3499
|
opportunityScore: "desc",
|
|
3440
3500
|
potentialClicks: "desc",
|
|
3441
3501
|
impressions: "desc",
|
|
3442
3502
|
position: "asc"
|
|
3443
|
-
}
|
|
3503
|
+
};
|
|
3504
|
+
function analyzeOpportunity(keywords, options = {}) {
|
|
3505
|
+
const minImpressions = options.minImpressions ?? 100;
|
|
3506
|
+
const positionWeight = options.weights?.position ?? 1;
|
|
3507
|
+
const impressionsWeight = options.weights?.impressions ?? 1;
|
|
3508
|
+
const ctrGapWeight = options.weights?.ctrGap ?? 1;
|
|
3509
|
+
const totalWeight = positionWeight + impressionsWeight + ctrGapWeight;
|
|
3510
|
+
const sortBy = options.sortBy ?? "opportunityScore";
|
|
3511
|
+
const results = [];
|
|
3512
|
+
for (const row of keywords) {
|
|
3513
|
+
const impressions = num(row.impressions);
|
|
3514
|
+
if (impressions < minImpressions) continue;
|
|
3515
|
+
const position = num(row.position);
|
|
3516
|
+
const ctr = num(row.ctr);
|
|
3517
|
+
const clicks = num(row.clicks);
|
|
3518
|
+
const positionScore = calculatePositionScore(position);
|
|
3519
|
+
const impressionScore = calculateImpressionScore(impressions);
|
|
3520
|
+
const ctrGapScore = calculateCtrGapScore(ctr, position);
|
|
3521
|
+
const weightedProduct = positionScore ** positionWeight * impressionScore ** impressionsWeight * ctrGapScore ** ctrGapWeight;
|
|
3522
|
+
const opportunityScore = Math.round(weightedProduct ** (1 / totalWeight) * 100);
|
|
3523
|
+
const potentialClicks = Math.round(impressions * getExpectedCtr(Math.min(3, position)));
|
|
3524
|
+
results.push({
|
|
3525
|
+
keyword: row.query,
|
|
3526
|
+
page: row.page ?? null,
|
|
3527
|
+
clicks,
|
|
3528
|
+
impressions,
|
|
3529
|
+
ctr,
|
|
3530
|
+
position,
|
|
3531
|
+
opportunityScore,
|
|
3532
|
+
potentialClicks,
|
|
3533
|
+
factors: {
|
|
3534
|
+
positionScore,
|
|
3535
|
+
impressionScore,
|
|
3536
|
+
ctrGapScore
|
|
3537
|
+
}
|
|
3538
|
+
});
|
|
3539
|
+
}
|
|
3540
|
+
const direction = SORT_DIR[sortBy];
|
|
3541
|
+
results.sort((left, right) => direction === "asc" ? left[sortBy] - right[sortBy] : right[sortBy] - left[sortBy]);
|
|
3542
|
+
return options.limit ? results.slice(0, options.limit) : results;
|
|
3543
|
+
}
|
|
3444
3544
|
const opportunityAnalyzer = defineAnalyzer({
|
|
3445
3545
|
id: "opportunity",
|
|
3446
3546
|
buildSql(params) {
|
|
@@ -3554,51 +3654,15 @@ const opportunityAnalyzer = defineAnalyzer({
|
|
|
3554
3654
|
return { queries: queriesQueryState(periodOf(params), params.limit) };
|
|
3555
3655
|
},
|
|
3556
3656
|
reduceRows(rows, params) {
|
|
3557
|
-
const
|
|
3558
|
-
const
|
|
3559
|
-
const positionWeight = 1;
|
|
3560
|
-
const impressionsWeight = 1;
|
|
3561
|
-
const ctrGapWeight = 1;
|
|
3562
|
-
const sortBy = "opportunityScore";
|
|
3563
|
-
const results = [];
|
|
3564
|
-
for (const row of keywords) {
|
|
3565
|
-
const impressions = num(row.impressions);
|
|
3566
|
-
const position = num(row.position);
|
|
3567
|
-
const ctr = num(row.ctr);
|
|
3568
|
-
const clicks = num(row.clicks);
|
|
3569
|
-
if (impressions < minImpressions) continue;
|
|
3570
|
-
const positionScore = calculatePositionScore(position);
|
|
3571
|
-
const impressionScore = calculateImpressionScore(impressions);
|
|
3572
|
-
const ctrGapScore = calculateCtrGapScore(ctr, position);
|
|
3573
|
-
const geometricMean = (positionScore ** positionWeight * impressionScore ** impressionsWeight * ctrGapScore ** ctrGapWeight) ** (1 / 3);
|
|
3574
|
-
const opportunityScore = Math.round(geometricMean * 100);
|
|
3575
|
-
const targetCtr = getExpectedCtr(Math.min(3, position));
|
|
3576
|
-
const potentialClicks = Math.round(impressions * targetCtr);
|
|
3577
|
-
results.push({
|
|
3578
|
-
keyword: row.query,
|
|
3579
|
-
page: row.page ?? null,
|
|
3580
|
-
clicks,
|
|
3581
|
-
impressions,
|
|
3582
|
-
ctr,
|
|
3583
|
-
position,
|
|
3584
|
-
opportunityScore,
|
|
3585
|
-
potentialClicks,
|
|
3586
|
-
factors: {
|
|
3587
|
-
positionScore,
|
|
3588
|
-
impressionScore,
|
|
3589
|
-
ctrGapScore
|
|
3590
|
-
}
|
|
3591
|
-
});
|
|
3592
|
-
}
|
|
3593
|
-
const sorted = sortResults(results, sortBy);
|
|
3594
|
-
const paged = paginateInMemory(sorted, {
|
|
3657
|
+
const results = analyzeOpportunity((Array.isArray(rows) ? rows : []) ?? [], { minImpressions: params.minImpressions });
|
|
3658
|
+
const paged = paginateSortedInMemory(results, {
|
|
3595
3659
|
limit: params.limit,
|
|
3596
3660
|
offset: params.offset
|
|
3597
|
-
});
|
|
3661
|
+
}, (left, right) => right.opportunityScore - left.opportunityScore);
|
|
3598
3662
|
return {
|
|
3599
3663
|
results: paged,
|
|
3600
3664
|
meta: {
|
|
3601
|
-
total:
|
|
3665
|
+
total: results.length,
|
|
3602
3666
|
returned: paged.length
|
|
3603
3667
|
}
|
|
3604
3668
|
};
|
|
@@ -4270,6 +4334,12 @@ const stlDecomposeAnalyzer = defineAnalyzer({
|
|
|
4270
4334
|
}
|
|
4271
4335
|
});
|
|
4272
4336
|
const DEFAULT_ROW_LIMIT$1 = 25e3;
|
|
4337
|
+
function paginateStrikingDistance(results, options) {
|
|
4338
|
+
return paginateSortedInMemory(results, {
|
|
4339
|
+
limit: options.limit ?? 1e3,
|
|
4340
|
+
offset: options.offset
|
|
4341
|
+
}, (left, right) => right.potentialClicks - left.potentialClicks);
|
|
4342
|
+
}
|
|
4273
4343
|
function filterStrikingDistance(rows, options = {}) {
|
|
4274
4344
|
const minPosition = options.minPosition ?? 4;
|
|
4275
4345
|
const maxPosition = options.maxPosition ?? 20;
|
|
@@ -4300,11 +4370,7 @@ const strikingDistanceAnalyzer = defineAnalyzer({
|
|
|
4300
4370
|
id: "striking-distance",
|
|
4301
4371
|
reduce(rows, params) {
|
|
4302
4372
|
const results = filterStrikingDistance(Array.isArray(rows) ? rows : [], params);
|
|
4303
|
-
|
|
4304
|
-
const paged = paginateInMemory(results, {
|
|
4305
|
-
limit: params.limit ?? 1e3,
|
|
4306
|
-
offset: params.offset
|
|
4307
|
-
});
|
|
4373
|
+
const paged = paginateStrikingDistance(results, params);
|
|
4308
4374
|
return {
|
|
4309
4375
|
results: paged,
|
|
4310
4376
|
meta: {
|
|
@@ -4680,7 +4746,18 @@ const trendsAnalyzer = defineAnalyzer({
|
|
|
4680
4746
|
}
|
|
4681
4747
|
});
|
|
4682
4748
|
const DEFAULT_ROW_LIMIT = 25e3;
|
|
4683
|
-
|
|
4749
|
+
function analyzeZeroClick(rows, options = {}) {
|
|
4750
|
+
const minImpressions = options.minImpressions ?? 1e3;
|
|
4751
|
+
const maxCtr = options.maxCtr ?? .03;
|
|
4752
|
+
const maxPosition = options.maxPosition ?? 10;
|
|
4753
|
+
const queryMap = /* @__PURE__ */ new Map();
|
|
4754
|
+
for (const row of rows) {
|
|
4755
|
+
if (row.impressions < minImpressions || row.position > maxPosition || row.ctr > maxCtr) continue;
|
|
4756
|
+
const existing = queryMap.get(row.query);
|
|
4757
|
+
if (!existing || row.position < existing.position) queryMap.set(row.query, { ...row });
|
|
4758
|
+
}
|
|
4759
|
+
return [...queryMap.values()].sort((left, right) => right.impressions - left.impressions);
|
|
4760
|
+
}
|
|
4684
4761
|
const defaultAnalyzerRegistry = createAnalyzerRegistry({ defined: [
|
|
4685
4762
|
bayesianCtrAnalyzer,
|
|
4686
4763
|
bipartitePagerankAnalyzer,
|
|
@@ -4793,30 +4870,15 @@ const defaultAnalyzerRegistry = createAnalyzerRegistry({ defined: [
|
|
|
4793
4870
|
return { rows: gsc.select(query, page).where(between(date, period.startDate, period.endDate)).limit(limit).getState() };
|
|
4794
4871
|
},
|
|
4795
4872
|
reduceRows(rows, params) {
|
|
4796
|
-
const
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
if (row.impressions < minImpressions) continue;
|
|
4803
|
-
if (row.position > maxPosition) continue;
|
|
4804
|
-
if (row.ctr > maxCtr) continue;
|
|
4805
|
-
const existing = queryMap.get(row.query);
|
|
4806
|
-
if (!existing || row.position < existing.position) queryMap.set(row.query, {
|
|
4807
|
-
query: row.query,
|
|
4808
|
-
page: row.page,
|
|
4809
|
-
clicks: row.clicks,
|
|
4810
|
-
impressions: row.impressions,
|
|
4811
|
-
ctr: row.ctr,
|
|
4812
|
-
position: row.position
|
|
4813
|
-
});
|
|
4814
|
-
}
|
|
4815
|
-
const results = sortRowResults(Array.from(queryMap.values()), "impressions", "desc");
|
|
4816
|
-
const paged = paginateInMemory(results, {
|
|
4873
|
+
const results = analyzeZeroClick(Array.isArray(rows) ? rows : [], {
|
|
4874
|
+
minImpressions: params.minImpressions ?? 1e3,
|
|
4875
|
+
maxCtr: params.maxCtr ?? .03,
|
|
4876
|
+
maxPosition: params.maxPosition ?? 10
|
|
4877
|
+
});
|
|
4878
|
+
const paged = paginateSortedInMemory(results, {
|
|
4817
4879
|
limit: params.limit,
|
|
4818
4880
|
offset: params.offset
|
|
4819
|
-
});
|
|
4881
|
+
}, (left, right) => right.impressions - left.impressions);
|
|
4820
4882
|
return {
|
|
4821
4883
|
results: paged,
|
|
4822
4884
|
meta: {
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Analyzer, Analyzer as Analyzer$1, AnalyzerCapabilityError, AnalyzerRegistry, AnalyzerRegistry as AnalyzerRegistry$1, AnalyzerRegistryInit, AnalyzerVariants, DefineAnalyzerOptions, DefinedAnalyzer, Plan, ReduceContext, ReduceCtx, Reducer, RequiredCapability, RowQueriesPlan, SqlExtraQuery, SqlPlan, SqlPlanSpec, TypedRowQuery, createAnalyzerRegistry, defineAnalyzer, runAnalyzerFromSource } from "@gscdump/engine/analyzer";
|
|
2
|
-
import { AnalysisPeriod, ComparisonMode, ComparisonPeriod, PadTimeseriesOptions, ResolveWindowOptions, ResolvedWindow, WindowPreset, comparisonOf, padTimeseries, periodOf, resolveWindow
|
|
2
|
+
import { AnalysisPeriod, ComparisonMode, ComparisonPeriod, PadTimeseriesOptions, ResolveWindowOptions, ResolvedWindow, WindowPreset, comparisonOf, padTimeseries, periodOf, resolveWindow } from "@gscdump/engine/period";
|
|
3
3
|
import { AnalysisParams, AnalysisParams as AnalysisParams$1, AnalysisResult, AnalysisResult as AnalysisResult$1, AnalysisTool, num } from "@gscdump/engine/analysis-types";
|
|
4
4
|
import { Result } from "gscdump/result";
|
|
5
5
|
import { BuilderState } from "gscdump/query";
|
|
6
|
-
import { AnalysisQuerySource, AnalysisQuerySource as AnalysisQuerySource$1, AnalysisSourceKind, AttachedTableRunner, AttachedTableRunner as AnalyzerRunner, AttachedTableSourceOptions, AttachedTableSourceOptions as BrowserAnalyzeOptions, ENGINE_QUERY_CAPABILITIES, EngineQuerySourceOptions, ExecuteSqlOptions, FileSet, QueryRow, QueryRow as QueryRow$1, SourceCapabilities,
|
|
6
|
+
import { AnalysisQuerySource, AnalysisQuerySource as AnalysisQuerySource$1, AnalysisSourceKind, AttachedTableRunner, AttachedTableRunner as AnalyzerRunner, AttachedTableSourceOptions, AttachedTableSourceOptions as BrowserAnalyzeOptions, ENGINE_QUERY_CAPABILITIES, EngineQuerySourceOptions, ExecuteSqlOptions, FileSet, QueryRow, QueryRow as QueryRow$1, SourceCapabilities, createEngineQuerySource, queryRows, rewriteForTableSource, runAnalyzerWithEngine } from "@gscdump/engine/source";
|
|
7
7
|
import { DefineReportOptions, DefinedReport, DefinedReport as DefinedReport$1, ReportAction, ReportContext, ReportContext as ReportContext$1, ReportFinding, ReportParams, ReportPlanStep, ReportResult, ReportResult as ReportResult$1, ReportSection } from "@gscdump/engine/report";
|
|
8
8
|
import { PlannerCapabilities } from "gscdump/query/plan";
|
|
9
9
|
type ActionSource = 'cannibalization' | 'striking-distance' | 'ctr-anomaly' | 'change-point' | 'opportunity';
|
|
@@ -78,6 +78,13 @@ interface BrandSegmentationOptions {
|
|
|
78
78
|
/** Minimum impressions for a keyword to be included. Default: 10 */
|
|
79
79
|
minImpressions?: number;
|
|
80
80
|
}
|
|
81
|
+
interface BrandSegmentationRow {
|
|
82
|
+
query?: string;
|
|
83
|
+
keyword?: string;
|
|
84
|
+
variants?: readonly string[];
|
|
85
|
+
clicks?: number | null;
|
|
86
|
+
impressions?: number | null;
|
|
87
|
+
}
|
|
81
88
|
interface BrandSummary {
|
|
82
89
|
brandClicks: number;
|
|
83
90
|
nonBrandClicks: number;
|
|
@@ -85,16 +92,17 @@ interface BrandSummary {
|
|
|
85
92
|
brandImpressions: number;
|
|
86
93
|
nonBrandImpressions: number;
|
|
87
94
|
}
|
|
88
|
-
interface BrandSegmentationResult {
|
|
89
|
-
brand:
|
|
90
|
-
nonBrand:
|
|
95
|
+
interface BrandSegmentationResult<T extends BrandSegmentationRow = QueriesRow> {
|
|
96
|
+
brand: T[];
|
|
97
|
+
nonBrand: T[];
|
|
91
98
|
summary: BrandSummary;
|
|
92
99
|
}
|
|
93
100
|
/**
|
|
94
101
|
* Pure helper: segment keywords into brand and non-brand based on provided
|
|
95
102
|
* brand terms. Re-exported from `@gscdump/analysis` for portable callers.
|
|
96
103
|
*/
|
|
97
|
-
declare function analyzeBrandSegmentation(keywords: QueriesRow[], options: BrandSegmentationOptions): BrandSegmentationResult
|
|
104
|
+
declare function analyzeBrandSegmentation(keywords: QueriesRow[], options: BrandSegmentationOptions): BrandSegmentationResult<QueriesRow>;
|
|
105
|
+
declare function analyzeBrandSegmentation<T extends BrandSegmentationRow>(keywords: T[], options: BrandSegmentationOptions): BrandSegmentationResult<T>;
|
|
98
106
|
type CannibalizationSortMetric = 'clicks' | 'impressions' | 'positionSpread' | 'pageCount';
|
|
99
107
|
interface CannibalizationOptions {
|
|
100
108
|
/** Minimum impressions for a query to be considered. Default: 10 */
|
|
@@ -295,6 +303,7 @@ interface MoversResult {
|
|
|
295
303
|
* recent changes between two periods.
|
|
296
304
|
*/
|
|
297
305
|
declare function analyzeMovers(input: MoversInput, options?: MoversOptions): MoversResult;
|
|
306
|
+
type OpportunitySortMetric = 'opportunityScore' | 'potentialClicks' | 'impressions' | 'position';
|
|
298
307
|
interface OpportunityFactors {
|
|
299
308
|
positionScore: number;
|
|
300
309
|
impressionScore: number;
|
|
@@ -311,6 +320,19 @@ interface OpportunityResult {
|
|
|
311
320
|
potentialClicks: number;
|
|
312
321
|
factors: OpportunityFactors;
|
|
313
322
|
}
|
|
323
|
+
interface OpportunityWeights {
|
|
324
|
+
position?: number;
|
|
325
|
+
impressions?: number;
|
|
326
|
+
ctrGap?: number;
|
|
327
|
+
}
|
|
328
|
+
interface OpportunityOptions {
|
|
329
|
+
minImpressions?: number;
|
|
330
|
+
weights?: OpportunityWeights;
|
|
331
|
+
sortBy?: OpportunitySortMetric;
|
|
332
|
+
limit?: number;
|
|
333
|
+
}
|
|
334
|
+
/** Pure opportunity scorer shared by hosted and package analyzer callers. */
|
|
335
|
+
declare function analyzeOpportunity(keywords: QueriesRow[], options?: OpportunityOptions): OpportunityResult[];
|
|
314
336
|
type SeasonalityMetric = 'clicks' | 'impressions';
|
|
315
337
|
interface SeasonalityOptions {
|
|
316
338
|
/** Metric to analyze for seasonality. Default: clicks */
|
|
@@ -371,12 +393,25 @@ interface StrikingDistanceResult {
|
|
|
371
393
|
/** Estimated clicks at ~15% CTR (the average for positions 1–3). */
|
|
372
394
|
potentialClicks: number;
|
|
373
395
|
}
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
396
|
+
interface StrikingDistanceFilterOptions {
|
|
397
|
+
minPosition?: number;
|
|
398
|
+
maxPosition?: number;
|
|
399
|
+
minImpressions?: number;
|
|
400
|
+
maxCtr?: number;
|
|
401
|
+
}
|
|
402
|
+
interface StrikingDistanceOptions extends StrikingDistanceFilterOptions {
|
|
403
|
+
limit?: number;
|
|
404
|
+
offset?: number;
|
|
405
|
+
}
|
|
406
|
+
/** Pure striking-distance analysis for consumers that already own the rows. */
|
|
407
|
+
declare function analyzeStrikingDistance(rows: readonly {
|
|
408
|
+
query: unknown;
|
|
409
|
+
page?: unknown;
|
|
410
|
+
clicks: unknown;
|
|
411
|
+
impressions: unknown;
|
|
412
|
+
ctr: unknown;
|
|
413
|
+
position: unknown;
|
|
414
|
+
}[], options?: StrikingDistanceOptions): StrikingDistanceResult[];
|
|
380
415
|
interface ZeroClickResult {
|
|
381
416
|
query: string;
|
|
382
417
|
page: string;
|
|
@@ -385,6 +420,13 @@ interface ZeroClickResult {
|
|
|
385
420
|
ctr: number;
|
|
386
421
|
position: number;
|
|
387
422
|
}
|
|
423
|
+
interface ZeroClickOptions {
|
|
424
|
+
minImpressions?: number;
|
|
425
|
+
maxCtr?: number;
|
|
426
|
+
maxPosition?: number;
|
|
427
|
+
}
|
|
428
|
+
/** Pure zero-click detector shared by hosted and package analyzer callers. */
|
|
429
|
+
declare function analyzeZeroClick(rows: QueryPageRow[], options?: ZeroClickOptions): ZeroClickResult[];
|
|
388
430
|
declare function analyzeInBrowser(runner: AttachedTableRunner, opts: AttachedTableSourceOptions, params: AnalysisParams$1, registry: AnalyzerRegistry$1): Promise<AnalysisResult$1>;
|
|
389
431
|
/**
|
|
390
432
|
* Default analyzer registry built from every in-tree analyzer. Convenience
|
|
@@ -604,4 +646,4 @@ interface InMemoryQuerySourceOptions {
|
|
|
604
646
|
}
|
|
605
647
|
declare function createInMemoryQuerySource(options: InMemoryQuerySourceOptions): AnalysisQuerySource$1;
|
|
606
648
|
declare const SQL_ANALYZERS: readonly Analyzer$1[];
|
|
607
|
-
export { type ActionPriorityResult, type ActionPrioritySourceState, type ActionPrioritySourceStatus, type ActionSource, type AnalysisError, type AnalysisErrorKind, type AnalysisParams, type AnalysisPeriod, type AnalysisQuerySource, type AnalysisResult, type AnalysisSourceKind, type AnalysisTool, type Analyzer, AnalyzerCapabilityError, type AnalyzerRegistry, type AnalyzerRegistryInit, type AnalyzerRunner, type AnalyzerVariants, type BaseMetrics, type BrandSegmentationOptions, type BrandSegmentationResult, type BrandSummary, type BrowserAnalyzeOptions, type CannibalizationCompetitor, type CannibalizationEvent, type CannibalizationOptions, type CannibalizationPage, type CannibalizationResult, type CannibalizationSortMetric, type ClusterType, type ClusteringOptions, type ClusteringResult, type ComparisonMode, type ComparisonPeriod, type CompositeSourceOptions, type ConcentrationInput, type ConcentrationItem, type ConcentrationOptions, type ConcentrationResult, type ConcentrationRiskLevel, DEFAULT_PRIORITY_SOURCES, type DateRow, type DecayInput, type DecayOptions, type DecayResult, type DecaySeriesPoint, type DecaySortMetric, type DefineAnalyzerOptions, type DefineReportOptions, type DefinedAnalyzer, type DefinedReport, type DryRunReportResult, ENGINE_QUERY_CAPABILITIES, type Effort, type EngineQuerySourceOptions, type ExecuteSqlOptions, type FileSet, type FormatReportOptions, INTENT_CLASSIFIER_VERSION, IN_MEMORY_DEFAULT_CAPABILITIES, type InMemoryQuerySourceOptions, type IntentClassification, type KeywordCluster, type MonthlyData, type MoverData, type MoversInput, type MoversOptions, type MoversResult, type MoversSortMetric, NORMALIZER_VERSION, type OpportunityResult, type PadTimeseriesOptions, type PageRow, type Plan, type PriorityAction, type QueriesRow, type QueryPageRow, type QueryRow, REPORTS, ROW_ANALYZERS, type ReduceContext, type ReduceCtx, type Reducer, type ReportAction, type ReportContext, type ReportFinding, type ReportPlanStep, type ReportResult, type ReportSection, type RequiredCapability, type ResolveWindowOptions, type ResolvedWindow, type RowQueriesPlan, type RunReportOptions, SEARCH_INTENT_CODE, SQL_ANALYZERS, type SearchIntent, type SeasonalityMetric, type SeasonalityOptions, type SeasonalityResult, type SitemapDelta, type SitemapHealthDiff, type SitemapHealthInput, type SitemapHealthRow, type SitemapHealthTotals, type SortOrder, type SourceCapabilities, type SqlExtraQuery, type SqlPlan, type SqlPlanSpec, type StrikingDistanceInputRow, type
|
|
649
|
+
export { type ActionPriorityResult, type ActionPrioritySourceState, type ActionPrioritySourceStatus, type ActionSource, type AnalysisError, type AnalysisErrorKind, type AnalysisParams, type AnalysisPeriod, type AnalysisQuerySource, type AnalysisResult, type AnalysisSourceKind, type AnalysisTool, type Analyzer, AnalyzerCapabilityError, type AnalyzerRegistry, type AnalyzerRegistryInit, type AnalyzerRunner, type AnalyzerVariants, type BaseMetrics, type BrandSegmentationOptions, type BrandSegmentationResult, type BrandSegmentationRow, type BrandSummary, type BrowserAnalyzeOptions, type CannibalizationCompetitor, type CannibalizationEvent, type CannibalizationOptions, type CannibalizationPage, type CannibalizationResult, type CannibalizationSortMetric, type ClusterType, type ClusteringOptions, type ClusteringResult, type ComparisonMode, type ComparisonPeriod, type CompositeSourceOptions, type ConcentrationInput, type ConcentrationItem, type ConcentrationOptions, type ConcentrationResult, type ConcentrationRiskLevel, DEFAULT_PRIORITY_SOURCES, type DateRow, type DecayInput, type DecayOptions, type DecayResult, type DecaySeriesPoint, type DecaySortMetric, type DefineAnalyzerOptions, type DefineReportOptions, type DefinedAnalyzer, type DefinedReport, type DryRunReportResult, ENGINE_QUERY_CAPABILITIES, type Effort, type EngineQuerySourceOptions, type ExecuteSqlOptions, type FileSet, type FormatReportOptions, INTENT_CLASSIFIER_VERSION, IN_MEMORY_DEFAULT_CAPABILITIES, type InMemoryQuerySourceOptions, type IntentClassification, type KeywordCluster, type MonthlyData, type MoverData, type MoversInput, type MoversOptions, type MoversResult, type MoversSortMetric, NORMALIZER_VERSION, type OpportunityFactors, type OpportunityOptions, type OpportunityResult, type OpportunitySortMetric, type OpportunityWeights, type PadTimeseriesOptions, type PageRow, type Plan, type PriorityAction, type QueriesRow, type QueryPageRow, type QueryRow, REPORTS, ROW_ANALYZERS, type ReduceContext, type ReduceCtx, type Reducer, type ReportAction, type ReportContext, type ReportFinding, type ReportPlanStep, type ReportResult, type ReportSection, type RequiredCapability, type ResolveWindowOptions, type ResolvedWindow, type RowQueriesPlan, type RunReportOptions, SEARCH_INTENT_CODE, SQL_ANALYZERS, type SearchIntent, type SeasonalityMetric, type SeasonalityOptions, type SeasonalityResult, type SitemapDelta, type SitemapHealthDiff, type SitemapHealthInput, type SitemapHealthRow, type SitemapHealthTotals, type SortOrder, type SourceCapabilities, type SqlExtraQuery, type SqlPlan, type SqlPlanSpec, type StrikingDistanceInputRow, type StrikingDistanceOptions, type StrikingDistanceResult, type TypedRowQuery, type WindowPreset, type ZeroClickOptions, type ZeroClickResult, analysisErrorToException, analysisErrors, analyzeBrandSegmentation, analyzeCannibalization, analyzeClustering, analyzeConcentration, analyzeDecay, analyzeInBrowser, analyzeKeywordConcentration, analyzeMovers, analyzeOpportunity, analyzePageConcentration, analyzeSeasonality, analyzeStrikingDistance, analyzeZeroClick, classifyQueryIntent, comparisonOf, createAnalyzerRegistry, createCompositeSource, createEngineQuerySource, createInMemoryQuerySource, createSorter, decodeIntent, defaultAnalyzerRegistry, defaultReportRegistry, defineAnalyzer, diffSitemapHealth, dryRunReport, encodeIntent, formatAnalysisError, formatReport, isAnalysisError, mergePriorityActions, normalizePriorityActions, normalizeQuery, num, padTimeseries, periodOf, queryRows, resolveWindow, rewriteForTableSource, runAnalyzerFromSource, runAnalyzerWithEngine, runReport, runReportResult, scorePriorityActions };
|