import type { DatabaseAdapter } from "./types.js"; /** Column metadata returned by columnInfo(). */ export interface ColumnInfoResult { name: string; type: string; size: number | null; decimals: number | null; nullable: boolean; primary_key: boolean; } /** * DatabaseResult — wraps fetched rows with convenience methods. * * Mirrors Python's `DatabaseResult` dataclass from tina4_python.database.adapter. * Provides iteration, JSON/CSV export, pagination metadata, and array-like access. */ export declare class DatabaseResult implements Iterable> { readonly records: Record[]; readonly columns: string[]; readonly count: number; readonly limit: number; readonly offset: number; private readonly _adapter?; private readonly _sql?; private _columnInfoCache?; [index: number]: Record | undefined; constructor(records?: Record[], columns?: string[], count?: number, limit?: number, offset?: number, adapter?: DatabaseAdapter, sql?: string); /** JSON string of records. */ toJson(): string; /** CSV with header row. */ toCsv(): string; /** Same as records — plain array of row objects. */ toArray(): Record[]; /** * Describe the page this result IS — the canonical pagination envelope. * * Takes NO arguments and derives every field from the query that produced this * result (ADR-0043). Passing ANY argument RAISES: a DatabaseResult holds no * connection, so an argument could only re-slice the rows already in memory and * then report total_pages for pages it can never reach. To read page N, FETCH * page N (limit + offset) and call this with no arguments. * * The envelope is EXACTLY seven snake_case keys, identical across all four * frameworks: `records, total, page, per_page, total_pages, limit, offset`. * * per_page = the query's limit * page = floor(offset / limit) + 1 * total = the TRUE total for the filter — Database.fetch (and * QueryBuilder.get) run a COUNT probe whenever a limit was * applied — NEVER the number of rows returned * total_pages = ceil(total / per_page) * records = the rows the query returned, VERBATIM (never re-sliced) * limit = the SQL limit actually applied * offset = the SQL offset actually applied * * The JSON payload is snake_case even though the method name is camelCase — a * JSON key is data, not a language surface (ADR-0043). The old duplicate and * camelCase keys (`data`, `count`, `perPage`, `totalPages`, `has_next`, * `has_prev`) are removed: Node emitted 13 keys, the worst offender of the four. * * @throws {TypeError} if called with any argument. */ toPaginate(): { records: Record[]; total: number; page: number; per_page: number; total_pages: number; limit: number; offset: number; }; /** Iterable — for (const row of result) */ [Symbol.iterator](): Iterator>; /** Total count — cross-framework parity with Python/Ruby. */ size(): number; /** Number of records in this page. */ get length(): number; /** Array-like indexed access with negative index support. */ at(index: number): Record | undefined; /** JSON.stringify support — serialises as the records array. */ toJSON(): Record[]; /** * Return column metadata for the query's table. * * Lazy — only queries the database when explicitly called. Caches the * result so subsequent calls return immediately without re-querying. */ columnInfo(): ColumnInfoResult[]; /** Extract table name from a SQL query using simple regex. */ private _extractTableFromSql; /** Query the database adapter for column metadata. */ private _queryColumnMetadata; /** Normalize adapter column info to standard format. */ private _normalizeColumns; /** Parse size and decimals from a type string like VARCHAR(255) or NUMERIC(10,2). */ private _parseTypeSize; /** Derive basic column info from record keys when no adapter is available. */ private _fallbackColumnInfo; }