import { DnaType } from '@ytrynot/dna'; import { z } from 'zod'; /** * @interface IForeignKeyDefinition * @description Defines a foreign key constraint. */ interface IForeignKeyDefinition { /** Target table name. */ table: string; /** Target column name in the foreign table. */ col: string; /** Referential integrity action on row deletion. */ onDelete?: "CASCADE" | "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION"; /** Referential integrity action on row update. */ onUpdate?: "CASCADE" | "SET NULL" | "SET DEFAULT" | "RESTRICT" | "NO ACTION"; } /** * @interface IUniqueConstraint * @description Defines a composite UNIQUE constraint at the table level. */ interface IUniqueConstraint { /** Columns that together must be unique. */ columns: string[]; /** Optional constraint name (SQLite ignores names but they're useful for documentation). */ name?: string; } /** * @interface qbTableOptions * @description Configuration options for Data Definition Language (DDL) generation. */ interface qbTableOptions { /** Override for the primary key (string or composite array). */ primaryKey?: string | string[]; /** Map of columns to their foreign key definitions. */ foreignKeys?: Record; /** Map of columns to their default SQL values. */ defaults?: Record; /** List of columns that must have a UNIQUE constraint (single-column). */ unique?: string[]; /** Composite UNIQUE constraints (multi-column). */ uniqueConstraints?: IUniqueConstraint[]; /** Table-level CHECK constraints (e.g. `["age >= 18", "status IN ('active', 'inactive')"]`). */ checks?: string[]; } /** * @interface IOnConflictConfig * @description Internal configuration for ON CONFLICT clauses, set by the * `OnConflictBuilder` sub-builder returned by `.onConflict()`. */ interface IOnConflictConfig { /** Conflict target columns (empty array = no target, bare ON CONFLICT). */ target: string[]; /** Optional partial-index WHERE predicate on the conflict target. */ targetWhere?: string; /** Conflict action: DO NOTHING or DO UPDATE. */ action: "NOTHING" | "UPDATE"; /** Columns to update with `excluded.col` (auto-generated). Used when action is UPDATE. */ updateFields?: string[]; /** Manual SET expressions: `{ col: "expr" }` → `col = expr`. Overrides updateFields when present. */ updateRaw?: Record; /** Optional WHERE predicate on the DO UPDATE SET clause. */ updateWhere?: string; } /** * @type tsQueryMode * @description Supported SQL operation modes for the Builder. */ type tsQueryMode = "SELECT" | "INSERT" | "INSERT_MULTI" | "INSERT_DEFAULT" | "UPDATE" | "DELETE" | "UPSERT" | "COUNT" | "CREATE_INDEX"; /** * @interface IJoinDefinition * @description Internal structure for SQL JOIN clauses. */ interface IJoinDefinition { /** Type of join (e.g., 'INNER', 'LEFT', 'RIGHT'). */ type: string; /** Table name or compiled subquery string. */ target: string; /** The ON join condition. */ on: string; } /** * @interface IOrderByDefinition * @description Internal structure for SQL ORDER BY clauses. */ interface IOrderByDefinition { /** Column name to sort by. */ field: string; /** Sort direction. */ dir: "ASC" | "DESC"; } /** * @type tsWhereDefinition * @description Definition for standard WHERE conditions. * - string: column name (defaults to 'col = @col') * - object: map column to a specific parameter name. */ type tsWhereDefinition = string | { /** The database column name. */ col: string; /** The parameter name in the query. */ param: string; }; /** * @interface IWhereInDefinition * @description Structure for WHERE IN clauses. */ interface IWhereInDefinition { /** The database column name. */ col: string; /** List of literal values or a subquery Builder. */ target: string[] | Builder; } /** * @interface ICaseBranch * @description Represents a single branch in a CASE WHEN expression. */ interface ICaseBranch { /** The condition after WHEN. */ when: string; /** The result after THEN. */ then: string; } /** * @interface IWindowDefinition * @description Configuration for Window Functions (OVER clause). */ interface IWindowDefinition { /** The function call (e.g., 'ROW_NUMBER()'). */ func: string; /** Optional columns for the PARTITION BY clause. */ partitionBy?: string[]; /** Optional ordering within the window. */ orderBy?: IOrderByDefinition[]; } /** * @type tsSqliteType * @description SQLite column types supported by the DDL generator. */ type tsSqliteType = "TEXT" | "INTEGER" | "REAL" | "BOOLEAN" | "DATETIME" | "BLOB"; /** * @type tsDefaultValue * @description Default value for a column, accepting two signatures: * - **Tagged**: `{ [type]: value }` — the DDL engine knows the type and quotes * automatically into a SQL literal. * - `{ string: "pending" }` → `DEFAULT 'pending'` * - `{ number: 42 }` → `DEFAULT 42` * - `{ boolean: true }` → `DEFAULT TRUE` * - `{ date: new Date("2024-01-01") }` → `DEFAULT '2024-01-01T00:00:00.000Z'` * - `{ raw: "CURRENT_TIMESTAMP" }` → `DEFAULT CURRENT_TIMESTAMP` (escape hatch) * - **Direct**: `value` (string or number) — passes through `.toString()`, * treated as raw SQL. The user provides the complete literal. * - `"CURRENT_TIMESTAMP"` → `CURRENT_TIMESTAMP` * - `42` → `42` * - `"'user'"` → `'user'` (user supplies the quotes) * * Introspectors (Zod, DNA) always produce the tagged form since they know the * schema type. Manual `qbColumn` definitions may use either form. */ type tsDefaultValue = { string: string; } | { number: number; } | { boolean: boolean; } | { date: Date; } | { raw: string; } | string | number; /** * @type qbTable * @description Public alias for `qbColumn[]` — the column array passed to * `QueryBuilder.createTable()`. */ type qbTable = qbColumn[]; /** * @interface qbColumn * @description Neutral column representation produced by schema introspectors * (Zod or DNA). Consumed by the DDL engine to generate CREATE TABLE statements. * This abstraction avoids duplicating SQL generation logic per schema library. * Public column definition for the schema-agnostic DDL path * (`QueryBuilder.createTable()`). Maps internally to `qbColumn`. * Use this when you don't have a Zod or DNA schema and want to define * a table directly with column shapes. * * @example * ```ts * import { QueryBuilder, type qbColumn } from "@ytrynot/qb"; * * const columns: qbColumn[] = [ * { name: "id", sqliteType: "TEXT", optional: false, hasDefault: false, meta: { pk: true } }, * { name: "email", sqliteType: "TEXT", optional: false, hasDefault: false, meta: { unique: true } }, * ]; * const ddl = QueryBuilder.createTable("users", columns); * ``` */ interface qbColumn { /** Column name. */ name: string; /** SQLite type mapped from the source schema type. */ sqliteType: tsSqliteType; /** Whether the column is optional (NOT NULL omitted). */ optional: boolean; /** Whether the column has a default value (DEFAULT clause emitted). */ hasDefault: boolean; /** Default value (tagged or direct) when `hasDefault` is true. See `tsDefaultValue`. */ defaultValue?: tsDefaultValue; /** Whether the column is an auto-increment primary key. */ pkauto?: boolean; /** Whether the column is marked as UNIQUE. */ unique?: boolean; /** Foreign key reference, if any. */ fk?: string | IForeignKeyDefinition; /** Column-level CHECK constraint (e.g. `"age >= 0"`). */ check?: string; /** Raw metadata bag from the source schema (for advanced overrides). */ meta: Record; } /** * @interface ISchemaIntrospector * @description Contract for schema introspectors. Each adapter (Zod, DNA) * implements this to produce a neutral qbColumn[] from its native schema. */ interface ISchemaIntrospector { /** Extract the column shapes from a schema. Returns null if not an object schema. */ getColumns(schema: S): qbColumn[] | null; /** Detect the primary key column name from a schema. Returns null if none. */ getPrimaryKey(schema: S): string | null; } /** * @interface TableDef * @description Return type of `QueryBuilder.defTable()`. * Contains pre-built generic SQL statements (DDL + DML) and a `req` getter * that returns a fresh Builder pre-configured with the table name and uniqueKeys. */ interface TableDef { /** DDL: CREATE TABLE IF NOT EXISTS statement. */ createTable: string; /** DML: SELECT * FROM . */ getAll: string; /** DML: SELECT * FROM
WHERE = @. */ getById: string; /** DML: INSERT INTO
(...) VALUES (...). */ insert: string; /** DML: UPDATE
SET ... WHERE = @. */ update: string; /** DML: DELETE FROM
WHERE = @. */ delete: string; /** DML: INSERT ... ON CONFLICT() DO UPDATE SET .... */ upsert: string; /** Returns a fresh Builder pre-configured with the table name and uniqueKeys for custom queries. */ readonly req: Builder; /** Alias for `req`. */ readonly q: Builder; } /** * @class Builder * @description Fluent DML Query Builder for constructing SQL queries. * Supports SELECT, INSERT, UPDATE, DELETE, UPSERT, and COUNT operations. */ declare class Builder { #private; /** * @constructor * @param {string} table - The table name. * @param {string[]} [uniqueKeys] - Optional unique keys (conflict targets) for upsert auto-deduction. */ constructor(table: string, uniqueKeys?: string[]); /** * @function as * @description Sets a table alias for the query (e.g., "users u"). * @param {string} alias - The alias name. * @returns {this} The current Builder instance for chaining. * @usage `.as("u")` */ as(alias: string): this; /** * @function clone * @description Creates an independent copy of the current Builder instance. * Useful for reusing a base query (e.g., pagination with a count and a select). * @returns {Builder} A new Builder instance with the same state. */ clone(): Builder; /** * @function select * @description Configure the query to retrieve specific columns. * @param {string[]} [fields=['*']] - Columns to select (array form). * @returns {this} The current Builder instance for chaining. * @usage `.select(['id', 'name'])` or `.select('id', 'name')` * @impact Changes mode to 'SELECT'. */ select(fields: string[]): this; select(...fields: string[]): this; /** * @function count * @description Configure the query to perform a SELECT COUNT(*) operation. * @returns {this} The current Builder instance for chaining. * @usage `.count()` * @impact Changes mode to 'COUNT'. */ count(): this; /** * @function insert * @description Configure the query for inserting new rows. * @param {string[]} fields - The names of the columns to insert into (array form). * @returns {this} The current Builder instance for chaining. * @usage `.insert(['level', 'message'])` or `.insert('level', 'message')` * @impact Changes mode to 'INSERT'. */ insert(fields: string[]): this; insert(...fields: string[]): this; /** * @function insertMulti * @description Configure the query for a multi-row INSERT. * * Generates `rowCount` groups of named placeholders, indexed 0-based per row: * `VALUES (@col_0, @col_1), (@col_2, @col_3), ...` * * The builder does not take values — only column names. The user binds values * at the driver level using the generated parameter names. * * @param {string[]} fields - The names of the columns to insert. * @param {number} rowCount - Number of value groups (rows) to generate. * @returns {this} The current Builder instance for chaining. * @usage * `.insertMulti(['email', 'name'], 3)` * // → INSERT INTO users (email, name) VALUES (@email_0, @name_0), (@email_1, @name_1), (@email_2, @name_2) * @impact Changes mode to 'INSERT_MULTI'. * @note SQLite limits the number of bound parameters per statement (`SQLITE_MAX_VARIABLE_NUMBER`: * 999 in older versions, 32766 in recent ones). `fields.length * rowCount` must not exceed * your driver's limit. qb does not validate this — split large batches if needed. */ insertMulti(fields: string[], rowCount: number): this; /** * @function insertDefaultValues * @description Configure the query for an INSERT with all columns set to their default values. * Produces `INSERT INTO table DEFAULT VALUES`. * @returns {this} The current Builder instance for chaining. * @usage `.insertDefaultValues()` * @impact Changes mode to 'INSERT_DEFAULT'. */ insertDefaultValues(): this; /** * @function update * @description Configure the query for updating existing rows. * @param {string[]} fields - The names of the columns to update (array form). * @returns {this} The current Builder instance for chaining. * @usage `.update(['status']).where(['id'])` or `.update('status').where('id')` * @impact Changes mode to 'UPDATE'. */ update(fields: string[]): this; update(...fields: string[]): this; /** * @function delete * @description Configure the query for row deletion. * @returns {this} The current Builder instance for chaining. * @usage `.delete().where(['expired'])` * @impact Changes mode to 'DELETE'. */ delete(): this; /** * @function uniqueKeys * @description Sets the unique keys (conflict targets) for the builder. * When set, `.upsert()` can be called without explicit uniqueKeys — they are auto-deduced. * @param {...string[]} keys - The column names that are unique (PRIMARY KEY or UNIQUE constraints). * @returns {this} The current Builder instance for chaining. * @usage `.uniqueKeys("email")` or `.uniqueKeys("email", "tenant_id")` */ uniqueKeys(...keys: string[]): this; /** * @function onConflict * @description Starts an ON CONFLICT clause (UPSERT). Returns an `OnConflictBuilder` * that exposes `.doNothing()` and `.doUpdate()` / `.doUpdateRaw()`. * * Must be called after `.insert()`. The conflict config is rendered in the INSERT * statement when `.toSQL()` is called. * * @param {string | string[]} [target] - Conflict target column(s). Omit for a bare `ON CONFLICT` (no target). * @param {object} [options] - Optional settings. * @param {string} [options.where] - Partial-index WHERE predicate on the conflict target. * @returns {OnConflictBuilder} A sub-builder for the conflict action. * @usage * `.insert(['email', 'name']).onConflict('email').doUpdate(['name'])` * `.insert(['email', 'name']).onConflict('email').doNothing()` * `.insert(['a', 'b']).onConflict('a', { where: 'b > 0' }).doUpdate(['b'])` */ onConflict(target?: string | string[], options?: { where?: string; }): OnConflictBuilder; /** * @function upsert * @description Configure the query for UPSERT (Insert or Update on conflict). * Uses the `uniqueKeys` pre-configured via `.uniqueKeys()` or `defTable()` as conflict targets. * Update fields are auto-deduced: all fields not in uniqueKeys become `DO UPDATE SET col = excluded.col`. * Throws if no uniqueKeys are configured. * * For advanced ON CONFLICT control (DO NOTHING, partial index WHERE, raw expressions, * WHERE on DO UPDATE), use `.insert().onConflict(cols).doUpdate()/.doNothing()/.doUpdateRaw()` instead. * * @see {@link Builder#onConflict} for the full ON CONFLICT sub-builder API. * @param {string[]} fields - The names of the columns to insert/update. Conflict targets must be included in fields. * @returns {this} The current Builder instance for chaining. * @throws {Error} If no uniqueKeys are configured. Call `.uniqueKeys()` first or use `defTable()`. * @usage `.upsert(['email', 'name'])` or `.upsert('email', 'name')` (requires uniqueKeys set) * @impact Changes mode to 'UPSERT'. */ upsert(fields: string[]): this; upsert(field: string, ...rest: string[]): this; /** * @function where * @description Adds standard WHERE conditions. * @param {(string | { col: string, param: string })[]} fields - List of columns (string) or column-parameter mappings (array form). * @returns {this} The current Builder instance for chaining. * @usage `.where(['status', { col: 'type_id', param: 'type' }])` or `.where('status')` */ where(fields: tsWhereDefinition[]): this; where(...fields: tsWhereDefinition[]): this; /** * @function whereColumn * @description Adds a WHERE condition comparing two columns. * @param {string} col1 - The first column name. * @param {string} col2 - The second column name. * @returns {this} The current Builder instance for chaining. * @usage `.whereColumn('updated_at', 'created_at')` */ whereColumn(col1: string, col2: string): this; /** * @function whereLiteral * @description Adds a WHERE condition with a literal SQL value. * @param {string} col - The column name. * @param {string} value - The literal SQL value (e.g., "'active'", "CURRENT_TIMESTAMP"). * @returns {this} The current Builder instance for chaining. * @usage `.whereLiteral('status', "'deleted'")` */ whereLiteral(col: string, value: string): this; /** * @function whereIn * @description Adds a WHERE IN clause. * @param {string} col - The column name. * @param {string[] | Builder} target - List of values or a subquery Builder. * @returns {this} The current Builder instance for chaining. * @usage `.whereIn('id', ['1', '2'])` or `.whereIn('id', subquery)` */ whereIn(col: string, target: string[] | Builder): this; /** * @function whereRaw * @description Adds a raw SQL WHERE condition. * @param {string} condition - The raw SQL condition. * @returns {this} The current Builder instance for chaining. * @usage `.whereRaw("json_extract(meta, '$.id') = '123'")` */ whereRaw(condition: string): this; /** * @function createIndex * @description Configure the query to create an index. * @param {string} indexName - Name of the index. * @param {string[]} columns - Columns or expressions to include in the index (e.g. `['email']` or `['LOWER(name)']`). * @param {object} [options] - Optional settings. * @param {string} [options.where] - Partial-index WHERE predicate (e.g. `'active = 1'`). Must exactly match the index expression. * @returns {this} The current Builder instance for chaining. * @usage `.createIndex('idx_user_email', ['email'])` or `.createIndex('idx_active', ['email'], { where: 'active = 1' })` * @impact Changes mode to 'CREATE_INDEX'. */ createIndex(indexName: string, columns: string[], options?: { where?: string; }): this; /** * @function limit * @description Adds a LIMIT clause. * @param {number} n - Maximum number of rows to return. * @returns {this} The current Builder instance for chaining. */ limit(n: number): this; /** * @function offset * @description Adds an OFFSET clause. * @param {number} n - Number of rows to skip. * @returns {this} The current Builder instance for chaining. */ offset(n: number): this; /** * @function returning * @description Adds a RETURNING clause (SQLite 3.35+). * @param {string[]} [fields=['*']] - Columns to return. * @returns {this} The current Builder instance for chaining. * @usage `.insert(['name']).returning(['id'])` */ returning(fields?: string[]): this; /** * @function selectRaw * @description Adds a raw SQL expression to the SELECT clause. * @param {string} rawSql - The raw SQL expression. * @returns {this} The current Builder instance for chaining. * @usage `.selectRaw('COUNT(*) as total')` */ selectRaw(rawSql: string): this; /** * @function selectCase * @description Adds a CASE WHEN SQL expression to the SELECT clause. * @param {string} alias - Alias for the resulting column. * @param {{ when: string, then: string }[]} branches - list of WHEN conditions and THEN results. * @param {string} [elseValue] - Optional ELSE result. * @returns {this} The current Builder instance for chaining. * @usage `.selectCase('status_label', [{ when: 'status = 1', then: "'Active'" }])` */ selectCase(alias: string, branches: ICaseBranch[], elseValue?: string): this; /** * @function selectWindow * @description Adds a Window Function (OVER clause) to the SELECT clause. * @param {string} alias - Alias for the resulting column. * @param {Object} def - Window definition. * @param {string} def.func - Window function (e.g., 'ROW_NUMBER()'). * @param {string[]} [def.partitionBy] - Optional PARTITION BY columns. * @param {Object[]} [def.orderBy] - Optional ORDER BY configuration. * @returns {this} The current Builder instance for chaining. * @usage `.selectWindow('row_num', { func: 'ROW_NUMBER()', partitionBy: ['dept'] })` */ selectWindow(alias: string, def: IWindowDefinition): this; /** * @function orderBy * @description Adds an ORDER BY clause. * @param {string} field - Column name to sort by. * @param {'ASC' | 'DESC'} [dir='ASC'] - Sort direction. * @returns {this} The current Builder instance for chaining. */ orderBy(field: string, dir?: "ASC" | "DESC"): this; /** * @function groupBy * @description Adds a GROUP BY clause. * @param {string[]} fields - Column names to group by. * @returns {this} The current Builder instance for chaining. */ groupBy(fields: string[]): this; /** * @function distinct * @description Adds a DISTINCT clause to SELECT, removing duplicate rows from the result set. * @returns {this} The current Builder instance for chaining. * @usage `.select(['dept']).distinct()` */ distinct(): this; /** * @function having * @description Adds a HAVING clause for GROUP BY filtering (on aggregates like COUNT, SUM, AVG). * @param {tsWhereDefinition | tsWhereDefinition[]} conditions - HAVING conditions (same shape as `.where()`). * @returns {this} The current Builder instance for chaining. * @usage `.groupBy(['user_id']).having(['COUNT(*) > 5'])` */ having(conditions: tsWhereDefinition[] | tsWhereDefinition): this; having(...conditions: tsWhereDefinition[]): this; /** * @function joinLeft * @description Adds a LEFT JOIN clause. * @param {string | Builder} target - Table name or subquery Builder. * @param {string} onOrAlias - ON condition (string) or subquery Alias. * @param {string} [onCondition] - ON condition if first param is a Builder. * @returns {this} The current Builder instance for chaining. */ joinLeft(target: string | Builder, onOrAlias: string, onCondition?: string): this; /** * @function joinInner * @description Adds an INNER JOIN clause. * @param {string | Builder} target - Table name or subquery Builder. * @param {string} onOrAlias - ON condition (string) or subquery Alias. * @param {string} [onCondition] - ON condition if first param is a Builder. * @returns {this} The current Builder instance for chaining. */ joinInner(target: string | Builder, onOrAlias: string, onCondition?: string): this; /** * @function joinRight * @description Adds a RIGHT JOIN clause. * @param {string | Builder} target - Table name or subquery Builder. * @param {string} onOrAlias - ON condition (string) or subquery Alias. * @param {string} [onCondition] - ON condition if first param is a Builder. * @returns {this} The current Builder instance for chaining. */ joinRight(target: string | Builder, onOrAlias: string, onCondition?: string): this; /** * @function asExists * @description Wraps the current query into an EXISTS (...) expression. * @returns {string} Compiled SQL. */ asExists(): string; /** * @function asNotExists * @description Wraps the current query into a NOT EXISTS (...) expression. * @returns {string} Compiled SQL. */ asNotExists(): string; /** * @function search * @description Searches a text pattern across `columnsToSearch` (via `LIKE @search_term`) and filters exact values on `columnsToFilter` (via `col = @col`). * @param {string[]} columnsToSearch - Columns where the text pattern is searched with LIKE. * @param {(string | { col: string, param: string })[]} [columnsToFilter=[]] - Columns filtered by exact match (col = @col). * @returns {this} The current Builder instance for chaining. * @usage `.search(['name', 'email'], ['status'])` -> requires passing `{ search_term: '%value%', status: 'active' }` at execution time. * @impact Changes mode to 'SELECT'. */ search(columnsToSearch: string[], columnsToFilter?: tsWhereDefinition[]): this; /** * @function toSQL * @description Compiles the current builder state into a final SQL string. * @returns {string} The compiled SQL query. * @throws {Error} If the query mode is unknown. */ toSQL(): string; } /** * @class OnConflictBuilder * @description Sub-builder returned by `Builder.onConflict()`. * Exposes `.doNothing()` and `.doUpdate()` / `.doUpdateRaw()` to complete * the ON CONFLICT clause. Each method returns the parent `Builder` for chaining. * * The conflict config is injected into the parent `Builder` via a closure * captured at construction time — no public setter is exposed on `Builder`. */ declare class OnConflictBuilder { #private; /** * @constructor * @param {Builder} parent - The parent Builder instance. * @param {string[]} target - Conflict target columns (empty = bare ON CONFLICT). * @param {string} [targetWhere] - Optional partial-index WHERE predicate. * @param {(config: IOnConflictConfig) => void} setter - Closure that sets the config on the parent's private field. */ constructor(parent: Builder, target: string[], targetWhere: string | undefined, setter: (config: IOnConflictConfig) => void); /** * @function doNothing * @description Sets the conflict action to DO NOTHING. * @returns {Builder} The parent Builder for chaining. * @usage `.onConflict('email').doNothing()` */ doNothing(): Builder; /** * @function doUpdate * @description Sets the conflict action to DO UPDATE SET with auto-generated `excluded.col` references. * @param {string[]} fields - Columns to update (each becomes `col = excluded.col`). * @param {string} [where] - Optional WHERE predicate on the DO UPDATE clause. * @returns {Builder} The parent Builder for chaining. * @usage `.onConflict('email').doUpdate(['name'])` */ doUpdate(fields: string[], where?: string): Builder; /** * @function doUpdateRaw * @description Sets the conflict action to DO UPDATE SET with manual expressions. * @param {Record} sets - Map of column → SQL expression (e.g. `{ count: 'count + 1' }`). * @param {string} [where] - Optional WHERE predicate on the DO UPDATE clause. * @returns {Builder} The parent Builder for chaining. * @usage `.onConflict('email').doUpdateRaw({ updated_at: 'CURRENT_TIMESTAMP', count: 'count + 1' })` */ doUpdateRaw(sets: Record, where?: string): Builder; } /** * @class PragmaBuilder * @description Fluent builder for SQLite PRAGMA statements. */ declare class PragmaBuilder { #private; /** * @function foreignKeys * @description Enforces foreign key constraints. * @param {boolean} [on=true] * @returns {this} */ foreignKeys(on?: boolean): this; /** * @function journalMode * @description Sets the journal mode (e.g., WAL, DELETE, MEMORY). * @param {'WAL' | 'DELETE' | 'MEMORY' | 'TRUNCATE' | 'PERSIST' | 'OFF'} mode * @returns {this} */ journalMode(mode: "WAL" | "DELETE" | "MEMORY" | "TRUNCATE" | "PERSIST" | "OFF"): this; /** * @function synchronous * @description Controls disk synchronization (OFF, NORMAL, FULL, EXTRA). * @param {'OFF' | 'NORMAL' | 'FULL' | 'EXTRA'} level * @returns {this} */ synchronous(level: "OFF" | "NORMAL" | "FULL" | "EXTRA"): this; /** * @function cacheSize * @description Sets the database cache size. * @param {number} size - Positive for pages, negative for kilobytes. * @returns {this} */ cacheSize(size: number): this; /** * @function tempStore * @description Sets where temporary tables/indexes are stored. * @param {'DEFAULT' | 'FILE' | 'MEMORY'} location * @returns {this} */ tempStore(location: "DEFAULT" | "FILE" | "MEMORY"): this; /** * @function raw * @description Adds a custom PRAGMA statement. * @param {string} key * @param {string | number} value * @returns {this} */ raw(key: string, value: string | number): this; /** * @function busyTimeout * @description Sets the timeout (ms) for busy handlers before returning SQLITE_BUSY. * @param {number} ms * @returns {this} */ busyTimeout(ms: number): this; /** * @function mmap_size * @description Sets the mmap limit (bytes) for memory-mapped I/O. * @param {number} bytes * @returns {this} */ mmap_size(bytes: number): this; /** * @function pageSize * @description Sets the database page size (must be power of 2 between 512 and 65536). * @param {number} bytes * @returns {this} */ pageSize(bytes: number): this; /** * @function autoVacuum * @description Sets the auto-vacuum mode. * @param {'NONE' | 'FULL' | 'INCREMENTAL'} mode * @returns {this} */ autoVacuum(mode: "NONE" | "FULL" | "INCREMENTAL"): this; /** * @function optimize * @description Runs the query planner optimization (should be called before closing). * @returns {this} */ optimize(): this; /** * @function build * @description Compiles the pragma statements into a single SQL string. * @returns {string} Combined PRAGMA statements. */ toSQL(): string; } /** * @function resolveDefault * @description Resolves a `tsDefaultValue` (tagged or direct) into a SQL literal * string suitable for a `DEFAULT` clause. * * - **Tagged form** `{ [type]: value }`: the type tag determines quoting. * - `{ string: "pending" }` → `'pending'` (single-quoted, `'` escaped as `''`) * - `{ number: 42 }` → `42` (unquoted) * - `{ boolean: true }` → `TRUE` / `FALSE` (SQLite 3.23+) * - `{ date: new Date("2024-01-01") }` → `'2024-01-01T00:00:00.000Z'` (ISO 8601, quoted) * - `{ raw: "CURRENT_TIMESTAMP" }` → `CURRENT_TIMESTAMP` (verbatim SQL) * - **Direct form** (string or number): passes through `.toString()` as raw SQL. * - `"CURRENT_TIMESTAMP"` → `CURRENT_TIMESTAMP` * - `42` → `42` * * @param {tsDefaultValue | undefined} def - The default value to resolve. * @returns {string | undefined} The SQL literal string, or `undefined` if no default. */ declare function resolveDefault(def: tsDefaultValue | undefined): string | undefined; /** * Fluent SQL Query Builder for agnostic DDL and DML generation. * (Unified Public Entry Point) * * Supports two schema introspectors: * - Zod v4 (via `defTable` / `reqCreateTable`) * - DNA (via `defTable` / `reqCreateTable`) * * `defTable(name, def).req` returns a schema-aware Builder with uniqueKeys pre-configured, * enabling auto-deduction of conflict targets for upsert without explicit uniqueKeys. */ declare class QueryBuilder { #private; /** * **Entry Point**: Start building a query for a specific table. * @param {string} name - Table name. * @param {string} [alias] - Optional table alias. * @returns {Builder} * @usage `QueryBuilder.table('users', 'u')` */ static table(name: string, uniqueKeys?: string[]): Builder; /** * **Entry Point**: Start building SQLite PRAGMA statements. * @returns {PragmaBuilder} * @usage `QueryBuilder.pragma().foreignKeys(true).toSQL()` */ static pragma(): PragmaBuilder; /** * @function enableForeignKeys * @description Shortcut to generate the SQLite PRAGMA to enable foreign key enforcement. * @returns {string} `PRAGMA foreign_keys = ON;` * @usage `QueryBuilder.enableForeignKeys()` */ static enableForeignKeys(): string; /** * @function dropTable * @description Generates a DROP TABLE IF EXISTS statement. * @param {string} tableName - Target table. * @returns {string} Compiled SQL query. * @usage `QueryBuilder.dropTable('users')` */ static dropTable(tableName: string): string; /** * @function dropIndex * @description Generates a `DROP INDEX IF EXISTS` statement. * @param {string} indexName - Name of the index to drop. * @returns {string} Compiled SQL. * @usage `QueryBuilder.dropIndex('idx_users_email')` → `DROP INDEX IF EXISTS idx_users_email;` */ static dropIndex(indexName: string): string; /** * @function createTable * @description Generates a `CREATE TABLE IF NOT EXISTS` statement from manually-constructed * column definitions. This is the schema-agnostic DDL path — no Zod or DNA schema required. * Use this when you need fine-grained control over column definitions or when you don't * have a validation schema. * * @param {string} tableName - Name of the table to create. * @param {qbTable} columns - Column definitions (manually constructed). * @param {qbTableOptions} [options={}] - Manual overrides (primaryKey, foreignKeys, defaults, unique). * * @returns {string} Compiled SQL DDL. * * @example * ```ts * import { QueryBuilder, type qbTable } from "@ytrynot/qb"; * * const columns: qbTable = [ * { name: "id", sqliteType: "TEXT", optional: false, hasDefault: false, meta: { pk: true } }, * { name: "email", sqliteType: "TEXT", optional: false, hasDefault: false, meta: { unique: true } }, * ]; * const ddl = QueryBuilder.createTable("users", columns); * ``` */ static createTable(tableName: string, columns: qbTable, options?: qbTableOptions): string; /** * @function reqCreateTable * @description Shortcut for `defTable(name, def, options).createTable` — returns only the DDL string. * @param {string} tableName - Target table name. * @param {z.ZodTypeAny | DnaType | qbColumn[]} def - Schema definition (Zod, DNA, or manual columns). * @param {qbTableOptions} [options={}] - Manual overrides for DDL. * @returns {string} Compiled SQL DDL. * @throws {TypeError} If `def` is not a Zod schema, DNA schema, or `qbColumn[]`. * @throws {Error} If the schema cannot be resolved to an object shape. */ static reqCreateTable(tableName: string, def: z.ZodTypeAny | DnaType | qbColumn[], options?: qbTableOptions): string; /** * @function defTable * @description Defines a table from any schema source and generates all SQL statements (DDL + DML). * Automatically detects the schema type: * - `z.ZodTypeAny` → uses Zod v4 introspector * - `DnaType` → uses DNA introspector * - `qbColumn[]` → uses columns directly (manual) * * Automatically detects Primary Key (via `.meta({pk:true})`, `pkauto`, or 'id'/'uuid' convention). * * @param {string} tableName - Target table name. * @param {z.ZodTypeAny | DnaType | qbColumn[]} def - Schema definition (Zod, DNA, or manual columns). * @param {qbTableOptions} [options={}] - Manual overrides for DDL (primaryKey, foreignKeys, defaults, unique). * For composite primary keys, pass `primaryKey: ['col1', 'col2']`. Without this option, only the first * column with `meta.pk: true` is used as the PK for pre-built queries (getById, update, delete, upsert). * @returns {TableDef} Object with pre-built SQL statements and `req`/`q` getter for custom queries. * @throws {TypeError} If `def` is not a Zod schema, DNA schema, or `qbColumn[]`. * @throws {Error} If the schema cannot be resolved to an object shape (not a ZodObject/DnaObject or wrapper). * * @example * ```ts * // From Zod * const users = QueryBuilder.defTable("users", UserSchema); * users.createTable; // CREATE TABLE IF NOT EXISTS users (...) * users.getAll; // SELECT * FROM users * users.req.select("id", "name").where("id").toSQL(); // custom query * users.req.upsert("email", "name").toSQL(); // uniqueKeys auto-deduced * * // From DNA * const orders = QueryBuilder.defTable("orders", OrderSchema); * * // From manual qbColumn[] * const logs = QueryBuilder.defTable("logs", logColumns); * * // Composite PK (manual columns) * const members = QueryBuilder.defTable("members", memberColumns, { * primaryKey: ["tenant_id", "user_id"], * }); * members.getById; // SELECT * FROM members WHERE tenant_id = @tenant_id AND user_id = @user_id * ``` */ static defTable(tableName: string, def: z.ZodTypeAny | DnaType | qbColumn[], options?: qbTableOptions): TableDef; } /** Short alias for `QueryBuilder`. */ declare const qb: typeof QueryBuilder; /** Uppercase alias for `QueryBuilder`. */ declare const QB: typeof QueryBuilder; export { type ICaseBranch, type IForeignKeyDefinition, type IJoinDefinition, type IOnConflictConfig, type IOrderByDefinition, type ISchemaIntrospector, type IUniqueConstraint, type IWhereInDefinition, type IWindowDefinition, OnConflictBuilder, QB, QueryBuilder, type TableDef, qb, type qbColumn, type qbTable, type qbTableOptions, resolveDefault, type tsDefaultValue, type tsQueryMode, type tsSqliteType, type tsWhereDefinition };