This commit is contained in:
Benjamin Toby 2026-03-08 08:24:36 +01:00
parent 3a4ba2ca6f
commit 9e922be199
3 changed files with 81 additions and 170 deletions

106
CLAUDE.md
View File

@ -1,106 +0,0 @@
Default to using Bun instead of Node.js.
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
- Use `bun test` instead of `jest` or `vitest`
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
- Bun automatically loads .env, so don't use dotenv.
## APIs
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
- `Bun.redis` for Redis. Don't use `ioredis`.
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
- `WebSocket` is built-in. Don't use `ws`.
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
- Bun.$`ls` instead of execa.
## Testing
Use `bun test` to run tests.
```ts#index.test.ts
import { test, expect } from "bun:test";
test("hello world", () => {
expect(1).toBe(1);
});
```
## Frontend
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
Server:
```ts#index.ts
import index from "./index.html"
Bun.serve({
routes: {
"/": index,
"/api/users/:id": {
GET: (req) => {
return new Response(JSON.stringify({ id: req.params.id }));
},
},
},
// optional websocket support
websocket: {
open: (ws) => {
ws.send("Hello, world!");
},
message: (ws, message) => {
ws.send(message);
},
close: (ws) => {
// handle close
}
},
development: {
hmr: true,
console: true,
}
})
```
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
```html#index.html
<html>
<body>
<h1>Hello, world!</h1>
<script type="module" src="./frontend.tsx"></script>
</body>
</html>
```
With the following `frontend.tsx`:
```tsx#frontend.tsx
import React from "react";
// import .css files directly and it works
import './index.css';
import { createRoot } from "react-dom/client";
const root = createRoot(document.body);
export default function Frontend() {
return <h1>Hello, world!</h1>;
}
root.render(<Frontend />);
```
Then, run index.ts
```sh
bun --hot ./index.ts
```
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.md`.

141
README.md
View File

@ -1,8 +1,8 @@
# Node SQLite
@moduletrace/bun-sqlite
@moduletrace/nsqlite
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.
A schema-driven SQLite manager for Node.js, featuring automatic schema synchronization, type-safe CRUD operations, vector embedding support (via `sqlite-vec`), and TypeScript type definition generation.
---
@ -47,7 +47,7 @@ A schema-driven SQLite manager for [Bun](https://bun.sh), featuring automatic sc
## Prerequisites
`@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.
`@moduletrace/nsqlite` 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):
@ -55,14 +55,12 @@ Add the following to your project's `.npmrc` file (create it at the root of your
@moduletrace:registry=https://git.tben.me/api/packages/moduletrace/npm/
```
This works for both `bun` and `npm`.
---
## Installation
```bash
bun add @moduletrace/bun-sqlite
npm install @moduletrace/nsqlite
```
---
@ -74,7 +72,7 @@ bun add @moduletrace/bun-sqlite
Create `bun-sqlite.config.ts` at your project root:
```ts
import type { BunSQLiteConfig } from "@moduletrace/bun-sqlite";
import type { BunSQLiteConfig } from "@moduletrace/nsqlite";
const config: BunSQLiteConfig = {
db_name: "my-app.db",
@ -92,7 +90,7 @@ export default config;
Create `./db/schema.ts` (matching `db_schema_file_name` above):
```ts
import type { BUN_SQLITE_DatabaseSchemaType } from "@moduletrace/bun-sqlite";
import type { BUN_SQLITE_DatabaseSchemaType } from "@moduletrace/nsqlite";
const schema: BUN_SQLITE_DatabaseSchemaType = {
dbName: "my-app",
@ -114,7 +112,7 @@ export default schema;
### 3. Sync the schema to SQLite
```bash
bunx bun-sqlite schema
npx bun-sqlite schema
```
This creates the SQLite database file and creates/updates all tables to match your schema.
@ -122,27 +120,27 @@ This creates the SQLite database file and creates/updates all tables to match yo
### 4. Use the CRUD API
```ts
import BunSQLite from "@moduletrace/bun-sqlite";
import NodeSQLite from "@moduletrace/nsqlite";
// Insert
await BunSQLite.insert({
await NodeSQLite.insert({
table: "users",
data: [{ first_name: "Alice", email: "alice@example.com" }],
});
// Select
const result = await BunSQLite.select({ table: "users" });
const result = await NodeSQLite.select({ table: "users" });
console.log(result.payload); // Alice's row
// Update
await BunSQLite.update({
await NodeSQLite.update({
table: "users",
targetId: 1,
data: { first_name: "Alicia" },
});
// Delete
await BunSQLite.delete({ table: "users", targetId: 1 });
await NodeSQLite.delete({ table: "users", targetId: 1 });
```
---
@ -248,7 +246,7 @@ The package provides a `bun-sqlite` CLI binary.
### `schema` — Sync database to schema
```bash
bunx bun-sqlite schema [options]
npx bun-sqlite schema [options]
```
| Option | Description |
@ -260,19 +258,19 @@ bunx bun-sqlite schema [options]
```bash
# Sync schema only
bunx bun-sqlite schema
npx bun-sqlite schema
# Sync schema and regenerate types
bunx bun-sqlite schema --typedef
npx bun-sqlite schema --typedef
# Sync schema, recreate vector tables, and regenerate types
bunx bun-sqlite schema --vector --typedef
npx bun-sqlite schema --vector --typedef
```
### `typedef` — Generate TypeScript types only
```bash
bunx bun-sqlite typedef
npx bun-sqlite typedef
```
Reads the schema and writes TypeScript type definitions to the path configured in `typedef_file_path`.
@ -282,7 +280,7 @@ Reads the schema and writes TypeScript type definitions to the path configured i
### `backup` — Back up the database
```bash
bunx bun-sqlite backup
npx 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).
@ -290,7 +288,7 @@ Copies the current database file into `db_backup_dir` with a timestamped filenam
**Example:**
```bash
bunx bun-sqlite backup
npx bun-sqlite backup
# Backing up database ...
# DB Backup Success!
```
@ -300,7 +298,7 @@ bunx bun-sqlite backup
### `restore` — Restore the database from a backup
```bash
bunx bun-sqlite restore
npx 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.
@ -308,7 +306,7 @@ Presents an interactive list of available backups sorted by date (newest first).
**Example:**
```bash
bunx bun-sqlite restore
npx bun-sqlite restore
# Restoring up database ...
# ? Select a backup: (Use arrow keys)
# Backup #1: Mon Mar 02 2026 14:30:00
@ -325,7 +323,7 @@ bunx bun-sqlite restore
Import the default export:
```ts
import BunSQLite from "@moduletrace/bun-sqlite";
import NodeSQLite from "@moduletrace/nsqlite";
```
All methods return an `APIResponseObject<T>`:
@ -351,7 +349,7 @@ All methods return an `APIResponseObject<T>`:
### Select
```ts
BunSQLite.select<T>({ table, query?, count?, targetId? })
NodeSQLite.select<T>({ table, query?, count?, targetId? })
```
| Parameter | Type | Description |
@ -365,13 +363,13 @@ BunSQLite.select<T>({ table, query?, count?, targetId? })
```ts
// Get all users
const res = await BunSQLite.select({ table: "users" });
const res = await NodeSQLite.select({ table: "users" });
// Get by ID
const res = await BunSQLite.select({ table: "users", targetId: 42 });
const res = await NodeSQLite.select({ table: "users", targetId: 42 });
// Filter with LIKE
const res = await BunSQLite.select<UserType>({
const res = await NodeSQLite.select<UserType>({
table: "users",
query: {
query: {
@ -381,11 +379,11 @@ const res = await BunSQLite.select<UserType>({
});
// Count rows
const res = await BunSQLite.select({ table: "users", count: true });
const res = await NodeSQLite.select({ table: "users", count: true });
console.log(res.count);
// Pagination
const res = await BunSQLite.select({
const res = await NodeSQLite.select({
table: "users",
query: { limit: 10, page: 2 },
});
@ -396,7 +394,7 @@ const res = await BunSQLite.select({
### Insert
```ts
BunSQLite.insert<T>({ table, data });
NodeSQLite.insert<T>({ table, data });
```
| Parameter | Type | Description |
@ -409,7 +407,7 @@ BunSQLite.insert<T>({ table, data });
**Example:**
```ts
const res = await BunSQLite.insert({
const res = await NodeSQLite.insert({
table: "users",
data: [
{ first_name: "Alice", last_name: "Smith", email: "alice@example.com" },
@ -425,7 +423,7 @@ console.log(res.postInsertReturn?.insertId); // last inserted row ID
### Update
```ts
BunSQLite.update<T>({ table, data, query?, targetId? })
NodeSQLite.update<T>({ table, data, query?, targetId? })
```
| Parameter | Type | Description |
@ -443,14 +441,14 @@ A WHERE clause is required. If no condition matches, `success` is `false`.
```ts
// Update by ID
await BunSQLite.update({
await NodeSQLite.update({
table: "users",
targetId: 1,
data: { first_name: "Alicia" },
});
// Update with custom query
await BunSQLite.update({
await NodeSQLite.update({
table: "users",
data: { last_name: "Doe" },
query: {
@ -466,7 +464,7 @@ await BunSQLite.update({
### Delete
```ts
BunSQLite.delete<T>({ table, query?, targetId? })
NodeSQLite.delete<T>({ table, query?, targetId? })
```
| Parameter | Type | Description |
@ -481,10 +479,10 @@ A WHERE clause is required. If no condition is provided, `success` is `false`.
```ts
// Delete by ID
await BunSQLite.delete({ table: "users", targetId: 1 });
await NodeSQLite.delete({ table: "users", targetId: 1 });
// Delete with condition
await BunSQLite.delete({
await NodeSQLite.delete({
table: "users",
query: {
query: {
@ -499,7 +497,7 @@ await BunSQLite.delete({
### Raw SQL
```ts
BunSQLite.sql<T>({ sql, values? })
NodeSQLite.sql<T>({ sql, values? })
```
| Parameter | Type | Description |
@ -513,11 +511,11 @@ SELECT statements return rows; all other statements return `postInsertReturn`.
```ts
// SELECT
const res = await BunSQLite.sql<UserType>({ sql: "SELECT * FROM users" });
const res = await NodeSQLite.sql<UserType>({ sql: "SELECT * FROM users" });
console.log(res.payload);
// INSERT with params
await BunSQLite.sql({
await NodeSQLite.sql({
sql: "INSERT INTO users (first_name, email) VALUES (?, ?)",
values: ["Charlie", "charlie@example.com"],
});
@ -566,7 +564,13 @@ Set `equality` on any query field to control the comparison:
| `LIKE` | `LIKE '%value%'` |
| `LIKE_RAW` | `LIKE 'value'` (no auto-wrapping) |
| `LIKE_LOWER` | `LOWER(field) LIKE '%value%'` |
| `LIKE_LOWER_RAW` | `LOWER(field) LIKE 'value'` (no auto-wrapping) |
| `NOT LIKE` | `NOT LIKE '%value%'` |
| `NOT LIKE_RAW` | `NOT LIKE 'value'` (no auto-wrapping) |
| `NOT_LIKE_LOWER` | `LOWER(field) NOT LIKE '%value%'` |
| `NOT_LIKE_LOWER_RAW` | `LOWER(field) NOT LIKE 'value'` (no auto-wrapping) |
| `REGEXP` | `REGEXP 'value'` |
| `FULLTEXT` | Full-text search match |
| `GREATER THAN` | `>` |
| `GREATER THAN OR EQUAL` | `>=` |
| `LESS THAN` | `<` |
@ -574,15 +578,19 @@ Set `equality` on any query field to control the comparison:
| `IN` | `IN (val1, val2, ...)` — pass array as value |
| `NOT IN` | `NOT IN (...)` |
| `BETWEEN` | `BETWEEN val1 AND val2` — pass `[val1, val2]` as value |
| `NOT BETWEEN` | `NOT BETWEEN val1 AND val2` |
| `IS NULL` | `IS NULL` |
| `IS NOT NULL` | `IS NOT NULL` |
| `EXISTS` | `EXISTS (subquery)` |
| `NOT EXISTS` | `NOT EXISTS (subquery)` |
| `MATCH` | sqlite-vec vector nearest-neighbor search |
| `MATCH_BOOLEAN` | SQLite full-text search in boolean mode |
**Example:**
```ts
// Find users with email NOT NULL, ordered by created_at DESC, limit 20
const res = await BunSQLite.select<UserType>({
const res = await NodeSQLite.select<UserType>({
table: "users",
query: {
query: {
@ -597,7 +605,7 @@ const res = await BunSQLite.select<UserType>({
### JOIN
```ts
const res = await BunSQLite.select({
const res = await NodeSQLite.select({
table: "posts",
query: {
join: [
@ -616,7 +624,7 @@ const res = await BunSQLite.select({
## Vector Table Support
`@moduletrace/bun-sqlite` integrates with [`sqlite-vec`](https://github.com/asg017/sqlite-vec) for storing and querying vector embeddings.
`@moduletrace/nsqlite` integrates with [`sqlite-vec`](https://github.com/asg017/sqlite-vec) for storing and querying vector embeddings.
### Define a vector table in the schema
@ -652,16 +660,16 @@ const res = await BunSQLite.select({
```bash
# Initial sync
bunx bun-sqlite schema
npx bun-sqlite schema
# Recreate vector tables (e.g. after changing vectorSize)
bunx bun-sqlite schema --vector
npx bun-sqlite schema --vector
```
### Query vectors
```ts
const res = await BunSQLite.select({
const res = await NodeSQLite.select({
table: "documents",
query: {
query: {
@ -685,7 +693,7 @@ Run the `typedef` command (or pass `--typedef` to `schema`) to generate a `.ts`
- A `const` array of all table names (`BunSQLiteTables`)
- A `type` for each table (named `BUN_SQLITE_<DB_NAME>_<TABLE_NAME>`)
- A union type `BUN_SQLITE_<DB_NAME>_ALL_TYPEDEFS`
- An intersection type `BUN_SQLITE_<DB_NAME>_ALL_TYPEDEFS`
**Example output** (`db/types/db.ts`):
@ -704,16 +712,16 @@ export type BUN_SQLITE_MY_APP_USERS = {
email?: string;
};
export type BUN_SQLITE_MY_APP_ALL_TYPEDEFS = BUN_SQLITE_MY_APP_USERS;
export type BUN_SQLITE_MY_APP_ALL_TYPEDEFS = BUN_SQLITE_MY_APP_USERS; // intersection of all table types
```
Use the generated types with the CRUD API for full type safety:
```ts
import BunSQLite from "@moduletrace/bun-sqlite";
import NodeSQLite from "@moduletrace/nsqlite";
import { BUN_SQLITE_MY_APP_USERS, BunSQLiteTables } from "./db/types/db";
const res = await BunSQLite.select<BUN_SQLITE_MY_APP_USERS>({
const res = await NodeSQLite.select<BUN_SQLITE_MY_APP_USERS>({
table: "users" as (typeof BunSQLiteTables)[number],
});
```
@ -735,27 +743,32 @@ Every table automatically receives the following fields — you do not need to d
## Project Structure
```
bun-sqlite/
node-sqlite/
├── src/
│ ├── index.ts # Main export (BunSQLite object)
│ ├── index.ts # Main export (NodeSQLite object)
│ ├── commands/
│ │ ├── index.ts # CLI entry point
│ │ ├── schema.ts # `schema` command
│ │ ├── typedef.ts # `typedef` command
│ │ ├── backup.ts # `backup` command
│ │ └── restore.ts # `restore` command
│ ├── data/
│ │ ├── app-data.ts # App-level constants (config filename, defaults)
│ │ └── grab-dir-names.ts # Directory name helpers
│ ├── functions/
│ │ └── init.ts # Config + schema loader
│ ├── lib/sqlite/
│ │ ├── index.ts # Database client (bun:sqlite + sqlite-vec)
│ │ ├── index.ts # Database client (better-sqlite3 + sqlite-vec)
│ │ ├── db-schema-manager.ts # Schema synchronization engine
│ │ ├── schema.ts # Internal schema reference
│ │ ├── 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
│ │ ├── db-schema-to-typedef.ts # Schema-to-TypeScript converter (main)
│ │ └── schema-to-typedef.ts # Schema-to-TypeScript converter (legacy)
│ ├── types/
│ │ └── index.ts # All TypeScript types and interfaces
│ └── utils/
@ -770,16 +783,20 @@ bun-sqlite/
│ ├── 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
├── test-01/ # Basic example project
│ ├── bun-sqlite.config.ts
│ ├── db/
│ │ ├── bun-sqlite-schema.ts
│ │ └── types/bun-sqlite.ts # Generated types
│ └── src/
│ └── sql.ts
└── test-02/ # Full CRUD example project
├── bun-sqlite.config.ts
├── db/
│ ├── bun-sqlite-schema.ts
│ └── types/bun-sqlite.ts # Generated types
└── src/
├── index.ts
└── src/crud/
├── insert.ts
├── select.ts
├── delete.ts
└── sql.ts
└── update.ts
```
---

View File

@ -1,7 +1,7 @@
{
"name": "@moduletrace/nsqlite",
"version": "1.0.8",
"description": "SQLite manager for Bun",
"version": "1.0.1",
"description": "SQLite manager for Node JS",
"author": "Benjamin Toby",
"main": "dist/index.js",
"bin": {