# Bun SQLite @moduletrace/bun-sqlite A schema-driven SQLite manager for [Bun](https://bun.sh), featuring automatic schema synchronization, type-safe CRUD operations, vector embedding support (via `sqlite-vec`), and TypeScript type definition generation. --- ## Table of Contents - [Features](#features) - [Prerequisites](#prerequisites) - [Installation](#installation) - [Quick Start](#quick-start) - [Configuration](#configuration) - [Schema Definition](#schema-definition) - [CLI Commands](#cli-commands) - [`schema`](#schema--sync-database-to-schema) - [`typedef`](#typedef--generate-typescript-types-only) - [`backup`](#backup--back-up-the-database) - [`restore`](#restore--restore-the-database-from-a-backup) - [`export`](#export--portable-database--schema-archive) - [`import`](#import--database-file-sql-script-or-export-archive) - [CRUD API](#crud-api) - [Select](#select) - [Insert](#insert) - [Update](#update) - [Delete](#delete) - [Raw SQL](#raw-sql) - [Query API Reference](#query-api-reference) - [Vector Table Support](#vector-table-support) - [Queue Tables](#queue-tables) - [TypeScript Type Generation](#typescript-type-generation) - [Default Fields](#default-fields) - [Project Structure](#project-structure) - [Testing](#testing) --- ## Features - **Schema-first design** — define your database in TypeScript; the library syncs your SQLite file to match - **Automatic migrations** — adds new columns, recreates tables for complex changes, drops removed tables - **Type-safe CRUD** — fully generic `select`, `insert`, `update`, `delete` functions with TypeScript generics - **Rich query DSL** — filtering, ordering, pagination, joins, grouping, full-text search, sub-query counts - **Vector table support** — create and manage `sqlite-vec` virtual tables for AI/ML embeddings - **TypeScript codegen** — generate `.ts` type definitions from your schema automatically - **Zero-config defaults** — `id`, `created_at`, and `updated_at` fields are added to every table automatically --- ## Prerequisites This works for just `bun`, so `bun` (>= 1.3) should be installed. --- ## Installation There are two ways to install BunSQLite: ### Via the official npm registry `@moduletrace/bun-sqlite` is published to a private Gitea npm registry. You must configure your package manager to resolve the `@moduletrace` scope from that registry before installing. Add the following to your project's `.npmrc` file (create it at the root of your project if it doesn't exist): ```ini @moduletrace:registry=https://git.tben.me/api/packages/moduletrace/npm/ ``` After this you can run ```bash bun add @moduletrace/bun-sqlite ``` ### Via Github To install directly from github simply run ```bash bun add github:moduletrace/bun-sqlite ``` --- ## Quick Start ### 1. Create the config file Create `bun-sqlite.config.ts` at your project root: ```ts import type { BunSQLiteConfig } from "@moduletrace/bun-sqlite"; const config: BunSQLiteConfig = { db_name: "my-app.db", db_dir: "./db", // optional: where to store the db file db_backup_dir: ".backups", // optional: name of backups directory. Relative to the db dir. typedef_file_path: "./db/types/db.ts", // optional: where to write generated types }; export default config; ``` ### 2. Define your schema Create `./db/schema.ts` in `db_dir` (or `schema.js`, `schema.json`, `schema.yaml`, `schema.yml`): ```ts import type { BUN_SQLITE_DatabaseSchemaType } from "@moduletrace/bun-sqlite"; const schema: BUN_SQLITE_DatabaseSchemaType = { dbName: "my-app", tables: [ { tableName: "users", fields: [ { fieldName: "first_name", dataType: "TEXT" }, { fieldName: "last_name", dataType: "TEXT" }, { fieldName: "email", dataType: "TEXT", unique: true }, ], }, ], }; export default schema; ``` ### 3. Sync the schema to SQLite ```bash bunx bun-sqlite schema ``` This creates the SQLite database file and creates/updates all tables to match your schema. ### 4. Use the CRUD API ```ts import BunSQLite from "@moduletrace/bun-sqlite"; // Insert await BunSQLite.insert({ table: "users", data: [{ first_name: "Alice", email: "alice@example.com" }], }); // Select const result = await BunSQLite.select({ table: "users" }); console.log(result.payload); // Alice's row // Update await BunSQLite.update({ table: "users", targetId: 1, data: { first_name: "Alicia" }, }); // Delete await BunSQLite.delete({ table: "users", targetId: 1 }); ``` --- ## Configuration The config file is `bun-sqlite.config.ts` at the root of your project. `bun-sqlite.config.js`, `.json`, `.yaml` and `.yml` also work; keep only one. `.ts`/`.js` files must `export default` the config. | Field | Type | Required | Description | | --------------------- | -------- | -------- | ------------------------------------------------------------------------------------------ | | `db_name` | `string` | Yes | Filename for the SQLite database (e.g. `"app.db"`) | | `db_schema_file_name` | `string` | No | Schema filename relative to `db_dir`. Defaults to `schema.ts\|js\|json\|yaml\|yml` in `db_dir` | | `db_backup_dir` | `string` | No | Directory for database backups, relative to `db_dir` | | `db_dir` | `string` | No | Root directory for the database file and schema. Defaults to project root | | `typedef_file_path` | `string` | No | Output path for generated TypeScript types, relative to project root | | `max_backups` | `number` | No | Maximum number of backup files to keep. Oldest are deleted automatically. Defaults to `10` | | `max_exports` | `number` | No | Maximum number of archives kept in `{db_dir}/.exports` by `export`. Defaults to `10` | | `wal_mode` | `boolean`| No | Enable SQLite `WAL` journal mode | --- ## Schema Definition ### Database Schema ```ts interface BUN_SQLITE_DatabaseSchemaType { dbName?: string; tables: BUN_SQLITE_TableSchemaType[]; } ``` ### Table Schema ```ts interface BUN_SQLITE_TableSchemaType { tableName: string; tableDescription?: string; fields: BUN_SQLITE_FieldSchemaType[]; indexes?: BUN_SQLITE_IndexSchemaType[]; uniqueConstraints?: BUN_SQLITE_UniqueConstraintSchemaType[]; parentTableName?: string; // inherit fields from another table in the schema tableNameOld?: string; // rename: set this to the old name to trigger ALTER TABLE RENAME isVector?: boolean; // mark this as a sqlite-vec virtual table vectorType?: string; // virtual table type, defaults to "vec0" isQueue?: boolean | BUN_SQLITE_QueueOptions; // make this a job queue (see Queue Tables) } ``` ### Field Schema ```ts type BUN_SQLITE_FieldSchemaType = { fieldName?: string; dataType: "TEXT" | "INTEGER"; primaryKey?: boolean; autoIncrement?: boolean; notNullValue?: boolean; unique?: boolean; defaultValue?: string | number; defaultValueLiteral?: string; // raw SQL literal, e.g. "CURRENT_TIMESTAMP" foreignKey?: BUN_SQLITE_ForeignKeyType; isVector?: boolean; // vector embedding column vectorSize?: number; // embedding dimensions (default: 1536) sideCar?: boolean; // sqlite-vec "+" prefix for side-car columns updatedField?: boolean; // flag that this field definition has changed }; ``` ### Foreign Key ```ts interface BUN_SQLITE_ForeignKeyType { destinationTableName: string; destinationTableColumnName: string; cascadeDelete?: boolean; cascadeUpdate?: boolean; } ``` ### Index ```ts interface BUN_SQLITE_IndexSchemaType { indexName?: string; indexType?: "regular" | "full_text" | "vector"; indexTableFields?: { value: string; dataType: string }[]; } ``` ### Unique Constraint ```ts interface BUN_SQLITE_UniqueConstraintSchemaType { constraintName?: string; constraintTableFields?: { value: string }[]; } ``` --- ## CLI Commands The package provides a `bun-sqlite` CLI binary. ### `schema` — Sync database to schema ```bash bunx bun-sqlite schema [options] ``` | Option | Description | | ----------------- | ---------------------------------------------------------- | | `-v`, `--vector` | Drop and recreate all vector (`sqlite-vec`) virtual tables | | `-t`, `--typedef` | Also generate TypeScript type definitions after syncing | | `--dry-run` | Print the planned schema changes without applying them | | `-y`, `--yes` | Apply destructive changes (dropped tables / columns) without confirmation | **Examples:** ```bash # Sync schema only bunx bun-sqlite schema # Sync schema and regenerate types bunx bun-sqlite schema --typedef # Sync schema, recreate vector tables, and regenerate types bunx bun-sqlite schema --vector --typedef # Review planned changes first bunx bun-sqlite schema --dry-run ``` Only tables whose definition changed are rebuilt. Changes that discard data (dropping a table, or a column removed from the schema) are listed and need confirmation before they run. Non-interactive runs (CI, scripts) refuse them unless `--yes` is passed. The plan is exact: `--dry-run` runs the whole sync in a transaction and rolls it back. ### `typedef` — Generate TypeScript types only ```bash bunx bun-sqlite typedef ``` Reads the schema and writes TypeScript type definitions to the path configured in `typedef_file_path`. --- ### `backup` — Back up the database ```bash bunx bun-sqlite backup ``` Copies the current database file into `db_backup_dir` with a timestamped filename. After copying, the oldest backups are automatically pruned so the number of stored backups never exceeds `max_backups` (default: 10). **Example:** ```bash bunx bun-sqlite backup # Backing up database ... # DB Backup Success! ``` --- ### `restore` — Restore the database from a backup ```bash bunx bun-sqlite restore ``` Presents an interactive list of available backups sorted by date (newest first). Select a backup to overwrite the current database file with it. **Example:** ```bash bunx bun-sqlite restore # Restoring up database ... # ? Select a backup: (Use arrow keys) # ❯ Backup #1: Mon Mar 02 2026 14:30:00 # Backup #2: Sun Mar 01 2026 09:15:42 # DB Restore Success! ``` > If no backups exist, the command exits with an error and a reminder to run `backup` first. --- ### `export` — Portable database + schema archive ```bash # Write {db_name}-{timestamp}.tar.gz into {db_dir}/.exports bunx bun-sqlite export # Explicit path / format bunx bun-sqlite export -o ./my-app.tar.gz bunx bun-sqlite export -f zip ``` The archive holds `database.sqlite` (a consistent `VACUUM INTO` snapshot) and your schema file as `schema.`. Archives in `.exports` are trimmed to `max_exports`. `.zip` needs the `zip` binary. --- ### `import` — Database file, SQL script, or export archive ```bash # Interactive picker (from {db_dir}/.exports) bunx bun-sqlite import # Full export (database + schema) bunx bun-sqlite import ./my-app.tar.gz # Archive: database only / schema only bunx bun-sqlite import ./my-app.tar.gz --sql-only bunx bun-sqlite import ./my-app.tar.gz --schema-only # A SQLite database file, or an SQL script (run into a fresh database) bunx bun-sqlite import ./other.db bunx bun-sqlite import ./dump.sql ``` The current database is backed up to `db_backup_dir` before it is replaced. A `.ts`/`.js` schema from an archive runs as code on the next load, so `import` asks before writing it (pass `--yes` to skip the prompt). --- ## CRUD API Import the default export: ```ts import BunSQLite from "@moduletrace/bun-sqlite"; ``` All methods return an `APIResponseObject`: ```ts { success: boolean; payload?: T[]; // array of rows (select; also insert/update) singleRes?: T; // first row (select; also insert/update) count?: number; // total count (when count: true) postInsertReturn?: { affectedRows?: number; insertId?: number; }; error?: string; msg?: string; debug?: any; } ``` --- ### Select ```ts BunSQLite.select({ table, query?, count?, targetId? }) ``` | Parameter | Type | Description | | ---------- | --------------------- | ------------------------------------------------------------ | | `table` | `string` | Table name | | `query` | `ServerQueryParam` | Query/filter options (see [Query API](#query-api-reference)) | | `count` | `boolean` | Return row count instead of rows | | `targetId` | `string \| number` | Shorthand to filter by `id` | **Examples:** ```ts // Get all users const res = await BunSQLite.select({ table: "users" }); // Get by ID const res = await BunSQLite.select({ table: "users", targetId: 42 }); // Filter with LIKE const res = await BunSQLite.select({ table: "users", query: { query: { first_name: { value: "Ali", equality: "LIKE" }, }, }, }); // Count rows const res = await BunSQLite.select({ table: "users", count: true }); console.log(res.count); // Pagination const res = await BunSQLite.select({ table: "users", query: { limit: 10, page: 2 }, }); ``` --- ### Insert ```ts BunSQLite.insert({ table, data, update_on_duplicate?, returning? }); ``` | Parameter | Type | Description | | --------------------- | --------- | ---------------------------------------------------------------- | | `table` | `string` | Table name | | `data` | `T[]` | Array of row objects to insert | | `update_on_duplicate` | `boolean` | Upsert via `ON CONFLICT ... DO UPDATE` | | `returning` | `boolean` | Return the inserted record(s). Defaults to `true` | `created_at` and `updated_at` are set automatically to `Date.now()`. On success the inserted record(s) come back in `singleRes` (first row) and `payload` (all rows, for multi-row inserts). `postInsertReturn` is still populated for backwards compatibility. **Example:** ```ts const res = await BunSQLite.insert({ table: "users", data: [ { first_name: "Alice", last_name: "Smith", email: "alice@example.com" }, { first_name: "Bob", last_name: "Jones", email: "bob@example.com" }, ], }); console.log(res.singleRes); // first inserted record (with its id) console.log(res.payload); // every inserted record console.log(res.postInsertReturn?.insertId); // last inserted row ID ``` Pass `returning: false` to skip `RETURNING` (e.g. when inserting very large `BLOB`/vector values); the response then only contains `postInsertReturn`. --- ### Update ```ts BunSQLite.update({ table, data, query?, targetId?, returning? }) ``` | Parameter | Type | Description | | ---------- | --------------------- | -------------------------------------------------------- | | `table` | `string` | Table name | | `data` | `Partial` | Fields to update | | `query` | `ServerQueryParam` | WHERE clause conditions | | `targetId` | `string \| number` | Shorthand to filter by `id` | | `returning`| `boolean` | Return the updated record(s). Defaults to `true` | A WHERE clause is required. If no condition matches, `success` is `false`. `updated_at` is set automatically to `Date.now()`. On success the updated record(s) come back in `singleRes` (first row) and `payload`. Vector tables fall back to `postInsertReturn` automatically, because SQLite does not support `UPDATE ... RETURNING` on virtual tables. **Examples:** ```ts // Update by ID const res = await BunSQLite.update({ table: "users", targetId: 1, data: { first_name: "Alicia" }, }); console.log(res.singleRes); // the updated record // Update with custom query await BunSQLite.update({ table: "users", data: { last_name: "Doe" }, query: { query: { email: { value: "alice@example.com", equality: "EQUAL" }, }, }, }); ``` --- ### Delete ```ts BunSQLite.delete({ table, query?, targetId? }) ``` | Parameter | Type | Description | | ---------- | --------------------- | --------------------------- | | `table` | `string` | Table name | | `query` | `ServerQueryParam` | WHERE clause conditions | | `targetId` | `string \| number` | Shorthand to filter by `id` | A WHERE clause is required. If no condition is provided, `success` is `false`. **Examples:** ```ts // Delete by ID await BunSQLite.delete({ table: "users", targetId: 1 }); // Delete with condition await BunSQLite.delete({ table: "users", query: { query: { first_name: { value: "Ben", equality: "LIKE" }, }, }, }); ``` --- ### Raw SQL ```ts BunSQLite.sql({ sql, values? }) ``` | Parameter | Type | Description | | --------- | ---------------------- | -------------------- | | `sql` | `string` | Raw SQL statement | | `values` | `(string \| number)[]` | Parameterized values | SELECT statements return rows; all other statements return `postInsertReturn`. **Examples:** ```ts // SELECT const res = await BunSQLite.sql({ sql: "SELECT * FROM users" }); console.log(res.payload); // INSERT with params await BunSQLite.sql({ sql: "INSERT INTO users (first_name, email) VALUES (?, ?)", values: ["Charlie", "charlie@example.com"], }); ``` --- ## Query API Reference The `query` parameter on `select`, `update`, and `delete` accepts a `ServerQueryParam` object: ```ts type ServerQueryParam = { query?: { [key in keyof T]: ServerQueryObject }; selectFields?: (keyof T | { fieldName: keyof T; alias?: string })[]; omitFields?: (keyof T)[]; limit?: number; page?: number; offset?: number; order?: | { field: keyof T; strategy: "ASC" | "DESC" } | { field: keyof T; strategy: "ASC" | "DESC" }[]; searchOperator?: "AND" | "OR"; // how multiple query fields are joined (default: AND) join?: ServerQueryParamsJoin[]; group?: | keyof T | { field: keyof T; table?: string } | (keyof T | { field: keyof T; table?: string })[]; countSubQueries?: ServerQueryParamsCount[]; fullTextSearch?: { fields: (keyof T)[]; searchTerm: string; scoreAlias: string; }; }; ``` ### Equality Operators Set `equality` on any query field to control the comparison: | Equality | SQL Equivalent | | ----------------------- | ------------------------------------------------------ | | `EQUAL` (default) | `=` | | `NOT EQUAL` | `!=` | | `LIKE` | `LIKE '%value%'` | | `LIKE_RAW` | `LIKE 'value'` (no auto-wrapping) | | `LIKE_LOWER` | `LOWER(field) LIKE '%value%'` | | `NOT LIKE` | `NOT LIKE '%value%'` | | `GREATER THAN` | `>` | | `GREATER THAN OR EQUAL` | `>=` | | `LESS THAN` | `<` | | `LESS THAN OR EQUAL` | `<=` | | `IN` | `IN (val1, val2, ...)` — pass array as value | | `NOT IN` | `NOT IN (...)` | | `BETWEEN` | `BETWEEN val1 AND val2` — pass `[val1, val2]` as value | | `IS NULL` | `IS NULL` | | `IS NOT NULL` | `IS NOT NULL` | | `MATCH` | sqlite-vec vector nearest-neighbor search | **Example:** ```ts // Find users with email NOT NULL, ordered by created_at DESC, limit 20 const res = await BunSQLite.select({ table: "users", query: { query: { email: { equality: "IS NOT NULL" }, }, order: { field: "created_at", strategy: "DESC" }, limit: 20, }, }); ``` ### JOIN ```ts const res = await BunSQLite.select({ table: "posts", query: { join: [ { joinType: "LEFT JOIN", tableName: "users", match: { source: "user_id", target: "id" }, selectFields: ["first_name", "email"], }, ], }, }); ``` --- ## Vector Table Support `@moduletrace/bun-sqlite` integrates with [`sqlite-vec`](https://github.com/asg017/sqlite-vec) for storing and querying vector embeddings. ### Define a vector table in the schema ```ts { tableName: "documents", isVector: true, vectorType: "vec0", // defaults to "vec0" fields: [ { fieldName: "embedding", dataType: "TEXT", isVector: true, vectorSize: 1536, // embedding dimensions }, { fieldName: "title", dataType: "TEXT", sideCar: true, // stored as a side-car column (+title) for efficiency }, { fieldName: "body", dataType: "TEXT", sideCar: true, }, ], } ``` > **Side-car columns** (`sideCar: true`) use sqlite-vec's `+column` syntax. They are stored separately from the vector index, keeping the index lean and fast while still being queryable alongside vector results. ### Sync vector tables ```bash # Initial sync bunx bun-sqlite schema # Recreate vector tables (e.g. after changing vectorSize) bunx bun-sqlite schema --vector ``` ### Query vectors ```ts const res = await BunSQLite.select({ table: "documents", query: { query: { embedding: { equality: "MATCH", value: "", vector: true, vectorFunction: "vec_f32", }, }, limit: 5, }, }); ``` --- ## Queue Tables Set `isQueue` on a table to use it as a job queue. Your fields are the job payload; the schema sync adds the queue columns and an index on `(status, available_at)`. ```ts { tableName: "email_jobs", isQueue: { maxAttempts: 3, // claims before a failing job is marked "failed" (default 3) lockTimeout: 300_000, // ms a job can stay "processing" before it is released (default 5 min) backoff: 1000, // base retry delay in ms, doubled each attempt (default 1000) deleteOnComplete: false, // delete completed jobs instead of keeping them (default false) }, fields: [ { fieldName: "recipient", dataType: "TEXT" }, { fieldName: "subject", dataType: "TEXT" }, ], } ``` `isQueue: true` uses the defaults. A table can't be both a queue and a vector table. | Field | Type | Description | | -------------- | ---------------------------- | ---------------------------------------------------- | | `status` | `TEXT NOT NULL` | `pending`, `processing`, `completed` or `failed` | | `attempts` | `INTEGER NOT NULL DEFAULT 0` | Times the job has been claimed | | `available_at` | `INTEGER NOT NULL DEFAULT 0` | Unix timestamp (ms) from which the job can be claimed | | `locked_at` | `INTEGER` | Unix timestamp (ms) when the job was claimed | | `locked_by` | `TEXT` | Worker that claimed the job | | `last_error` | `TEXT` | Error from the last failed attempt | ### Working the queue ```ts // Enqueue (optionally delayed, in ms) await BunSQLite.queue.add({ table: "email_jobs", data: [{ recipient: "a@example.com", subject: "Hi" }], delay: 0, }); // Worker loop const { payload: jobs } = await BunSQLite.queue.claim({ table: "email_jobs", worker: "worker-1", limit: 10, }); for (const job of jobs || []) { try { await sendEmail(job); await BunSQLite.queue.complete({ table: "email_jobs", id: job.id }); } catch (error: any) { await BunSQLite.queue.fail({ table: "email_jobs", id: job.id, error: error.message, }); } } ``` - `claim` sets jobs to `processing` in a single `UPDATE ... RETURNING` statement. SQLite only allows one writer at a time, so two workers never get the same job, even across processes. - `fail` puts the job back to `pending` with an exponential backoff, or sets it to `failed` once it has used `maxAttempts`. - Jobs stuck in `processing` longer than `lockTimeout` (for example, the worker crashed) are released by `claim` before it picks jobs. You can also call `BunSQLite.queue.release_stale({ table })` directly. - `complete` and `fail` only touch jobs that are `processing`. --- ## TypeScript Type Generation Run the `typedef` command (or pass `--typedef` to `schema`) to generate a `.ts` file containing: - A `const` array of all table names (`BunSQLiteTables`) - A `type` for each table (named `BUN_SQLITE__`) - A union type `BUN_SQLITE__ALL_TYPEDEFS` **Example output** (`db/types/db.ts`): ```ts export const BunSQLiteTables = ["users"] as const; export type BUN_SQLITE_MY_APP_USERS = { /** The unique identifier of the record. */ id?: number; /** The time when the record was created. (Unix Timestamp) */ created_at?: number; /** The time when the record was updated. (Unix Timestamp) */ updated_at?: number; first_name?: string; last_name?: string; email?: string; }; export type BUN_SQLITE_MY_APP_ALL_TYPEDEFS = BUN_SQLITE_MY_APP_USERS; ``` Use the generated types with the CRUD API for full type safety: ```ts import BunSQLite from "@moduletrace/bun-sqlite"; import { BUN_SQLITE_MY_APP_USERS, BunSQLiteTables } from "./db/types/db"; const res = await BunSQLite.select({ table: "users" as (typeof BunSQLiteTables)[number], }); ``` --- ## Default Fields Every table automatically receives the following fields — you do not need to declare them in your schema: | Field | Type | Description | | ------------ | -------------------------------------------- | -------------------------------------- | | `id` | `INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL` | Unique row identifier | | `created_at` | `INTEGER` | Unix timestamp set on insert | | `updated_at` | `INTEGER` | Unix timestamp updated on every update | --- ## Project Structure ``` bun-sqlite/ ├── src/ │ ├── index.ts # Main export (BunSQLite object) │ ├── commands/ │ │ ├── index.ts # CLI entry point │ │ ├── schema.ts # `schema` command │ │ ├── typedef.ts # `typedef` command │ │ ├── backup.ts # `backup` command │ │ ├── restore.ts # `restore` command │ │ ├── export.ts # `export` command │ │ └── import.ts # `import` command │ ├── functions/ │ │ ├── init.ts # Loads config, schema and client into globals │ │ └── grab-sqlite-client.ts # Database client (bun:sqlite + sqlite-vec) │ ├── lib/ │ │ ├── db-handler.ts # Runs every CRUD query │ │ └── schema/ # Schema synchronization (one step per file, + tests) │ ├── lib/sqlite/ │ │ ├── db-select.ts # Select implementation │ │ ├── db-insert.ts # Insert implementation │ │ ├── db-update.ts # Update implementation │ │ ├── db-delete.ts # Delete implementation │ │ ├── db-sql.ts # Raw SQL implementation │ │ ├── db-generate-type-defs.ts # Type def generator │ │ └── schema-to-typedef.ts # Schema-to-TypeScript converter │ ├── types/ │ │ └── index.ts # All TypeScript types and interfaces │ └── utils/ │ ├── sql-generator.ts # SELECT query builder │ ├── sql-insert-generator.ts # INSERT query builder │ ├── sql-gen-operator-gen.ts # Equality operator mapper │ ├── sql-equality-parser.ts # Equality string parser │ ├── append-default-fields-to-db-schema.ts │ ├── resolve-and-load-data-file.ts # Load .ts/.js/.json/.yaml config and schema files │ ├── export-archive.ts # Read/write export archives │ ├── grab-db-dir.ts # Resolve db/backup/export directory paths │ ├── grab-db-backup-file-name.ts # Generate timestamped backup filename │ ├── grab-sorted-backups.ts # List backups sorted newest-first │ ├── grab-backup-data.ts # Parse metadata from a backup filename │ └── trim-backups.ts # Prune oldest backups over max_backups └── test/ └── test-01/ # Example project using the library ├── bun-sqlite.config.ts ├── db/ │ ├── bun-sqlite-schema.ts │ └── types/bun-sqlite.ts # Generated types └── src/ ├── insert.ts ├── select.ts ├── delete.ts └── sql.ts ``` --- ## Testing ```bash bun run test ``` Runs the unit tests (`tests/`) and the schema sync tests (`src/lib/schema/*.test.ts`) against in-memory SQLite databases. No setup needed. --- ## License MIT