import { AnalyzerRegistry } from "@gscdump/engine/analyzer"; import { Result } from "gscdump/result"; import { AsyncDuckDB, AsyncDuckDBConnection, DuckDBBundles, DuckDBConfig } from "@duckdb/duckdb-wasm"; import { AnalysisParams } from "@gscdump/engine/analysis-types"; interface QueryResult { rows: Record[]; queryMs: number; } interface AnalyzeResult { results: Record[]; meta: Record; queryMs: number; } interface DuckDBWasmBootResult { db: AsyncDuckDB; conn: AsyncDuckDBConnection; } interface BootDuckDBWasmOptions { /** * DuckDB-WASM logger. Defaults to a `ConsoleLogger` thresholded at * `LogLevel.WARNING`, so real warnings/errors still surface but the per-query * INFO events (START/OK/RUN) — which DuckDB's default `ConsoleLogger()` emits * as raw objects, flooding the host console with dozens of lines per render — * are dropped. Pass `new ConsoleLogger(LogLevel.DEBUG)` to see everything, or * `new VoidLogger()` to silence it entirely. */ logger?: unknown; /** * Override the jsDelivr-hosted bundle map. Required in environments where * the default CDN is unreachable or where hosts must serve the WASM + * worker assets themselves (e.g. Cloudflare Workers' 25 MB per-asset cap). */ bundles?: DuckDBBundles; /** * Extra DuckDB open config. The browser runtime always forces HTTP files * into range-only mode (reliable HEAD probes, no full HTTP fallback) so a * server that cannot answer bounded reads fails closed. */ config?: DuckDBConfig; /** * Cap DuckDB's memory with `SET memory_limit=` right after open (e.g. * `'2GB'`, `'512MB'`). A runaway query then errors with a clean * out-of-memory rather than growing the WASM heap until the tab crashes — * DuckDB has no statement-level timeout, so abandoned queries are otherwise * only stopped by the caller's `AbortSignal` (which the runtime forwards to * `conn.cancelSent()`). Opt-in: omit to keep DuckDB-WASM's default sizing. * Accepts `` with an optional `B`/`KB`/`MB`/`GB`/`TB` suffix. */ memoryLimit?: string; } interface BrowserParquetFile { bytes: Uint8Array; name?: string; } interface BrowserParquetTable { table: string; files: BrowserParquetFile[]; } interface BrowserParquetUrlTable { table: string; urls: string[]; } interface AttachParquetTablesOptions { db: AsyncDuckDB; conn: AsyncDuckDBConnection; tables: BrowserParquetTable[]; schema?: string; } interface AttachParquetUrlTablesOptions { db: AsyncDuckDB; conn: AsyncDuckDBConnection; tables: BrowserParquetUrlTable[]; fetch?: typeof fetch; schema?: string; /** * Request init used only for runtime-owned HEAD / one-byte Range preflights. * DuckDB-WASM's internal HTTP reader cannot receive custom fetch headers; * URL reads must therefore be authorized by the URL itself. */ fetchInit?: RequestInit; /** * Caps simultaneous URL preflights. Browser source endpoints should already * return small, coverage-planned URL sets; this is the runtime's local * guard against accidental unbounded attachment. */ fetchConcurrency?: number; /** Reject before preflight when the URL set exceeds this many parquet files. */ maxFiles?: number; /** Reject before registration when hinted or authoritative bytes exceed budget. */ maxBytes?: number; /** Abort signal passed through to URL preflights and registration. */ signal?: AbortSignal; /** * How DuckDB reads the parquet bytes: * - `'http'` (default): register the URL as an HTTP file; DuckDB issues * its own range reads during query execution. Right for large parquet * where only some column chunks are touched per query. * - `'buffer'`: fetch the full file once into an `ArrayBuffer` and register * it via `registerFileBuffer`. DuckDB makes zero HTTP calls after * registration — every query reads from the in-memory copy. Right for * tiny files (e.g. `dates` daily-totals parquet, <50 KB per file) where * the per-file round-trip overhead dominates the actual bytes moved. */ attachMode?: 'http' | 'buffer'; /** * When `true` (default), skip per-file HEAD/Range preflight if the URL * carries a signed `?s=.` size hint — the size is already * known and DuckDB will learn `Accept-Ranges` from its first read. * Eliminates one round-trip per file on hosts that mint size hints. Set * `false` to force preflight against URLs whose size you don't trust. */ trustSizeHint?: boolean; /** * Manifest version the caller associates with this set of URLs. Returned * on the resulting handle so callers can compare against a fresh manifest * probe without re-attaching. Purely advisory — the runtime never derives * behavior from the value itself. */ version?: number | string; /** * Called once per parquet file after it's been fetched and registered with * DuckDB. For URL-backed HTTP files this means "preflighted and registered" * rather than fully downloaded; DuckDB then performs range reads during the * query. Fires in bounded-concurrency completion order, which is still not * guaranteed to match manifest URL order. Used by UI progress indicators to * tick a per-site counter; a no-op default keeps the hot path free. */ onFileAttached?: (info: { table: string; index: number; total: number; }) => void; } /** * Handle returned from {@link attachParquetUrlTables}. Lets callers detach * the created views (for lazy re-attach on a new manifest version) or cheap- * check the embedded version against a fresh probe. */ interface AttachedTablesHandle { version: number | string | undefined; tables: string[]; schema: string; detach: () => Promise; } interface BrowserAnalysisRuntime { db: AsyncDuckDB; conn: AsyncDuckDBConnection; query: (sql: string, params?: unknown[], signal?: AbortSignal) => Promise; analyze: (params: AnalysisParams, registry: AnalyzerRegistry, options?: { signal?: AbortSignal; }) => Promise; /** * Returns true when `expected` doesn't match the version the runtime was * attached with — cheap check callers can run before each query to decide * whether to detach + re-attach against a fresher manifest. Undefined * values on either side compare equal so the no-version path is a no-op. */ isStale: (expected: number | string | undefined) => boolean; /** Update the runtime's cached manifest version in-place (e.g. after a re-attach). */ setVersion: (version: number | string | undefined) => void; /** * Update the list of attached table names. Lets callers fast-fail in * `analyze()` when a SQL plan references a table that wasn't in the manifest * for this site (e.g. site has only `queries` parquet, analyzer wants * `page_queries`) — surface a clean `AttachedTableMissingError` so the * caller can route to cloud fallback without paying the SQL execution cost. */ setAttachedTables: (tables: readonly string[]) => void; close: () => Promise; } declare class BrowserAttachBudgetExceededError extends Error { name: string; } interface BrowserAttachError { kind: 'browser-attach-budget-exceeded'; message: string; /** Which budget tripped: the file count, the hinted byte plan, or the running byte plan. */ budget: 'maxFiles' | 'maxBytes'; } declare const browserAttachErrors: { readonly maxFilesExceeded: (files: number, maxFiles: number) => BrowserAttachError; readonly hintedBytesExceeded: (hintedBytes: number, maxBytes: number) => BrowserAttachError; readonly plannedBytesExceeded: (plannedBytes: number, maxBytes: number) => BrowserAttachError; }; declare function isBrowserAttachError(value: unknown): value is BrowserAttachError; declare function bootDuckDBWasm(options?: BootDuckDBWasmOptions): Promise; declare function attachParquetTables(options: AttachParquetTablesOptions): Promise; /** * Errors-as-values core for {@link attachParquetUrlTables}: returns a typed * {@link BrowserAttachError} when the requested file set blows the local file- * count / byte budget, so callers can branch (route the affected tables * server-side) instead of catching an untyped throw. WASM/DuckDB/HTTP IO * failures stay defects and propagate. `attachParquetUrlTables` is the thin * throwing wrapper preserving the historical `BrowserAttachBudgetExceededError`. */ declare function attachParquetUrlTablesResult(options: AttachParquetUrlTablesOptions): Promise>; /** * Attach browser parquet URL tables, throwing * {@link BrowserAttachBudgetExceededError} when the requested set blows the * file-count / byte budget. Thin throwing wrapper over * {@link attachParquetUrlTablesResult}; existing call sites and their * `instanceof BrowserAttachBudgetExceededError` checks keep holding. */ declare function attachParquetUrlTables(options: AttachParquetUrlTablesOptions): Promise; declare function createBrowserAnalysisRuntime(boot: DuckDBWasmBootResult, options?: { schema?: string; version?: number | string; attachedTables?: readonly string[]; }): BrowserAnalysisRuntime; export { AnalyzeResult, AttachParquetTablesOptions, AttachParquetUrlTablesOptions, AttachedTablesHandle, BootDuckDBWasmOptions, BrowserAnalysisRuntime, BrowserAttachBudgetExceededError, BrowserAttachError, BrowserParquetFile, BrowserParquetTable, BrowserParquetUrlTable, DuckDBWasmBootResult, QueryResult, attachParquetTables, attachParquetUrlTables, attachParquetUrlTablesResult, bootDuckDBWasm, browserAttachErrors, createBrowserAnalysisRuntime, isBrowserAttachError };