@gscdump/sdk 5.6.0 → 5.7.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.
@@ -1,12 +1,60 @@
1
- import { GscdumpSyncStatusResponse, GscdumpUserSite, PartnerLifecycleSite } from "@gscdump/contracts";
1
+ import { GscdumpSyncStatusResponse, LifecycleProgress, PartnerLifecycleSite, SiteHoldReason } from "@gscdump/contracts";
2
2
  /**
3
3
  * Stable lifecycle fields shared by the legacy partner response and public v1.
4
4
  * Keeping the adapters structural lets consumers migrate to v1 without
5
5
  * re-introducing legacy-only fields such as `intId` or `lifecycleRevision`.
6
6
  */
7
7
  export type LifecycleSiteLike = Pick<PartnerLifecycleSite, 'siteId' | 'externalSiteId' | 'requestedUrl' | 'gscPropertyUrl' | 'permissionLevel' | 'analytics' | 'indexing' | 'latestError' | 'updatedAt'>;
8
- export declare function analyticsStatusToSyncStatus(status: LifecycleSiteLike['analytics']['status']): GscdumpUserSite['syncStatus'];
9
- export declare function lifecycleSiteToSyncStatus(site: LifecycleSiteLike): GscdumpSyncStatusResponse;
8
+ /**
9
+ * A Site's Backfill, read from the lifecycle. Only `Ready` says the record
10
+ * serves reads: gscdump.com reports `ready` only once the Team catalog serves
11
+ * them.
12
+ *
13
+ * `analytics.queryable` never decides it. That flag is also true for
14
+ * `queryable_live`, where a partner reads Google live while the record holds
15
+ * 0 days, so a status-only mapping called a Site connected a second ago synced.
16
+ *
17
+ * - `Starting`: registered, and no Sync job has finished yet.
18
+ * - `Running`: Sync jobs are open.
19
+ * - `PreparingRecord`: the Backfill finished, and the record does not serve reads yet.
20
+ * - `Ready`: the record serves reads.
21
+ * - `NoRows`: every Sync job finished and none failed, but the host has no
22
+ * Search Console rows, so the record never gets a synced range and the
23
+ * lifecycle stays `queryable_live`.
24
+ * - `Partial`: some days failed to sync.
25
+ * - `Held`: no Backfill starts until the user acts. `size_pending` is still
26
+ * measuring, so it reads as `Starting`.
27
+ * - `Failed`: the Backfill stopped.
28
+ */
29
+ export type LifecycleBackfill = {
30
+ _tag: 'Starting';
31
+ } | {
32
+ _tag: 'Running';
33
+ } | {
34
+ _tag: 'PreparingRecord';
35
+ } | {
36
+ _tag: 'Ready';
37
+ } | {
38
+ _tag: 'NoRows';
39
+ } | {
40
+ _tag: 'Partial';
41
+ } | {
42
+ _tag: 'Held';
43
+ hold: Exclude<SiteHoldReason, 'size_pending'>;
44
+ } | {
45
+ _tag: 'Failed';
46
+ };
47
+ /** The lifecycle fields the Backfill reader needs. A host older than contracts 4.7.0 sends no `hold`. */
48
+ export interface LifecycleBackfillInput {
49
+ analytics: Pick<LifecycleSiteLike['analytics'], 'status'> & {
50
+ progress: Pick<LifecycleProgress, 'completed' | 'failed' | 'total'>;
51
+ };
52
+ hold?: SiteHoldReason | null;
53
+ }
54
+ export declare function resolveLifecycleBackfill(site: LifecycleBackfillInput): LifecycleBackfill;
55
+ /** Whether the first Backfill is done: the record serves reads, or the host has no rows to read. */
56
+ export declare function isLifecycleBackfillComplete(backfill: LifecycleBackfill): boolean;
57
+ export declare function lifecycleSiteToSyncStatus(site: LifecycleSiteLike & Pick<LifecycleBackfillInput, 'hold'>): GscdumpSyncStatusResponse;
10
58
  export declare function findLifecycleSite<TSite extends LifecycleSiteLike>(lifecycle: {
11
59
  sites: readonly TSite[];
12
60
  }, siteIdOrPropertyUrl: string): TSite | null;
