@gscdump/sdk 1.5.0 → 1.5.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.
@@ -133,6 +133,106 @@ const GROWTH_CLICKS_PCT_90D = 10;
133
133
  const GROWTH_IMPRESSIONS_PCT_90D = 20;
134
134
  const DECLINE_CLICKS_PCT = -10;
135
135
  const MIN_DECLINE_PRIOR_CLICKS = 50;
136
+ const SEVERE_COVERAGE_PERCENT = 20;
137
+ const SEVERE_COVERAGE_MIN_URLS = 20;
138
+ const SEVERE_NOT_INDEXED_MIN_URLS = 10;
139
+ function severeCoverageReason(counts, totalUrls) {
140
+ if (counts.hardBlocks > Math.max(5, totalUrls * .05) && counts.hardBlocks >= counts.unknown && counts.hardBlocks >= counts.crawled) return "crawl";
141
+ const indexability = Math.max(counts.canonical, counts.noindex);
142
+ if (indexability > Math.max(5, totalUrls * .05) && indexability >= counts.unknown && indexability >= counts.crawled) return "indexability";
143
+ if (counts.generic > 0 && counts.unknown + counts.discovered + counts.crawled + counts.hardBlocks + counts.canonical + counts.noindex === 0) return "unspecified";
144
+ const candidates = [
145
+ {
146
+ reason: "unknown",
147
+ count: counts.unknown,
148
+ severe: counts.unknown >= Math.max(10, totalUrls * .4)
149
+ },
150
+ {
151
+ reason: "discovered",
152
+ count: counts.discovered,
153
+ severe: counts.discovered >= Math.max(10, totalUrls * .15)
154
+ },
155
+ {
156
+ reason: "crawled",
157
+ count: counts.crawled,
158
+ severe: counts.crawled >= Math.max(10, totalUrls * .15)
159
+ }
160
+ ];
161
+ const severe = candidates.filter((candidate) => candidate.severe);
162
+ const winner = (severe.length > 0 ? severe : candidates).sort((left, right) => right.count - left.count)[0];
163
+ return winner && winner.count > 0 ? winner.reason : "mixed";
164
+ }
165
+ function severeCoverageStage(input, totalUrls, indexed) {
166
+ const notIndexed = Math.max(0, totalUrls - indexed);
167
+ const indexedPercent = totalUrls > 0 ? indexed / totalUrls * 100 : 0;
168
+ if (totalUrls < SEVERE_COVERAGE_MIN_URLS || notIndexed < SEVERE_NOT_INDEXED_MIN_URLS || indexedPercent >= SEVERE_COVERAGE_PERCENT) return null;
169
+ const issues = input.issues ?? [];
170
+ const counts = {
171
+ unknown: countSearchConsoleIssues(issues, "unknown_to_google"),
172
+ discovered: countSearchConsoleIssues(issues, "discovered_not_indexed"),
173
+ crawled: countSearchConsoleIssues(issues, "crawled_not_indexed"),
174
+ hardBlocks: countSearchConsoleIssues(issues, "blocked_robots", "server_error", "access_denied", "access_forbidden", "forbidden", "blocked_4xx", "redirect_error", "crawl_error", "not_found", "soft_404"),
175
+ canonical: countSearchConsoleIssues(issues, "canonical_mismatch", "canonical_cross_domain"),
176
+ noindex: countSearchConsoleIssues(issues, "noindex"),
177
+ generic: countSearchConsoleIssues(issues, "not_indexed")
178
+ };
179
+ if (Object.values(counts).every((count) => count === 0)) return null;
180
+ const reason = severeCoverageReason(counts, totalUrls);
181
+ const key = reason === "crawl" ? "crawl_blocked" : reason === "indexability" ? "indexability_blocked" : reason === "unknown" ? "weak_discovery" : reason === "discovered" ? "discovery_backlog" : "index_rejection";
182
+ const copy = {
183
+ crawl: {
184
+ summary: `Google is reporting hard access or fetch faults on ${formatSearchConsoleCount(counts.hardBlocks)} URLs.`,
185
+ primaryAction: "Fix fetch, server, robots, and broken-response faults before content work."
186
+ },
187
+ indexability: {
188
+ summary: "Google can reach pages, but canonical or indexability signals are preventing clean inclusion.",
189
+ primaryAction: "Make canonical and index directives agree on the URLs that should rank."
190
+ },
191
+ unknown: {
192
+ summary: `Google still does not know about ${formatSearchConsoleCount(counts.unknown)} of the inspected URLs.`,
193
+ primaryAction: "Audit sitemap freshness, sitemap membership, and internal links from already-indexed pages."
194
+ },
195
+ discovered: {
196
+ summary: `Google has discovered ${formatSearchConsoleCount(counts.discovered)} URLs but has not crawled them yet.`,
197
+ primaryAction: "Reduce low-value inventory and strengthen internal links to the pages that should be crawled."
198
+ },
199
+ crawled: {
200
+ summary: `Google crawled ${formatSearchConsoleCount(counts.crawled)} URLs but chose not to keep them in the index.`,
201
+ primaryAction: "Rule out blockers, then improve, consolidate, or noindex thin and duplicate page sets."
202
+ },
203
+ unspecified: {
204
+ summary: `Google reports ${formatSearchConsoleCount(counts.generic || notIndexed)} URLs as not indexed, but the current diagnostic snapshot does not expose a narrower reason bucket.`,
205
+ primaryAction: "Refresh URL Inspection diagnostics and join affected URLs to sitemap, internal-link, canonical, fetch, and rendered-content evidence."
206
+ },
207
+ mixed: {
208
+ summary: `${formatSearchConsoleCount(notIndexed)} URLs are not indexed, with multiple Google coverage reasons present.`,
209
+ primaryAction: "Split the affected URLs by Google coverage reason before changing the site."
210
+ }
211
+ }[reason];
212
+ return {
213
+ ...stage(key, [
214
+ {
215
+ label: "Indexed pages",
216
+ value: `${formatSearchConsoleCount(indexed)} of ${formatSearchConsoleCount(totalUrls)}`,
217
+ source: "indexing"
218
+ },
219
+ ...counts.unknown > 0 ? [{
220
+ label: "Unknown URLs",
221
+ value: formatSearchConsoleCount(counts.unknown),
222
+ source: "indexing"
223
+ }] : [],
224
+ ...counts.crawled > 0 ? [{
225
+ label: "Crawled, not indexed",
226
+ value: formatSearchConsoleCount(counts.crawled),
227
+ source: "indexing"
228
+ }] : []
229
+ ]),
230
+ severity: reason === "crawl" || reason === "indexability" ? "error" : "warning",
231
+ summary: copy.summary,
232
+ primaryAction: copy.primaryAction,
233
+ sprintFindingTypes: ["search-console-stage", "pages-not-indexed"]
234
+ };
235
+ }
136
236
  function classifySearchConsoleStage(input) {
137
237
  const issues = input.issues ?? [];
138
238
  const summary = input.summary ?? null;
@@ -185,6 +285,8 @@ function classifySearchConsoleStage(input) {
185
285
  value: `${formatSearchConsoleCount(indexed)} of ${formatSearchConsoleCount(totalUrls)}`,
186
286
  source: "indexing"
187
287
  }]);
