@gscdump/analysis 0.32.12 → 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/analyzer/index.mjs +153 -384
- package/dist/default-registry.mjs +153 -384
- package/dist/index.mjs +267 -609
- package/dist/report/index.mjs +114 -225
- package/dist/semantic/index.d.mts +8 -8
- package/dist/semantic/index.mjs +77 -77
- package/package.json +4 -4
package/dist/index.mjs
CHANGED
|
@@ -225,15 +225,26 @@ function scorePriorityActions(actions) {
|
|
|
225
225
|
actions.sort((a, b) => b.priorityScore - a.priorityScore);
|
|
226
226
|
return actions;
|
|
227
227
|
}
|
|
228
|
-
function
|
|
229
|
-
if (typeof
|
|
230
|
-
if (typeof
|
|
231
|
-
if (
|
|
232
|
-
const n = Number(
|
|
228
|
+
function rowNumber(value) {
|
|
229
|
+
if (typeof value === "number") return Number.isFinite(value) ? value : 0;
|
|
230
|
+
if (typeof value === "bigint") return Number(value);
|
|
231
|
+
if (value == null) return 0;
|
|
232
|
+
const n = Number(value);
|
|
233
233
|
return Number.isFinite(n) ? n : 0;
|
|
234
234
|
}
|
|
235
|
-
function
|
|
236
|
-
return
|
|
235
|
+
function rowString(value) {
|
|
236
|
+
return value == null ? "" : String(value);
|
|
237
|
+
}
|
|
238
|
+
function rowBoolean(value) {
|
|
239
|
+
return value === true || value === 1 || value === "true";
|
|
240
|
+
}
|
|
241
|
+
function parseJsonRows(value) {
|
|
242
|
+
if (Array.isArray(value)) return value;
|
|
243
|
+
if (typeof value === "string" && value.length > 0) {
|
|
244
|
+
const parsed = JSON.parse(value);
|
|
245
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
246
|
+
}
|
|
247
|
+
return [];
|
|
237
248
|
}
|
|
238
249
|
const bayesianCtrAnalyzer = defineAnalyzer$1({
|
|
239
250
|
id: "bayesian-ctr",
|
|
@@ -373,24 +384,24 @@ const bayesianCtrAnalyzer = defineAnalyzer$1({
|
|
|
373
384
|
const arr = Array.isArray(rows) ? rows : [];
|
|
374
385
|
const minImpressions = params.minImpressions ?? 50;
|
|
375
386
|
const results = arr.map((r) => ({
|
|
376
|
-
keyword:
|
|
377
|
-
page:
|
|
378
|
-
clicks:
|
|
379
|
-
impressions:
|
|
380
|
-
observedCtr:
|
|
381
|
-
position:
|
|
382
|
-
bucket:
|
|
383
|
-
priorAlpha:
|
|
384
|
-
priorBeta:
|
|
385
|
-
bucketPriorMean:
|
|
386
|
-
posteriorMean:
|
|
387
|
-
posteriorSd:
|
|
388
|
-
ciLow:
|
|
389
|
-
ciHigh:
|
|
390
|
-
shrinkageDelta:
|
|
391
|
-
expectedClicksDelta:
|
|
392
|
-
significance:
|
|
393
|
-
classification:
|
|
387
|
+
keyword: rowString(r.keyword),
|
|
388
|
+
page: rowString(r.page),
|
|
389
|
+
clicks: rowNumber(r.clicks),
|
|
390
|
+
impressions: rowNumber(r.impressions),
|
|
391
|
+
observedCtr: rowNumber(r.observedCtr),
|
|
392
|
+
position: rowNumber(r.position),
|
|
393
|
+
bucket: rowNumber(r.bucket),
|
|
394
|
+
priorAlpha: rowNumber(r.priorAlpha),
|
|
395
|
+
priorBeta: rowNumber(r.priorBeta),
|
|
396
|
+
bucketPriorMean: rowNumber(r.bucketPriorMean),
|
|
397
|
+
posteriorMean: rowNumber(r.posteriorMean),
|
|
398
|
+
posteriorSd: rowNumber(r.posteriorSd),
|
|
399
|
+
ciLow: rowNumber(r.ciLow),
|
|
400
|
+
ciHigh: rowNumber(r.ciHigh),
|
|
401
|
+
shrinkageDelta: rowNumber(r.shrinkageDelta),
|
|
402
|
+
expectedClicksDelta: rowNumber(r.expectedClicksDelta),
|
|
403
|
+
significance: rowNumber(r.significance),
|
|
404
|
+
classification: rowString(r.classification)
|
|
394
405
|
}));
|
|
395
406
|
const under = results.filter((r) => r.classification === "underperforming").length;
|
|
396
407
|
const over = results.filter((r) => r.classification === "overperforming").length;
|
|
@@ -408,17 +419,6 @@ const bayesianCtrAnalyzer = defineAnalyzer$1({
|
|
|
408
419
|
});
|
|
409
420
|
const BIPARTITE_PAGERANK_ITERATIONS = 25;
|
|
410
421
|
const BIPARTITE_PAGERANK_DAMPING = .85;
|
|
411
|
-
function str$22(v) {
|
|
412
|
-
return v == null ? "" : String(v);
|
|
413
|
-
}
|
|
414
|
-
function parseJsonList$16(v) {
|
|
415
|
-
if (Array.isArray(v)) return v;
|
|
416
|
-
if (typeof v === "string" && v.length > 0) {
|
|
417
|
-
const parsed = JSON.parse(v);
|
|
418
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
419
|
-
}
|
|
420
|
-
return [];
|
|
421
|
-
}
|
|
422
422
|
const bipartitePagerankAnalyzer = defineAnalyzer$1({
|
|
423
423
|
id: "bipartite-pagerank",
|
|
424
424
|
buildSql(params) {
|
|
@@ -624,8 +624,8 @@ const bipartitePagerankAnalyzer = defineAnalyzer$1({
|
|
|
624
624
|
const iterations = BIPARTITE_PAGERANK_ITERATIONS;
|
|
625
625
|
const d = BIPARTITE_PAGERANK_DAMPING;
|
|
626
626
|
const results = arr.map((r) => ({
|
|
627
|
-
kind:
|
|
628
|
-
id:
|
|
627
|
+
kind: rowString(r.kind),
|
|
628
|
+
id: rowString(r.id),
|
|
629
629
|
rank: num$1(r.rank),
|
|
630
630
|
bridging: num$1(r.bridging),
|
|
631
631
|
anchoring: num$1(r.anchoring),
|
|
@@ -635,7 +635,7 @@ const bipartitePagerankAnalyzer = defineAnalyzer$1({
|
|
|
635
635
|
const first = arr[0] ?? {};
|
|
636
636
|
const queryCount = num$1(first.queryCount);
|
|
637
637
|
const urlCount = num$1(first.urlCount);
|
|
638
|
-
const deltas =
|
|
638
|
+
const deltas = parseJsonRows(first.deltasJson).map((e) => ({
|
|
639
639
|
step: num$1(e.step),
|
|
640
640
|
l1: num$1(e.l1)
|
|
641
641
|
}));
|
|
@@ -735,9 +735,6 @@ function analysisErrorToException(error) {
|
|
|
735
735
|
function escapeRegexAlt(s) {
|
|
736
736
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
737
737
|
}
|
|
738
|
-
function str$21(v) {
|
|
739
|
-
return v == null ? "" : String(v);
|
|
740
|
-
}
|
|
741
738
|
function requireBrandTermsResult(brandTerms) {
|
|
742
739
|
return brandTerms?.length ? ok(brandTerms) : err(analysisErrors.missingBrandTerms());
|
|
743
740
|
}
|
|
@@ -813,13 +810,13 @@ const brandAnalyzer = defineAnalyzer$1({
|
|
|
813
810
|
},
|
|
814
811
|
reduceSql(rows) {
|
|
815
812
|
const normalized = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
816
|
-
query:
|
|
817
|
-
page: r.page == null ? void 0 :
|
|
813
|
+
query: rowString(r.query),
|
|
814
|
+
page: r.page == null ? void 0 : rowString(r.page),
|
|
818
815
|
clicks: num$1(r.clicks),
|
|
819
816
|
impressions: num$1(r.impressions),
|
|
820
817
|
ctr: num$1(r.ctr),
|
|
821
818
|
position: num$1(r.position),
|
|
822
|
-
segment:
|
|
819
|
+
segment: rowString(r.segment)
|
|
823
820
|
}));
|
|
824
821
|
let brandClicks = 0;
|
|
825
822
|
let nonBrandClicks = 0;
|
|
@@ -896,17 +893,6 @@ const sortRowResults$1 = createSorter((item, metric) => {
|
|
|
896
893
|
case "pageCount": return item.pages.length;
|
|
897
894
|
}
|
|
898
895
|
}, "clicks");
|
|
899
|
-
function str$20(v) {
|
|
900
|
-
return v == null ? "" : String(v);
|
|
901
|
-
}
|
|
902
|
-
function parseJsonList$15(v) {
|
|
903
|
-
if (Array.isArray(v)) return v;
|
|
904
|
-
if (typeof v === "string" && v.length > 0) {
|
|
905
|
-
const parsed = JSON.parse(v);
|
|
906
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
907
|
-
}
|
|
908
|
-
return [];
|
|
909
|
-
}
|
|
910
896
|
function analyzeCannibalization(rows, options = {}) {
|
|
911
897
|
const { minImpressions = 10, maxPositionSpread = 10, minPages = 2, sortBy = "clicks", sortOrder = "desc" } = options;
|
|
912
898
|
const queryMap = /* @__PURE__ */ new Map();
|
|
@@ -1059,19 +1045,19 @@ const cannibalizationAnalyzer = defineAnalyzer$1({
|
|
|
1059
1045
|
},
|
|
1060
1046
|
reduceSql(rows) {
|
|
1061
1047
|
const events = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
1062
|
-
keyword:
|
|
1048
|
+
keyword: rowString(r.keyword),
|
|
1063
1049
|
totalImpressions: num$1(r.totalImpressions),
|
|
1064
1050
|
totalClicks: num$1(r.totalClicks),
|
|
1065
1051
|
competitorCount: num$1(r.competitorCount),
|
|
1066
|
-
leaderUrl:
|
|
1052
|
+
leaderUrl: rowString(r.leaderUrl),
|
|
1067
1053
|
leaderCtr: num$1(r.leaderCtr),
|
|
1068
1054
|
leaderPosition: num$1(r.leaderPosition),
|
|
1069
1055
|
hhi: num$1(r.hhi),
|
|
1070
1056
|
fragmentation: num$1(r.fragmentation),
|
|
1071
1057
|
stolenClicks: num$1(r.stolenClicks),
|
|
1072
1058
|
severity: num$1(r.severity),
|
|
1073
|
-
competitors:
|
|
1074
|
-
url:
|
|
1059
|
+
competitors: parseJsonRows(r.competitors).map((c) => ({
|
|
1060
|
+
url: rowString(c.url),
|
|
1075
1061
|
clicks: num$1(c.clicks),
|
|
1076
1062
|
impressions: num$1(c.impressions),
|
|
1077
1063
|
ctr: num$1(c.ctr),
|
|
@@ -1148,24 +1134,6 @@ const cannibalizationAnalyzer = defineAnalyzer$1({
|
|
|
1148
1134
|
};
|
|
1149
1135
|
}
|
|
1150
1136
|
});
|
|
1151
|
-
function num$5(v) {
|
|
1152
|
-
if (typeof v === "number") return v;
|
|
1153
|
-
if (typeof v === "bigint") return Number(v);
|
|
1154
|
-
if (v == null) return 0;
|
|
1155
|
-
const n = Number(v);
|
|
1156
|
-
return Number.isFinite(n) ? n : 0;
|
|
1157
|
-
}
|
|
1158
|
-
function str$19(v) {
|
|
1159
|
-
return v == null ? "" : String(v);
|
|
1160
|
-
}
|
|
1161
|
-
function parseJsonList$14(v) {
|
|
1162
|
-
if (Array.isArray(v)) return v;
|
|
1163
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1164
|
-
const parsed = JSON.parse(v);
|
|
1165
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1166
|
-
}
|
|
1167
|
-
return [];
|
|
1168
|
-
}
|
|
1169
1137
|
const changePointAnalyzer = defineAnalyzer$1({
|
|
1170
1138
|
id: "change-point",
|
|
1171
1139
|
buildSql(params) {
|
|
@@ -1318,24 +1286,24 @@ const changePointAnalyzer = defineAnalyzer$1({
|
|
|
1318
1286
|
const metric = params.metric === "clicks" || params.metric === "impressions" ? params.metric : "position";
|
|
1319
1287
|
const lowerIsBetter = metric === "position";
|
|
1320
1288
|
const results = arr.map((r) => {
|
|
1321
|
-
const delta =
|
|
1289
|
+
const delta = rowNumber(r.delta);
|
|
1322
1290
|
const improved = lowerIsBetter ? delta < 0 : delta > 0;
|
|
1323
1291
|
return {
|
|
1324
|
-
keyword:
|
|
1325
|
-
page:
|
|
1326
|
-
totalDays:
|
|
1327
|
-
totalImpressions:
|
|
1328
|
-
changeDate:
|
|
1329
|
-
llr:
|
|
1330
|
-
leftMean:
|
|
1331
|
-
rightMean:
|
|
1292
|
+
keyword: rowString(r.keyword),
|
|
1293
|
+
page: rowString(r.page),
|
|
1294
|
+
totalDays: rowNumber(r.totalDays),
|
|
1295
|
+
totalImpressions: rowNumber(r.totalImpressions),
|
|
1296
|
+
changeDate: rowString(r.changeDate),
|
|
1297
|
+
llr: rowNumber(r.llr),
|
|
1298
|
+
leftMean: rowNumber(r.leftMean),
|
|
1299
|
+
rightMean: rowNumber(r.rightMean),
|
|
1332
1300
|
delta,
|
|
1333
|
-
leftStddev:
|
|
1334
|
-
rightStddev:
|
|
1301
|
+
leftStddev: rowNumber(r.leftStddev),
|
|
1302
|
+
rightStddev: rowNumber(r.rightStddev),
|
|
1335
1303
|
direction: improved ? "improved" : "worsened",
|
|
1336
|
-
series:
|
|
1337
|
-
date:
|
|
1338
|
-
value:
|
|
1304
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
1305
|
+
date: rowString(s.date),
|
|
1306
|
+
value: rowNumber(s.value)
|
|
1339
1307
|
}))
|
|
1340
1308
|
};
|
|
1341
1309
|
});
|
|
@@ -1372,17 +1340,6 @@ const INTENT_PREFIXES = [
|
|
|
1372
1340
|
"near me"
|
|
1373
1341
|
];
|
|
1374
1342
|
const WHITESPACE_RE$2 = /\s+/;
|
|
1375
|
-
function str$18(v) {
|
|
1376
|
-
return v == null ? "" : String(v);
|
|
1377
|
-
}
|
|
1378
|
-
function parseJsonList$13(v) {
|
|
1379
|
-
if (Array.isArray(v)) return v;
|
|
1380
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1381
|
-
const parsed = JSON.parse(v);
|
|
1382
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1383
|
-
}
|
|
1384
|
-
return [];
|
|
1385
|
-
}
|
|
1386
1343
|
function extractIntentPrefix(keyword) {
|
|
1387
1344
|
const lower = keyword.toLowerCase();
|
|
1388
1345
|
for (const prefix of INTENT_PREFIXES) if (lower.startsWith(`${prefix} `) || lower.startsWith(prefix)) return prefix;
|
|
@@ -1520,14 +1477,14 @@ const clusteringAnalyzer = defineAnalyzer$1({
|
|
|
1520
1477
|
},
|
|
1521
1478
|
reduceSql(rows) {
|
|
1522
1479
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
1523
|
-
clusterName:
|
|
1524
|
-
clusterType:
|
|
1480
|
+
clusterName: rowString(r.clusterName),
|
|
1481
|
+
clusterType: rowString(r.clusterType),
|
|
1525
1482
|
keywordCount: num$1(r.keywordCount),
|
|
1526
1483
|
totalClicks: num$1(r.totalClicks),
|
|
1527
1484
|
totalImpressions: num$1(r.totalImpressions),
|
|
1528
1485
|
avgPosition: num$1(r.avgPosition),
|
|
1529
|
-
keywords:
|
|
1530
|
-
query:
|
|
1486
|
+
keywords: parseJsonRows(r.keywords).map((k) => ({
|
|
1487
|
+
query: rowString(k.query),
|
|
1531
1488
|
clicks: num$1(k.clicks),
|
|
1532
1489
|
impressions: num$1(k.impressions),
|
|
1533
1490
|
ctr: num$1(k.ctr),
|
|
@@ -1557,17 +1514,6 @@ const clusteringAnalyzer = defineAnalyzer$1({
|
|
|
1557
1514
|
};
|
|
1558
1515
|
}
|
|
1559
1516
|
});
|
|
1560
|
-
function str$17(v) {
|
|
1561
|
-
return v == null ? "" : String(v);
|
|
1562
|
-
}
|
|
1563
|
-
function parseJsonList$12(v) {
|
|
1564
|
-
if (Array.isArray(v)) return v;
|
|
1565
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1566
|
-
const parsed = JSON.parse(v);
|
|
1567
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1568
|
-
}
|
|
1569
|
-
return [];
|
|
1570
|
-
}
|
|
1571
1517
|
function calculateGini(values) {
|
|
1572
1518
|
if (values.length === 0) return 0;
|
|
1573
1519
|
const sorted = [...values].sort((a, b) => a - b);
|
|
@@ -1708,20 +1654,20 @@ const concentrationAnalyzer = defineAnalyzer$1({
|
|
|
1708
1654
|
},
|
|
1709
1655
|
reduceSql(rows, params) {
|
|
1710
1656
|
const r = (Array.isArray(rows) ? rows : [])[0] ?? {};
|
|
1711
|
-
const topRaw =
|
|
1657
|
+
const topRaw = parseJsonRows(r.topNItems);
|
|
1712
1658
|
return {
|
|
1713
1659
|
results: [{
|
|
1714
1660
|
giniCoefficient: num$1(r.giniCoefficient),
|
|
1715
1661
|
hhi: num$1(r.hhi),
|
|
1716
1662
|
topNConcentration: num$1(r.topNConcentration),
|
|
1717
1663
|
topNItems: topRaw.map((t) => ({
|
|
1718
|
-
key:
|
|
1664
|
+
key: rowString(t.key),
|
|
1719
1665
|
clicks: num$1(t.clicks),
|
|
1720
1666
|
share: num$1(t.share)
|
|
1721
1667
|
})),
|
|
1722
1668
|
totalItems: num$1(r.totalItems),
|
|
1723
1669
|
totalClicks: num$1(r.totalClicks),
|
|
1724
|
-
riskLevel:
|
|
1670
|
+
riskLevel: rowString(r.riskLevel)
|
|
1725
1671
|
}],
|
|
1726
1672
|
meta: {
|
|
1727
1673
|
total: 1,
|
|
@@ -1746,16 +1692,6 @@ const concentrationAnalyzer = defineAnalyzer$1({
|
|
|
1746
1692
|
};
|
|
1747
1693
|
}
|
|
1748
1694
|
});
|
|
1749
|
-
function num$4(v) {
|
|
1750
|
-
if (typeof v === "number") return v;
|
|
1751
|
-
if (typeof v === "bigint") return Number(v);
|
|
1752
|
-
if (v == null) return 0;
|
|
1753
|
-
const n = Number(v);
|
|
1754
|
-
return Number.isFinite(n) ? n : 0;
|
|
1755
|
-
}
|
|
1756
|
-
function str$16(v) {
|
|
1757
|
-
return v == null ? "" : String(v);
|
|
1758
|
-
}
|
|
1759
1695
|
const contentVelocityAnalyzer = defineAnalyzer$1({
|
|
1760
1696
|
id: "content-velocity",
|
|
1761
1697
|
buildSql(params) {
|
|
@@ -1812,9 +1748,9 @@ const contentVelocityAnalyzer = defineAnalyzer$1({
|
|
|
1812
1748
|
startDateD.setUTCDate(startDateD.getUTCDate() - days);
|
|
1813
1749
|
const startDate = toIsoDate(startDateD);
|
|
1814
1750
|
const weekly = arr.map((r) => ({
|
|
1815
|
-
week:
|
|
1816
|
-
newKeywords:
|
|
1817
|
-
totalKeywords:
|
|
1751
|
+
week: rowString(r.week),
|
|
1752
|
+
newKeywords: rowNumber(r.newKeywords),
|
|
1753
|
+
totalKeywords: rowNumber(r.totalKeywords)
|
|
1818
1754
|
}));
|
|
1819
1755
|
const total = weekly.reduce((s, w) => s + w.newKeywords, 0);
|
|
1820
1756
|
const avg = weekly.length > 0 ? total / weekly.length : 0;
|
|
@@ -1837,27 +1773,6 @@ const contentVelocityAnalyzer = defineAnalyzer$1({
|
|
|
1837
1773
|
};
|
|
1838
1774
|
}
|
|
1839
1775
|
});
|
|
1840
|
-
function num$3(v) {
|
|
1841
|
-
if (typeof v === "number") return v;
|
|
1842
|
-
if (typeof v === "bigint") return Number(v);
|
|
1843
|
-
if (v == null) return 0;
|
|
1844
|
-
const n = Number(v);
|
|
1845
|
-
return Number.isFinite(n) ? n : 0;
|
|
1846
|
-
}
|
|
1847
|
-
function str$15(v) {
|
|
1848
|
-
return v == null ? "" : String(v);
|
|
1849
|
-
}
|
|
1850
|
-
function bool$2(v) {
|
|
1851
|
-
return v === true || v === 1 || v === "true";
|
|
1852
|
-
}
|
|
1853
|
-
function parseJsonList$11(v) {
|
|
1854
|
-
if (Array.isArray(v)) return v;
|
|
1855
|
-
if (typeof v === "string" && v.length > 0) {
|
|
1856
|
-
const parsed = JSON.parse(v);
|
|
1857
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1858
|
-
}
|
|
1859
|
-
return [];
|
|
1860
|
-
}
|
|
1861
1776
|
const ctrAnomalyAnalyzer = defineAnalyzer$1({
|
|
1862
1777
|
id: "ctr-anomaly",
|
|
1863
1778
|
buildSql(params) {
|
|
@@ -1999,26 +1914,26 @@ const ctrAnomalyAnalyzer = defineAnalyzer$1({
|
|
|
1999
1914
|
const minRollingN = 14;
|
|
2000
1915
|
const zThreshold = params.threshold ?? 2;
|
|
2001
1916
|
const anomalies = arr.map((r) => ({
|
|
2002
|
-
keyword:
|
|
2003
|
-
page:
|
|
2004
|
-
breachDaysDown:
|
|
2005
|
-
breachDaysUp:
|
|
2006
|
-
clicksLost:
|
|
2007
|
-
severity:
|
|
2008
|
-
maxZ:
|
|
2009
|
-
baselineCtr:
|
|
2010
|
-
baselinePosition:
|
|
2011
|
-
totalImpressions:
|
|
2012
|
-
totalClicks:
|
|
2013
|
-
series:
|
|
2014
|
-
date:
|
|
2015
|
-
ctr:
|
|
2016
|
-
position:
|
|
2017
|
-
impressions:
|
|
2018
|
-
rollingCtr: s.rollingCtr == null ? null :
|
|
2019
|
-
rollingStddev: s.rollingStddev == null ? null :
|
|
2020
|
-
z:
|
|
2021
|
-
breach:
|
|
1917
|
+
keyword: rowString(r.keyword),
|
|
1918
|
+
page: rowString(r.page),
|
|
1919
|
+
breachDaysDown: rowNumber(r.breachDaysDown),
|
|
1920
|
+
breachDaysUp: rowNumber(r.breachDaysUp),
|
|
1921
|
+
clicksLost: rowNumber(r.clicksLost),
|
|
1922
|
+
severity: rowNumber(r.severityRaw),
|
|
1923
|
+
maxZ: rowNumber(r.maxZ),
|
|
1924
|
+
baselineCtr: rowNumber(r.baselineCtr),
|
|
1925
|
+
baselinePosition: rowNumber(r.baselinePosition),
|
|
1926
|
+
totalImpressions: rowNumber(r.totalImpressions),
|
|
1927
|
+
totalClicks: rowNumber(r.totalClicks),
|
|
1928
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
1929
|
+
date: rowString(s.date),
|
|
1930
|
+
ctr: rowNumber(s.ctr),
|
|
1931
|
+
position: rowNumber(s.position),
|
|
1932
|
+
impressions: rowNumber(s.impressions),
|
|
1933
|
+
rollingCtr: s.rollingCtr == null ? null : rowNumber(s.rollingCtr),
|
|
1934
|
+
rollingStddev: s.rollingStddev == null ? null : rowNumber(s.rollingStddev),
|
|
1935
|
+
z: rowNumber(s.z),
|
|
1936
|
+
breach: rowBoolean(s.breach)
|
|
2022
1937
|
}))
|
|
2023
1938
|
}));
|
|
2024
1939
|
const totalClicksLost = anomalies.reduce((s, a) => s + a.clicksLost, 0);
|
|
@@ -2035,24 +1950,6 @@ const ctrAnomalyAnalyzer = defineAnalyzer$1({
|
|
|
2035
1950
|
};
|
|
2036
1951
|
}
|
|
2037
1952
|
});
|
|
2038
|
-
function num$2(v) {
|
|
2039
|
-
if (typeof v === "number") return v;
|
|
2040
|
-
if (typeof v === "bigint") return Number(v);
|
|
2041
|
-
if (v == null) return 0;
|
|
2042
|
-
const n = Number(v);
|
|
2043
|
-
return Number.isFinite(n) ? n : 0;
|
|
2044
|
-
}
|
|
2045
|
-
function str$14(v) {
|
|
2046
|
-
return v == null ? "" : String(v);
|
|
2047
|
-
}
|
|
2048
|
-
function parseJsonList$10(v) {
|
|
2049
|
-
if (Array.isArray(v)) return v;
|
|
2050
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2051
|
-
const parsed = JSON.parse(v);
|
|
2052
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2053
|
-
}
|
|
2054
|
-
return [];
|
|
2055
|
-
}
|
|
2056
1953
|
const ctrCurveAnalyzer = defineAnalyzer$1({
|
|
2057
1954
|
id: "ctr-curve",
|
|
2058
1955
|
buildSql(params) {
|
|
@@ -2147,22 +2044,22 @@ const ctrCurveAnalyzer = defineAnalyzer$1({
|
|
|
2147
2044
|
const arr = Array.isArray(rows) ? rows : [];
|
|
2148
2045
|
const { startDate, endDate } = periodOf$1(params);
|
|
2149
2046
|
const row = arr[0] ?? {};
|
|
2150
|
-
const curve =
|
|
2151
|
-
bucket:
|
|
2152
|
-
avgCtr:
|
|
2153
|
-
medianPosition:
|
|
2154
|
-
keywordCount:
|
|
2155
|
-
totalClicks:
|
|
2156
|
-
totalImpressions:
|
|
2047
|
+
const curve = parseJsonRows(row.curve_json).map((r) => ({
|
|
2048
|
+
bucket: rowString(r.bucket),
|
|
2049
|
+
avgCtr: rowNumber(r.avgCtr),
|
|
2050
|
+
medianPosition: rowNumber(r.medianPosition),
|
|
2051
|
+
keywordCount: rowNumber(r.keywordCount),
|
|
2052
|
+
totalClicks: rowNumber(r.totalClicks),
|
|
2053
|
+
totalImpressions: rowNumber(r.totalImpressions)
|
|
2157
2054
|
}));
|
|
2158
|
-
const outliers =
|
|
2159
|
-
query:
|
|
2160
|
-
clicks:
|
|
2161
|
-
impressions:
|
|
2162
|
-
ctr:
|
|
2163
|
-
position:
|
|
2164
|
-
expectedCtr:
|
|
2165
|
-
ctrDiff:
|
|
2055
|
+
const outliers = parseJsonRows(row.outliers_json).map((r) => ({
|
|
2056
|
+
query: rowString(r.query),
|
|
2057
|
+
clicks: rowNumber(r.clicks),
|
|
2058
|
+
impressions: rowNumber(r.impressions),
|
|
2059
|
+
ctr: rowNumber(r.ctr),
|
|
2060
|
+
position: rowNumber(r.position),
|
|
2061
|
+
expectedCtr: rowNumber(r.expectedCtr),
|
|
2062
|
+
ctrDiff: rowNumber(r.ctrDiff)
|
|
2166
2063
|
}));
|
|
2167
2064
|
return {
|
|
2168
2065
|
results: curve,
|
|
@@ -2175,17 +2072,6 @@ const ctrCurveAnalyzer = defineAnalyzer$1({
|
|
|
2175
2072
|
};
|
|
2176
2073
|
}
|
|
2177
2074
|
});
|
|
2178
|
-
function str$13(v) {
|
|
2179
|
-
return v == null ? "" : String(v);
|
|
2180
|
-
}
|
|
2181
|
-
function parseJsonList$9(v) {
|
|
2182
|
-
if (Array.isArray(v)) return v;
|
|
2183
|
-
if (typeof v === "string" && v.length > 0) {
|
|
2184
|
-
const parsed = JSON.parse(v);
|
|
2185
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
2186
|
-
}
|
|
2187
|
-
return [];
|
|
2188
|
-
}
|
|
2189
2075
|
const darkTrafficAnalyzer = defineAnalyzer$1({
|
|
2190
2076
|
id: "dark-traffic",
|
|
2191
2077
|
buildSql(params) {
|
|
@@ -2287,8 +2173,8 @@ const darkTrafficAnalyzer = defineAnalyzer$1({
|
|
|
2287
2173
|
const darkClicks = Math.max(0, totalClicks - attributedClicks);
|
|
2288
2174
|
const darkPercent = totalClicks > 0 ? darkClicks / totalClicks : 0;
|
|
2289
2175
|
return {
|
|
2290
|
-
results:
|
|
2291
|
-
url:
|
|
2176
|
+
results: parseJsonRows(row.pages_json).map((r) => ({
|
|
2177
|
+
url: rowString(r.url),
|
|
2292
2178
|
totalClicks: num$1(r.totalClicks),
|
|
2293
2179
|
attributedClicks: num$1(r.attributedClicks),
|
|
2294
2180
|
darkClicks: num$1(r.darkClicks),
|
|
@@ -3040,17 +2926,6 @@ const sortResults$1 = createMetricSorter("lostClicks", {
|
|
|
3040
2926
|
declinePercent: "desc",
|
|
3041
2927
|
currentClicks: "asc"
|
|
3042
2928
|
});
|
|
3043
|
-
function str$12(v) {
|
|
3044
|
-
return v == null ? "" : String(v);
|
|
3045
|
-
}
|
|
3046
|
-
function parseJsonList$8(v) {
|
|
3047
|
-
if (Array.isArray(v)) return v;
|
|
3048
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3049
|
-
const parsed = JSON.parse(v);
|
|
3050
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3051
|
-
}
|
|
3052
|
-
return [];
|
|
3053
|
-
}
|
|
3054
2929
|
function analyzeDecay(input, options = {}) {
|
|
3055
2930
|
const { minPreviousClicks = 50, threshold = .2, sortBy = "lostClicks" } = options;
|
|
3056
2931
|
const currentMap = buildPeriodMap(input.current, (r) => r.page, (r) => ({
|
|
@@ -3177,7 +3052,7 @@ const decayAnalyzer = defineAnalyzer$1({
|
|
|
3177
3052
|
},
|
|
3178
3053
|
reduceSql(rows, params) {
|
|
3179
3054
|
const mapped = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3180
|
-
page:
|
|
3055
|
+
page: rowString(r.page),
|
|
3181
3056
|
currentClicks: num$1(r.currentClicks),
|
|
3182
3057
|
previousClicks: num$1(r.previousClicks),
|
|
3183
3058
|
lostClicks: num$1(r.lostClicks),
|
|
@@ -3185,8 +3060,8 @@ const decayAnalyzer = defineAnalyzer$1({
|
|
|
3185
3060
|
currentPosition: num$1(r.currentPosition),
|
|
3186
3061
|
previousPosition: num$1(r.previousPosition),
|
|
3187
3062
|
positionDrop: num$1(r.positionDrop),
|
|
3188
|
-
series:
|
|
3189
|
-
week:
|
|
3063
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
3064
|
+
week: rowString(s.week),
|
|
3190
3065
|
clicks: num$1(s.clicks),
|
|
3191
3066
|
impressions: num$1(s.impressions)
|
|
3192
3067
|
}))
|
|
@@ -3224,9 +3099,6 @@ const decayAnalyzer = defineAnalyzer$1({
|
|
|
3224
3099
|
};
|
|
3225
3100
|
}
|
|
3226
3101
|
});
|
|
3227
|
-
function str$11(v) {
|
|
3228
|
-
return v == null ? "" : String(v);
|
|
3229
|
-
}
|
|
3230
3102
|
const deviceGapAnalyzer = defineAnalyzer$1({
|
|
3231
3103
|
id: "device-gap",
|
|
3232
3104
|
buildSql(params) {
|
|
@@ -3277,8 +3149,8 @@ const deviceGapAnalyzer = defineAnalyzer$1({
|
|
|
3277
3149
|
const arr = Array.isArray(rows) ? rows : [];
|
|
3278
3150
|
const { startDate, endDate } = periodOf$1(params);
|
|
3279
3151
|
const typed = arr.map((r) => ({
|
|
3280
|
-
date:
|
|
3281
|
-
device:
|
|
3152
|
+
date: rowString(r.date),
|
|
3153
|
+
device: rowString(r.device).toUpperCase(),
|
|
3282
3154
|
clicks: num$1(r.clicks),
|
|
3283
3155
|
impressions: num$1(r.impressions),
|
|
3284
3156
|
ctr: num$1(r.ctr),
|
|
@@ -3356,17 +3228,6 @@ const deviceGapAnalyzer = defineAnalyzer$1({
|
|
|
3356
3228
|
};
|
|
3357
3229
|
}
|
|
3358
3230
|
});
|
|
3359
|
-
function str$10(v) {
|
|
3360
|
-
return v == null ? "" : String(v);
|
|
3361
|
-
}
|
|
3362
|
-
function parseJsonList$7(v) {
|
|
3363
|
-
if (Array.isArray(v)) return v;
|
|
3364
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3365
|
-
const parsed = JSON.parse(v);
|
|
3366
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3367
|
-
}
|
|
3368
|
-
return [];
|
|
3369
|
-
}
|
|
3370
3231
|
const INTENT_ATLAS_STOP_WORDS = [
|
|
3371
3232
|
"the",
|
|
3372
3233
|
"a",
|
|
@@ -3509,14 +3370,14 @@ const intentAtlasAnalyzer = defineAnalyzer$1({
|
|
|
3509
3370
|
},
|
|
3510
3371
|
reduceSql(rows) {
|
|
3511
3372
|
const clusters = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3512
|
-
clusterKey:
|
|
3373
|
+
clusterKey: rowString(r.clusterKey),
|
|
3513
3374
|
keywordCount: num$1(r.keywordCount),
|
|
3514
3375
|
totalImpressions: num$1(r.totalImpressions),
|
|
3515
3376
|
totalClicks: num$1(r.totalClicks),
|
|
3516
3377
|
ctr: num$1(r.ctr),
|
|
3517
3378
|
avgPosition: num$1(r.avgPosition),
|
|
3518
|
-
keywords:
|
|
3519
|
-
query:
|
|
3379
|
+
keywords: parseJsonRows(r.keywords).slice(0, 25).map((k) => ({
|
|
3380
|
+
query: rowString(k.query),
|
|
3520
3381
|
impressions: num$1(k.impressions),
|
|
3521
3382
|
clicks: num$1(k.clicks),
|
|
3522
3383
|
position: num$1(k.position)
|
|
@@ -3534,17 +3395,6 @@ const intentAtlasAnalyzer = defineAnalyzer$1({
|
|
|
3534
3395
|
};
|
|
3535
3396
|
}
|
|
3536
3397
|
});
|
|
3537
|
-
function str$9(v) {
|
|
3538
|
-
return v == null ? "" : String(v);
|
|
3539
|
-
}
|
|
3540
|
-
function parseJsonList$6(v) {
|
|
3541
|
-
if (Array.isArray(v)) return v;
|
|
3542
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3543
|
-
const parsed = JSON.parse(v);
|
|
3544
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3545
|
-
}
|
|
3546
|
-
return [];
|
|
3547
|
-
}
|
|
3548
3398
|
const keywordBreadthAnalyzer = defineAnalyzer$1({
|
|
3549
3399
|
id: "keyword-breadth",
|
|
3550
3400
|
buildSql(params) {
|
|
@@ -3619,18 +3469,18 @@ const keywordBreadthAnalyzer = defineAnalyzer$1({
|
|
|
3619
3469
|
const arr = Array.isArray(rows) ? rows : [];
|
|
3620
3470
|
const { startDate, endDate } = periodOf$1(params);
|
|
3621
3471
|
const row = arr[0] ?? {};
|
|
3622
|
-
const distribution =
|
|
3623
|
-
bucket:
|
|
3472
|
+
const distribution = parseJsonRows(row.distribution_json).sort((a, b) => num$1(a.sortKey) - num$1(b.sortKey)).map((r) => ({
|
|
3473
|
+
bucket: rowString(r.bucket),
|
|
3624
3474
|
pageCount: num$1(r.pageCount)
|
|
3625
3475
|
}));
|
|
3626
|
-
const fragile =
|
|
3627
|
-
url:
|
|
3476
|
+
const fragile = parseJsonRows(row.fragile_json).map((r) => ({
|
|
3477
|
+
url: rowString(r.url),
|
|
3628
3478
|
keywordCount: num$1(r.keywordCount),
|
|
3629
3479
|
clicks: num$1(r.clicks),
|
|
3630
3480
|
impressions: num$1(r.impressions)
|
|
3631
3481
|
}));
|
|
3632
|
-
const authority =
|
|
3633
|
-
url:
|
|
3482
|
+
const authority = parseJsonRows(row.authority_json).map((r) => ({
|
|
3483
|
+
url: rowString(r.url),
|
|
3634
3484
|
keywordCount: num$1(r.keywordCount),
|
|
3635
3485
|
clicks: num$1(r.clicks),
|
|
3636
3486
|
impressions: num$1(r.impressions)
|
|
@@ -3653,23 +3503,12 @@ const keywordBreadthAnalyzer = defineAnalyzer$1({
|
|
|
3653
3503
|
};
|
|
3654
3504
|
}
|
|
3655
3505
|
});
|
|
3656
|
-
function str$8(v) {
|
|
3657
|
-
return v == null ? "" : String(v);
|
|
3658
|
-
}
|
|
3659
|
-
function parseJsonList$5(v) {
|
|
3660
|
-
if (Array.isArray(v)) return v;
|
|
3661
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3662
|
-
const parsed = JSON.parse(v);
|
|
3663
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3664
|
-
}
|
|
3665
|
-
return [];
|
|
3666
|
-
}
|
|
3667
3506
|
function downsampleLogRank(points) {
|
|
3668
3507
|
const all = points.map((p) => ({
|
|
3669
3508
|
rank: num$1(p.rank),
|
|
3670
3509
|
impressions: num$1(p.impressions),
|
|
3671
3510
|
clicks: num$1(p.clicks),
|
|
3672
|
-
query:
|
|
3511
|
+
query: rowString(p.query)
|
|
3673
3512
|
}));
|
|
3674
3513
|
if (all.length <= 80) return all;
|
|
3675
3514
|
const top = all.slice(0, 10);
|
|
@@ -3778,7 +3617,7 @@ const longTailAnalyzer = defineAnalyzer$1({
|
|
|
3778
3617
|
},
|
|
3779
3618
|
reduceSql(rows) {
|
|
3780
3619
|
const results = (Array.isArray(rows) ? rows : []).map((r) => ({
|
|
3781
|
-
page:
|
|
3620
|
+
page: rowString(r.page),
|
|
3782
3621
|
queryCount: num$1(r.queryCount),
|
|
3783
3622
|
totalImpressions: num$1(r.totalImpressions),
|
|
3784
3623
|
totalClicks: num$1(r.totalClicks),
|
|
@@ -3787,8 +3626,8 @@ const longTailAnalyzer = defineAnalyzer$1({
|
|
|
3787
3626
|
r2: num$1(r.r2),
|
|
3788
3627
|
headImpressions: num$1(r.headImpressions),
|
|
3789
3628
|
headShare: num$1(r.headShare),
|
|
3790
|
-
fingerprint:
|
|
3791
|
-
points: downsampleLogRank(
|
|
3629
|
+
fingerprint: rowString(r.fingerprint),
|
|
3630
|
+
points: downsampleLogRank(parseJsonRows(r.pointsJson))
|
|
3792
3631
|
}));
|
|
3793
3632
|
const counts = {
|
|
3794
3633
|
"flat-tail": 0,
|
|
@@ -3806,17 +3645,6 @@ const longTailAnalyzer = defineAnalyzer$1({
|
|
|
3806
3645
|
};
|
|
3807
3646
|
}
|
|
3808
3647
|
});
|
|
3809
|
-
function str$7(v) {
|
|
3810
|
-
return v == null ? "" : String(v);
|
|
3811
|
-
}
|
|
3812
|
-
function parseJsonList$4(v) {
|
|
3813
|
-
if (Array.isArray(v)) return v;
|
|
3814
|
-
if (typeof v === "string" && v.length > 0) {
|
|
3815
|
-
const parsed = JSON.parse(v);
|
|
3816
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
3817
|
-
}
|
|
3818
|
-
return [];
|
|
3819
|
-
}
|
|
3820
3648
|
function withCanonicalFields(r) {
|
|
3821
3649
|
return {
|
|
3822
3650
|
...r,
|
|
@@ -4042,8 +3870,8 @@ const moversAnalyzer = defineAnalyzer$1({
|
|
|
4042
3870
|
const baselineImpressions = Math.round(num$1(r.baselineImpressions));
|
|
4043
3871
|
const baselinePosition = num$1(r.baselinePosition);
|
|
4044
3872
|
return {
|
|
4045
|
-
keyword:
|
|
4046
|
-
page: r.page == null ? null :
|
|
3873
|
+
keyword: rowString(r.keyword),
|
|
3874
|
+
page: r.page == null ? null : rowString(r.page),
|
|
4047
3875
|
recentClicks,
|
|
4048
3876
|
recentImpressions,
|
|
4049
3877
|
recentPosition,
|
|
@@ -4054,9 +3882,9 @@ const moversAnalyzer = defineAnalyzer$1({
|
|
|
4054
3882
|
clicksChangePercent: num$1(r.clicksChangePercent),
|
|
4055
3883
|
impressionsChangePercent: num$1(r.impressionsChangePercent),
|
|
4056
3884
|
positionChange: num$1(r.positionChange),
|
|
4057
|
-
direction:
|
|
4058
|
-
series:
|
|
4059
|
-
week:
|
|
3885
|
+
direction: rowString(r.direction),
|
|
3886
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
3887
|
+
week: rowString(s.week),
|
|
4060
3888
|
clicks: num$1(s.clicks),
|
|
4061
3889
|
impressions: num$1(s.impressions)
|
|
4062
3890
|
}))
|
|
@@ -4292,9 +4120,6 @@ const opportunityAnalyzer = defineAnalyzer$1({
|
|
|
4292
4120
|
};
|
|
4293
4121
|
}
|
|
4294
4122
|
});
|
|
4295
|
-
function str$6(v) {
|
|
4296
|
-
return v == null ? "" : String(v);
|
|
4297
|
-
}
|
|
4298
4123
|
const positionDistributionAnalyzer = defineAnalyzer$1({
|
|
4299
4124
|
id: "position-distribution",
|
|
4300
4125
|
buildSql(params) {
|
|
@@ -4331,7 +4156,7 @@ const positionDistributionAnalyzer = defineAnalyzer$1({
|
|
|
4331
4156
|
const { startDate, endDate } = periodOf$1(params);
|
|
4332
4157
|
return {
|
|
4333
4158
|
results: arr.map((r) => ({
|
|
4334
|
-
date:
|
|
4159
|
+
date: rowString(r.date),
|
|
4335
4160
|
pos_1_3: num$1(r.pos_1_3),
|
|
4336
4161
|
pos_4_10: num$1(r.pos_4_10),
|
|
4337
4162
|
pos_11_20: num$1(r.pos_11_20),
|
|
@@ -4346,9 +4171,6 @@ const positionDistributionAnalyzer = defineAnalyzer$1({
|
|
|
4346
4171
|
};
|
|
4347
4172
|
}
|
|
4348
4173
|
});
|
|
4349
|
-
function str$5(v) {
|
|
4350
|
-
return v == null ? "" : String(v);
|
|
4351
|
-
}
|
|
4352
4174
|
const positionVolatilityAnalyzer = defineAnalyzer$1({
|
|
4353
4175
|
id: "position-volatility",
|
|
4354
4176
|
buildSql(params) {
|
|
@@ -4449,8 +4271,8 @@ const positionVolatilityAnalyzer = defineAnalyzer$1({
|
|
|
4449
4271
|
const byPage = /* @__PURE__ */ new Map();
|
|
4450
4272
|
const allDates = /* @__PURE__ */ new Set();
|
|
4451
4273
|
for (const r of arr) {
|
|
4452
|
-
const page =
|
|
4453
|
-
const date =
|
|
4274
|
+
const page = rowString(r.page);
|
|
4275
|
+
const date = rowString(r.date);
|
|
4454
4276
|
allDates.add(date);
|
|
4455
4277
|
const entry = byPage.get(page) ?? {
|
|
4456
4278
|
page,
|
|
@@ -4485,17 +4307,6 @@ const positionVolatilityAnalyzer = defineAnalyzer$1({
|
|
|
4485
4307
|
};
|
|
4486
4308
|
}
|
|
4487
4309
|
});
|
|
4488
|
-
function str$4(v) {
|
|
4489
|
-
return v == null ? "" : String(v);
|
|
4490
|
-
}
|
|
4491
|
-
function parseJsonList$3(v) {
|
|
4492
|
-
if (Array.isArray(v)) return v;
|
|
4493
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4494
|
-
const parsed = JSON.parse(v);
|
|
4495
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4496
|
-
}
|
|
4497
|
-
return [];
|
|
4498
|
-
}
|
|
4499
4310
|
const queryMigrationAnalyzer = defineAnalyzer$1({
|
|
4500
4311
|
id: "query-migration",
|
|
4501
4312
|
buildSql(params) {
|
|
@@ -4617,17 +4428,17 @@ const queryMigrationAnalyzer = defineAnalyzer$1({
|
|
|
4617
4428
|
prevStart = toIsoDate(new Date(curStartMs - MS_PER_DAY - span));
|
|
4618
4429
|
}
|
|
4619
4430
|
const edges = arr.map((r) => ({
|
|
4620
|
-
sourcePage:
|
|
4621
|
-
targetPage:
|
|
4431
|
+
sourcePage: rowString(r.source_page),
|
|
4432
|
+
targetPage: rowString(r.target_page),
|
|
4622
4433
|
weight: num$1(r.weight),
|
|
4623
4434
|
queryCount: num$1(r.query_count),
|
|
4624
4435
|
exactCount: num$1(r.exact_count),
|
|
4625
4436
|
fuzzyCount: num$1(r.query_count) - num$1(r.exact_count),
|
|
4626
|
-
examples:
|
|
4627
|
-
sourceQuery:
|
|
4628
|
-
targetQuery:
|
|
4437
|
+
examples: parseJsonRows(r.examplesJson).slice(0, 8).map((e) => ({
|
|
4438
|
+
sourceQuery: rowString(e.sourceQuery),
|
|
4439
|
+
targetQuery: rowString(e.targetQuery),
|
|
4629
4440
|
absorbed: num$1(e.absorbed),
|
|
4630
|
-
matchType:
|
|
4441
|
+
matchType: rowString(e.matchType)
|
|
4631
4442
|
}))
|
|
4632
4443
|
}));
|
|
4633
4444
|
const nodeAgg = /* @__PURE__ */ new Map();
|
|
@@ -4666,12 +4477,6 @@ const queryMigrationAnalyzer = defineAnalyzer$1({
|
|
|
4666
4477
|
};
|
|
4667
4478
|
}
|
|
4668
4479
|
});
|
|
4669
|
-
function str$3(v) {
|
|
4670
|
-
return v == null ? "" : String(v);
|
|
4671
|
-
}
|
|
4672
|
-
function bool$1(v) {
|
|
4673
|
-
return v === true || v === 1 || v === "true";
|
|
4674
|
-
}
|
|
4675
4480
|
function calculateCV(values) {
|
|
4676
4481
|
if (values.length === 0) return 0;
|
|
4677
4482
|
const mean = values.reduce((a, b) => a + b, 0) / values.length;
|
|
@@ -4765,11 +4570,11 @@ const seasonalityAnalyzer = defineAnalyzer$1({
|
|
|
4765
4570
|
reduceSql(rows) {
|
|
4766
4571
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4767
4572
|
const breakdown = arr.map((r) => ({
|
|
4768
|
-
month:
|
|
4573
|
+
month: rowString(r.month),
|
|
4769
4574
|
value: num$1(r.value),
|
|
4770
4575
|
vsAverage: num$1(r.vsAverage),
|
|
4771
|
-
isPeak:
|
|
4772
|
-
isTrough:
|
|
4576
|
+
isPeak: rowBoolean(r.isPeak),
|
|
4577
|
+
isTrough: rowBoolean(r.isTrough)
|
|
4773
4578
|
}));
|
|
4774
4579
|
const first = arr[0];
|
|
4775
4580
|
const strength = first ? num$1(first.strength) : 0;
|
|
@@ -4801,20 +4606,6 @@ const seasonalityAnalyzer = defineAnalyzer$1({
|
|
|
4801
4606
|
};
|
|
4802
4607
|
}
|
|
4803
4608
|
});
|
|
4804
|
-
function str$2(v) {
|
|
4805
|
-
return v == null ? "" : String(v);
|
|
4806
|
-
}
|
|
4807
|
-
function bool(v) {
|
|
4808
|
-
return v === true || v === 1 || v === "true";
|
|
4809
|
-
}
|
|
4810
|
-
function parseJsonList$2(v) {
|
|
4811
|
-
if (Array.isArray(v)) return v;
|
|
4812
|
-
if (typeof v === "string" && v.length > 0) {
|
|
4813
|
-
const parsed = JSON.parse(v);
|
|
4814
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
4815
|
-
}
|
|
4816
|
-
return [];
|
|
4817
|
-
}
|
|
4818
4609
|
const stlDecomposeAnalyzer = defineAnalyzer$1({
|
|
4819
4610
|
id: "stl-decompose",
|
|
4820
4611
|
buildSql(params) {
|
|
@@ -4967,21 +4758,21 @@ const stlDecomposeAnalyzer = defineAnalyzer$1({
|
|
|
4967
4758
|
const arr = Array.isArray(rows) ? rows : [];
|
|
4968
4759
|
const metric = params.metric === "clicks" ? "clicks" : "impressions";
|
|
4969
4760
|
const results = arr.map((r) => ({
|
|
4970
|
-
keyword:
|
|
4971
|
-
page:
|
|
4761
|
+
keyword: rowString(r.keyword),
|
|
4762
|
+
page: rowString(r.page),
|
|
4972
4763
|
totalImpressions: num$1(r.totalImpressions),
|
|
4973
4764
|
days: num$1(r.days),
|
|
4974
4765
|
seasonalStrength: num$1(r.seasonalStrength),
|
|
4975
4766
|
trendStrength: num$1(r.trendStrength),
|
|
4976
4767
|
residualAnomalies: num$1(r.residualAnomalies),
|
|
4977
4768
|
trendSlope: num$1(r.trendSlope),
|
|
4978
|
-
series:
|
|
4979
|
-
date:
|
|
4769
|
+
series: parseJsonRows(r.seriesJson).map((s) => ({
|
|
4770
|
+
date: rowString(s.date),
|
|
4980
4771
|
observed: num$1(s.observed),
|
|
4981
4772
|
trend: s.trend == null ? null : num$1(s.trend),
|
|
4982
4773
|
seasonal: s.seasonal == null ? null : num$1(s.seasonal),
|
|
4983
4774
|
residual: s.residual == null ? null : num$1(s.residual),
|
|
4984
|
-
anomaly:
|
|
4775
|
+
anomaly: rowBoolean(s.anomaly)
|
|
4985
4776
|
}))
|
|
4986
4777
|
}));
|
|
4987
4778
|
return {
|
|
@@ -5064,17 +4855,6 @@ const strikingDistanceAnalyzer = defineAnalyzer$1({
|
|
|
5064
4855
|
return { queries: queriesQueryState(periodOf$1(params), params.limit ?? DEFAULT_ROW_LIMIT$1) };
|
|
5065
4856
|
}
|
|
5066
4857
|
});
|
|
5067
|
-
function str$1(v) {
|
|
5068
|
-
return v == null ? "" : String(v);
|
|
5069
|
-
}
|
|
5070
|
-
function parseJsonList$1(v) {
|
|
5071
|
-
if (Array.isArray(v)) return v;
|
|
5072
|
-
if (typeof v === "string" && v.length > 0) {
|
|
5073
|
-
const parsed = JSON.parse(v);
|
|
5074
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
5075
|
-
}
|
|
5076
|
-
return [];
|
|
5077
|
-
}
|
|
5078
4858
|
const survivalAnalyzer = defineAnalyzer$1({
|
|
5079
4859
|
id: "survival",
|
|
5080
4860
|
buildSql(params) {
|
|
@@ -5227,7 +5007,7 @@ const survivalAnalyzer = defineAnalyzer$1({
|
|
|
5227
5007
|
const startDate = params.startDate ?? daysAgo(183);
|
|
5228
5008
|
const windowDays = Math.round((new Date(endDate).getTime() - new Date(startDate).getTime()) / MS_PER_DAY) + 1;
|
|
5229
5009
|
const results = arr.map((r) => {
|
|
5230
|
-
const curve =
|
|
5010
|
+
const curve = parseJsonRows(r.curveJson).map((p) => ({
|
|
5231
5011
|
tenure: num$1(p.tenure),
|
|
5232
5012
|
survival: num$1(p.survival),
|
|
5233
5013
|
atRisk: num$1(p.atRisk),
|
|
@@ -5250,7 +5030,7 @@ const survivalAnalyzer = defineAnalyzer$1({
|
|
|
5250
5030
|
const last = curve[curve.length - 1];
|
|
5251
5031
|
if (medianTenure === 0 && last && last.survival > .5) medianTenure = last.tenure;
|
|
5252
5032
|
return {
|
|
5253
|
-
cohort:
|
|
5033
|
+
cohort: rowString(r.cohort),
|
|
5254
5034
|
episodeCount: num$1(r.episodeCount),
|
|
5255
5035
|
censoringRate: num$1(r.censoringRate),
|
|
5256
5036
|
medianTenure,
|
|
@@ -5267,17 +5047,6 @@ const survivalAnalyzer = defineAnalyzer$1({
|
|
|
5267
5047
|
};
|
|
5268
5048
|
}
|
|
5269
5049
|
});
|
|
5270
|
-
function str(v) {
|
|
5271
|
-
return v == null ? "" : String(v);
|
|
5272
|
-
}
|
|
5273
|
-
function parseJsonList(v) {
|
|
5274
|
-
if (Array.isArray(v)) return v;
|
|
5275
|
-
if (typeof v === "string" && v.length > 0) {
|
|
5276
|
-
const parsed = JSON.parse(v);
|
|
5277
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
5278
|
-
}
|
|
5279
|
-
return [];
|
|
5280
|
-
}
|
|
5281
5050
|
const trendsAnalyzer = defineAnalyzer$1({
|
|
5282
5051
|
id: "trends",
|
|
5283
5052
|
buildSql(params) {
|
|
@@ -5388,20 +5157,20 @@ const trendsAnalyzer = defineAnalyzer$1({
|
|
|
5388
5157
|
const startDate = params.startDate || toIsoDate(/* @__PURE__ */ new Date(Date.parse(endDate) - (weeks * 7 - 1) * MS_PER_DAY));
|
|
5389
5158
|
const dim = params.dimension === "keywords" ? "keywords" : "pages";
|
|
5390
5159
|
const results = arr.map((r) => {
|
|
5391
|
-
const series =
|
|
5392
|
-
week:
|
|
5160
|
+
const series = parseJsonRows(r.seriesJson).map((s) => ({
|
|
5161
|
+
week: rowString(s.week),
|
|
5393
5162
|
clicks: num$1(s.clicks),
|
|
5394
5163
|
impressions: num$1(s.impressions)
|
|
5395
5164
|
}));
|
|
5396
5165
|
return {
|
|
5397
|
-
[dim === "keywords" ? "query" : "page"]:
|
|
5166
|
+
[dim === "keywords" ? "query" : "page"]: rowString(r.entity),
|
|
5398
5167
|
totalClicks: num$1(r.totalClicks),
|
|
5399
5168
|
totalImpressions: num$1(r.totalImpressions),
|
|
5400
5169
|
weeksWithData: num$1(r.weeksWithData),
|
|
5401
5170
|
slope: num$1(r.slope),
|
|
5402
5171
|
growthRatio: num$1(r.growthRatio),
|
|
5403
5172
|
avgPosition: num$1(r.avgPosition),
|
|
5404
|
-
trend:
|
|
5173
|
+
trend: rowString(r.trend),
|
|
5405
5174
|
series
|
|
5406
5175
|
};
|
|
5407
5176
|
});
|
|
@@ -5639,6 +5408,27 @@ function requireComparisonWindowResult(report, window, message) {
|
|
|
5639
5408
|
function requireComparisonWindow(report, window, message) {
|
|
5640
5409
|
return unwrapResult(requireComparisonWindowResult(report, window, message), analysisErrorToException);
|
|
5641
5410
|
}
|
|
5411
|
+
function reportRows(result) {
|
|
5412
|
+
return result?.results ?? [];
|
|
5413
|
+
}
|
|
5414
|
+
function sectionCoverage(result) {
|
|
5415
|
+
return result ? "full" : "partial";
|
|
5416
|
+
}
|
|
5417
|
+
function sectionArtifact(result, analyzer, params = {}) {
|
|
5418
|
+
return result ? {
|
|
5419
|
+
analyzer,
|
|
5420
|
+
params: {
|
|
5421
|
+
type: analyzer,
|
|
5422
|
+
...params
|
|
5423
|
+
}
|
|
5424
|
+
} : void 0;
|
|
5425
|
+
}
|
|
5426
|
+
function truncation(total, kept) {
|
|
5427
|
+
return total > kept ? {
|
|
5428
|
+
kept,
|
|
5429
|
+
total
|
|
5430
|
+
} : void 0;
|
|
5431
|
+
}
|
|
5642
5432
|
const DEFAULT_MAX$7 = 5;
|
|
5643
5433
|
const brandReport = defineReport({
|
|
5644
5434
|
id: "brand",
|
|
@@ -5688,7 +5478,7 @@ const brandReport = defineReport({
|
|
|
5688
5478
|
}
|
|
5689
5479
|
});
|
|
5690
5480
|
function buildBrandSplitSection(res, max) {
|
|
5691
|
-
const rows = res
|
|
5481
|
+
const rows = reportRows(res);
|
|
5692
5482
|
const summary = res?.meta?.summary;
|
|
5693
5483
|
const findings = rows.filter((r) => r.segment === "brand").sort((a, b) => b.clicks - a.clicks).slice(0, max).map((r) => ({
|
|
5694
5484
|
entity: {
|
|
@@ -5710,16 +5500,13 @@ function buildBrandSplitSection(res, max) {
|
|
|
5710
5500
|
severity: "info",
|
|
5711
5501
|
summary: { magnitudeLabel: summary ? `brand share ${(brandShare * 100).toFixed(1)}% (${summary.brandClicks} brand vs ${summary.nonBrandClicks} non-brand clicks)` : "no summary available" },
|
|
5712
5502
|
findings,
|
|
5713
|
-
coverage: res
|
|
5503
|
+
coverage: sectionCoverage(res),
|
|
5714
5504
|
actions: [],
|
|
5715
|
-
artifact: res
|
|
5716
|
-
analyzer: "brand",
|
|
5717
|
-
params: { type: "brand" }
|
|
5718
|
-
} : void 0
|
|
5505
|
+
artifact: sectionArtifact(res, "brand")
|
|
5719
5506
|
};
|
|
5720
5507
|
}
|
|
5721
5508
|
function buildConcentrationSection(res, max) {
|
|
5722
|
-
const head = (res
|
|
5509
|
+
const head = reportRows(res)[0];
|
|
5723
5510
|
const findings = (head?.topNItems ?? []).slice(0, max).map((it) => ({
|
|
5724
5511
|
entity: {
|
|
5725
5512
|
kind: "query",
|
|
@@ -5737,15 +5524,9 @@ function buildConcentrationSection(res, max) {
|
|
|
5737
5524
|
severity: head ? severity : "info",
|
|
5738
5525
|
summary: head ? { magnitudeLabel: `HHI ${head.hhi.toFixed(0)} (${head.riskLevel}); top-N share ${(head.topNConcentration * 100).toFixed(1)}%` } : {},
|
|
5739
5526
|
findings,
|
|
5740
|
-
coverage: res
|
|
5527
|
+
coverage: sectionCoverage(res),
|
|
5741
5528
|
actions: [],
|
|
5742
|
-
artifact: res
|
|
5743
|
-
analyzer: "concentration",
|
|
5744
|
-
params: {
|
|
5745
|
-
type: "concentration",
|
|
5746
|
-
dimension: "keywords"
|
|
5747
|
-
}
|
|
5748
|
-
} : void 0
|
|
5529
|
+
artifact: sectionArtifact(res, "concentration", { dimension: "keywords" })
|
|
5749
5530
|
};
|
|
5750
5531
|
}
|
|
5751
5532
|
const DEFAULT_MAX$6 = 5;
|
|
@@ -5811,7 +5592,7 @@ const growthReport = defineReport({
|
|
|
5811
5592
|
}
|
|
5812
5593
|
});
|
|
5813
5594
|
function buildContentVelocity(res) {
|
|
5814
|
-
const rows = res
|
|
5595
|
+
const rows = reportRows(res);
|
|
5815
5596
|
const totalNew = rows.reduce((s, r) => s + r.newKeywords, 0);
|
|
5816
5597
|
const avgPerWeek = rows.length > 0 ? totalNew / rows.length : 0;
|
|
5817
5598
|
return {
|
|
@@ -5820,16 +5601,13 @@ function buildContentVelocity(res) {
|
|
|
5820
5601
|
severity: "info",
|
|
5821
5602
|
summary: { magnitudeLabel: `${totalNew} new keywords across ${rows.length} weeks (avg ${avgPerWeek.toFixed(1)}/wk)` },
|
|
5822
5603
|
findings: [],
|
|
5823
|
-
coverage: res
|
|
5604
|
+
coverage: sectionCoverage(res),
|
|
5824
5605
|
actions: [],
|
|
5825
|
-
artifact: res
|
|
5826
|
-
analyzer: "content-velocity",
|
|
5827
|
-
params: { type: "content-velocity" }
|
|
5828
|
-
} : void 0
|
|
5606
|
+
artifact: sectionArtifact(res, "content-velocity")
|
|
5829
5607
|
};
|
|
5830
5608
|
}
|
|
5831
5609
|
function buildKeywordBreadth(res) {
|
|
5832
|
-
const rows = res
|
|
5610
|
+
const rows = reportRows(res);
|
|
5833
5611
|
const totalPages = rows.reduce((s, r) => s + r.pageCount, 0);
|
|
5834
5612
|
const top = rows[0];
|
|
5835
5613
|
return {
|
|
@@ -5838,32 +5616,26 @@ function buildKeywordBreadth(res) {
|
|
|
5838
5616
|
severity: "info",
|
|
5839
5617
|
summary: { magnitudeLabel: top ? `${totalPages} pages; modal bucket "${top.bucket}" (${top.pageCount} pages)` : "no data" },
|
|
5840
5618
|
findings: [],
|
|
5841
|
-
coverage: res
|
|
5619
|
+
coverage: sectionCoverage(res),
|
|
5842
5620
|
actions: [],
|
|
5843
|
-
artifact: res
|
|
5844
|
-
analyzer: "keyword-breadth",
|
|
5845
|
-
params: { type: "keyword-breadth" }
|
|
5846
|
-
} : void 0
|
|
5621
|
+
artifact: sectionArtifact(res, "keyword-breadth")
|
|
5847
5622
|
};
|
|
5848
5623
|
}
|
|
5849
5624
|
function buildIntentAtlas(res) {
|
|
5850
|
-
const rows = res
|
|
5625
|
+
const rows = reportRows(res);
|
|
5851
5626
|
return {
|
|
5852
5627
|
id: "intent-atlas",
|
|
5853
5628
|
title: "Intent atlas",
|
|
5854
5629
|
severity: "info",
|
|
5855
5630
|
summary: { magnitudeLabel: `${rows.length} clusters covering ${rows.reduce((s, r) => s + r.keywordCount, 0)} keywords` },
|
|
5856
5631
|
findings: [],
|
|
5857
|
-
coverage: res
|
|
5632
|
+
coverage: sectionCoverage(res),
|
|
5858
5633
|
actions: [],
|
|
5859
|
-
artifact: res
|
|
5860
|
-
analyzer: "intent-atlas",
|
|
5861
|
-
params: { type: "intent-atlas" }
|
|
5862
|
-
} : void 0
|
|
5634
|
+
artifact: sectionArtifact(res, "intent-atlas")
|
|
5863
5635
|
};
|
|
5864
5636
|
}
|
|
5865
5637
|
function buildLongTail(res, max) {
|
|
5866
|
-
const rows = (res
|
|
5638
|
+
const rows = reportRows(res).sort((a, b) => {
|
|
5867
5639
|
const rank = {
|
|
5868
5640
|
"head-heavy": 0,
|
|
5869
5641
|
"balanced": 1,
|
|
@@ -5892,16 +5664,10 @@ function buildLongTail(res, max) {
|
|
|
5892
5664
|
severity: headHeavy > rows.length / 3 ? "medium" : "info",
|
|
5893
5665
|
summary: { magnitudeLabel: `${rows.length} pages analysed; ${headHeavy} head-heavy` },
|
|
5894
5666
|
findings,
|
|
5895
|
-
truncated: rows.length
|
|
5896
|
-
|
|
5897
|
-
total: rows.length
|
|
5898
|
-
} : void 0,
|
|
5899
|
-
coverage: res ? "full" : "partial",
|
|
5667
|
+
truncated: truncation(rows.length, kept.length),
|
|
5668
|
+
coverage: sectionCoverage(res),
|
|
5900
5669
|
actions: [],
|
|
5901
|
-
artifact: res
|
|
5902
|
-
analyzer: "long-tail",
|
|
5903
|
-
params: { type: "long-tail" }
|
|
5904
|
-
} : void 0
|
|
5670
|
+
artifact: sectionArtifact(res, "long-tail")
|
|
5905
5671
|
};
|
|
5906
5672
|
}
|
|
5907
5673
|
const DEFAULT_MAX$5 = 5;
|
|
@@ -5958,7 +5724,7 @@ const healthReport = defineReport({
|
|
|
5958
5724
|
}
|
|
5959
5725
|
});
|
|
5960
5726
|
function buildCtrAnomalySection(res, max) {
|
|
5961
|
-
const rows = (res
|
|
5727
|
+
const rows = reportRows(res).filter((r) => r.breachDaysDown > 0).sort((a, b) => b.clicksLost - a.clicksLost);
|
|
5962
5728
|
const kept = rows.slice(0, max);
|
|
5963
5729
|
const totalLost = kept.reduce((s, r) => s + r.clicksLost, 0);
|
|
5964
5730
|
const findings = kept.map((r) => ({
|
|
@@ -5984,20 +5750,14 @@ function buildCtrAnomalySection(res, max) {
|
|
|
5984
5750
|
magnitudeLabel: `${Math.round(totalLost)} clicks lost vs baseline`
|
|
5985
5751
|
},
|
|
5986
5752
|
findings,
|
|
5987
|
-
truncated: rows.length
|
|
5988
|
-
|
|
5989
|
-
total: rows.length
|
|
5990
|
-
} : void 0,
|
|
5991
|
-
coverage: res ? "full" : "partial",
|
|
5753
|
+
truncated: truncation(rows.length, kept.length),
|
|
5754
|
+
coverage: sectionCoverage(res),
|
|
5992
5755
|
actions: [],
|
|
5993
|
-
artifact: res
|
|
5994
|
-
analyzer: "ctr-anomaly",
|
|
5995
|
-
params: { type: "ctr-anomaly" }
|
|
5996
|
-
} : void 0
|
|
5756
|
+
artifact: sectionArtifact(res, "ctr-anomaly")
|
|
5997
5757
|
};
|
|
5998
5758
|
}
|
|
5999
5759
|
function buildChangePointSection$1(res, max) {
|
|
6000
|
-
const rows = (res
|
|
5760
|
+
const rows = reportRows(res).filter((r) => r.direction === "worsened").sort((a, b) => b.llr - a.llr);
|
|
6001
5761
|
const kept = rows.slice(0, max);
|
|
6002
5762
|
const findings = kept.map((r) => ({
|
|
6003
5763
|
entity: {
|
|
@@ -6016,20 +5776,14 @@ function buildChangePointSection$1(res, max) {
|
|
|
6016
5776
|
severity: kept.length ? "medium" : "info",
|
|
6017
5777
|
summary: { magnitudeLabel: `${kept.length} worsening segments` },
|
|
6018
5778
|
findings,
|
|
6019
|
-
truncated: rows.length
|
|
6020
|
-
|
|
6021
|
-
total: rows.length
|
|
6022
|
-
} : void 0,
|
|
6023
|
-
coverage: res ? "full" : "partial",
|
|
5779
|
+
truncated: truncation(rows.length, kept.length),
|
|
5780
|
+
coverage: sectionCoverage(res),
|
|
6024
5781
|
actions: [],
|
|
6025
|
-
artifact: res
|
|
6026
|
-
analyzer: "change-point",
|
|
6027
|
-
params: { type: "change-point" }
|
|
6028
|
-
} : void 0
|
|
5782
|
+
artifact: sectionArtifact(res, "change-point")
|
|
6029
5783
|
};
|
|
6030
5784
|
}
|
|
6031
5785
|
function buildPositionVolatilitySection(res, max) {
|
|
6032
|
-
const rows = (res
|
|
5786
|
+
const rows = reportRows(res).sort((a, b) => b.peakVolatility - a.peakVolatility);
|
|
6033
5787
|
const kept = rows.slice(0, max);
|
|
6034
5788
|
const findings = kept.map((r) => ({
|
|
6035
5789
|
entity: {
|
|
@@ -6048,16 +5802,10 @@ function buildPositionVolatilitySection(res, max) {
|
|
|
6048
5802
|
severity: kept.length ? "low" : "info",
|
|
6049
5803
|
summary: {},
|
|
6050
5804
|
findings,
|
|
6051
|
-
truncated: rows.length
|
|
6052
|
-
|
|
6053
|
-
total: rows.length
|
|
6054
|
-
} : void 0,
|
|
6055
|
-
coverage: res ? "full" : "partial",
|
|
5805
|
+
truncated: truncation(rows.length, kept.length),
|
|
5806
|
+
coverage: sectionCoverage(res),
|
|
6056
5807
|
actions: [],
|
|
6057
|
-
artifact: res
|
|
6058
|
-
analyzer: "position-volatility",
|
|
6059
|
-
params: { type: "position-volatility" }
|
|
6060
|
-
} : void 0
|
|
5808
|
+
artifact: sectionArtifact(res, "position-volatility")
|
|
6061
5809
|
};
|
|
6062
5810
|
}
|
|
6063
5811
|
const DEFAULT_MAX$4 = 5;
|
|
@@ -6133,7 +5881,7 @@ const moversReport = defineReport({
|
|
|
6133
5881
|
}
|
|
6134
5882
|
});
|
|
6135
5883
|
function buildMoversSection(res, direction, max, minChange) {
|
|
6136
|
-
const rows = (res
|
|
5884
|
+
const rows = reportRows(res).filter((r) => r.direction === direction && Math.abs(r.clicksChange) >= minChange).sort((a, b) => Math.abs(b.clicksChange) - Math.abs(a.clicksChange));
|
|
6137
5885
|
const total = rows.length;
|
|
6138
5886
|
const kept = rows.slice(0, max);
|
|
6139
5887
|
const findings = kept.map((r) => ({
|
|
@@ -6165,21 +5913,15 @@ function buildMoversSection(res, direction, max, minChange) {
|
|
|
6165
5913
|
direction: totalDelta > 0 ? "up" : totalDelta < 0 ? "down" : "flat"
|
|
6166
5914
|
},
|
|
6167
5915
|
findings,
|
|
6168
|
-
truncated: total
|
|
6169
|
-
|
|
6170
|
-
total
|
|
6171
|
-
} : void 0,
|
|
6172
|
-
coverage: res ? "full" : "partial",
|
|
5916
|
+
truncated: truncation(total, kept.length),
|
|
5917
|
+
coverage: sectionCoverage(res),
|
|
6173
5918
|
actions: [],
|
|
6174
|
-
artifact: res
|
|
6175
|
-
analyzer: "movers",
|
|
6176
|
-
params: { type: "movers" }
|
|
6177
|
-
} : void 0
|
|
5919
|
+
artifact: sectionArtifact(res, "movers")
|
|
6178
5920
|
};
|
|
6179
5921
|
}
|
|
6180
5922
|
function buildDeclinersSection(moversRes, decayRes, max, minChange) {
|
|
6181
|
-
const decliningQueries = (moversRes
|
|
6182
|
-
const lostPages = (decayRes
|
|
5923
|
+
const decliningQueries = reportRows(moversRes).filter((r) => r.direction === "declining" && Math.abs(r.clicksChange) >= minChange).slice(0, max);
|
|
5924
|
+
const lostPages = reportRows(decayRes).sort((a, b) => b.lostClicks - a.lostClicks).slice(0, max);
|
|
6183
5925
|
const findings = [...decliningQueries.map((r) => ({
|
|
6184
5926
|
entity: {
|
|
6185
5927
|
kind: "query",
|
|
@@ -6238,7 +5980,7 @@ function buildDeclinersSection(moversRes, decayRes, max, minChange) {
|
|
|
6238
5980
|
};
|
|
6239
5981
|
}
|
|
6240
5982
|
function buildStrikingSection$1(res, max) {
|
|
6241
|
-
const rows = (res
|
|
5983
|
+
const rows = reportRows(res).sort((a, b) => b.potentialClicks - a.potentialClicks);
|
|
6242
5984
|
const kept = rows.slice(0, max);
|
|
6243
5985
|
const findings = kept.map((r) => ({
|
|
6244
5986
|
entity: {
|
|
@@ -6259,16 +6001,10 @@ function buildStrikingSection$1(res, max) {
|
|
|
6259
6001
|
severity: "low",
|
|
6260
6002
|
summary: { magnitudeLabel: `${kept.reduce((s, r) => s + r.potentialClicks, 0)} potential clicks` },
|
|
6261
6003
|
findings,
|
|
6262
|
-
truncated: rows.length
|
|
6263
|
-
|
|
6264
|
-
total: rows.length
|
|
6265
|
-
} : void 0,
|
|
6266
|
-
coverage: res ? "full" : "partial",
|
|
6004
|
+
truncated: truncation(rows.length, kept.length),
|
|
6005
|
+
coverage: sectionCoverage(res),
|
|
6267
6006
|
actions: [],
|
|
6268
|
-
artifact: res
|
|
6269
|
-
analyzer: "striking-distance",
|
|
6270
|
-
params: { type: "striking-distance" }
|
|
6271
|
-
} : void 0
|
|
6007
|
+
artifact: sectionArtifact(res, "striking-distance")
|
|
6272
6008
|
};
|
|
6273
6009
|
}
|
|
6274
6010
|
const DEFAULT_MAX$3 = 5;
|
|
@@ -6334,7 +6070,7 @@ const opportunitiesReport = defineReport({
|
|
|
6334
6070
|
}
|
|
6335
6071
|
});
|
|
6336
6072
|
function buildStrikingSection(res, max) {
|
|
6337
|
-
const rows = (res
|
|
6073
|
+
const rows = reportRows(res).sort((a, b) => b.potentialClicks - a.potentialClicks);
|
|
6338
6074
|
const kept = rows.slice(0, max);
|
|
6339
6075
|
const findings = kept.map((r) => ({
|
|
6340
6076
|
entity: {
|
|
@@ -6356,20 +6092,14 @@ function buildStrikingSection(res, max) {
|
|
|
6356
6092
|
severity: "low",
|
|
6357
6093
|
summary: { magnitudeLabel: `${Math.round(totalPotential)} potential clicks` },
|
|
6358
6094
|
findings,
|
|
6359
|
-
truncated: rows.length
|
|
6360
|
-
|
|
6361
|
-
total: rows.length
|
|
6362
|
-
} : void 0,
|
|
6363
|
-
coverage: res ? "full" : "partial",
|
|
6095
|
+
truncated: truncation(rows.length, kept.length),
|
|
6096
|
+
coverage: sectionCoverage(res),
|
|
6364
6097
|
actions: [],
|
|
6365
|
-
artifact: res
|
|
6366
|
-
analyzer: "striking-distance",
|
|
6367
|
-
params: { type: "striking-distance" }
|
|
6368
|
-
} : void 0
|
|
6098
|
+
artifact: sectionArtifact(res, "striking-distance")
|
|
6369
6099
|
};
|
|
6370
6100
|
}
|
|
6371
6101
|
function buildOpportunitySection(res, max) {
|
|
6372
|
-
const rows = (res
|
|
6102
|
+
const rows = reportRows(res).sort((a, b) => b.opportunityScore - a.opportunityScore);
|
|
6373
6103
|
const kept = rows.slice(0, max);
|
|
6374
6104
|
return {
|
|
6375
6105
|
id: "low-ctr",
|
|
@@ -6390,11 +6120,8 @@ function buildOpportunitySection(res, max) {
|
|
|
6390
6120
|
},
|
|
6391
6121
|
why: r.page ? `low CTR on ${r.page}` : "low CTR vs position"
|
|
6392
6122
|
})),
|
|
6393
|
-
truncated: rows.length
|
|
6394
|
-
|
|
6395
|
-
total: rows.length
|
|
6396
|
-
} : void 0,
|
|
6397
|
-
coverage: res ? "full" : "partial",
|
|
6123
|
+
truncated: truncation(rows.length, kept.length),
|
|
6124
|
+
coverage: sectionCoverage(res),
|
|
6398
6125
|
actions: kept.slice(0, 1).map((r) => ({
|
|
6399
6126
|
kind: "fix",
|
|
6400
6127
|
target: r.page ? {
|
|
@@ -6406,14 +6133,11 @@ function buildOpportunitySection(res, max) {
|
|
|
6406
6133
|
},
|
|
6407
6134
|
rationale: "Rewrite title/description to lift CTR at this position"
|
|
6408
6135
|
})),
|
|
6409
|
-
artifact: res
|
|
6410
|
-
analyzer: "opportunity",
|
|
6411
|
-
params: { type: "opportunity" }
|
|
6412
|
-
} : void 0
|
|
6136
|
+
artifact: sectionArtifact(res, "opportunity")
|
|
6413
6137
|
};
|
|
6414
6138
|
}
|
|
6415
6139
|
function buildZeroClickSection(res, max) {
|
|
6416
|
-
const rows = (res
|
|
6140
|
+
const rows = reportRows(res).sort((a, b) => b.impressions - a.impressions);
|
|
6417
6141
|
const kept = rows.slice(0, max);
|
|
6418
6142
|
const findings = kept.map((r) => ({
|
|
6419
6143
|
entity: {
|
|
@@ -6433,20 +6157,14 @@ function buildZeroClickSection(res, max) {
|
|
|
6433
6157
|
severity: "info",
|
|
6434
6158
|
summary: { magnitudeLabel: `${kept.reduce((s, r) => s + r.impressions, 0)} impressions wasted` },
|
|
6435
6159
|
findings,
|
|
6436
|
-
truncated: rows.length
|
|
6437
|
-
|
|
6438
|
-
total: rows.length
|
|
6439
|
-
} : void 0,
|
|
6440
|
-
coverage: res ? "full" : "partial",
|
|
6160
|
+
truncated: truncation(rows.length, kept.length),
|
|
6161
|
+
coverage: sectionCoverage(res),
|
|
6441
6162
|
actions: [],
|
|
6442
|
-
artifact: res
|
|
6443
|
-
analyzer: "zero-click",
|
|
6444
|
-
params: { type: "zero-click" }
|
|
6445
|
-
} : void 0
|
|
6163
|
+
artifact: sectionArtifact(res, "zero-click")
|
|
6446
6164
|
};
|
|
6447
6165
|
}
|
|
6448
6166
|
function buildMigrationSection$1(res, max) {
|
|
6449
|
-
const rows = (res
|
|
6167
|
+
const rows = reportRows(res).sort((a, b) => b.weight - a.weight);
|
|
6450
6168
|
const kept = rows.slice(0, max);
|
|
6451
6169
|
return {
|
|
6452
6170
|
id: "query-migration",
|
|
@@ -6464,16 +6182,10 @@ function buildMigrationSection$1(res, max) {
|
|
|
6464
6182
|
},
|
|
6465
6183
|
why: `migrating from ${r.sourcePage}`
|
|
6466
6184
|
})),
|
|
6467
|
-
truncated: rows.length
|
|
6468
|
-
|
|
6469
|
-
total: rows.length
|
|
6470
|
-
} : void 0,
|
|
6471
|
-
coverage: res ? "full" : "partial",
|
|
6185
|
+
truncated: truncation(rows.length, kept.length),
|
|
6186
|
+
coverage: sectionCoverage(res),
|
|
6472
6187
|
actions: [],
|
|
6473
|
-
artifact: res
|
|
6474
|
-
analyzer: "query-migration",
|
|
6475
|
-
params: { type: "query-migration" }
|
|
6476
|
-
} : void 0
|
|
6188
|
+
artifact: sectionArtifact(res, "query-migration")
|
|
6477
6189
|
};
|
|
6478
6190
|
}
|
|
6479
6191
|
const DEFAULT_MAX$2 = 10;
|
|
@@ -6524,7 +6236,7 @@ const prePublishReport = defineReport({
|
|
|
6524
6236
|
}
|
|
6525
6237
|
});
|
|
6526
6238
|
function buildCannibalizationSection$1(res, matches, max) {
|
|
6527
|
-
const rows = (res
|
|
6239
|
+
const rows = reportRows(res).filter((r) => matches(r.keyword) || (r.competitors ?? []).some((p) => matches(p.url))).sort((a, b) => b.totalClicks - a.totalClicks);
|
|
6528
6240
|
const kept = rows.slice(0, max);
|
|
6529
6241
|
const findings = kept.map((r) => {
|
|
6530
6242
|
const pageCount = r.competitorCount ?? r.competitors?.length ?? 0;
|
|
@@ -6547,23 +6259,17 @@ function buildCannibalizationSection$1(res, matches, max) {
|
|
|
6547
6259
|
severity: kept.length ? "high" : "info",
|
|
6548
6260
|
summary: { magnitudeLabel: kept.length ? `${kept.length} existing competition` : "no existing competition" },
|
|
6549
6261
|
findings,
|
|
6550
|
-
truncated: rows.length
|
|
6551
|
-
|
|
6552
|
-
total: rows.length
|
|
6553
|
-
} : void 0,
|
|
6554
|
-
coverage: res ? "full" : "partial",
|
|
6262
|
+
truncated: truncation(rows.length, kept.length),
|
|
6263
|
+
coverage: sectionCoverage(res),
|
|
6555
6264
|
actions: kept.slice(0, 1).map(() => ({
|
|
6556
6265
|
kind: "fix",
|
|
6557
6266
|
rationale: "Decide before publishing: redirect existing page, target a different angle, or accept overlap."
|
|
6558
6267
|
})),
|
|
6559
|
-
artifact: res
|
|
6560
|
-
analyzer: "cannibalization",
|
|
6561
|
-
params: { type: "cannibalization" }
|
|
6562
|
-
} : void 0
|
|
6268
|
+
artifact: sectionArtifact(res, "cannibalization")
|
|
6563
6269
|
};
|
|
6564
6270
|
}
|
|
6565
6271
|
function buildStrikingPeersSection(res, matches, max, topic) {
|
|
6566
|
-
const rows = (res
|
|
6272
|
+
const rows = reportRows(res).filter((r) => matches(r.keyword) || matches(r.page)).sort((a, b) => b.potentialClicks - a.potentialClicks);
|
|
6567
6273
|
const kept = rows.slice(0, max);
|
|
6568
6274
|
const findings = kept.map((r) => ({
|
|
6569
6275
|
entity: {
|
|
@@ -6583,16 +6289,10 @@ function buildStrikingPeersSection(res, matches, max, topic) {
|
|
|
6583
6289
|
severity: kept.length ? "low" : "info",
|
|
6584
6290
|
summary: { magnitudeLabel: kept.length ? `${kept.length} adjacent rankings for "${topic}"` : "no adjacent rankings" },
|
|
6585
6291
|
findings,
|
|
6586
|
-
truncated: rows.length
|
|
6587
|
-
|
|
6588
|
-
total: rows.length
|
|
6589
|
-
} : void 0,
|
|
6590
|
-
coverage: res ? "full" : "partial",
|
|
6292
|
+
truncated: truncation(rows.length, kept.length),
|
|
6293
|
+
coverage: sectionCoverage(res),
|
|
6591
6294
|
actions: [],
|
|
6592
|
-
artifact: res
|
|
6593
|
-
analyzer: "striking-distance",
|
|
6594
|
-
params: { type: "striking-distance" }
|
|
6595
|
-
} : void 0
|
|
6295
|
+
artifact: sectionArtifact(res, "striking-distance")
|
|
6596
6296
|
};
|
|
6597
6297
|
}
|
|
6598
6298
|
const DEFAULT_LIMIT = 40;
|
|
@@ -6758,7 +6458,7 @@ const risksReport = defineReport({
|
|
|
6758
6458
|
}
|
|
6759
6459
|
});
|
|
6760
6460
|
function buildDecaySection(res, max) {
|
|
6761
|
-
const rows = (res
|
|
6461
|
+
const rows = reportRows(res).sort((a, b) => b.lostClicks - a.lostClicks);
|
|
6762
6462
|
const kept = rows.slice(0, max);
|
|
6763
6463
|
const totalLost = kept.reduce((s, r) => s + r.lostClicks, 0);
|
|
6764
6464
|
const findings = kept.map((r) => ({
|
|
@@ -6788,11 +6488,8 @@ function buildDecaySection(res, max) {
|
|
|
6788
6488
|
magnitudeLabel: `${Math.round(totalLost)} clicks lost`
|
|
6789
6489
|
},
|
|
6790
6490
|
findings,
|
|
6791
|
-
truncated: rows.length
|
|
6792
|
-
|
|
6793
|
-
total: rows.length
|
|
6794
|
-
} : void 0,
|
|
6795
|
-
coverage: res ? "full" : "partial",
|
|
6491
|
+
truncated: truncation(rows.length, kept.length),
|
|
6492
|
+
coverage: sectionCoverage(res),
|
|
6796
6493
|
actions: kept.slice(0, 1).map((r) => ({
|
|
6797
6494
|
kind: "analyzer",
|
|
6798
6495
|
target: {
|
|
@@ -6802,14 +6499,11 @@ function buildDecaySection(res, max) {
|
|
|
6802
6499
|
params: { type: "change-point" },
|
|
6803
6500
|
rationale: "Investigate change-point on the worst-affected page"
|
|
6804
6501
|
})),
|
|
6805
|
-
artifact: res
|
|
6806
|
-
analyzer: "decay",
|
|
6807
|
-
params: { type: "decay" }
|
|
6808
|
-
} : void 0
|
|
6502
|
+
artifact: sectionArtifact(res, "decay")
|
|
6809
6503
|
};
|
|
6810
6504
|
}
|
|
6811
6505
|
function buildCannibalizationSection(res, max) {
|
|
6812
|
-
const rows = (res
|
|
6506
|
+
const rows = reportRows(res).sort((a, b) => b.totalClicks - a.totalClicks);
|
|
6813
6507
|
const kept = rows.slice(0, max);
|
|
6814
6508
|
const findings = kept.map((r) => {
|
|
6815
6509
|
const pageCount = r.competitorCount ?? r.competitors?.length ?? 0;
|
|
@@ -6832,20 +6526,14 @@ function buildCannibalizationSection(res, max) {
|
|
|
6832
6526
|
severity: kept.length ? "medium" : "info",
|
|
6833
6527
|
summary: {},
|
|
6834
6528
|
findings,
|
|
6835
|
-
truncated: rows.length
|
|
6836
|
-
|
|
6837
|
-
total: rows.length
|
|
6838
|
-
} : void 0,
|
|
6839
|
-
coverage: res ? "full" : "partial",
|
|
6529
|
+
truncated: truncation(rows.length, kept.length),
|
|
6530
|
+
coverage: sectionCoverage(res),
|
|
6840
6531
|
actions: [],
|
|
6841
|
-
artifact: res
|
|
6842
|
-
analyzer: "cannibalization",
|
|
6843
|
-
params: { type: "cannibalization" }
|
|
6844
|
-
} : void 0
|
|
6532
|
+
artifact: sectionArtifact(res, "cannibalization")
|
|
6845
6533
|
};
|
|
6846
6534
|
}
|
|
6847
6535
|
function buildDarkTrafficSection(res, max) {
|
|
6848
|
-
const rows = (res
|
|
6536
|
+
const rows = reportRows(res).sort((a, b) => b.darkClicks - a.darkClicks);
|
|
6849
6537
|
const kept = rows.slice(0, max);
|
|
6850
6538
|
const totalDark = kept.reduce((s, r) => s + r.darkClicks, 0);
|
|
6851
6539
|
const findings = kept.map((r) => ({
|
|
@@ -6865,20 +6553,14 @@ function buildDarkTrafficSection(res, max) {
|
|
|
6865
6553
|
severity: kept.length ? "low" : "info",
|
|
6866
6554
|
summary: { magnitudeLabel: `${Math.round(totalDark)} unattributed clicks` },
|
|
6867
6555
|
findings,
|
|
6868
|
-
truncated: rows.length
|
|
6869
|
-
|
|
6870
|
-
total: rows.length
|
|
6871
|
-
} : void 0,
|
|
6872
|
-
coverage: res ? "full" : "partial",
|
|
6556
|
+
truncated: truncation(rows.length, kept.length),
|
|
6557
|
+
coverage: sectionCoverage(res),
|
|
6873
6558
|
actions: [],
|
|
6874
|
-
artifact: res
|
|
6875
|
-
analyzer: "dark-traffic",
|
|
6876
|
-
params: { type: "dark-traffic" }
|
|
6877
|
-
} : void 0
|
|
6559
|
+
artifact: sectionArtifact(res, "dark-traffic")
|
|
6878
6560
|
};
|
|
6879
6561
|
}
|
|
6880
6562
|
function buildDeviceGapSection(res, max) {
|
|
6881
|
-
const rows = (res
|
|
6563
|
+
const rows = reportRows(res).sort((a, b) => Math.abs(b.gaps.positionGap) - Math.abs(a.gaps.positionGap));
|
|
6882
6564
|
const kept = rows.slice(0, max);
|
|
6883
6565
|
return {
|
|
6884
6566
|
id: "device-gap",
|
|
@@ -6898,16 +6580,10 @@ function buildDeviceGapSection(res, max) {
|
|
|
6898
6580
|
},
|
|
6899
6581
|
why: `desktop vs mobile delta`
|
|
6900
6582
|
})),
|
|
6901
|
-
truncated: rows.length
|
|
6902
|
-
|
|
6903
|
-
total: rows.length
|
|
6904
|
-
} : void 0,
|
|
6905
|
-
coverage: res ? "full" : "partial",
|
|
6583
|
+
truncated: truncation(rows.length, kept.length),
|
|
6584
|
+
coverage: sectionCoverage(res),
|
|
6906
6585
|
actions: [],
|
|
6907
|
-
artifact: res
|
|
6908
|
-
analyzer: "device-gap",
|
|
6909
|
-
params: { type: "device-gap" }
|
|
6910
|
-
} : void 0
|
|
6586
|
+
artifact: sectionArtifact(res, "device-gap")
|
|
6911
6587
|
};
|
|
6912
6588
|
}
|
|
6913
6589
|
function resolveTarget(opts) {
|
|
@@ -7014,7 +6690,7 @@ const triageReport = defineReport({
|
|
|
7014
6690
|
}
|
|
7015
6691
|
});
|
|
7016
6692
|
function buildChangePointSection(res, kind, matches, max) {
|
|
7017
|
-
const rows = (res
|
|
6693
|
+
const rows = reportRows(res).filter((r) => matches(kind === "page" ? r.page : r.keyword)).sort((a, b) => b.llr - a.llr);
|
|
7018
6694
|
const kept = rows.slice(0, max);
|
|
7019
6695
|
const findings = kept.map((r) => ({
|
|
7020
6696
|
entity: {
|
|
@@ -7033,20 +6709,14 @@ function buildChangePointSection(res, kind, matches, max) {
|
|
|
7033
6709
|
severity: kept.length ? "medium" : "info",
|
|
7034
6710
|
summary: { magnitudeLabel: `${kept.length} change-point${kept.length === 1 ? "" : "s"}` },
|
|
7035
6711
|
findings,
|
|
7036
|
-
truncated: rows.length
|
|
7037
|
-
|
|
7038
|
-
total: rows.length
|
|
7039
|
-
} : void 0,
|
|
7040
|
-
coverage: res ? "full" : "partial",
|
|
6712
|
+
truncated: truncation(rows.length, kept.length),
|
|
6713
|
+
coverage: sectionCoverage(res),
|
|
7041
6714
|
actions: [],
|
|
7042
|
-
artifact: res
|
|
7043
|
-
analyzer: "change-point",
|
|
7044
|
-
params: { type: "change-point" }
|
|
7045
|
-
} : void 0
|
|
6715
|
+
artifact: sectionArtifact(res, "change-point")
|
|
7046
6716
|
};
|
|
7047
6717
|
}
|
|
7048
6718
|
function buildMigrationSection(res, kind, matches, max, _rawTarget) {
|
|
7049
|
-
const rows = (res
|
|
6719
|
+
const rows = reportRows(res).filter((r) => kind === "page" ? matches(r.sourcePage) || matches(r.targetPage) : false).sort((a, b) => b.weight - a.weight);
|
|
7050
6720
|
const kept = rows.slice(0, max);
|
|
7051
6721
|
const findings = kept.map((r) => ({
|
|
7052
6722
|
entity: {
|
|
@@ -7065,20 +6735,14 @@ function buildMigrationSection(res, kind, matches, max, _rawTarget) {
|
|
|
7065
6735
|
severity: "info",
|
|
7066
6736
|
summary: { magnitudeLabel: kind === "page" ? `${kept.length} migration edges touching target` : "N/A for query target" },
|
|
7067
6737
|
findings,
|
|
7068
|
-
truncated: rows.length
|
|
7069
|
-
|
|
7070
|
-
total: rows.length
|
|
7071
|
-
} : void 0,
|
|
7072
|
-
coverage: res ? "full" : "partial",
|
|
6738
|
+
truncated: truncation(rows.length, kept.length),
|
|
6739
|
+
coverage: sectionCoverage(res),
|
|
7073
6740
|
actions: [],
|
|
7074
|
-
artifact: res
|
|
7075
|
-
analyzer: "query-migration",
|
|
7076
|
-
params: { type: "query-migration" }
|
|
7077
|
-
} : void 0
|
|
6741
|
+
artifact: sectionArtifact(res, "query-migration")
|
|
7078
6742
|
};
|
|
7079
6743
|
}
|
|
7080
6744
|
function buildVolatilitySection(res, kind, matches, max) {
|
|
7081
|
-
const rows = kind === "page" ? (res
|
|
6745
|
+
const rows = kind === "page" ? reportRows(res).filter((r) => matches(r.page)) : [];
|
|
7082
6746
|
const kept = rows.sort((a, b) => b.peakVolatility - a.peakVolatility).slice(0, max);
|
|
7083
6747
|
const findings = kept.map((r) => ({
|
|
7084
6748
|
entity: {
|
|
@@ -7097,16 +6761,10 @@ function buildVolatilitySection(res, kind, matches, max) {
|
|
|
7097
6761
|
severity: kept.length ? "low" : "info",
|
|
7098
6762
|
summary: { magnitudeLabel: kind === "page" ? `${kept.length} volatile day${kept.length === 1 ? "" : "s"}` : "N/A for query target" },
|
|
7099
6763
|
findings,
|
|
7100
|
-
truncated: rows.length
|
|
7101
|
-
|
|
7102
|
-
total: rows.length
|
|
7103
|
-
} : void 0,
|
|
7104
|
-
coverage: res ? "full" : "partial",
|
|
6764
|
+
truncated: truncation(rows.length, kept.length),
|
|
6765
|
+
coverage: sectionCoverage(res),
|
|
7105
6766
|
actions: [],
|
|
7106
|
-
artifact: res
|
|
7107
|
-
analyzer: "position-volatility",
|
|
7108
|
-
params: { type: "position-volatility" }
|
|
7109
|
-
} : void 0
|
|
6767
|
+
artifact: sectionArtifact(res, "position-volatility")
|
|
7110
6768
|
};
|
|
7111
6769
|
}
|
|
7112
6770
|
const REPORTS = [
|