@lobomfz/db 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -4
- package/package.json +8 -5
- package/src/database.ts +184 -271
- package/src/dialect/serialize.ts +1 -1
- package/src/env.ts +2 -1
- package/src/fts/resolve.ts +208 -0
- package/src/fts/sql.ts +148 -0
- package/src/fts/tokenizer.ts +27 -0
- package/src/index.ts +18 -1
- package/src/json.ts +13 -0
- package/src/migration/data-loss-error.ts +29 -0
- package/src/migration/diff.ts +269 -25
- package/src/migration/drift-error.ts +9 -0
- package/src/migration/execute.ts +78 -26
- package/src/migration/guided.ts +136 -0
- package/src/migration/introspect.ts +138 -1
- package/src/migration/registry.ts +43 -0
- package/src/migration/types.ts +110 -1
- package/src/plugin.ts +206 -3
- package/src/schema-compiler.ts +362 -0
- package/src/types.ts +110 -12
- package/src/vector-index/load.ts +67 -0
- package/src/vector-index/nearest.ts +68 -0
- package/src/vector-index/resolve.ts +78 -0
- package/src/vector-index/sql.ts +76 -0
- package/src/vector.ts +13 -0
- package/src/write-validation-plugin.ts +92 -0
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ bun add @lobomfz/db
|
|
|
13
13
|
```typescript
|
|
14
14
|
import { Database, generated, type } from "@lobomfz/db";
|
|
15
15
|
|
|
16
|
-
const db =
|
|
16
|
+
const db = await Database.open({
|
|
17
17
|
path: "data.db",
|
|
18
18
|
schema: {
|
|
19
19
|
tables: {
|
|
@@ -68,7 +68,7 @@ type("number.integer").configure({ references: "users.id", onDelete: "cascade" }
|
|
|
68
68
|
JSON columns are validated against the schema on write by default. To also validate on read:
|
|
69
69
|
|
|
70
70
|
```typescript
|
|
71
|
-
|
|
71
|
+
await Database.open({
|
|
72
72
|
// ...
|
|
73
73
|
validation: { onRead: true }, // default: { onRead: false }
|
|
74
74
|
});
|
|
@@ -76,7 +76,7 @@ new Database({
|
|
|
76
76
|
|
|
77
77
|
## Migrations
|
|
78
78
|
|
|
79
|
-
Schema changes are applied automatically on startup.
|
|
79
|
+
Schema changes are applied automatically on startup. `Database.open(...)` is async because migrations run before it resolves: the library compares your Arktype schema against the actual SQLite database and applies the minimum set of operations to bring them in sync. No migration files, no version tracking — the database itself is the source of truth.
|
|
80
80
|
|
|
81
81
|
### What's supported
|
|
82
82
|
|
|
@@ -102,7 +102,58 @@ Table rebuilds follow SQLite's [recommended procedure](https://www.sqlite.org/la
|
|
|
102
102
|
- Adding a NOT NULL column without DEFAULT to a table **with data** throws an error
|
|
103
103
|
- Changing a nullable column to NOT NULL without DEFAULT throws if any row has NULL in that column
|
|
104
104
|
- Nullable-to-NOT-NULL with DEFAULT uses `COALESCE` to fill existing NULLs
|
|
105
|
-
-
|
|
105
|
+
- Without a guided migration, column renames are treated as drop + add (data in the old column is not preserved)
|
|
106
|
+
|
|
107
|
+
### Refusing destructive changes
|
|
108
|
+
|
|
109
|
+
By default the diff applies whatever brings the database in sync, including drops. Set `failOnDataLoss` to turn dropping a table, dropping a column, or a data-discarding type change into a thrown `DataLossError` instead — the database is left untouched.
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
await Database.open({
|
|
113
|
+
// ...
|
|
114
|
+
failOnDataLoss: true,
|
|
115
|
+
});
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Configuring a `migration` (see below) routes through the guided path instead, where the drops are intrinsic to the restructure. The gate does not apply there; the drops are logged via `console.warn` so they are still visible.
|
|
119
|
+
|
|
120
|
+
### Guided data migrations
|
|
121
|
+
|
|
122
|
+
The automatic diff can rename or restructure a table only by dropping the old shape. When you need to preserve and transform the data — rename a column, split one column into two, convert a type — pass a `migration` with a stable `id` and a `run` function. It receives the same typed Kysely client and runs inside the migration transaction.
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
await Database.open({
|
|
126
|
+
path: "data.db",
|
|
127
|
+
schema: {
|
|
128
|
+
tables: {
|
|
129
|
+
users: type({ id: generated("autoincrement"), name: "string", email: "string" }),
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
migration: {
|
|
133
|
+
id: "rename-user-fields",
|
|
134
|
+
run: async ({ db }) => {
|
|
135
|
+
const rows = await db
|
|
136
|
+
.selectFrom("users")
|
|
137
|
+
.select(["id", sql<string>`full_name`.as("full_name")])
|
|
138
|
+
.execute();
|
|
139
|
+
|
|
140
|
+
for (const row of rows) {
|
|
141
|
+
await db.updateTable("users").set({ name: row.full_name }).where("id", "=", row.id).execute();
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
The migration runs as expand then contract, all in one transaction:
|
|
149
|
+
|
|
150
|
+
1. New tables and new columns are added (new columns nullable, so the backfill can write them).
|
|
151
|
+
2. Your `run` backfills and transforms the data, reading old columns with raw `sql` and writing the new ones through the typed client.
|
|
152
|
+
3. The schema is reconciled to the target: leftover columns and tables are dropped (logged via `console.warn`) and `NOT NULL` is tightened.
|
|
153
|
+
|
|
154
|
+
`run` already executes inside the migration transaction, so do not open your own with `db.transaction(...)`. Your reads and writes are already atomic with the rest of the migration; if `run` throws, the whole transaction rolls back and the database is left exactly as it was. Referential integrity is checked with `PRAGMA foreign_key_check` before the commit, even when `foreign_keys` is disabled.
|
|
155
|
+
|
|
156
|
+
Each `id` runs at most once. The applied set lives in a `__migrations` table; a fresh database records the `id` as a baseline without running it (the schema is created directly). Editing the body of an already-applied migration throws `MigrationDriftError` — migrations are immutable once applied, so give new logic a fresh `id`.
|
|
106
157
|
|
|
107
158
|
## License
|
|
108
159
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lobomfz/db",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Bun SQLite database with Arktype schemas and typed Kysely client",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"arktype",
|
|
@@ -32,15 +32,18 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {},
|
|
34
34
|
"devDependencies": {
|
|
35
|
-
"@types/bun": "^1.3.
|
|
36
|
-
"@typescript/native-preview": "^7.0.0-dev.
|
|
37
|
-
"oxfmt": "^0.
|
|
38
|
-
"oxlint": "^1.
|
|
35
|
+
"@types/bun": "^1.3.12",
|
|
36
|
+
"@typescript/native-preview": "^7.0.0-dev.20260417.1",
|
|
37
|
+
"oxfmt": "^0.45.0",
|
|
38
|
+
"oxlint": "^1.60.0"
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
41
|
"@ark/schema": ">=0.56.0",
|
|
42
42
|
"@ark/util": ">=0.56.0",
|
|
43
43
|
"arktype": "^2.1.29",
|
|
44
44
|
"kysely": "^0.28.14"
|
|
45
|
+
},
|
|
46
|
+
"optionalDependencies": {
|
|
47
|
+
"sqlite-vec": "^0.1.9"
|
|
45
48
|
}
|
|
46
49
|
}
|