import { SearchType as SearchType$1, TableName as TableName$1 } from "../storage.mjs"; import { ComparisonFilter } from "./types.mjs"; import { Grain } from "@gscdump/contracts"; import { BuilderState } from "gscdump/query"; interface RunQueryCtx { userId: string; siteId: string; table: TableName$1; searchType?: SearchType$1; /** * Temporal granularity. `'day'` (default) drives `enumeratePartitions` to * emit `daily/{date}` only; hourly partitions are skipped by construction. * `'hour'` is reserved for hourly read paths and must use the dedicated * hourly query surface (callers pass `partitions: [hourly/{date}]` * directly through `runSQL`). */ grain?: Grain; } interface RunSQLFn { (opts: { ctx: { userId: string; siteId: string; }; table: TableName$1; fileSets: Record; sql: string; params: unknown[]; searchType?: SearchType$1; }): Promise<{ rows: Array>; }>; } interface CanonicalQueryDimSource { /** Object keys for the versioned query_dim parquet. */ keys: string[]; normalizerVersion: number; intentVersion?: number; builtAt?: number; } interface PrimaryColumnarSource { /** Pre-resolved compacted/Iceberg parquet object keys for the requested fact table. */ keys: string[]; /** Oldest covered date (`YYYY-MM-DD`). Omit to assert the source covers all older dates. */ coversFrom?: string; /** Newest covered date (`YYYY-MM-DD`). Omit to assert the source covers the current tail. */ coversThrough?: string; } /** * Optional overlay that serves a resolver extra (e.g. canonical-variant * grouping, keyed `'canonicalExtras'`) from a precomputed source — typically a * materialised rollup — instead of the live window-function SQL. Return the * rows in the exact shape the live extra produces (`mergeExtras` consumes * either source unchanged), or `null` to decline so the caller falls back to * the live query. Pure seam: storage/tenant routing lives in the host's * implementation, not here. See ADR-0017. */ interface ResolveExtraFn { (opts: { key: string; state: BuilderState; ctx: RunQueryCtx; dateRange: { startDate: string; endDate: string; }; }): Promise> | null>; } interface RunOptimizedQueryOptions { /** Overlay tried per extra before the live SQL; absent → today's live path. */ resolveExtra?: ResolveExtraFn; /** * Versioned query dimension backing canonical reads over fact rows. Required * whenever a query groups or filters by `queryCanonical`; a canonical rollup * source may carry the same metadata/key set via `canonicalSource.queryDim`. */ queryDim?: CanonicalQueryDimSource; /** * Primary fact-file source for consumer reads. Callers pass compacted or * Iceberg data-file keys with explicit coverage metadata; raw daily * partitions are only used when `primarySourceFallback: 'raw'` is set. */ primarySource?: PrimaryColumnarSource; /** * Explicit compatibility escape hatch for raw daily shards. Default is * strict: missing/empty/stale primary coverage fails with * `QuerySourceCoverageError`. */ primarySourceFallback?: 'raw'; /** * Opt-in canonical-primary performance (ADR-0018 Gap 2): object keys of the * `query_canonical_daily` rollup parquet(s). When supplied AND the query is * coverable (`canonicalRollupCovers`) AND the window is within the rollup's * coverage, the MAIN query reads these * pre-summed `(query_canonical × date)` rows instead of re-aggregating raw * partitions. Variant extras still need fact-grain rows, so they read * `primarySource` when present and raw partitions only via * `primarySourceFallback: 'raw'`. * * `coversThrough` (ISO `YYYY-MM-DD`, the rollup's newest covered date) gates * staleness: the source is used only when `dateRange.endDate <= coversThrough`, * else strict mode fails so the recent tail is never silently undercounted. * Omit to assert full coverage (use with care). */ canonicalSource?: { keys: string[]; coversThrough?: string; queryDim?: CanonicalQueryDimSource; }; /** Optional version gates for the query dimension backing `canonicalSource`. */ canonicalRequirements?: { normalizerVersion?: number; intentVersion?: number; }; } type QuerySourceKind = 'canonical-rollup' | 'primary-columnar' | 'raw-partitions'; type QuerySourceFallbackKind = 'primary-source-missing' | 'primary-source-empty' | 'primary-source-missing-coverage' | 'primary-source-stale-coverage' | 'canonical-source-missing' | 'canonical-source-empty' | 'canonical-source-not-coverable' | 'canonical-source-stale-coverage' | 'query-dim-missing' | 'query-dim-empty' | 'query-dim-stale-version'; interface QuerySourceFallback { kind: QuerySourceFallbackKind; message: string; } interface QuerySourceDecision { kind: QuerySourceKind; fallback?: QuerySourceFallback; fallbacks?: QuerySourceFallback[]; } declare class QuerySourceCoverageError extends Error { readonly fallback: QuerySourceFallback; name: string; constructor(fallback: QuerySourceFallback); } interface OptimizedQueryResult { rows: Array>; totalCount: number; totals: { clicks: number; impressions: number; ctr: number; position: number; }; extras: Array<{ key: string; rows: Array>; }>; source: QuerySourceDecision; extraSource?: QuerySourceDecision; } interface ComparisonQueryResult { rows: Array>; totalCount: number; totals: Record; source: QuerySourceDecision; } declare function runOptimizedQuery(runSQL: RunSQLFn, ctx: RunQueryCtx, state: BuilderState, dateRange: { startDate: string; endDate: string; }, options?: RunOptimizedQueryOptions): Promise; declare function runComparisonQuery(runSQL: RunSQLFn, ctx: RunQueryCtx, current: BuilderState, previous: BuilderState, windows: { current: { startDate: string; endDate: string; }; previous: { startDate: string; endDate: string; }; }, filter?: ComparisonFilter, options?: RunOptimizedQueryOptions): Promise; export { CanonicalQueryDimSource, ComparisonQueryResult, OptimizedQueryResult, PrimaryColumnarSource, QuerySourceCoverageError, QuerySourceDecision, QuerySourceFallback, QuerySourceFallbackKind, QuerySourceKind, ResolveExtraFn, RunOptimizedQueryOptions, RunQueryCtx, RunSQLFn, runComparisonQuery, runOptimizedQuery };