@doync/schema-codegen
v0.4.0
Published
Generate a plain doync schema module from SQLite migration DDL via an in-memory node:sqlite oracle
Maintainers
Readme
@doync/schema-codegen
Generate a plain doync schema module (satisfies DoyncSchema) from your SQLite migration DDL. Pass the emitted schema to createDoync({ schema, ctxValidationSchema }). One command (or one function call) replaces hand-archaeology of column names, affinities, primary-key order, and NOT NULL.
Codegen-time only. The engine is Node's built-in node:sqlite (DatabaseSync). This package never runs inside Cloudflare Workers or any other non-Node runtime — use it in a build script, a CI step, or at the terminal, then commit the emitted source next to your schema.
Install
pnpm add -D @doync/schema-codegenRequires Node ≥ 22.5 (when node:sqlite stabilized). Zero runtime npm dependencies.
Why real SQLite
Exotic DDL comes out right because the database built it:
- Composite primary keys declared mid-column-list land in key order, not table order.
- Layered
ALTER TABLE(add / drop / rename column, rename table) collapses to the final shape. - Declared types map through SQLite's five-rule affinity derivation (
VARCHAR(255)→text,INT8→integer,DOUBLE PRECISION→real,DECIMAL(10,2)→numeric, empty →blob). STRICTtables and generated columns report whateverpragma_table_xinfosees after the DDL applies.
CLI
# Print the tables: array to stdout
pnpm schema-codegen migrations/
# or
npx schema-codegen path/to/0001_init.sql path/to/0002_add_priority.sql
# Write to a file
schema-codegen migrations/ --out src/schema.tables.tsInputs are .sql files and/or directories of .sql files. Files run in sorted full-path order (so 0001_…sql before 0002_…sql). Multiple ;-separated statements per file are fine.
Programmatic API
import {
generateTablesFromSql,
generateTablesFromFiles,
} from '@doync/schema-codegen'
// From ordered SQL strings:
const { tables, migrations, source } = generateTablesFromSql(
migrationSql.map((m) => m.sql),
)
// Or from files on disk:
const fromDisk = generateTablesFromFiles(['./migrations'])
// `source` is a ready module — commit and import:
//
// import { schema } from './generated'
// export const { defineQuery, … } = createDoync({
// schema,
// ctxValidationSchema,
// })
//
// `tables` / `migrations` are the same shape as data if you prefer to wire without eval.doync-fit: DoyncSchema
Emitted column objects match doync's consumer column surface:
{
name: string
type: 'text' | 'integer' | 'real' | 'blob' | 'numeric'
pk?: true // omitted when false
notNull?: true // omitted when false
}Primary-key columns are emitted first, in primary-key ordinal order; remaining columns follow in table order. That matches the Drizzle auto-derivation convention so hand-written, Drizzle-derived, and codegen-produced schemas compare equal.
Internal tables are skipped:
sqlite_*— SQLite catalog / sequence tables__doync_*— doync-reserved engine tables
Generated columns
pragma_table_info omits generated columns. This package reads pragma_table_xinfo instead, so VIRTUAL and STORED generated columns appear in the emission with their declared affinity and nullability. doync's boot verifier also reads the physical schema; if you ship a generated column on a synced table, list it here so declaration and database agree. (There is no separate generated flag on the column surface — affinity + name + nullability is what boot checks today.)
STRICT tables
STRICT is a table-level SQLite property. Column affinities still flow from declared types the same way; the emitter does not special-case STRICT. After migrations apply, what you see in the emission is what SQLite stored.
Affinity rules (normative)
Declared type substring match, case-insensitive, first match wins:
- contains
INT→integer - contains
CHAR,CLOB, orTEXT→text - contains
BLOB, or the declared type is empty →blob - contains
REAL,FLOA, orDOUB→real - else →
numeric
The affinity rules are documented here; the helper itself lives on @doync/schema-codegen/internal.
