gscdump 0.1.3 → 0.3.0

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
@@ -66,7 +66,7 @@ Drizzle-style query builder with full type safety. Filter constraints flow throu
66
66
  ```ts
67
67
  import { and, between, contains, country, Country, date, device, Device, eq, gsc, inArray, page } from 'gscdump/query'
68
68
 
69
- const result = await gsc
69
+ const body = gsc
70
70
  .select('page', 'query', 'device', 'country')
71
71
  .where(and(
72
72
  eq(device, Device.MOBILE),
@@ -74,14 +74,9 @@ const result = await gsc
74
74
  contains(page, '/blog/'),
75
75
  between(date, '2024-01-01', '2024-01-31')
76
76
  ))
77
- .siteUrl('https://example.com')
78
- .execute(client)
79
-
80
- // Fully typed results - narrowed by filters
81
- result.rows[0].device // type: 'MOBILE' (narrowed by eq)
82
- result.rows[0].country // type: 'usa' | 'gbr' (narrowed by inArray)
83
- result.rows[0].page // type: string (contains doesn't narrow)
84
- result.rows[0].clicks // type: number
77
+ .toBody()
78
+
79
+ // Use with client.searchAnalytics.query(siteUrl, body)
85
80
  ```
86
81
 
87
82
  **With date helpers:**