@@ -8,26 +8,57 @@ function normalizeGscPropertyKey(url) {
8
8
  if (!/^https?:\/\//.test(value)) return "";
9
9
  return `url:${normalizeLifecycleUrl(value)}`;
10
10
  }
11
- function analyticsStatusToSyncStatus(status) {
11
+ function resolveLifecycleBackfill(site) {
12
+ const { status, progress } = site.analytics;
13
+ const open = progress.total > 0 && progress.completed + progress.failed < progress.total;
14
+ const hold = site.hold && site.hold !== "size_pending" ? site.hold : null;
12
15
  switch (status) {
13
- case "ready":
16
+ case "ready": return { _tag: "Ready" };
17
+ case "failed": return { _tag: "Failed" };
18
+ case "syncing": return { _tag: "Running" };
19
+ case "preparing": return { _tag: "PreparingRecord" };
14
20
  case "queryable_live":
15
- case "queryable_partial": return "synced";
16
- case "syncing":
17
- case "preparing": return "syncing";
18
- case "failed": return "error";
19
- default: return "pending";
21
+ if (open) return { _tag: "Running" };
22
+ if (progress.failed > 0) return { _tag: "Partial" };
23
+ if (progress.total > 0 && progress.completed >= progress.total) return { _tag: "NoRows" };
24
+ return hold ? {
25
+ _tag: "Held",
26
+ hold
27
+ } : { _tag: "Starting" };
28
+ case "queryable_partial":
29
+ if (open) return { _tag: "Running" };
30
+ return progress.failed > 0 ? { _tag: "Partial" } : { _tag: "Running" };
31
+ default: return hold ? {
32
+ _tag: "Held",
33
+ hold
34
+ } : { _tag: "Starting" };
35
+ }
36
+ }
37
+ function isLifecycleBackfillComplete(backfill) {
38
+ return backfill._tag === "Ready" || backfill._tag === "NoRows";
39
+ }
40
+ function backfillSyncStatus(backfill) {
41
+ switch (backfill._tag) {
42
+ case "Starting":
43
+ case "Held": return "pending";
44
+ case "Running":
45
+ case "PreparingRecord": return "syncing";
46
+ case "Ready":
47
+ case "NoRows": return "synced";
48
+ case "Partial":
49
+ case "Failed": return "error";
20
50
  }
21
51
  }
22
52
  function lifecycleSiteToSyncStatus(site) {
23
- const syncStatus = analyticsStatusToSyncStatus(site.analytics.status);
53
+ const backfill = resolveLifecycleBackfill(site);
54
+ const isSyncing = backfill._tag === "Starting" || backfill._tag === "Running" || backfill._tag === "PreparingRecord";
24
55
  const completed = site.analytics.progress.completed;
25
56
  const failed = site.analytics.progress.failed;
26
57
  const total = site.analytics.progress.total;
27
58
  const queued = Math.max(total - completed - failed, 0);
28
59
  return {
29
60
  siteUrl: site.gscPropertyUrl || site.requestedUrl,
30
- syncStatus,
61
+ syncStatus: backfillSyncStatus(backfill),
31
62
  oldestDateAvailable: site.analytics.syncedRange.oldest,
32
63
  oldestDateSynced: site.analytics.syncedRange.oldest,
33
64
  newestDateSynced: site.analytics.syncedRange.newest,
@@ -35,11 +66,7 @@ function lifecycleSiteToSyncStatus(site) {
35
66
  lastError: site.latestError?.message ?? null,
36
67
  jobs: {
37
68
  queued,
38
- processing: [
39
- "queued",
40
- "preparing",
41
- "syncing"
42
- ].includes(site.analytics.status) ? 1 : 0,
69
+ processing: isSyncing ? 1 : 0,
43
70
  completed,
44
71
  failed
45
72
  },
@@ -47,13 +74,9 @@ function lifecycleSiteToSyncStatus(site) {
47
74
  jobProgress: site.analytics.progress.percent,
48
75
  daysSynced: completed,
49
76
  daysAvailable: total,
50
- isSyncing: [
51
- "queued",
52
- "preparing",
53
- "syncing"
54
- ].includes(site.analytics.status),
77
+ isSyncing,
55
78
  hasData: site.analytics.queryable,
56
- isComplete: site.analytics.queryable && site.analytics.status === "ready",
79
+ isComplete: isLifecycleBackfillComplete(backfill),
57
80
  tables: {}
58
81
  };
59
82
  }
@@ -62,4 +85,4 @@ function findLifecycleSite(lifecycle, siteIdOrPropertyUrl) {
62
85
  const propertyKey = normalizeGscPropertyKey(siteIdOrPropertyUrl);
63
86
  return lifecycle.sites.find((site) => site.siteId === siteIdOrPropertyUrl || site.externalSiteId === siteIdOrPropertyUrl || !!propertyKey && normalizeGscPropertyKey(site.gscPropertyUrl) === propertyKey || !site.gscPropertyUrl && normalizeLifecycleUrl(site.requestedUrl) === normalized || !propertyKey && normalizeLifecycleUrl(site.requestedUrl) === normalized) ?? null;
64
87
  }
65
- export { analyticsStatusToSyncStatus, findLifecycleSite, lifecycleSiteToSyncStatus };
88
+ export { findLifecycleSite, isLifecycleBackfillComplete, lifecycleSiteToSyncStatus, resolveLifecycleBackfill };
package/dist/package.mjs CHANGED
@@ -1,2 +1,2 @@
1
- var version = "5.6.0";
1
+ var version = "5.7.1";
2
2
  export { version };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@gscdump/sdk",
3
3
  "type": "module",
4
- "version": "5.6.0",
4
+ "version": "5.7.1",
5
5
  "description": "Consumer SDK for hosted gscdump.com integrations.",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -144,8 +144,8 @@
144
144
  "node": ">=22.13.0"
145
145
  },
146
146
  "dependencies": {
147
- "@gscdump/contracts": "^5.6.0",
148
- "gscdump": "^5.6.0",
147
+ "@gscdump/contracts": "^5.7.1",
148
+ "gscdump": "^5.7.1",
149
149
  "ofetch": "^1.5.1",
150
150
  "zod": "^4.6.5"
151
151
  },