gscdump 0.38.2 → 0.40.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -159,7 +159,7 @@ const cannibalization = analyzeCannibalization(keywordPageData)
159
159
  - [`@gscdump/cli`](../cli) — CLI: `sync`, `query`, `dump`, `analyze`, `mcp`, `store` admin.
160
160
  - [`@gscdump/engine`](../engine) — Append-only Parquet/DuckDB storage engine.
161
161
  - [`@gscdump/analysis`](../analysis) — SEO analyzers (row-based + DuckDB-native + D1-ready).
162
- - [`@gscdump/nuxt`](../nuxt) — Nuxt layer wrapping the full stack.
162
+ - [`@gscdump/sdk`](../sdk) — Hosted API and ticketed realtime clients.
163
163
 
164
164
  ## License
165
165
 
@@ -662,6 +662,10 @@ declare function exchangeAuthCodeResult(code: string, clientId: string, clientSe
662
662
  declare function exchangeAuthCode(code: string, clientId: string, clientSecret: string, redirectUri: string): Promise<OAuthTokens & {
663
663
  refreshToken?: string;
664
664
  }>;
665
+ interface FetchSitesWithSitemapsOptions {
666
+ /** Maximum concurrent sitemap-list requests. Defaults to 4. */
667
+ concurrency?: number;
668
+ }
665
669
  /**
666
670
  * Fetches all sites the authenticated user has access to in Google Search Console.
667
671
  */
@@ -669,7 +673,7 @@ declare function fetchSites(client: GoogleSearchConsoleClient): Promise<ApiSite[
669
673
  /**
670
674
  * Fetches all verified sites with their sitemaps from Google Search Console.
671
675
  */
672
- declare function fetchSitesWithSitemaps(client: GoogleSearchConsoleClient): Promise<(Site & {
676
+ declare function fetchSitesWithSitemaps(client: GoogleSearchConsoleClient, options?: FetchSitesWithSitemapsOptions): Promise<(Site & {
673
677
  sitemaps: ApiSitemap[];
674
678
  })[]>;
675
679
  /**
@@ -705,6 +709,11 @@ declare function deleteSite(client: GoogleSearchConsoleClient, siteUrl: string):
705
709
  * `sc-domain:example.com`) to the Site Verification API's site shape.
706
710
  */
707
711
  declare function siteUrlToVerificationSite(siteUrl: string): VerificationSite;
712
+ /** Resolve a Search Console property and method to Google's verification target. */
713
+ declare function resolveVerificationTarget(siteUrl: string, method?: VerificationMethod): {
714
+ site: VerificationSite;
715
+ method: VerificationMethod;
716
+ };
708
717
  /**
709
718
  * Methods valid for a given site shape. SITE properties can use META/FILE/
710
719
  * ANALYTICS/TAG_MANAGER; INET_DOMAIN must use DNS_TXT or DNS_CNAME.
@@ -736,4 +745,4 @@ declare function getVerifiedSite(client: GoogleSearchConsoleClient, id: string):
736
745
  * on the property are unaffected.
737
746
  */
738
747
  declare function unverifySite(client: GoogleSearchConsoleClient, id: string): Promise<void>;
739
- export { ApiSite, ApiSitemap, ApiSitemapContent, Auth, AuthClient, AuthOptions, CallOptions, DataRow, DimensionFilter, DimensionFilterGroup, DiscoverSitemapOptions, FetchSitemapUrlsOptions, GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GoogleSearchConsoleClient, GoogleSearchConsoleClientOptions, GscApiError, GscApiErrorInfo, GscError, GscErrorKind, GscPropertyCandidate, GscdumpApiOptions, IndexStatusResult, IndexingEligibility, IndexingIneligibleReason, IndexingMetadata, IndexingNotificationType, IndexingResult, InspectUrlIndexResponse, InspectUrlResult, InspectionPriority, MobileUsabilityResult, OAuthTokens, ParsedGscSiteUrl, ParsedIndexingResult, Period, PublishUrlNotificationResponse, QueryReturn, RequiredNonNullable, ResolvedAnalyticsRange, RichResultsResult, SearchAnalyticsQuery, SearchAnalyticsResponse, Site, SiteAnalytics, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, UrlInspectionResult, UrlNotificationMetadata, VerificationMethod, VerificationSite, VerificationSiteType, VerificationToken, VerificationWebResource, addSite, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, createAuth, createFetch, deleteSite, deleteSitemap, discoverSitemap, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, getIndexingEligibility, getIndexingMetadata, getNextCheckAfter, getNextCheckPriority, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, inspectUrl, inspectUrlFlat, isPermissionDeniedError, isVerifiedGscPermission, isVerifiedGscProperty, listVerifiedSites, matchGscSite, normalizeGscSiteUrl, normalizeRegistrationTarget, parseGoogleError, parseGoogleScopes, parseGscSiteUrl, pickBestGscProperty, refreshAccessToken, refreshAccessTokenResult, requestIndexing, rethrowAsGscApiError, runSequentialBatch, siteUrlToVerificationSite, storageError, submitSitemap, unverifySite, urlMatchKey, verificationMethodsFor, verifySite };
748
+ export { ApiSite, ApiSitemap, ApiSitemapContent, Auth, AuthClient, AuthOptions, CallOptions, DataRow, DimensionFilter, DimensionFilterGroup, DiscoverSitemapOptions, FetchSitemapUrlsOptions, FetchSitesWithSitemapsOptions, GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GoogleSearchConsoleClient, GoogleSearchConsoleClientOptions, GscApiError, GscApiErrorInfo, GscError, GscErrorKind, GscPropertyCandidate, GscdumpApiOptions, IndexStatusResult, IndexingEligibility, IndexingIneligibleReason, IndexingMetadata, IndexingNotificationType, IndexingResult, InspectUrlIndexResponse, InspectUrlResult, InspectionPriority, MobileUsabilityResult, OAuthTokens, ParsedGscSiteUrl, ParsedIndexingResult, Period, PublishUrlNotificationResponse, QueryReturn, RequiredNonNullable, ResolvedAnalyticsRange, RichResultsResult, SearchAnalyticsQuery, SearchAnalyticsResponse, Site, SiteAnalytics, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, UrlInspectionResult, UrlNotificationMetadata, VerificationMethod, VerificationSite, VerificationSiteType, VerificationToken, VerificationWebResource, addSite, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, createAuth, createFetch, deleteSite, deleteSitemap, discoverSitemap, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, getIndexingEligibility, getIndexingMetadata, getNextCheckAfter, getNextCheckPriority, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, inspectUrl, inspectUrlFlat, isPermissionDeniedError, isVerifiedGscPermission, isVerifiedGscProperty, listVerifiedSites, matchGscSite, normalizeGscSiteUrl, normalizeRegistrationTarget, parseGoogleError, parseGoogleScopes, parseGscSiteUrl, pickBestGscProperty, refreshAccessToken, refreshAccessTokenResult, requestIndexing, resolveVerificationTarget, rethrowAsGscApiError, runSequentialBatch, siteUrlToVerificationSite, storageError, submitSitemap, unverifySite, urlMatchKey, verificationMethodsFor, verifySite };
@@ -2497,9 +2497,16 @@ function pickBestGscProperty(origin, availableSites) {
2497
2497
  const matches = availableSites.filter((p) => matchGscSite(origin, p.siteUrl));
2498
2498
  if (!matches.length) return void 0;
2499
2499
  const isDomain = (p) => !!p.siteUrl?.startsWith("sc-domain:");
2500
+ const isHttps = (p) => !!p.siteUrl?.startsWith("https://");
2501
+ const pickTier = (pool) => pool.find(isDomain) ?? pool.find(isHttps) ?? pool[0];
2500
2502
  const verified = matches.filter((p) => isVerifiedGscPermission(p.permissionLevel));
2501
2503
  const pool = verified.length ? verified : matches;
2502
- return pool.find(isDomain) ?? pool[0];
2504
+ const originHost = stripWww(parseGscSiteUrl(origin).hostname.toLowerCase());
2505
+ const exact = pool.filter((property) => {
2506
+ if (!property.siteUrl) return false;
2507
+ return stripWww(parseGscSiteUrl(property.siteUrl).hostname.toLowerCase()) === originHost;
2508
+ });
2509
+ return pickTier(exact.length ? exact : pool);
2503
2510
  }
2504
2511
  function findBestGscProperty(targetDomain, properties) {
2505
2512
  const cleanTarget = stripWww(targetDomain);
@@ -2862,15 +2869,16 @@ async function postOAuthTokenResult(body, op) {
2862
2869
  async function fetchSites(client) {
2863
2870
  return client.sites();
2864
2871
  }
2865
- async function fetchSitesWithSitemaps(client) {
2872
+ async function fetchSitesWithSitemaps(client, options = {}) {
2866
2873
  const sites = (await client.sites()).filter((s) => !!s.siteUrl && s.permissionLevel !== "siteUnverifiedUser");
2867
- return Promise.all(sites.map(async (site) => {
2874
+ const requestedConcurrency = options.concurrency ?? 4;
2875
+ return runSequentialBatch(sites, async (site) => {
2868
2876
  const sitemaps = await client.sitemaps.list(site.siteUrl).catch(() => []);
2869
2877
  return {
2870
2878
  ...site,
2871
2879
  sitemaps
2872
2880
  };
2873
- }));
2881
+ }, { concurrency: Number.isFinite(requestedConcurrency) ? Math.max(1, Math.floor(requestedConcurrency)) : 4 });
2874
2882
  }
2875
2883
  async function fetchSitemaps(client, siteUrl) {
2876
2884
  return client.sitemaps.list(siteUrl);
@@ -2891,6 +2899,22 @@ async function deleteSite(client, siteUrl) {
2891
2899
  return client.sites.delete(siteUrl);
2892
2900
  }
2893
2901
  const SC_DOMAIN_PREFIX = "sc-domain:";
2902
+ function bareDomain(siteUrl) {
2903
+ if (siteUrl.startsWith(SC_DOMAIN_PREFIX)) return siteUrl.slice(10);
2904
+ try {
2905
+ return new URL(siteUrl.startsWith("http") ? siteUrl : `https://${siteUrl}`).host;
2906
+ } catch {
2907
+ return siteUrl;
2908
+ }
2909
+ }
2910
+ function urlPrefix(siteUrl) {
2911
+ try {
2912
+ const url = new URL(siteUrl.startsWith("http") ? siteUrl : `https://${siteUrl}`);
2913
+ return `${url.protocol}//${url.host}/`;
2914
+ } catch {
2915
+ return siteUrl;
2916
+ }
2917
+ }
2894
2918
  function siteUrlToVerificationSite(siteUrl) {
2895
2919
  if (siteUrl.startsWith(SC_DOMAIN_PREFIX)) return {
2896
2920
  type: "INET_DOMAIN",
@@ -2901,6 +2925,32 @@ function siteUrlToVerificationSite(siteUrl) {
2901
2925
  identifier: siteUrl
2902
2926
  };
2903
2927
  }
2928
+ function resolveVerificationTarget(siteUrl, method) {
2929
+ const isDomainProperty = siteUrl.startsWith(SC_DOMAIN_PREFIX);
2930
+ const chosen = method ?? (isDomainProperty ? "DNS_TXT" : "META");
2931
+ const isDns = chosen === "DNS_TXT" || chosen === "DNS_CNAME";
2932
+ if (isDomainProperty && !isDns) return {
2933
+ site: {
2934
+ type: "INET_DOMAIN",
2935
+ identifier: bareDomain(siteUrl)
2936
+ },
2937
+ method: "DNS_TXT"
2938
+ };
2939
+ if (isDns) return {
2940
+ site: {
2941
+ type: "INET_DOMAIN",
2942
+ identifier: bareDomain(siteUrl)
2943
+ },
2944
+ method: chosen
2945
+ };
2946
+ return {
2947
+ site: {
2948
+ type: "SITE",
2949
+ identifier: urlPrefix(siteUrl)
2950
+ },
2951
+ method: chosen
2952
+ };
2953
+ }
2904
2954
  function verificationMethodsFor(site) {
2905
2955
  if (site.type === "INET_DOMAIN") return ["DNS_TXT", "DNS_CNAME"];
2906
2956
  return [
@@ -2911,20 +2961,20 @@ function verificationMethodsFor(site) {
2911
2961
  ];
2912
2962
  }
2913
2963
  async function getVerificationToken(client, siteUrl, method) {
2914
- const site = siteUrlToVerificationSite(siteUrl);
2964
+ const { site, method: verificationMethod } = resolveVerificationTarget(siteUrl, method);
2915
2965
  return {
2916
2966
  ...await client.verification.getToken({
2917
2967
  site,
2918
- verificationMethod: method
2968
+ verificationMethod
2919
2969
  }),
2920
2970
  site
2921
2971
  };
2922
2972
  }
2923
2973
  async function verifySite(client, siteUrl, method) {
2924
- const site = siteUrlToVerificationSite(siteUrl);
2974
+ const { site, method: verificationMethod } = resolveVerificationTarget(siteUrl, method);
2925
2975
  return client.verification.insert({
2926
2976
  site,
2927
- verificationMethod: method
2977
+ verificationMethod
2928
2978
  });
2929
2979
  }
2930
2980
  async function listVerifiedSites(client) {
@@ -2936,4 +2986,4 @@ async function getVerifiedSite(client, id) {
2936
2986
  async function unverifySite(client, id) {
2937
2987
  return client.verification.delete(id);
2938
2988
  }
2939
- export { GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GscApiError, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, addSite, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, createAuth, createFetch, deleteSite, deleteSitemap, discoverSitemap, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, getIndexingEligibility, getIndexingMetadata, getNextCheckAfter, getNextCheckPriority, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, inspectUrl, inspectUrlFlat, isPermissionDeniedError, isVerifiedGscPermission, isVerifiedGscProperty, listVerifiedSites, matchGscSite, normalizeGscSiteUrl, normalizeRegistrationTarget, parseGoogleError, parseGoogleScopes, parseGscSiteUrl, pickBestGscProperty, refreshAccessToken, refreshAccessTokenResult, requestIndexing, rethrowAsGscApiError, runSequentialBatch, siteUrlToVerificationSite, storageError, submitSitemap, unverifySite, urlMatchKey, verificationMethodsFor, verifySite };
2989
+ export { GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GscApiError, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, addSite, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, createAuth, createFetch, deleteSite, deleteSitemap, discoverSitemap, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, getIndexingEligibility, getIndexingMetadata, getNextCheckAfter, getNextCheckPriority, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, inspectUrl, inspectUrlFlat, isPermissionDeniedError, isVerifiedGscPermission, isVerifiedGscProperty, listVerifiedSites, matchGscSite, normalizeGscSiteUrl, normalizeRegistrationTarget, parseGoogleError, parseGoogleScopes, parseGscSiteUrl, pickBestGscProperty, refreshAccessToken, refreshAccessTokenResult, requestIndexing, resolveVerificationTarget, rethrowAsGscApiError, runSequentialBatch, siteUrlToVerificationSite, storageError, submitSitemap, unverifySite, urlMatchKey, verificationMethodsFor, verifySite };
package/dist/dates.d.mts CHANGED
@@ -43,6 +43,8 @@ declare function groupIntoRanges(dates: string[], daysPerRange?: number): Array<
43
43
  declare function countDays(startDate: string, endDate: string): number;
44
44
  /** Oldest date GSC retains (~16 months ago). */
45
45
  declare function getOldestGscDate(): string;
46
+ /** Add `n` UTC days to a YYYY-MM-DD string (n may be negative). */
47
+ declare function addDays(dateStr: string, n: number): string;
46
48
  declare function getNextDate(dateStr: string): string;
47
49
  interface BackfillProgress {
48
50
  progress: number;
@@ -58,4 +60,4 @@ interface BackfillProgress {
58
60
  declare function getBackfillProgress(oldestDateSynced: string | null, newestDateSynced: string | null): BackfillProgress | null;
59
61
  declare function currentPstDate(): string;
60
62
  declare function daysAgoPst(n: number): string;
61
- export { DAYS_PER_RANGE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_RETENTION_MONTHS, MS_PER_DAY, countDays, currentPstDate, daysAgoPst, daysAgo as daysAgoUtc, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getLatestGscDate, getNextDate, getOldestGscDate, getPendingDates, getPstDate, groupIntoRanges, toIsoDate };
63
+ export { DAYS_PER_RANGE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_RETENTION_MONTHS, MS_PER_DAY, addDays, countDays, currentPstDate, daysAgoPst, daysAgo as daysAgoUtc, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getLatestGscDate, getNextDate, getOldestGscDate, getPendingDates, getPstDate, groupIntoRanges, toIsoDate };
package/dist/dates.mjs CHANGED
@@ -115,4 +115,4 @@ function daysAgoPst(n) {
115
115
  const { year, month, day } = pstDateParts();
116
116
  return format(subDays(new Date(year, month - 1, day, 12), n), "yyyy-MM-dd");
117
117
  }
118
- export { DAYS_PER_RANGE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_RETENTION_MONTHS, MS_PER_DAY, countDays, currentPstDate, daysAgoPst, daysAgo as daysAgoUtc, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getLatestGscDate, getNextDate, getOldestGscDate, getPendingDates, getPstDate, groupIntoRanges, toIsoDate };
118
+ export { DAYS_PER_RANGE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_RETENTION_MONTHS, MS_PER_DAY, addDays, countDays, currentPstDate, daysAgoPst, daysAgo as daysAgoUtc, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getLatestGscDate, getNextDate, getOldestGscDate, getPendingDates, getPstDate, groupIntoRanges, toIsoDate };
package/dist/index.d.mts CHANGED
@@ -565,6 +565,10 @@ declare function exchangeAuthCodeResult(code: string, clientId: string, clientSe
565
565
  declare function exchangeAuthCode(code: string, clientId: string, clientSecret: string, redirectUri: string): Promise<OAuthTokens & {
566
566
  refreshToken?: string;
567
567
  }>;
568
+ interface FetchSitesWithSitemapsOptions {
569
+ /** Maximum concurrent sitemap-list requests. Defaults to 4. */
570
+ concurrency?: number;
571
+ }
568
572
  /**
569
573
  * Fetches all sites the authenticated user has access to in Google Search Console.
570
574
  */
@@ -572,7 +576,7 @@ declare function fetchSites(client: GoogleSearchConsoleClient): Promise<ApiSite[
572
576
  /**
573
577
  * Fetches all verified sites with their sitemaps from Google Search Console.
574
578
  */
575
- declare function fetchSitesWithSitemaps(client: GoogleSearchConsoleClient): Promise<(Site & {
579
+ declare function fetchSitesWithSitemaps(client: GoogleSearchConsoleClient, options?: FetchSitesWithSitemapsOptions): Promise<(Site & {
576
580
  sitemaps: ApiSitemap[];
577
581
  })[]>;
578
582
  /**
@@ -608,6 +612,11 @@ declare function deleteSite(client: GoogleSearchConsoleClient, siteUrl: string):
608
612
  * `sc-domain:example.com`) to the Site Verification API's site shape.
609
613
  */
610
614
  declare function siteUrlToVerificationSite(siteUrl: string): VerificationSite;
615
+ /** Resolve a Search Console property and method to Google's verification target. */
616
+ declare function resolveVerificationTarget(siteUrl: string, method?: VerificationMethod): {
617
+ site: VerificationSite;
618
+ method: VerificationMethod;
619
+ };
611
620
  /**
612
621
  * Methods valid for a given site shape. SITE properties can use META/FILE/
613
622
  * ANALYTICS/TAG_MANAGER; INET_DOMAIN must use DNS_TXT or DNS_CNAME.
@@ -913,4 +922,4 @@ declare function normalizeSiteUrl(siteUrl: string): string;
913
922
  * Example: `https://www.Example.com/Foo/` → `example.com/foo`
914
923
  */
915
924
  declare function urlMatchKey(url: string): string | null;
916
- export { type AccountNextAction, type AccountStatus, type AnalyticsNextAction, type AnalyticsStatus, ApiSite, ApiSitemap, ApiSitemapContent, Auth, AuthClient, AuthOptions, BackfillProgress, CallOptions, DAYS_PER_RANGE, DataRow, DimensionFilter, DimensionFilterGroup, DiscoverSitemapOptions, Err, FetchSitemapUrlsOptions, GSCDUMP_ONBOARDING_CONTRACT_VERSION, GSCDUMP_OPTIONAL_INDEXING_SCOPE, GSCDUMP_REQUIRED_ANALYTICS_SCOPE, GSCDUMP_WRITE_ANALYTICS_SCOPE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_RETENTION_MONTHS, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GoogleSearchConsoleClient, GoogleSearchConsoleClientOptions, GscApiError, GscApiErrorInfo, GscError, GscErrorKind, GscPropertyCandidate, GscdumpApiOptions, INDEXING_ISSUE_FILTERS, INDEXING_ISSUE_LABELS, INDEXING_ISSUE_SEVERITY, IndexStatusResult, IndexingEligibility, IndexingIneligibleReason, IndexingIssueType, IndexingMetadata, type IndexingNextAction, IndexingNotificationType, IndexingResult, type IndexingStatus, InspectUrlIndexResponse, InspectUrlResult, InspectionPriority, KNOWN_COVERAGE_STATES, KNOWN_PAGE_FETCH_STATES, type LifecycleError, type LifecycleErrorCode, type LifecycleProgress, type LifecycleWebhookEnvelope, type LifecycleWebhookEvent, MS_PER_DAY, MobileUsabilityResult, OAuthTokens, Ok, ParsedGscSiteUrl, ParsedIndexingResult, type PartnerLifecycleAccount, type PartnerLifecycleResponse, type PartnerLifecycleSite, Period, type PropertyNextAction, type PropertyStatus, PublishUrlNotificationResponse, QueryReturn, type QuerySourceMode, RequiredNonNullable, ResolvedAnalyticsRange, Result, RichResultsResult, SearchAnalyticsQuery, SearchAnalyticsResponse, Site, SiteAnalytics, type SitemapNextAction, type SitemapStatus, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, UnmappedInspectionReasons, UrlInspectionResult, UrlNotificationMetadata, VerificationMethod, VerificationSite, VerificationSiteType, VerificationToken, VerificationWebResource, accountNextActions, accountStatuses, addDays, addSite, analyticsNextActions, analyticsStatuses, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, countDays, createAuth, createFetch, daysAgo, decodeSiteId, deleteSite, deleteSitemap, discoverSitemap, encodeSiteId, err, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getIndexingEligibility, getIndexingMetadata, getLatestGscDate, getNextCheckAfter, getNextCheckPriority, getNextDate, getOldestGscDate, getPendingDates, getPreviousDate, getPstDate, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, groupIntoRanges, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, hasOptionalIndexingScope, hasRequiredAnalyticsScope, indexingNextActions, indexingStatuses, inspectUrl, inspectUrlFlat, isErr, isOk, isPermissionDeniedError, isValidGscDate, isVerifiedGscPermission, isVerifiedGscProperty, lifecycleErrorCodes, lifecycleWebhookEvents, listVerifiedSites, mapErr, mapResult, matchGscSite, matchResult, normalizeGscSiteUrl, normalizeRegistrationTarget, normalizeSiteUrl, normalizeUrl, ok, parseGoogleError, parseGoogleScopes, parseGrantedScopes, parseGscSiteUrl, pickBestGscProperty, progressBar, propertyNextActions, propertyStatuses, querySourceModes, refreshAccessToken, refreshAccessTokenResult, requestIndexing, rethrowAsGscApiError, rowWithMetricDefaults, runSequentialBatch, siteUrlToVerificationSite, sitemapNextActions, sitemapStatuses, storageError, submitSitemap, toIsoDate, unmappedInspectionReasons, unverifySite, unwrapResult, urlMatchKey, verificationMethodsFor, verifySite };
925
+ export { type AccountNextAction, type AccountStatus, type AnalyticsNextAction, type AnalyticsStatus, ApiSite, ApiSitemap, ApiSitemapContent, Auth, AuthClient, AuthOptions, BackfillProgress, CallOptions, DAYS_PER_RANGE, DataRow, DimensionFilter, DimensionFilterGroup, DiscoverSitemapOptions, Err, FetchSitemapUrlsOptions, FetchSitesWithSitemapsOptions, GSCDUMP_ONBOARDING_CONTRACT_VERSION, GSCDUMP_OPTIONAL_INDEXING_SCOPE, GSCDUMP_REQUIRED_ANALYTICS_SCOPE, GSCDUMP_WRITE_ANALYTICS_SCOPE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_RETENTION_MONTHS, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GoogleSearchConsoleClient, GoogleSearchConsoleClientOptions, GscApiError, GscApiErrorInfo, GscError, GscErrorKind, GscPropertyCandidate, GscdumpApiOptions, INDEXING_ISSUE_FILTERS, INDEXING_ISSUE_LABELS, INDEXING_ISSUE_SEVERITY, IndexStatusResult, IndexingEligibility, IndexingIneligibleReason, IndexingIssueType, IndexingMetadata, type IndexingNextAction, IndexingNotificationType, IndexingResult, type IndexingStatus, InspectUrlIndexResponse, InspectUrlResult, InspectionPriority, KNOWN_COVERAGE_STATES, KNOWN_PAGE_FETCH_STATES, type LifecycleError, type LifecycleErrorCode, type LifecycleProgress, type LifecycleWebhookEnvelope, type LifecycleWebhookEvent, MS_PER_DAY, MobileUsabilityResult, OAuthTokens, Ok, ParsedGscSiteUrl, ParsedIndexingResult, type PartnerLifecycleAccount, type PartnerLifecycleResponse, type PartnerLifecycleSite, Period, type PropertyNextAction, type PropertyStatus, PublishUrlNotificationResponse, QueryReturn, type QuerySourceMode, RequiredNonNullable, ResolvedAnalyticsRange, Result, RichResultsResult, SearchAnalyticsQuery, SearchAnalyticsResponse, Site, SiteAnalytics, type SitemapNextAction, type SitemapStatus, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, UnmappedInspectionReasons, UrlInspectionResult, UrlNotificationMetadata, VerificationMethod, VerificationSite, VerificationSiteType, VerificationToken, VerificationWebResource, accountNextActions, accountStatuses, addDays, addSite, analyticsNextActions, analyticsStatuses, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, countDays, createAuth, createFetch, daysAgo, decodeSiteId, deleteSite, deleteSitemap, discoverSitemap, encodeSiteId, err, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getIndexingEligibility, getIndexingMetadata, getLatestGscDate, getNextCheckAfter, getNextCheckPriority, getNextDate, getOldestGscDate, getPendingDates, getPreviousDate, getPstDate, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, groupIntoRanges, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, hasOptionalIndexingScope, hasRequiredAnalyticsScope, indexingNextActions, indexingStatuses, inspectUrl, inspectUrlFlat, isErr, isOk, isPermissionDeniedError, isValidGscDate, isVerifiedGscPermission, isVerifiedGscProperty, lifecycleErrorCodes, lifecycleWebhookEvents, listVerifiedSites, mapErr, mapResult, matchGscSite, matchResult, normalizeGscSiteUrl, normalizeRegistrationTarget, normalizeSiteUrl, normalizeUrl, ok, parseGoogleError, parseGoogleScopes, parseGrantedScopes, parseGscSiteUrl, pickBestGscProperty, progressBar, propertyNextActions, propertyStatuses, querySourceModes, refreshAccessToken, refreshAccessTokenResult, requestIndexing, resolveVerificationTarget, rethrowAsGscApiError, rowWithMetricDefaults, runSequentialBatch, siteUrlToVerificationSite, sitemapNextActions, sitemapStatuses, storageError, submitSitemap, toIsoDate, unmappedInspectionReasons, unverifySite, unwrapResult, urlMatchKey, verificationMethodsFor, verifySite };
package/dist/index.mjs CHANGED
@@ -536,15 +536,16 @@ async function postOAuthTokenResult(body, op) {
536
536
  async function fetchSites(client) {
537
537
  return client.sites();
538
538
  }
539
- async function fetchSitesWithSitemaps(client) {
539
+ async function fetchSitesWithSitemaps(client, options = {}) {
540
540
  const sites = (await client.sites()).filter((s) => !!s.siteUrl && s.permissionLevel !== "siteUnverifiedUser");
541
- return Promise.all(sites.map(async (site) => {
541
+ const requestedConcurrency = options.concurrency ?? 4;
542
+ return runSequentialBatch(sites, async (site) => {
542
543
  const sitemaps = await client.sitemaps.list(site.siteUrl).catch(() => []);
543
544
  return {
544
545
  ...site,
545
546
  sitemaps
546
547
  };
547
- }));
548
+ }, { concurrency: Number.isFinite(requestedConcurrency) ? Math.max(1, Math.floor(requestedConcurrency)) : 4 });
548
549
  }
549
550
  async function fetchSitemaps(client, siteUrl) {
550
551
  return client.sitemaps.list(siteUrl);
@@ -565,6 +566,22 @@ async function deleteSite(client, siteUrl) {
565
566
  return client.sites.delete(siteUrl);
566
567
  }
567
568
  const SC_DOMAIN_PREFIX = "sc-domain:";
569
+ function bareDomain(siteUrl) {
570
+ if (siteUrl.startsWith(SC_DOMAIN_PREFIX)) return siteUrl.slice(10);
571
+ try {
572
+ return new URL(siteUrl.startsWith("http") ? siteUrl : `https://${siteUrl}`).host;
573
+ } catch {
574
+ return siteUrl;
575
+ }
576
+ }
577
+ function urlPrefix(siteUrl) {
578
+ try {
579
+ const url = new URL(siteUrl.startsWith("http") ? siteUrl : `https://${siteUrl}`);
580
+ return `${url.protocol}//${url.host}/`;
581
+ } catch {
582
+ return siteUrl;
583
+ }
584
+ }
568
585
  function siteUrlToVerificationSite(siteUrl) {
569
586
  if (siteUrl.startsWith(SC_DOMAIN_PREFIX)) return {
570
587
  type: "INET_DOMAIN",
@@ -575,6 +592,32 @@ function siteUrlToVerificationSite(siteUrl) {
575
592
  identifier: siteUrl
576
593
  };
577
594
  }
595
+ function resolveVerificationTarget(siteUrl, method) {
596
+ const isDomainProperty = siteUrl.startsWith(SC_DOMAIN_PREFIX);
597
+ const chosen = method ?? (isDomainProperty ? "DNS_TXT" : "META");
598
+ const isDns = chosen === "DNS_TXT" || chosen === "DNS_CNAME";
599
+ if (isDomainProperty && !isDns) return {
600
+ site: {
601
+ type: "INET_DOMAIN",
602
+ identifier: bareDomain(siteUrl)
603
+ },
604
+ method: "DNS_TXT"
605
+ };
606
+ if (isDns) return {
607
+ site: {
608
+ type: "INET_DOMAIN",
609
+ identifier: bareDomain(siteUrl)
610
+ },
611
+ method: chosen
612
+ };
613
+ return {
614
+ site: {
615
+ type: "SITE",
616
+ identifier: urlPrefix(siteUrl)
617
+ },
618
+ method: chosen
619
+ };
620
+ }
578
621
  function verificationMethodsFor(site) {
579
622
  if (site.type === "INET_DOMAIN") return ["DNS_TXT", "DNS_CNAME"];
580
623
  return [
@@ -585,20 +628,20 @@ function verificationMethodsFor(site) {
585
628
  ];
586
629
  }
587
630
  async function getVerificationToken(client, siteUrl, method) {
588
- const site = siteUrlToVerificationSite(siteUrl);
631
+ const { site, method: verificationMethod } = resolveVerificationTarget(siteUrl, method);
589
632
  return {
590
633
  ...await client.verification.getToken({
591
634
  site,
592
- verificationMethod: method
635
+ verificationMethod
593
636
  }),
594
637
  site
595
638
  };
596
639
  }
597
640
  async function verifySite(client, siteUrl, method) {
598
- const site = siteUrlToVerificationSite(siteUrl);
641
+ const { site, method: verificationMethod } = resolveVerificationTarget(siteUrl, method);
599
642
  return client.verification.insert({
600
643
  site,
601
- verificationMethod: method
644
+ verificationMethod
602
645
  });
603
646
  }
604
647
  async function listVerifiedSites(client) {
@@ -3083,9 +3126,16 @@ function pickBestGscProperty(origin, availableSites) {
3083
3126
  const matches = availableSites.filter((p) => matchGscSite(origin, p.siteUrl));
3084
3127
  if (!matches.length) return void 0;
3085
3128
  const isDomain = (p) => !!p.siteUrl?.startsWith("sc-domain:");
3129
+ const isHttps = (p) => !!p.siteUrl?.startsWith("https://");
3130
+ const pickTier = (pool) => pool.find(isDomain) ?? pool.find(isHttps) ?? pool[0];
3086
3131
  const verified = matches.filter((p) => isVerifiedGscPermission(p.permissionLevel));
3087
3132
  const pool = verified.length ? verified : matches;
3088
- return pool.find(isDomain) ?? pool[0];
3133
+ const originHost = stripWww(parseGscSiteUrl(origin).hostname.toLowerCase());
3134
+ const exact = pool.filter((property) => {
3135
+ if (!property.siteUrl) return false;
3136
+ return stripWww(parseGscSiteUrl(property.siteUrl).hostname.toLowerCase()) === originHost;
3137
+ });
3138
+ return pickTier(exact.length ? exact : pool);
3089
3139
  }
3090
3140
  function findBestGscProperty(targetDomain, properties) {
3091
3141
  const cleanTarget = stripWww(targetDomain);
@@ -3220,4 +3270,4 @@ function urlMatchKey(url) {
3220
3270
  if (!u) return null;
3221
3271
  return [u.hostname.replace(/^www\./, ""), u.pathname.replace(/\/$/, "") || "/"].join("").toLowerCase();
3222
3272
  }
3223
- export { DAYS_PER_RANGE, GSCDUMP_ONBOARDING_CONTRACT_VERSION, GSCDUMP_OPTIONAL_INDEXING_SCOPE, GSCDUMP_REQUIRED_ANALYTICS_SCOPE, GSCDUMP_WRITE_ANALYTICS_SCOPE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_RETENTION_MONTHS, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GscApiError, INDEXING_ISSUE_FILTERS, INDEXING_ISSUE_LABELS, INDEXING_ISSUE_SEVERITY, KNOWN_COVERAGE_STATES, KNOWN_PAGE_FETCH_STATES, MS_PER_DAY, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, accountNextActions, accountStatuses, addDays, addSite, analyticsNextActions, analyticsStatuses, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, countDays, createAuth, createFetch, daysAgo, decodeSiteId, deleteSite, deleteSitemap, discoverSitemap, encodeSiteId, err, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getIndexingEligibility, getIndexingMetadata, getLatestGscDate, getNextCheckAfter, getNextCheckPriority, getNextDate, getOldestGscDate, getPendingDates, getPreviousDate, getPstDate, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, groupIntoRanges, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, hasOptionalIndexingScope, hasRequiredAnalyticsScope, indexingNextActions, indexingStatuses, inspectUrl, inspectUrlFlat, isErr, isOk, isPermissionDeniedError, isValidGscDate, isVerifiedGscPermission, isVerifiedGscProperty, lifecycleErrorCodes, lifecycleWebhookEvents, listVerifiedSites, mapErr, mapResult, matchGscSite, matchResult, normalizeGscSiteUrl, normalizeRegistrationTarget, normalizeSiteUrl, normalizeUrl, ok, parseGoogleError, parseGoogleScopes, parseGrantedScopes, parseGscSiteUrl, pickBestGscProperty, progressBar, propertyNextActions, propertyStatuses, querySourceModes, refreshAccessToken, refreshAccessTokenResult, requestIndexing, rethrowAsGscApiError, rowWithMetricDefaults, runSequentialBatch, siteUrlToVerificationSite, sitemapNextActions, sitemapStatuses, storageError, submitSitemap, toIsoDate, unmappedInspectionReasons, unverifySite, unwrapResult, urlMatchKey, verificationMethodsFor, verifySite };
3273
+ export { DAYS_PER_RANGE, GSCDUMP_ONBOARDING_CONTRACT_VERSION, GSCDUMP_OPTIONAL_INDEXING_SCOPE, GSCDUMP_REQUIRED_ANALYTICS_SCOPE, GSCDUMP_WRITE_ANALYTICS_SCOPE, GSC_FINALIZED_LAG_DAYS, GSC_FRESHEST_LAG_DAYS, GSC_INDEXING_SCOPE, GSC_QUOTAS, GSC_READ_SCOPE, GSC_RETENTION_MONTHS, GSC_SITE_VERIFICATION_SCOPE, GSC_WRITE_SCOPE, GscApiError, INDEXING_ISSUE_FILTERS, INDEXING_ISSUE_LABELS, INDEXING_ISSUE_SEVERITY, KNOWN_COVERAGE_STATES, KNOWN_PAGE_FETCH_STATES, MS_PER_DAY, URL_INSPECTION_DAILY_LIMIT, URL_INSPECTION_EFFECTIVE_LIMIT, accountNextActions, accountStatuses, addDays, addSite, analyticsNextActions, analyticsStatuses, batchInspectUrls, batchRequestIndexing, canUseUrlInspection, classifyError, countDays, createAuth, createFetch, daysAgo, decodeSiteId, deleteSite, deleteSitemap, discoverSitemap, encodeSiteId, err, exchangeAuthCode, exchangeAuthCodeResult, fetchSitemap, fetchSitemapUrls, fetchSitemaps, fetchSites, fetchSitesWithSitemaps, findBestGscProperty, findExactGscProperty, formatErrorForCli, formatGscPropertyCandidates, generateGscDateRange, getBackfillProgress, getDateRange, getFreshestGscDate, getIndexingEligibility, getIndexingMetadata, getLatestGscDate, getNextCheckAfter, getNextCheckPriority, getNextDate, getOldestGscDate, getPendingDates, getPreviousDate, getPstDate, getVerificationToken, getVerifiedSite, googleSearchConsole, grantedScopeList, groupIntoRanges, gscErrorToException, gscPropertyMatchesTarget, gscdumpApi, hasGoogleScope, hasGscReadScope, hasGscWriteScope, hasIndexingScope, hasOptionalIndexingScope, hasRequiredAnalyticsScope, indexingNextActions, indexingStatuses, inspectUrl, inspectUrlFlat, isErr, isOk, isPermissionDeniedError, isValidGscDate, isVerifiedGscPermission, isVerifiedGscProperty, lifecycleErrorCodes, lifecycleWebhookEvents, listVerifiedSites, mapErr, mapResult, matchGscSite, matchResult, normalizeGscSiteUrl, normalizeRegistrationTarget, normalizeSiteUrl, normalizeUrl, ok, parseGoogleError, parseGoogleScopes, parseGrantedScopes, parseGscSiteUrl, pickBestGscProperty, progressBar, propertyNextActions, propertyStatuses, querySourceModes, refreshAccessToken, refreshAccessTokenResult, requestIndexing, resolveVerificationTarget, rethrowAsGscApiError, rowWithMetricDefaults, runSequentialBatch, siteUrlToVerificationSite, sitemapNextActions, sitemapStatuses, storageError, submitSitemap, toIsoDate, unmappedInspectionReasons, unverifySite, unwrapResult, urlMatchKey, verificationMethodsFor, verifySite };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "gscdump",
3
3
  "type": "module",
4
- "version": "0.38.2",
4
+ "version": "0.40.1",
5
5
  "description": "Google Search Console API wrapper with typed query builder, streaming pagination, and SEO analysis functions",
6
6
  "author": {
7
7
  "name": "Harlan Wilton",
@@ -100,10 +100,8 @@
100
100
  },
101
101
  "dependencies": {
102
102
  "date-fns": "^4.4.0",
103
- "defu": "^6.1.7",
104
103
  "ofetch": "^1.5.1",
105
- "ufo": "^1.6.4",
106
- "@gscdump/contracts": "0.38.2"
104
+ "@gscdump/contracts": "0.40.1"
107
105
  },
108
106
  "devDependencies": {
109
107
  "@googleapis/indexing": "^6.0.1",