import { type FormatSqlOptions } from './formatter.js'; import type { SqlfuHost } from './host.js'; import type { AsyncClient, Client } from './types.js'; import type { VendoredQueryAnalysis, VendoredQueryInput } from './typegen/analyze-vendored-typesql.js'; export type DiffSchemaInput = { baselineSql: string; desiredSql: string; allowDestructive: boolean; }; /** * Dialect-neutral input for query analysis. Re-exports the existing * vendored-typesql input shape — `{sqlPath, sqlContent}` is portable. */ export type QueryAnalysisInput = VendoredQueryInput; /** * Dialect-neutral output for query analysis (column types, parameter shapes, * query kind). Both the sqlite path (typesql) and the pg path (pgkit-derived) * produce values of this shape; downstream rendering is dialect-agnostic. */ export type QueryAnalysis = VendoredQueryAnalysis; /** * Per-column type info as consumed by the typegen rendering pipeline. Both * dialects produce values of this shape. */ export type DialectColumnInfo = { name: string; tsType: string; notNull: boolean; /** * Optional higher-level shape hint that overrides `tsType` for * encoding/decoding purposes (e.g. sqlite columns declared as `json` are * stringified before write and parsed after read). Dialect-neutral — * sqlite recognises declared-type=`json`; pg can map `json`/`jsonb`. */ logicalType?: LogicalType; /** * When true, `tsType` is already a plain TypeScript type expression from * schema metadata and should not be string-literal escaped when embedded in * generated output. */ plainTsType?: boolean; }; /** * Higher-level column shape hints recognized across dialects. Add new * values here when a new logical encoding becomes a first-class concept. */ export type LogicalType = 'json'; /** * Per-relation type info — table or view, with a column map and the original * `CREATE` SQL (used by view-shape inference for sqlite). Dialect-neutral * data, sqlite & pg both produce. */ export type RelationInfo = { kind: 'table' | 'view'; name: string; columns: ReadonlyMap; sql?: string; }; export type DialectForeignKey = { columns: string[]; referencedRelation: string; referencedColumns: string[]; }; /** * Opaque handle representing a materialized schema ready for typegen lookups + * analysis. Each dialect knows its own concrete shape; the value MUST only be * passed back to methods on the same dialect that produced it. Values are * `AsyncDisposable` so callers use `await using` to release dialect-owned * resources (pg temp schemas, transient sqlite files, open connections, etc.). */ export interface MaterializedTypegenSchema extends AsyncDisposable { /** Identifies the producing dialect. Used as a runtime sanity check. */ dialect: string; } export type Dialect = { /** Stable identifier; e.g. `'sqlite'`, `'postgresql'`. */ name: string; /** * Compute the ordered list of statements that takes a database from the * `baselineSql` shape to the `desiredSql` shape. SQL strings in, SQL * strings out — the dialect's internal representation of a schema does not * cross this boundary. */ diffSchema(host: SqlfuHost, input: DiffSchemaInput): Promise; /** Pretty-print a single SQL string in the dialect's native style. */ formatSql(sql: string, options?: FormatSqlOptions): string; /** Quote an identifier (table/column/index name) per the dialect's rules. */ quoteIdentifier(name: string): string; /** * The migration-bookkeeping table DDL for the default `'sqlfu'` migrations * preset. Dialect-locked presets (e.g. `'d1'`) bypass this and provide their * own DDL inline; we don't try to make those portable. */ defaultMigrationTableDdl(tableName: string): string; /** * Optional: wrap migration application in a dialect-native lock. SQLite is * single-writer at the file level so the default `sqliteDialect` omits this; * postgres uses `pg_advisory_xact_lock`. */ withMigrationLock?(client: AsyncClient, fn: () => Promise): Promise; /** * Apply `sourceSql` (a single DDL string — could be definitions.sql, could * be concatenated migrations) to a scratch database, then extract and * return the resulting schema as a canonical SQL string. Disposes the * scratch database before returning. * * Sqlite materializes against `host.openScratchDb` (in-memory sqlite); pg * uses its own connection (closed-over from the dialect's factory config) * to `CREATE DATABASE sqlfu_` and drop on completion. */ materializeSchemaSql(host: SqlfuHost, input: { sourceSql: string; excludedTables?: string[]; }): Promise; /** * Extract the canonical schema from a live client. Used by the * `live_schema` typegen authority and by drift checks against the user's * actual database. Sqlite reads from `sqlite_schema` (the `'main'` db); * pg reads from `pg_catalog` (the default `public` schema and any others * the dialect's options say to include). * * Accepts either a `SyncClient` or `AsyncClient` so callers can pass any * `client` regardless of driver shape; pg-flavored impls coerce to async * (and error on a sync client, since no pg driver is sync today). */ extractSchemaFromClient(client: Client, options?: { excludedTables?: string[]; }): Promise; /** * List relations (tables + views) on a live client. Used by the studio's * schema browser. Returns one entry per user-visible relation: * - `name` — relation identifier as it appears to SQL * - `kind` — 'table' or 'view' * - `sql` — definition string when available (sqlite returns the * `CREATE TABLE …` / `CREATE VIEW …` text from `sqlite_schema`; * pg returns `pg_get_viewdef(...)` for views, `undefined` for tables * since reconstructing CREATE TABLE syntactically requires more * than `pg_get_viewdef`) * * System tables (sqlite's reserved objects, postgres catalogs in * non-public schemas) are filtered out. */ listLiveRelations(client: Client): Promise>; /** * Look up one relation by name, same shape as one entry of * `listLiveRelations`. Throws if no relation exists with that name. * Distinct from `listLiveRelations` so callers don't have to filter * a possibly-large list to find one entry. */ getRelationInfo(client: Client, relationName: string): Promise<{ name: string; kind: 'table' | 'view'; sql?: string; }>; /** * Per-relation column metadata for the studio's row editor and schema * browser. Returns the columns in declaration order with: * - `name`, `type` — as the dialect reports them * - `notNull` — true when the column has a NOT NULL constraint * - `primaryKey` — true when the column is part of the primary key * * Sqlite's `PRAGMA table_xinfo` exposes hidden columns (e.g. * `__sqlfu_rowid__`); those are filtered out before returning. Pg * filters dropped/system attributes the same way. */ getRelationColumns(client: Client, relationName: string): Promise>; /** * Foreign keys declared by one relation. Used by the studio to build * forward and reverse row-navigation affordances. Views normally return * an empty list. */ getRelationForeignKeys(client: Client, relationName: string): Promise; /** * Apply pre-read schema source SQL to a fresh dialect-specific scratch * database, returning a handle ready for typegen lookups + query * analysis. The caller (typegen entry point) reads the schema source — * via `readSchemaForAuthority` — *before* this call, so the dialect * doesn't need to know which authority is in play. * * Sqlite's materialized form is a temp `.sqlite` file at * `/.sqlfu/typegen.db`; pg's is an ephemeral * `CREATE DATABASE`'d database. Both are disposed via * `Symbol.asyncDispose` on the returned handle. */ materializeTypegenSchema(host: SqlfuHost, input: { projectRoot: string; sourceSql: string; experimentalJsonTypes: boolean; }): Promise; /** Extract relation (table/view) shapes from the materialized schema. */ loadSchemaForTypegen(materialized: MaterializedTypegenSchema): Promise>; /** * Analyze a batch of queries against the materialized schema, producing * column/parameter type info for each one. */ analyzeQueries(materialized: MaterializedTypegenSchema, queries: QueryAnalysisInput[]): Promise; }; /** Real implementations registered by `typegen/index.ts` at module-load. */ type SqliteTypegenImpls = { materializeTypegenSchema: Dialect['materializeTypegenSchema']; loadSchemaForTypegen: Dialect['loadSchemaForTypegen']; analyzeQueries: Dialect['analyzeQueries']; }; /** * Called by `typegen/index.ts` at module-load to install the heavy-tier * typegen impls onto sqlite-dialect instances. After this runs, calls to * `sqliteDialect()` return objects with real typegen methods. Before it * runs (strict-tier paths), the typegen methods on a freshly-constructed * dialect are throwing stubs. */ export declare function registerSqliteTypegenImpls(impls: SqliteTypegenImpls): void; /** * Build a fresh sqlite `Dialect`. Currently takes no parameters — exists as a * factory for API parity with `pgDialect({...})` (see `@sqlfu/pg`), so users * can write `defineConfig({dialect: sqliteDialect()})` or `pgDialect({...})` * without remembering which is a value and which is a constructor. */ export declare function sqliteDialect(): Dialect; /** * Asserts a `MaterializedTypegenSchema` was produced by the sqlite dialect. * Exposed so the registration in `typegen/index.ts` can use it without * duplicating the cast logic. */ export declare function assertSqliteMaterialized(materialized: MaterializedTypegenSchema): { databasePath: string; experimentalJsonTypes: boolean; }; export {};