@@ -0,0 +1,513 @@
1
+ import "ofetch";
2
+ import { indexing_v3 } from "@googleapis/indexing/v3";
3
+ import { searchconsole_v1 } from "@googleapis/searchconsole/v1";
4
+
5
+ //#region src/analysis/types.d.ts
6
+ /**
7
+ * Shared types for analysis functions.
8
+ */
9
+ type SortOrder = 'asc' | 'desc';
10
+ /** Base search metrics */
11
+ interface BaseMetrics {
12
+ clicks: number;
13
+ impressions: number;
14
+ ctr: number;
15
+ position: number;
16
+ }
17
+ /** Keyword row from query */
18
+ interface KeywordRow extends BaseMetrics {
19
+ query: string;
20
+ page?: string;
21
+ }
22
+ /** Page row from query */
23
+ interface PageRow extends BaseMetrics {
24
+ page: string;
25
+ }
26
+ /** Date row from query */
27
+ interface DateRow extends BaseMetrics {
28
+ date: string;
29
+ }
30
+ /** Coerce nullable number to number, defaulting to 0 */
31
+ declare function num(value: number | null | undefined): number;
32
+ /** Create a generic sorter for any metric type */
33
+ declare function createSorter<T, M extends string>(getValue: (item: T, metric: M) => number, defaultMetric: M, defaultOrder?: SortOrder): (items: T[], sortBy?: M, sortOrder?: SortOrder) => T[];
34
+ //#endregion
35
+ //#region src/analysis/brand.d.ts
36
+ interface BrandSegmentationOptions {
37
+ /** Brand terms to match against keywords (case-insensitive) */
38
+ brandTerms: string[];
39
+ /** Minimum impressions for a keyword to be included. Default: 10 */
40
+ minImpressions?: number;
41
+ }
42
+ interface BrandSummary {
43
+ brandClicks: number;
44
+ nonBrandClicks: number;
45
+ brandShare: number;
46
+ brandImpressions: number;
47
+ nonBrandImpressions: number;
48
+ }
49
+ interface BrandSegmentationResult {
50
+ brand: KeywordRow[];
51
+ nonBrand: KeywordRow[];
52
+ summary: BrandSummary;
53
+ }
54
+ /**
55
+ * Segments keywords into brand and non-brand based on provided brand terms.
56
+ */
57
+ declare function analyzeBrandSegmentation(keywords: KeywordRow[], options: BrandSegmentationOptions): BrandSegmentationResult;
58
+ //#endregion
59
+ //#region src/analysis/clustering.d.ts
60
+ type ClusterType = 'prefix' | 'intent' | 'both';
61
+ interface ClusteringOptions {
62
+ /** Minimum keywords for a cluster to be reported. Default: 2 */
63
+ minClusterSize?: number;
64
+ /** Minimum impressions for a keyword to be included. Default: 10 */
65
+ minImpressions?: number;
66
+ /** Clustering method. Default: 'both' */
67
+ clusterBy?: ClusterType;
68
+ }
69
+ interface KeywordCluster {
70
+ clusterName: string;
71
+ clusterType: 'prefix' | 'intent';
72
+ keywords: KeywordRow[];
73
+ totalClicks: number;
74
+ totalImpressions: number;
75
+ avgPosition: number;
76
+ keywordCount: number;
77
+ }
78
+ interface ClusteringResult {
79
+ clusters: KeywordCluster[];
80
+ unclustered: KeywordRow[];
81
+ }
82
+ /**
83
+ * Clusters keywords by intent prefix or common word prefix.
84
+ */
85
+ declare function analyzeClustering(keywords: KeywordRow[], options?: ClusteringOptions): ClusteringResult;
86
+ //#endregion
87
+ //#region src/analysis/concentration.d.ts
88
+ type ConcentrationRiskLevel = 'low' | 'medium' | 'high';
89
+ interface ConcentrationOptions {
90
+ /** Number of top items to report. Default: 10 */
91
+ topN?: number;
92
+ }
93
+ interface ConcentrationItem {
94
+ key: string;
95
+ clicks: number;
96
+ share: number;
97
+ }
98
+ interface ConcentrationResult {
99
+ /** Gini coefficient: 0 = equal distribution, 1 = fully concentrated */
100
+ giniCoefficient: number;
101
+ /** Herfindahl-Hirschman Index: 0-10000, >2500 = highly concentrated */
102
+ hhi: number;
103
+ /** Percentage of total clicks from top N items */
104
+ topNConcentration: number;
105
+ topNItems: ConcentrationItem[];
106
+ totalItems: number;
107
+ totalClicks: number;
108
+ /** Risk level derived from HHI: <1500 low, 1500-2500 medium, >2500 high */
109
+ riskLevel: ConcentrationRiskLevel;
110
+ }
111
+ interface ConcentrationInput {
112
+ key: string;
113
+ clicks: number;
114
+ }
115
+ /**
116
+ * Analyzes traffic concentration across items (pages or keywords).
117
+ */
118
+ declare function analyzeConcentration(items: ConcentrationInput[], options?: ConcentrationOptions): ConcentrationResult;
119
+ /**
120
+ * Page concentration analysis.
121
+ */
122
+ declare function analyzePageConcentration(pages: PageRow[], options?: ConcentrationOptions): ConcentrationResult;
123
+ /**
124
+ * Keyword concentration analysis.
125
+ */
126
+ declare function analyzeKeywordConcentration(keywords: KeywordRow[], options?: ConcentrationOptions): ConcentrationResult;
127
+ //#endregion
128
+ //#region src/analysis/decay.d.ts
129
+ type DecaySortMetric = 'lostClicks' | 'declinePercent' | 'currentClicks';
130
+ interface DecayOptions {
131
+ /** Minimum clicks in previous period to consider. Default: 50 */
132
+ minPreviousClicks?: number;
133
+ /** Minimum decline percentage (0-1). Default: 0.2 (20%) */
134
+ threshold?: number;
135
+ /** Metric to sort results by. Default: lostClicks */
136
+ sortBy?: DecaySortMetric;
137
+ }
138
+ interface DecayInput {
139
+ current: PageRow[];
140
+ previous: PageRow[];
141
+ }
142
+ interface DecayResult {
143
+ page: string;
144
+ currentClicks: number;
145
+ previousClicks: number;
146
+ lostClicks: number;
147
+ declinePercent: number;
148
+ currentPosition: number;
149
+ previousPosition: number;
150
+ positionDrop: number;
151
+ }
152
+ /**
153
+ * Identifies "decaying" content - pages that have lost significant traffic.
154
+ */
155
+ declare function analyzeDecay(input: DecayInput, options?: DecayOptions): DecayResult[];
156
+ //#endregion
157
+ //#region src/core/types.d.ts
158
+ type ApiSite = searchconsole_v1.Schema$WmxSite;
159
+ type ApiSitemap = searchconsole_v1.Schema$WmxSitemap;
160
+ type SearchAnalyticsQuery = searchconsole_v1.Schema$SearchAnalyticsQueryRequest;
161
+ type SearchAnalyticsResponse = searchconsole_v1.Schema$SearchAnalyticsQueryResponse;
162
+ type InspectUrlIndexResponse = searchconsole_v1.Schema$InspectUrlIndexResponse;
163
+ type UrlNotificationMetadata = indexing_v3.Schema$UrlNotificationMetadata;
164
+ type PublishUrlNotificationResponse = indexing_v3.Schema$PublishUrlNotificationResponse;
165
+ //#endregion
166
+ //#region src/query/utils/countries.d.ts
167
+ declare const _default: {
168
+ name: string;
169
+ 'alpha-2': string;
170
+ 'alpha-3': string;
171
+ 'country-code': string;
172
+ }[];
173
+ //#endregion
174
+ //#region src/query/constants.d.ts
175
+ declare const Devices: {
176
+ readonly MOBILE: "MOBILE";
177
+ readonly DESKTOP: "DESKTOP";
178
+ readonly TABLET: "TABLET";
179
+ };
180
+ type Device = typeof Devices[keyof typeof Devices];
181
+ declare const SearchTypes: {
182
+ readonly WEB: "web";
183
+ readonly IMAGE: "image";
184
+ readonly VIDEO: "video";
185
+ readonly NEWS: "news";
186
+ };
187
+ type SearchType = typeof SearchTypes[keyof typeof SearchTypes];
188
+ declare const Countries: { [K in (typeof _default)[number]["alpha-3"]]: Lowercase<K> };
189
+ type Country = typeof Countries[keyof typeof Countries];
190
+ //#endregion
191
+ //#region src/query/types.d.ts
192
+ interface DimensionValueMap {
193
+ query: string;
194
+ queryCanonical: string;
195
+ page: string;
196
+ country: Country;
197
+ device: Device;
198
+ searchAppearance: string;
199
+ date: string;
200
+ }
201
+ type Dimension = keyof DimensionValueMap;
202
+ interface QueryParamValueMap {
203
+ searchType: SearchType;
204
+ }
205
+ type QueryParamName = keyof QueryParamValueMap;
206
+ declare const ColumnBrand: unique symbol;
207
+ interface Column<D extends Dimension> {
208
+ readonly [ColumnBrand]: D;
209
+ readonly dimension: D;
210
+ }
211
+ type FilterOperator = 'equals' | 'notEquals' | 'contains' | 'notContains' | 'includingRegex' | 'excludingRegex';
212
+ type DateOperator = 'gte' | 'gt' | 'lte' | 'lt' | 'between';
213
+ type MetricOperator = 'metricGte' | 'metricGt' | 'metricLte' | 'metricLt' | 'metricBetween';
214
+ type SpecialOperator = 'topLevel';
215
+ interface InternalFilter {
216
+ dimension: Dimension | QueryParamName | Metric;
217
+ operator: FilterOperator | DateOperator | MetricOperator | SpecialOperator;
218
+ expression: string;
219
+ expression2?: string;
220
+ }
221
+ declare const FilterBrand: unique symbol;
222
+ interface Filter<C = object> {
223
+ readonly [FilterBrand]: true;
224
+ readonly _constraints: C;
225
+ readonly _filters: InternalFilter[];
226
+ readonly _nestedGroups?: Filter<any>[];
227
+ readonly _groupType?: 'and' | 'or';
228
+ }
229
+ type GSCRow<D extends Dimension[], C> = { [K in D[number]]: K extends keyof C ? C[K] : DimensionValueMap[K] } & {
230
+ clicks: number;
231
+ impressions: number;
232
+ ctr: number;
233
+ position: number;
234
+ };
235
+ type Metric = 'clicks' | 'impressions' | 'ctr' | 'position';
236
+ declare const MetricColumnBrand: unique symbol;
237
+ interface MetricColumn<M extends Metric> {
238
+ readonly [MetricColumnBrand]: M;
239
+ readonly metric: M;
240
+ }
241
+ interface BuilderState {
242
+ dimensions: Dimension[];
243
+ metrics?: Metric[];
244
+ filter?: Filter<any>;
245
+ orderBy?: {
246
+ column: Metric | 'date';
247
+ dir: 'asc' | 'desc';
248
+ };
249
+ rowLimit?: number;
250
+ startRow?: number;
251
+ }
252
+ //#endregion
253
+ //#region src/query/builder.d.ts
254
+ type SelectableColumn = Column<Dimension> | MetricColumn<Metric>;
255
+ type OrderableColumn = MetricColumn<Metric> | Column<'date'>;
256
+ type ExtractDimensions<T extends SelectableColumn[]> = { [K in keyof T]: T[K] extends Column<infer D> ? D : never }[number] extends infer U ? Exclude<U, never>[] : never;
257
+ interface GSCQueryBuilder<D extends Dimension[] = [], C = object> {
258
+ select: {
259
+ <T extends Dimension[]>(...dims: T): GSCQueryBuilder<T, C>;
260
+ <T extends SelectableColumn[]>(...cols: T): GSCQueryBuilder<ExtractDimensions<T> & Dimension[], C>;
261
+ };
262
+ where: <F extends Filter<any>>(filter: F) => GSCQueryBuilder<D, C & F['_constraints']>;
263
+ orderBy: (col: OrderableColumn, dir: 'asc' | 'desc') => GSCQueryBuilder<D, C>;
264
+ limit: (n: number) => GSCQueryBuilder<D, C>;
265
+ offset: (n: number) => GSCQueryBuilder<D, C>;
266
+ toBody: () => SearchAnalyticsQuery;
267
+ getState: () => BuilderState;
268
+ }
269
+ //#endregion
270
+ //#region src/core/client.d.ts
271
+ interface GoogleSearchConsoleClient {
272
+ /** Query search analytics with builder, returns async generator yielding typed row batches */
273
+ query: <D extends Dimension[], C>(siteUrl: string, builder: GSCQueryBuilder<D, C>) => AsyncGenerator<GSCRow<D, C>[]>;
274
+ /** List all sites */
275
+ sites: () => Promise<ApiSite[]>;
276
+ /** Inspect a URL */
277
+ inspect: (siteUrl: string, url: string) => Promise<InspectUrlIndexResponse>;
278
+ /** Sitemap operations */
279
+ sitemaps: {
280
+ list: (siteUrl: string) => Promise<ApiSitemap[]>;
281
+ get: (siteUrl: string, feedpath: string) => Promise<ApiSitemap>;
282
+ submit: (siteUrl: string, feedpath: string) => Promise<void>;
283
+ delete: (siteUrl: string, feedpath: string) => Promise<void>;
284
+ };
285
+ /** Indexing API operations */
286
+ indexing: {
287
+ publish: (url: string, type: 'URL_UPDATED' | 'URL_DELETED') => Promise<PublishUrlNotificationResponse>;
288
+ getMetadata: (url: string) => Promise<UrlNotificationMetadata>;
289
+ };
290
+ /** @internal */
291
+ _rawQuery: (siteUrl: string, body: SearchAnalyticsQuery) => Promise<SearchAnalyticsResponse>;
292
+ }
293
+ //#endregion
294
+ //#region src/analysis/movers.d.ts
295
+ type MoversSortMetric = 'clicks' | 'impressions' | 'clicksChange' | 'impressionsChange' | 'positionChange';
296
+ interface MoversOptions {
297
+ /** Minimum change threshold to flag. Default: 0.2 (20%) */
298
+ changeThreshold?: number;
299
+ /** Minimum impressions in recent period. Default: 50 */
300
+ minImpressions?: number;
301
+ /** Metric to sort results by. Default: clicksChange */
302
+ sortBy?: MoversSortMetric;
303
+ }
304
+ interface MoversInput {
305
+ current: KeywordRow[];
306
+ previous: KeywordRow[];
307
+ /** If periods have different lengths, provide normalization factor (previous/current) */
308
+ normalizationFactor?: number;
309
+ }
310
+ interface MoverData {
311
+ keyword: string;
312
+ page: string | null;
313
+ recentClicks: number;
314
+ recentImpressions: number;
315
+ recentPosition: number;
316
+ baselineClicks: number;
317
+ baselineImpressions: number;
318
+ baselinePosition: number;
319
+ clicksChange: number;
320
+ clicksChangePercent: number;
321
+ impressionsChangePercent: number;
322
+ positionChange: number;
323
+ }
324
+ interface MoversResult {
325
+ rising: MoverData[];
326
+ declining: MoverData[];
327
+ stable: MoverData[];
328
+ }
329
+ /**
330
+ * Identifies movers and shakers - keywords with significant recent changes.
331
+ */
332
+ declare function analyzeMovers(input: MoversInput, options?: MoversOptions): MoversResult;
333
+ //#endregion
334
+ //#region src/analysis/opportunity.d.ts
335
+ type OpportunitySortMetric = 'opportunityScore' | 'potentialClicks' | 'impressions' | 'position';
336
+ interface OpportunityWeights {
337
+ position?: number;
338
+ impressions?: number;
339
+ ctrGap?: number;
340
+ }
341
+ interface OpportunityOptions {
342
+ /** Minimum impressions to consider. Default: 100 */
343
+ minImpressions?: number;
344
+ /** Custom weights for score factors. Default: all 1 */
345
+ weights?: OpportunityWeights;
346
+ /** Sort metric. Default: opportunityScore */
347
+ sortBy?: OpportunitySortMetric;
348
+ }
349
+ interface OpportunityFactors {
350
+ positionScore: number;
351
+ impressionScore: number;
352
+ ctrGapScore: number;
353
+ }
354
+ interface OpportunityResult {
355
+ keyword: string;
356
+ page: string | null;
357
+ clicks: number;
358
+ impressions: number;
359
+ ctr: number;
360
+ position: number;
361
+ opportunityScore: number;
362
+ potentialClicks: number;
363
+ factors: OpportunityFactors;
364
+ }
365
+ /**
366
+ * Scores keywords by optimization opportunity.
367
+ * Composite score combining position, impressions, and CTR gap factors.
368
+ */
369
+ declare function analyzeOpportunity(keywords: KeywordRow[], options?: OpportunityOptions): OpportunityResult[];
370
+ //#endregion
371
+ //#region src/analysis/seasonality.d.ts
372
+ type SeasonalityMetric = 'clicks' | 'impressions';
373
+ interface SeasonalityOptions {
374
+ /** Metric to analyze for seasonality. Default: clicks */
375
+ metric?: SeasonalityMetric;
376
+ }
377
+ interface MonthlyData {
378
+ month: string;
379
+ value: number;
380
+ vsAverage: number;
381
+ isPeak: boolean;
382
+ isTrough: boolean;
383
+ }
384
+ interface SeasonalityResult {
385
+ hasSeasonality: boolean;
386
+ /** Coefficient of variation: std dev / mean. Higher = more seasonal. */
387
+ strength: number;
388
+ peakMonths: string[];
389
+ troughMonths: string[];
390
+ monthlyBreakdown: MonthlyData[];
391
+ insufficientData: boolean;
392
+ }
393
+ /**
394
+ * Detects seasonality patterns by analyzing monthly traffic variation.
395
+ */
396
+ declare function analyzeSeasonality(dates: DateRow[], options?: SeasonalityOptions): SeasonalityResult;
397
+ //#endregion
398
+ //#region src/analysis/striking-distance.d.ts
399
+ type StrikingDistanceSortMetric = 'clicks' | 'impressions' | 'ctr' | 'position' | 'potentialClicks';
400
+ interface StrikingDistanceOptions {
401
+ /** Minimum position (inclusive). Default: 4 */
402
+ minPosition?: number;
403
+ /** Maximum position (inclusive). Default: 20 */
404
+ maxPosition?: number;
405
+ /** Minimum impressions. Default: 100 */
406
+ minImpressions?: number;
407
+ /** Maximum CTR (queries with low CTR have more potential). Default: 0.05 (5%) */
408
+ maxCtr?: number;
409
+ /** Sort metric. Default: potentialClicks */
410
+ sortBy?: StrikingDistanceSortMetric;
411
+ /** Sort order. Default: desc */
412
+ sortOrder?: SortOrder;
413
+ }
414
+ interface StrikingDistanceResult {
415
+ keyword: string;
416
+ page: string | null;
417
+ clicks: number;
418
+ impressions: number;
419
+ ctr: number;
420
+ position: number;
421
+ /** Estimated clicks if position improved to top 3 */
422
+ potentialClicks: number;
423
+ }
424
+ /**
425
+ * Finds striking distance keywords - high impressions, low CTR, position 4-20.
426
+ * These are "quick wins" that could gain significant traffic with small ranking improvements.
427
+ */
428
+ declare function analyzeStrikingDistance(keywords: KeywordRow[], options?: StrikingDistanceOptions): StrikingDistanceResult[];
429
+ //#endregion
430
+ //#region src/analysis/fetch.d.ts
431
+ interface AnalysisPeriod {
432
+ startDate: string;
433
+ endDate: string;
434
+ }
435
+ interface ComparisonPeriod {
436
+ current: AnalysisPeriod;
437
+ previous: AnalysisPeriod;
438
+ }
439
+ type QueryDimension = 'keywords' | 'pages' | 'dates';
440
+ interface QueryOptions {
441
+ /** Dimension to query. Default: keywords */
442
+ dimension?: QueryDimension;
443
+ /** Max rows to fetch. Default: 25000 */
444
+ limit?: number;
445
+ }
446
+ interface QueryResult {
447
+ keywords: KeywordRow[];
448
+ pages: PageRow[];
449
+ dates: DateRow[];
450
+ }
451
+ interface ComparisonQueryResult {
452
+ current: QueryResult;
453
+ previous: QueryResult;
454
+ }
455
+ /**
456
+ * Query search analytics data for a single period.
457
+ * Returns keywords, pages, and dates data.
458
+ * API calls: 3 (one per dimension)
459
+ */
460
+ declare function queryAnalytics(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: QueryOptions): Promise<QueryResult>;
461
+ /**
462
+ * Query search analytics data for current and previous periods.
463
+ * Returns comparison data for all dimensions.
464
+ * API calls: 6 (3 per period)
465
+ */
466
+ declare function queryComparison(client: GoogleSearchConsoleClient, siteUrl: string, periods: ComparisonPeriod, options?: QueryOptions): Promise<ComparisonQueryResult>;
467
+ /**
468
+ * Fetch keywords and analyze striking distance opportunities.
469
+ * API calls: 1
470
+ */
471
+ declare function fetchStrikingDistance(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: StrikingDistanceOptions): Promise<StrikingDistanceResult[]>;
472
+ /**
473
+ * Fetch keywords and analyze optimization opportunities.
474
+ * API calls: 1
475
+ */
476
+ declare function fetchOpportunity(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: OpportunityOptions): Promise<OpportunityResult[]>;
477
+ /**
478
+ * Fetch keywords and analyze brand vs non-brand segmentation.
479
+ * API calls: 1
480
+ */
481
+ declare function fetchBrandSegmentation(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options: BrandSegmentationOptions): Promise<BrandSegmentationResult>;
482
+ /**
483
+ * Fetch pages and analyze traffic concentration.
484
+ * API calls: 1
485
+ */
486
+ declare function fetchPageConcentration(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: ConcentrationOptions): Promise<ConcentrationResult>;
487
+ /**
488
+ * Fetch keywords and analyze traffic concentration.
489
+ * API calls: 1
490
+ */
491
+ declare function fetchKeywordConcentration(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: ConcentrationOptions): Promise<ConcentrationResult>;
492
+ /**
493
+ * Fetch keywords and analyze clusters.
494
+ * API calls: 1
495
+ */
496
+ declare function fetchClustering(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: ClusteringOptions): Promise<ClusteringResult>;
497
+ /**
498
+ * Fetch dates and analyze seasonality patterns.
499
+ * API calls: 1
500
+ */
501
+ declare function fetchSeasonality(client: GoogleSearchConsoleClient, siteUrl: string, period: AnalysisPeriod, options?: SeasonalityOptions): Promise<SeasonalityResult>;
502
+ /**
503
+ * Fetch pages for both periods and analyze content decay.
504
+ * API calls: 2
505
+ */
506
+ declare function fetchDecay(client: GoogleSearchConsoleClient, siteUrl: string, periods: ComparisonPeriod, options?: DecayOptions): Promise<DecayResult[]>;
507
+ /**
508
+ * Fetch keywords for both periods and analyze movers/shakers.
509
+ * API calls: 2
510
+ */
511
+ declare function fetchMovers(client: GoogleSearchConsoleClient, siteUrl: string, periods: ComparisonPeriod, options?: MoversOptions): Promise<MoversResult>;
512
+ //#endregion
513
+ export { type AnalysisPeriod, type BaseMetrics, type BrandSegmentationOptions, type BrandSegmentationResult, type BrandSummary, type ClusterType, type ClusteringOptions, type ClusteringResult, type ComparisonPeriod, type ComparisonQueryResult, type ConcentrationInput, type ConcentrationItem, type ConcentrationOptions, type ConcentrationResult, type ConcentrationRiskLevel, type DateRow, type DecayInput, type DecayOptions, type DecayResult, type DecaySortMetric, type KeywordCluster, type KeywordRow, type MonthlyData, type MoverData, type MoversInput, type MoversOptions, type MoversResult, type MoversSortMetric, type OpportunityFactors, type OpportunityOptions, type OpportunityResult, type OpportunitySortMetric, type OpportunityWeights, type PageRow, type QueryDimension, type QueryOptions, type QueryResult, type SeasonalityMetric, type SeasonalityOptions, type SeasonalityResult, type SortOrder, type StrikingDistanceOptions, type StrikingDistanceResult, type StrikingDistanceSortMetric, analyzeBrandSegmentation, analyzeClustering, analyzeConcentration, analyzeDecay, analyzeKeywordConcentration, analyzeMovers, analyzeOpportunity, analyzePageConcentration, analyzeSeasonality, analyzeStrikingDistance, createSorter, fetchBrandSegmentation, fetchClustering, fetchDecay, fetchKeywordConcentration, fetchMovers, fetchOpportunity, fetchPageConcentration, fetchSeasonality, fetchStrikingDistance, num, queryAnalytics, queryComparison };