288
+ const severeCoverage = isNascent ? null : severeCoverageStage(input, totalUrls, indexed);
289
+ if (severeCoverage) return severeCoverage;
188
290
  if (isDeclining) return stage("declining_visibility", [{
189
291
  label: "Clicks 90d",
190
292
  value: `${clicks90d.toFixed(1)}%`,
@@ -1,6 +1,6 @@
1
1
  import { RealtimeV1Cursor, RealtimeV1Event, RealtimeV1StreamHead, RealtimeV1StreamId } from "@gscdump/contracts/v1/realtime";
2
2
  type MaybePromise<T> = T | Promise<T>;
3
- declare const GSCDUMP_REALTIME_V1_SDK_VERSION: "1.5.0";
3
+ declare const GSCDUMP_REALTIME_V1_SDK_VERSION: "1.5.3";
4
4
  type GscdumpRealtimeV1TransportState = 'idle' | 'ticketing' | 'connecting' | 'handshaking' | 'replaying' | 'live' | 'waiting' | 'stopped' | 'terminal';
5
5
  type GscdumpRealtimeV1Freshness = 'unknown' | 'stale' | 'applying' | 'fresh' | 'resyncing' | 'degraded';
6
6
  type GscdumpRealtimeV1ErrorCode = 'cursor_store_failed' | 'effect_failed' | 'heartbeat_stale' | 'integration_failed' | 'protocol_error' | 'resync_failed' | 'runtime_unavailable' | 'socket_error' | 'ticket_invalid' | 'ticket_provider_failed' | 'upgrade_rejected';
@@ -1,6 +1,6 @@
1
1
  import { utf8Size } from "../utf8.mjs";
2
2
  import { GSCDUMP_REALTIME_ACK_POLICY, GSCDUMP_REALTIME_CLOSE_CODES, GSCDUMP_REALTIME_CONNECTION_POLICY, GSCDUMP_REALTIME_LIMITS, GSCDUMP_REALTIME_PING, GSCDUMP_REALTIME_PONG, GSCDUMP_REALTIME_PROTOCOL_VERSION, GSCDUMP_REALTIME_SUBPROTOCOL, REALTIME_V1_EVENT_NAMES, REALTIME_V1_RESOURCE_TYPES, createRealtimeV1Schemas } from "@gscdump/contracts/v1/realtime";
3
- const GSCDUMP_REALTIME_V1_SDK_VERSION = "1.5.0";
3
+ const GSCDUMP_REALTIME_V1_SDK_VERSION = "1.5.3";
4
4
  var GscdumpRealtimeV1Error = class extends Error {
5
5
  tag = "GscdumpRealtimeV1Error";
6
6
  code;
@@ -87,7 +87,7 @@ function createGscdumpRealtimeV1Client(options) {
87
87
  const schemas = createRealtimeV1Schemas();
88
88
  const runtime = options.runtime ?? defaultRuntime();
89
89
  const cursorStore = options.cursorStore ?? createMemoryCursorStore();
90
- const sdkVersion = options.sdkVersion ?? "1.5.0";
90
+ const sdkVersion = options.sdkVersion ?? "1.5.3";
91
91
  if (!sdkVersion) throw new TypeError("sdkVersion cannot be empty.");
92
92
  let running = false;
93
93
  let epoch = 0;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/sdk",
3
3
  "type": "module",
4
- "version": "1.5.0",
4
+ "version": "1.5.3",
5
5
  "description": "Consumer SDK for hosted gscdump.com integrations.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -146,9 +146,9 @@
146
146
  "dependencies": {
147
147
  "ofetch": "^1.5.1",
148
148
  "zod": "^4.4.3",
149
- "@gscdump/contracts": "^1.5.0",
150
- "@gscdump/engine": "^1.5.0",
151
- "gscdump": "^1.5.0"
149
+ "@gscdump/contracts": "^1.5.3",
150
+ "@gscdump/engine": "^1.5.3",
151
+ "gscdump": "^1.5.3"
152
152
  },
153
153
  "devDependencies": {
154
154
  "typescript": "^7.0.2",