import { GoogleSearchConsoleClient } from "gscdump"; import { SearchType } from "@gscdump/engine"; import { GscDataState, GscSearchAnalyticsMetadata } from "gscdump/contracts"; interface GscApiRow { keys: string[]; clicks: number; impressions: number; ctr: number; position: number; } interface SyncSliceDomainFilter { /** * Domain (eTLD+1 + subdomain) to scope the slice to — matches both * `www.` and bare variants. Strip the protocol; the regex is built here. */ domain?: string; } interface SyncSliceDimensionFilter { dimension: 'page' | 'query' | 'country' | 'device' | 'searchAppearance'; operator?: 'equals' | 'notEquals' | 'contains' | 'notContains' | 'includingRegex' | 'excludingRegex'; expression: string; } interface RunGscSyncSliceOptions { client: GoogleSearchConsoleClient; siteUrl: string; /** One of the engine sync-fan tables. Drives the dimension list. */ table: 'pages' | 'queries' | 'countries' | 'dates' | 'page_queries' | 'search_appearance' | 'search_appearance_pages' | 'search_appearance_queries' | 'search_appearance_page_queries' | 'hourly_pages'; startDate: string; endDate: string; domainFilter?: SyncSliceDomainFilter | null; /** Additional AND filters, e.g. `searchAppearance = AMP_BLUE_LINK`. */ dimensionFilters?: SyncSliceDimensionFilter[]; /** * Override the dimension list for this slice. Defaults to * `DIMENSIONS_BY_TABLE[table]`. Hosts that need bespoke groupings (e.g. * hourly Discover variants) supply this directly. */ dimensions?: string[]; /** * GSC `dataState` for the query. Defaults to `'all'`. Use `'hourly_all'` * for hourly Discover slices. */ dataState?: GscDataState; /** * Invoked per GSC API page with the rows fetched. Return a promise; the * loop awaits it before paging further. Throw a durable error to abort the * slice. A thrown AbortError / timeout stops the loop and returns retry * state at the current cursor so the continuation re-processes this page. */ onBatch: (rows: GscApiRow[]) => Promise; initialStartRow?: number; /** * Max GSC API pages per call. `Infinity` for unbounded; hosts cap this * based on path (D1 vs R2) and table. */ maxPages?: number; /** GSC page size. 500 is the D1-safe default; 10k is the R2 path. */ rowLimit?: number; /** * Soft CPU budget for the loop itself. Returns `hasMore` when crossed so * the continuation resumes from `nextStartRow`. */ cpuBudgetMs?: number; searchType?: SearchType; /** Invoked once per successful GSC API page. Hosts wire telemetry here. */ onPage?: (info: { searchType: SearchType; rowsThisPage: number; }) => void; } interface RunGscSyncSliceResult { totalRows: number; hasMore: boolean; nextStartRow: number; /** * Metadata from the LAST GSC API page seen during this slice run. When * `dataState='hourly_all'` and grouped by `hour`, this surfaces * `first_incomplete_hour` so hosts can watermark hourly progress. */ metadata?: GscSearchAnalyticsMetadata; } type SearchAppearanceContextGrain = 'page' | 'query' | 'page_query'; type SearchAppearanceContextTable = 'search_appearance_pages' | 'search_appearance_queries' | 'search_appearance_page_queries'; interface RunGscSearchAppearanceContextSliceOptions { client: GoogleSearchConsoleClient; siteUrl: string; startDate: string; endDate: string; domainFilter?: SyncSliceDomainFilter | null; /** Use a known appearance list to skip discovery. */ appearances?: string[]; /** Context grain to fetch for every discovered appearance. Defaults to page_query. */ grain?: SearchAppearanceContextGrain; /** Context table to fetch. Overrides `grain` when provided. */ table?: SearchAppearanceContextTable; dataState?: GscDataState; rowLimit?: number; maxPages?: number; cpuBudgetMs?: number; searchType?: SearchType; onTotalBatch?: (rows: GscApiRow[]) => Promise; onContextBatch: (batch: { searchAppearance: string; table: SearchAppearanceContextTable; rows: GscApiRow[]; }) => Promise; onPage?: (info: { searchType: SearchType; rowsThisPage: number; }) => void; continuation?: SearchAppearanceContinuation; } type SearchAppearanceContinuation = { phase: 'discovery'; appearances: string[]; nextStartRow: number; } | { phase: 'context'; appearances: string[]; appearanceIndex: number; nextStartRow: number; }; interface RunGscSearchAppearanceContextSliceResult { appearances: string[]; totalRows: number; hasMore: boolean; continuation?: SearchAppearanceContinuation; } declare function runGscSyncSlice(opts: RunGscSyncSliceOptions): Promise; /** * Implements GSC's required two-step search-appearance flow: * 1. group by `searchAppearance` alone to discover available appearances; * 2. for each appearance, filter by it and fetch page/query/date context. */ declare function runGscSearchAppearanceContextSlice(opts: RunGscSearchAppearanceContextSliceOptions): Promise; export { GscApiRow, RunGscSearchAppearanceContextSliceOptions, RunGscSearchAppearanceContextSliceResult, RunGscSyncSliceOptions, RunGscSyncSliceResult, SearchAppearanceContextGrain, SearchAppearanceContextTable, SearchAppearanceContinuation, SyncSliceDimensionFilter, SyncSliceDomainFilter, runGscSearchAppearanceContextSlice, runGscSyncSlice };