@gscdump/sdk 0.24.0 → 0.24.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/dist/index.d.mts +22 -0
- package/dist/index.mjs +116 -38
- package/package.json +5 -5
package/dist/index.d.mts
CHANGED
|
@@ -781,17 +781,39 @@ interface TriageEvidence {
|
|
|
781
781
|
label: string;
|
|
782
782
|
value: string;
|
|
783
783
|
}
|
|
784
|
+
/**
|
|
785
|
+
* Distance-to-next-stage for one axis, so the UI never re-derives thresholds.
|
|
786
|
+
*
|
|
787
|
+
* `direction`:
|
|
788
|
+
* - `advance` — up-path rung (waiting → emerging → growing). `pct` = value/target.
|
|
789
|
+
* - `escape` — off-ramp recovery (declining/faded/decayed) or a held health gate
|
|
790
|
+
* (crawl_faults/quality_rejection). `pct` = inverse distance: closer to passing ⇒ higher.
|
|
791
|
+
* - `sustain` — already good (growing/healthy). `nextStage` null, `pct` 1, framed as momentum.
|
|
792
|
+
*
|
|
793
|
+
* `gapLabel` is always a concrete count (pages/clicks/points), never a bare %.
|
|
794
|
+
*/
|
|
795
|
+
interface StageProgression {
|
|
796
|
+
nextStage: ReachStage | HealthStage | null;
|
|
797
|
+
metric: string;
|
|
798
|
+
value: number;
|
|
799
|
+
target: number;
|
|
800
|
+
pct: number;
|
|
801
|
+
gapLabel: string;
|
|
802
|
+
direction: 'advance' | 'escape' | 'sustain';
|
|
803
|
+
}
|
|
784
804
|
interface ReachVerdict {
|
|
785
805
|
stage: ReachStage;
|
|
786
806
|
summary: string;
|
|
787
807
|
primaryAction: string;
|
|
788
808
|
evidence: TriageEvidence[];
|
|
809
|
+
progression: StageProgression;
|
|
789
810
|
}
|
|
790
811
|
interface HealthVerdict {
|
|
791
812
|
stage: HealthStage;
|
|
792
813
|
summary: string;
|
|
793
814
|
primaryAction: string;
|
|
794
815
|
evidence: TriageEvidence[];
|
|
816
|
+
progression: StageProgression;
|
|
795
817
|
}
|
|
796
818
|
interface SiteTriage {
|
|
797
819
|
reach: ReachVerdict;
|
package/dist/index.mjs
CHANGED
|
@@ -1911,6 +1911,57 @@ function issueCount(issues, ...types) {
|
|
|
1911
1911
|
function fmt(n) {
|
|
1912
1912
|
return new Intl.NumberFormat("en").format(Math.max(0, Math.round(n)));
|
|
1913
1913
|
}
|
|
1914
|
+
function clamp01(n) {
|
|
1915
|
+
if (!Number.isFinite(n)) return 0;
|
|
1916
|
+
return Math.min(1, Math.max(0, n));
|
|
1917
|
+
}
|
|
1918
|
+
function pctStr(n) {
|
|
1919
|
+
return `${n >= 0 ? "+" : ""}${Math.round(n)}%`;
|
|
1920
|
+
}
|
|
1921
|
+
function advance(nextStage, metric, value, target, gapLabel) {
|
|
1922
|
+
return {
|
|
1923
|
+
nextStage,
|
|
1924
|
+
metric,
|
|
1925
|
+
value,
|
|
1926
|
+
target,
|
|
1927
|
+
pct: clamp01(value / target),
|
|
1928
|
+
gapLabel,
|
|
1929
|
+
direction: "advance"
|
|
1930
|
+
};
|
|
1931
|
+
}
|
|
1932
|
+
function escapeToward(nextStage, metric, value, target, gapLabel) {
|
|
1933
|
+
return {
|
|
1934
|
+
nextStage,
|
|
1935
|
+
metric,
|
|
1936
|
+
value,
|
|
1937
|
+
target,
|
|
1938
|
+
pct: clamp01(value / target),
|
|
1939
|
+
gapLabel,
|
|
1940
|
+
direction: "escape"
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1943
|
+
function escapeReduce(nextStage, metric, value, target, gapLabel) {
|
|
1944
|
+
return {
|
|
1945
|
+
nextStage,
|
|
1946
|
+
metric,
|
|
1947
|
+
value,
|
|
1948
|
+
target,
|
|
1949
|
+
pct: target <= 0 ? value <= 0 ? 1 : 0 : clamp01(2 - value / target),
|
|
1950
|
+
gapLabel,
|
|
1951
|
+
direction: "escape"
|
|
1952
|
+
};
|
|
1953
|
+
}
|
|
1954
|
+
function sustain(metric, value, gapLabel) {
|
|
1955
|
+
return {
|
|
1956
|
+
nextStage: null,
|
|
1957
|
+
metric,
|
|
1958
|
+
value,
|
|
1959
|
+
target: value,
|
|
1960
|
+
pct: 1,
|
|
1961
|
+
gapLabel,
|
|
1962
|
+
direction: "sustain"
|
|
1963
|
+
};
|
|
1964
|
+
}
|
|
1914
1965
|
const HEALTH_COPY = {
|
|
1915
1966
|
healthy: {
|
|
1916
1967
|
summary: "Google can crawl and index the pages that should be indexed.",
|
|
@@ -1942,30 +1993,41 @@ function classifyHealthStage(input) {
|
|
|
1942
1993
|
const intentionalDead = isIntentionalRetirementSite(input, notFound, serverError) ? notFound : 0;
|
|
1943
1994
|
const indexableUrls = Math.max(1, totalUrls - noindex - intentionalDead);
|
|
1944
1995
|
const hardBlocks = serverError + (input.crawlAuditBlockerCount ?? 0);
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1996
|
+
const faultTarget = Math.max(HARD_BLOCK_FLOOR, indexableUrls * HARD_BLOCK_SHARE);
|
|
1997
|
+
if (hardBlocks > faultTarget) {
|
|
1998
|
+
const toFix = Math.ceil(hardBlocks - faultTarget);
|
|
1999
|
+
return {
|
|
2000
|
+
stage: "crawl_faults",
|
|
2001
|
+
...HEALTH_COPY.crawl_faults,
|
|
2002
|
+
evidence: [{
|
|
2003
|
+
label: "Access faults (5xx / broken)",
|
|
2004
|
+
value: fmt(hardBlocks)
|
|
2005
|
+
}],
|
|
2006
|
+
progression: escapeReduce("healthy", "access faults", hardBlocks, faultTarget, `${fmt(hardBlocks)} faults — fix ~${fmt(toFix)} to clear the gate`)
|
|
2007
|
+
};
|
|
2008
|
+
}
|
|
1953
2009
|
const rejectPool = crawledNotIndexed + softFound;
|
|
1954
|
-
if (totalUrls > 0 && rejectPool > REJECT_MIN && rejectPool / totalUrls >= REJECT_SHARE)
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
2010
|
+
if (totalUrls > 0 && rejectPool > REJECT_MIN && rejectPool / totalUrls >= REJECT_SHARE) {
|
|
2011
|
+
const share = rejectPool / totalUrls;
|
|
2012
|
+
const toClear = Math.max(0, Math.ceil(rejectPool - REJECT_SHARE * totalUrls));
|
|
2013
|
+
return {
|
|
2014
|
+
stage: "quality_rejection",
|
|
2015
|
+
...HEALTH_COPY.quality_rejection,
|
|
2016
|
+
evidence: [{
|
|
2017
|
+
label: "Crawled, then refused",
|
|
2018
|
+
value: fmt(rejectPool)
|
|
2019
|
+
}, {
|
|
2020
|
+
label: "Share of known URLs",
|
|
2021
|
+
value: `${(share * 100).toFixed(0)}%`
|
|
2022
|
+
}],
|
|
2023
|
+
progression: escapeReduce("healthy", "crawled-rejected share", share, REJECT_SHARE, `${(share * 100).toFixed(0)}% rejected — improve/consolidate ~${fmt(toClear)} pages to clear`)
|
|
2024
|
+
};
|
|
2025
|
+
}
|
|
1965
2026
|
return {
|
|
1966
2027
|
stage: "healthy",
|
|
1967
2028
|
...HEALTH_COPY.healthy,
|
|
1968
|
-
evidence: []
|
|
2029
|
+
evidence: [],
|
|
2030
|
+
progression: sustain("indexing health", 1, "Indexable pages are getting indexed — no gate blocking reach.")
|
|
1969
2031
|
};
|
|
1970
2032
|
}
|
|
1971
2033
|
const REACH_COPY = {
|
|
@@ -1998,11 +2060,12 @@ const REACH_COPY = {
|
|
|
1998
2060
|
primaryAction: "Refresh the decayed top pages, or mark the site low-priority if it is no longer maintained."
|
|
1999
2061
|
}
|
|
2000
2062
|
};
|
|
2001
|
-
function reach(stage, evidence) {
|
|
2063
|
+
function reach(stage, evidence, progression) {
|
|
2002
2064
|
return {
|
|
2003
2065
|
stage,
|
|
2004
2066
|
...REACH_COPY[stage],
|
|
2005
|
-
evidence
|
|
2067
|
+
evidence,
|
|
2068
|
+
progression
|
|
2006
2069
|
};
|
|
2007
2070
|
}
|
|
2008
2071
|
function classifyReachStage(input) {
|
|
@@ -2018,27 +2081,39 @@ function classifyReachStage(input) {
|
|
|
2018
2081
|
if (imp12m != null && imp12m < LIFETIME_FLOOR) return reach("waiting_for_data", [{
|
|
2019
2082
|
label: "Impressions (12m)",
|
|
2020
2083
|
value: fmt(imp12m)
|
|
2021
|
-
}]);
|
|
2084
|
+
}], advance("emerging", "lifetime impressions", imp12m, LIFETIME_FLOOR, `${fmt(imp12m)} of ${fmt(LIFETIME_FLOOR)} lifetime impressions — collecting data, keep indexing`));
|
|
2022
2085
|
const hadRealReach = (imp12m ?? imp28d) > LIFETIME_FLOOR;
|
|
2023
2086
|
const isGrowing = clicks90dPct != null && clicks90dPct > GROWTH_CLICKS_PCT || imp90dPct != null && imp90dPct > GROWTH_IMPRESSIONS_PCT && posDelta != null && posDelta < 0;
|
|
2024
2087
|
if (hadRealReach && liveness != null && liveness < FADED_LIVENESS && !isGrowing) return reach("faded", [{
|
|
2025
2088
|
label: "Recent week vs peak",
|
|
2026
2089
|
value: `${(liveness * 100).toFixed(0)}%`
|
|
2027
|
-
}]);
|
|
2090
|
+
}], escapeToward("growing", "recent week vs peak (liveness)", liveness, .5, `recent week is ${(liveness * 100).toFixed(0)}% of peak — revive toward ~50%`));
|
|
2028
2091
|
if (clicks90dPct != null && clicks90dPct < DECLINE_CLICKS_PCT && clicks28dPct != null && clicks28dPct < DECLINE_CLICKS_PCT && priorClicks != null && priorClicks >= MIN_DECLINE_PRIOR_CLICKS) return reach("declining", [{
|
|
2029
2092
|
label: "Clicks 90d",
|
|
2030
|
-
value:
|
|
2093
|
+
value: pctStr(clicks90dPct)
|
|
2031
2094
|
}, {
|
|
2032
2095
|
label: "Clicks 28d",
|
|
2033
|
-
value:
|
|
2034
|
-
}]
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
value:
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2096
|
+
value: pctStr(clicks28dPct)
|
|
2097
|
+
}], {
|
|
2098
|
+
nextStage: "plateaued",
|
|
2099
|
+
metric: "90d clicks change",
|
|
2100
|
+
value: clicks90dPct,
|
|
2101
|
+
target: DECLINE_CLICKS_PCT,
|
|
2102
|
+
pct: clamp01(2 - Math.abs(clicks90dPct) / Math.abs(DECLINE_CLICKS_PCT)),
|
|
2103
|
+
gapLabel: `down ${pctStr(clicks90dPct)} (90d) — recover clicks above ${pctStr(DECLINE_CLICKS_PCT)} to exit`,
|
|
2104
|
+
direction: "escape"
|
|
2105
|
+
});
|
|
2106
|
+
if (isGrowing) {
|
|
2107
|
+
const growthPct = clicks90dPct ?? imp90dPct ?? 0;
|
|
2108
|
+
const growthLabel = clicks90dPct != null ? `${pctStr(clicks90dPct)} 90d clicks — comfortably growing; defend & expand` : `${pctStr(growthPct)} 90d impressions — comfortably growing; defend & expand`;
|
|
2109
|
+
return reach("growing", [...clicks90dPct != null ? [{
|
|
2110
|
+
label: "Clicks 90d",
|
|
2111
|
+
value: `+${clicks90dPct.toFixed(0)}%`
|
|
2112
|
+
}] : [], ...imp90dPct != null ? [{
|
|
2113
|
+
label: "Impressions 90d",
|
|
2114
|
+
value: `+${imp90dPct.toFixed(0)}%`
|
|
2115
|
+
}] : []], sustain("90d clicks growth", growthPct, growthLabel));
|
|
2116
|
+
}
|
|
2042
2117
|
const ctr = clicks28d != null && imp28d > 0 ? clicks28d / imp28d : null;
|
|
2043
2118
|
if (hadRealReach && imp28d >= NASCENT_IMPRESSIONS_28D && ctr != null && ctr < DECAY_CTR) return reach("decayed", [{
|
|
2044
2119
|
label: "Impressions (28d)",
|
|
@@ -2046,19 +2121,22 @@ function classifyReachStage(input) {
|
|
|
2046
2121
|
}, {
|
|
2047
2122
|
label: "Clicks (28d)",
|
|
2048
2123
|
value: fmt(clicks28d ?? 0)
|
|
2049
|
-
}]);
|
|
2124
|
+
}], escapeToward("growing", "CTR (clicks/impressions)", ctr, DECAY_CTR, `${(ctr * 100).toFixed(2)}% CTR on ${fmt(imp28d)} impressions — refresh content toward ~${(DECAY_CTR * 100).toFixed(1)}%`));
|
|
2125
|
+
const growthVal = clicks90dPct ?? 0;
|
|
2126
|
+
const growthGap = Math.max(0, GROWTH_CLICKS_PCT - growthVal);
|
|
2127
|
+
const growthProgression = (stage) => advance("growing", "90d clicks growth", growthVal, GROWTH_CLICKS_PCT, clicks90dPct != null ? `${pctStr(growthVal)} 90d clicks — need ${pctStr(growthGap)} more to clear the +${GROWTH_CLICKS_PCT}% growth bar` : `${stage === "plateaued" ? "flat" : "rising"} — reach +${GROWTH_CLICKS_PCT}% 90d clicks growth to break into growing`);
|
|
2050
2128
|
if (imp28d >= ESTABLISHED_IMPRESSIONS_28D) return reach("plateaued", [{
|
|
2051
2129
|
label: "Impressions (28d)",
|
|
2052
2130
|
value: fmt(imp28d)
|
|
2053
|
-
}]);
|
|
2131
|
+
}], growthProgression("plateaued"));
|
|
2054
2132
|
if (imp28d < NASCENT_IMPRESSIONS_28D) return reach("emerging", [{
|
|
2055
2133
|
label: "Impressions (28d)",
|
|
2056
2134
|
value: fmt(imp28d)
|
|
2057
|
-
}]);
|
|
2135
|
+
}], growthProgression("emerging"));
|
|
2058
2136
|
return reach("plateaued", [{
|
|
2059
2137
|
label: "Impressions (28d)",
|
|
2060
2138
|
value: fmt(imp28d)
|
|
2061
|
-
}]);
|
|
2139
|
+
}], growthProgression("plateaued"));
|
|
2062
2140
|
}
|
|
2063
2141
|
function classifySiteTriage(input) {
|
|
2064
2142
|
const health = classifyHealthStage(input);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/sdk",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.24.
|
|
4
|
+
"version": "0.24.1",
|
|
5
5
|
"description": "Consumer SDK for hosted gscdump.com integrations.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"node": ">=18"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
|
-
"gscdump": "0.24.
|
|
44
|
+
"gscdump": "0.24.1"
|
|
45
45
|
},
|
|
46
46
|
"peerDependenciesMeta": {
|
|
47
47
|
"gscdump": {
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"date-fns": "^4.3.0",
|
|
53
53
|
"ofetch": "^1.5.1",
|
|
54
54
|
"zod": "^4.4.3",
|
|
55
|
-
"@gscdump/analysis": "0.24.
|
|
56
|
-
"@gscdump/
|
|
57
|
-
"@gscdump/
|
|
55
|
+
"@gscdump/analysis": "0.24.1",
|
|
56
|
+
"@gscdump/contracts": "0.24.1",
|
|
57
|
+
"@gscdump/engine": "0.24.1"
|
|
58
58
|
},
|
|
59
59
|
"devDependencies": {
|
|
60
60
|
"typescript": "^6.0.3",
|