flint-orm 0.6.0 → 0.7.1
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 +2 -0
- package/dist/drivers/better-sqlite3.d.ts +6 -2
- package/dist/drivers/bun-sqlite.d.ts +6 -2
- package/dist/drivers/lazy-executor.d.ts +3 -1
- package/dist/drivers/libsql-web.d.ts +6 -2
- package/dist/drivers/libsql.d.ts +6 -2
- package/dist/drivers/turso-sync.d.ts +6 -2
- package/dist/drivers/turso.d.ts +6 -2
- package/dist/executor.d.ts +4 -2
- package/dist/flint.d.ts +3 -1
- package/dist/query/builder.d.ts +9 -3
- package/dist/src/cli.js +15 -14
- package/dist/src/entries/better-sqlite3.js +5 -8
- package/dist/src/entries/bun-sqlite.js +5 -8
- package/dist/src/entries/expressions.js +3 -6
- package/dist/src/entries/libsql-web.js +5 -7
- package/dist/src/entries/libsql.js +5 -7
- package/dist/src/entries/turso-sync.js +5 -7
- package/dist/src/entries/turso.js +5 -7
- package/dist/src/index.js +3 -6
- package/package.json +15 -3
- package/skills/batch-transactions/SKILL.md +120 -0
- package/skills/define-schema/SKILL.md +212 -0
- package/skills/define-schema/references/column-modifiers.md +176 -0
- package/skills/driver-selection/SKILL.md +180 -0
- package/skills/driver-selection/references/driver-comparison.md +141 -0
- package/skills/run-migrations/SKILL.md +163 -0
- package/skills/setup-client/SKILL.md +142 -0
- package/skills/update-schema/SKILL.md +186 -0
- package/skills/write-queries/SKILL.md +267 -0
- package/skills/write-queries/references/condition-operators.md +220 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: batch-transactions
|
|
3
|
+
description: >
|
|
4
|
+
Run multiple queries atomically in a single transaction. Covers db.batch(),
|
|
5
|
+
Executable interface, and transaction safety. Load when combining multiple
|
|
6
|
+
writes that must succeed or fail together, or when debugging transaction
|
|
7
|
+
issues.
|
|
8
|
+
metadata:
|
|
9
|
+
type: core
|
|
10
|
+
library: flint-orm
|
|
11
|
+
library_version: 0.7.0
|
|
12
|
+
sources:
|
|
13
|
+
- 'kavenlabs/flint-orm:src/flint.ts'
|
|
14
|
+
- 'kavenlabs/flint-orm:src/query/builder.ts'
|
|
15
|
+
- 'kavenlabs/flint-orm:README.md'
|
|
16
|
+
- 'kavenlabs/flint-orm:API.md'
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
# flint-orm — Batch Transactions
|
|
20
|
+
|
|
21
|
+
## Setup
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { flint } from 'flint-orm/bun-sqlite';
|
|
25
|
+
import { table, text, integer } from 'flint-orm/table';
|
|
26
|
+
import { eq } from 'flint-orm/expressions';
|
|
27
|
+
|
|
28
|
+
const users = table('users', {
|
|
29
|
+
id: text('id').primaryKey(),
|
|
30
|
+
name: text('name').notNull(),
|
|
31
|
+
totalOrders: integer('totalOrders').default(0),
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const posts = table('posts', {
|
|
35
|
+
id: text('id').primaryKey(),
|
|
36
|
+
userId: text('userId').notNull().references(users.id),
|
|
37
|
+
title: text('title').notNull(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
const db = flint({ url: './app.db' });
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Core Patterns
|
|
44
|
+
|
|
45
|
+
### Run multiple inserts atomically
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
await db.batch([
|
|
49
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }),
|
|
50
|
+
db.insert(posts).values({ id: 'p1', userId: 'u1', title: 'Hello' }),
|
|
51
|
+
]);
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Combine reads and writes
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
await db.batch([
|
|
58
|
+
db.update(users).set({ totalOrders: 1 }).where(eq(users.id, 'u1')),
|
|
59
|
+
db.insert(posts).values({ id: 'p1', userId: 'u1', title: 'Hello' }),
|
|
60
|
+
]);
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### All-or-nothing execution
|
|
64
|
+
|
|
65
|
+
If any query in the batch fails, all queries are rolled back:
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
// If the second insert fails, the first insert is also rolled back
|
|
69
|
+
await db.batch([
|
|
70
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }),
|
|
71
|
+
db.insert(posts).values({ id: 'p1', userId: 'u1', title: 'Hello' }), // If this fails...
|
|
72
|
+
]);
|
|
73
|
+
// ...the user insert is also rolled back
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Common Mistakes
|
|
77
|
+
|
|
78
|
+
### HIGH Not awaiting the batch call
|
|
79
|
+
|
|
80
|
+
Wrong:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
db.batch([
|
|
84
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }),
|
|
85
|
+
db.insert(posts).values({ id: 'p1', userId: 'u1', title: 'Hello' }),
|
|
86
|
+
]); // Not awaited!
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Correct:
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
await db.batch([
|
|
93
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }),
|
|
94
|
+
db.insert(posts).values({ id: 'p1', userId: 'u1', title: 'Hello' }),
|
|
95
|
+
]);
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`batch()` returns a Promise — without `await`, the transaction may not complete.
|
|
99
|
+
|
|
100
|
+
Source: src/flint.ts
|
|
101
|
+
|
|
102
|
+
### MEDIUM Passing non-Executable objects to batch()
|
|
103
|
+
|
|
104
|
+
Wrong:
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
db.batch([{ sql: 'INSERT ...', params: [] }]); // Not an Executable
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Correct:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
db.batch([
|
|
114
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }),
|
|
115
|
+
]);
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
`batch()` only accepts objects with a `.toSQL()` method (query builders), not raw SQL objects.
|
|
119
|
+
|
|
120
|
+
Source: src/flint.ts
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: define-schema
|
|
3
|
+
description: >
|
|
4
|
+
Create tables with columns, indexes, constraints, and foreign keys. Covers
|
|
5
|
+
table(), snakeCase.table(), column constructors (text, integer, boolean,
|
|
6
|
+
json, real, date), column modifiers (primaryKey, notNull, unique, default,
|
|
7
|
+
references, onDelete, onUpdate, autoIncrement, defaultNow, onUpdateTimestamp),
|
|
8
|
+
index builder, and InferRow/InsertRow types. Load when defining or modifying
|
|
9
|
+
database schemas.
|
|
10
|
+
metadata:
|
|
11
|
+
type: core
|
|
12
|
+
library: flint-orm
|
|
13
|
+
library_version: 0.7.0
|
|
14
|
+
sources:
|
|
15
|
+
- 'kavenlabs/flint-orm:src/schema/columns.ts'
|
|
16
|
+
- 'kavenlabs/flint-orm:src/schema/table.ts'
|
|
17
|
+
- 'kavenlabs/flint-orm:README.md'
|
|
18
|
+
- 'kavenlabs/flint-orm:API.md'
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# flint-orm — Schema Definition
|
|
22
|
+
|
|
23
|
+
## Setup
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import * as d from 'flint-orm/table';
|
|
27
|
+
|
|
28
|
+
const users = d.snakeCase.table('users', {
|
|
29
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
30
|
+
name: d.text().notNull(),
|
|
31
|
+
email: d.text().unique(),
|
|
32
|
+
age: d.integer(),
|
|
33
|
+
active: d.boolean().default(true),
|
|
34
|
+
createdAt: d.date().defaultNow(),
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Core Patterns
|
|
39
|
+
|
|
40
|
+
### Define a table with snake_case columns
|
|
41
|
+
|
|
42
|
+
Use `snakeCase.table()` to auto-convert camelCase keys to snake_case SQL names:
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
import * as d from 'flint-orm/table';
|
|
46
|
+
|
|
47
|
+
const users = d.snakeCase.table('users', {
|
|
48
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
49
|
+
firstName: d.text().notNull(), // SQL: first_name
|
|
50
|
+
lastName: d.text().notNull(), // SQL: last_name
|
|
51
|
+
createdAt: d.date().defaultNow(), // SQL: created_at
|
|
52
|
+
});
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Add indexes
|
|
56
|
+
|
|
57
|
+
Define indexes via the table callback:
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import * as d from 'flint-orm/table';
|
|
61
|
+
|
|
62
|
+
const users = d.snakeCase.table(
|
|
63
|
+
'users',
|
|
64
|
+
{
|
|
65
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
66
|
+
email: d.text().notNull(),
|
|
67
|
+
name: d.text().notNull(),
|
|
68
|
+
},
|
|
69
|
+
(t) => [
|
|
70
|
+
d.index('idx_users_email').on(t.email).unique(),
|
|
71
|
+
d.index('idx_users_name').on(t.name),
|
|
72
|
+
],
|
|
73
|
+
);
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Define foreign keys
|
|
77
|
+
|
|
78
|
+
Use `.references()` to link columns across tables:
|
|
79
|
+
|
|
80
|
+
```ts
|
|
81
|
+
import * as d from 'flint-orm/table';
|
|
82
|
+
|
|
83
|
+
const users = d.snakeCase.table('users', {
|
|
84
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
85
|
+
name: d.text().notNull(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const posts = d.snakeCase.table('posts', {
|
|
89
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
90
|
+
userId: d.integer().notNull().references(users.id),
|
|
91
|
+
title: d.text().notNull(),
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Derive TypeScript types
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import type { InferRow, InsertRow } from 'flint-orm/table';
|
|
99
|
+
|
|
100
|
+
type User = InferRow<typeof users>;
|
|
101
|
+
// { id: number; name: string; email: string | null; active: boolean; createdAt: Date }
|
|
102
|
+
|
|
103
|
+
type NewUser = InsertRow<typeof users>;
|
|
104
|
+
// { id?: number; name: string; email?: string; active?: boolean; createdAt?: Date }
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Column Types
|
|
108
|
+
|
|
109
|
+
| Function | TS Type | SQLite Storage | Notes |
|
|
110
|
+
|----------|---------|----------------|-------|
|
|
111
|
+
| `text()` | `string` | TEXT | |
|
|
112
|
+
| `integer()` | `number` | INTEGER | Supports `.autoIncrement()` |
|
|
113
|
+
| `boolean()` | `boolean` | INTEGER (0/1) | Encodes/decodes automatically |
|
|
114
|
+
| `json<T>()` | `T` | TEXT (JSON) | Generic, encodes/decodes automatically |
|
|
115
|
+
| `real()` | `number` | REAL | |
|
|
116
|
+
| `date()` | `Date` | INTEGER (epoch ms) | Supports `.defaultNow()`, `.onUpdateTimestamp()` |
|
|
117
|
+
|
|
118
|
+
## Column Modifiers
|
|
119
|
+
|
|
120
|
+
| Modifier | Applies to | Description |
|
|
121
|
+
|----------|------------|-------------|
|
|
122
|
+
| `.primaryKey()` | All | Mark as primary key |
|
|
123
|
+
| `.notNull()` | All | Disallow NULL values |
|
|
124
|
+
| `.unique()` | All | Add unique constraint |
|
|
125
|
+
| `.default(value)` | All | Static default value |
|
|
126
|
+
| `.defaultFn(fn)` | All | Dynamic default (called on insert) |
|
|
127
|
+
| `.references(col)` | All | Foreign key reference |
|
|
128
|
+
| `.onDelete(action)` | All | ON DELETE action (requires `.references()`) |
|
|
129
|
+
| `.onUpdate(action)` | All | ON UPDATE action (requires `.references()`) |
|
|
130
|
+
| `.autoIncrement()` | integer only | Auto-increment |
|
|
131
|
+
| `.defaultNow()` | date only | Use `Date.now()` as default |
|
|
132
|
+
| `.onUpdateTimestamp()` | date only | Always set to `Date.now()` on update |
|
|
133
|
+
|
|
134
|
+
## Common Mistakes
|
|
135
|
+
|
|
136
|
+
### HIGH Using table() with camelCase keys
|
|
137
|
+
|
|
138
|
+
Wrong:
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
import { table, text } from 'flint-orm/table';
|
|
142
|
+
|
|
143
|
+
const users = table('users', {
|
|
144
|
+
id: text('id').primaryKey(),
|
|
145
|
+
firstName: text('firstName'), // SQL: firstName, not first_name
|
|
146
|
+
});
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Correct:
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
import * as d from 'flint-orm/table';
|
|
153
|
+
|
|
154
|
+
const users = d.snakeCase.table('users', {
|
|
155
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
156
|
+
firstName: d.text().notNull(), // SQL: first_name
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
`table()` uses the object key as the SQL column name — camelCase keys produce camelCase SQL columns. Use `snakeCase.table()` for automatic conversion.
|
|
161
|
+
|
|
162
|
+
Source: maintainer interview
|
|
163
|
+
|
|
164
|
+
### HIGH Using .__internal in application code
|
|
165
|
+
|
|
166
|
+
Wrong:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
const typeName = users.name.__internal._type; // DON'T DO THIS
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Correct:
|
|
173
|
+
|
|
174
|
+
```ts
|
|
175
|
+
import type { InferRow } from 'flint-orm/table';
|
|
176
|
+
type User = InferRow<typeof users>;
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`.__internal` is for ORM internals — its shape can change between versions without notice.
|
|
180
|
+
|
|
181
|
+
Source: maintainer interview
|
|
182
|
+
|
|
183
|
+
### MEDIUM Calling .references() before table() stamps it
|
|
184
|
+
|
|
185
|
+
Wrong:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
import { text } from 'flint-orm/table';
|
|
189
|
+
const col = text('userId').references(users.id); // ERROR: users not yet defined
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Correct:
|
|
193
|
+
|
|
194
|
+
```ts
|
|
195
|
+
import * as d from 'flint-orm/table';
|
|
196
|
+
|
|
197
|
+
const users = d.snakeCase.table('users', {
|
|
198
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
const posts = d.snakeCase.table('posts', {
|
|
202
|
+
userId: d.integer().notNull().references(users.id),
|
|
203
|
+
});
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
TypeScript prevents this — `.references()` requires a column that has been attached to a table via `table()`.
|
|
207
|
+
|
|
208
|
+
Source: src/schema/columns.ts
|
|
209
|
+
|
|
210
|
+
## References
|
|
211
|
+
|
|
212
|
+
- [Full column modifiers reference](references/column-modifiers.md)
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Column Modifiers Reference
|
|
2
|
+
|
|
3
|
+
Detailed reference for all column modifiers in flint-orm.
|
|
4
|
+
|
|
5
|
+
## .primaryKey()
|
|
6
|
+
|
|
7
|
+
Mark a column as the primary key.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
const id = d.integer().primaryKey();
|
|
11
|
+
// SQL: id INTEGER PRIMARY KEY
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
**Notes:**
|
|
15
|
+
- Only one column per table can be primary key
|
|
16
|
+
- Primary keys are implicitly NOT NULL
|
|
17
|
+
|
|
18
|
+
## .notNull()
|
|
19
|
+
|
|
20
|
+
Disallow NULL values.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
const name = d.text().notNull();
|
|
24
|
+
// SQL: name TEXT NOT NULL
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
**Notes:**
|
|
28
|
+
- Adding NOT NULL to an existing column requires a default value (triggers table rebuild otherwise)
|
|
29
|
+
- Removing NOT NULL is an unsafe migration (triggers table rebuild)
|
|
30
|
+
|
|
31
|
+
## .unique()
|
|
32
|
+
|
|
33
|
+
Add a unique constraint.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
const email = d.text().unique();
|
|
37
|
+
// SQL: email TEXT UNIQUE
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
**Notes:**
|
|
41
|
+
- Adding or removing UNIQUE triggers a table rebuild
|
|
42
|
+
|
|
43
|
+
## .default(value)
|
|
44
|
+
|
|
45
|
+
Set a static default value.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
const active = d.boolean().default(true);
|
|
49
|
+
// SQL: active INTEGER DEFAULT 1
|
|
50
|
+
|
|
51
|
+
const status = d.text().default('active');
|
|
52
|
+
// SQL: status TEXT DEFAULT 'active'
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
**Notes:**
|
|
56
|
+
- The value must match the column's TypeScript type
|
|
57
|
+
- Removing a default is an unsafe migration (SQLite has no DROP DEFAULT syntax)
|
|
58
|
+
|
|
59
|
+
## .defaultFn(fn)
|
|
60
|
+
|
|
61
|
+
Set a dynamic default function called on insert when the value is omitted.
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const id = d.text().defaultFn(() => crypto.randomUUID());
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Notes:**
|
|
68
|
+
- The function is called at insert time, not at schema definition time
|
|
69
|
+
- Useful for UUIDs, timestamps, or computed values
|
|
70
|
+
|
|
71
|
+
## .references(target)
|
|
72
|
+
|
|
73
|
+
Define a foreign key reference to another table's column.
|
|
74
|
+
|
|
75
|
+
```ts
|
|
76
|
+
const users = d.snakeCase.table('users', {
|
|
77
|
+
id: d.integer().autoIncrement().primaryKey(),
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const posts = d.snakeCase.table('posts', {
|
|
81
|
+
userId: d.integer().notNull().references(users.id),
|
|
82
|
+
});
|
|
83
|
+
// SQL: userId INTEGER NOT NULL REFERENCES users(id)
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
**Notes:**
|
|
87
|
+
- The target column must be from a table defined with `table()`
|
|
88
|
+
- TypeScript prevents calling `.references()` before the target table exists
|
|
89
|
+
- Changing the FK target triggers a table rebuild
|
|
90
|
+
|
|
91
|
+
## .onDelete(action)
|
|
92
|
+
|
|
93
|
+
Set the ON DELETE action for a foreign key. Requires `.references()` to be called first.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const posts = d.snakeCase.table('posts', {
|
|
97
|
+
userId: d.integer().notNull().references(users.id).onDelete('cascade'),
|
|
98
|
+
});
|
|
99
|
+
// SQL: userId INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
**Valid actions:** `'cascade'`, `'set null'`, `'set default'`, `'restrict'`, `'no action'`
|
|
103
|
+
|
|
104
|
+
**Notes:**
|
|
105
|
+
- Changing ON DELETE action triggers a table rebuild
|
|
106
|
+
|
|
107
|
+
## .onUpdate(action)
|
|
108
|
+
|
|
109
|
+
Set the ON UPDATE action for a foreign key. Requires `.references()` to be called first.
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
const posts = d.snakeCase.table('posts', {
|
|
113
|
+
userId: d.integer().notNull().references(users.id).onUpdate('cascade'),
|
|
114
|
+
});
|
|
115
|
+
// SQL: userId INTEGER NOT NULL REFERENCES users(id) ON UPDATE CASCADE
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
**Valid actions:** `'cascade'`, `'set null'`, `'set default'`, `'restrict'`, `'no action'`
|
|
119
|
+
|
|
120
|
+
**Notes:**
|
|
121
|
+
- Changing ON UPDATE action triggers a table rebuild
|
|
122
|
+
|
|
123
|
+
## .autoIncrement() (integer only)
|
|
124
|
+
|
|
125
|
+
Mark an integer column as auto-increment (SQLite ROWID alias).
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
const id = d.integer().primaryKey().autoIncrement();
|
|
129
|
+
// SQL: id INTEGER PRIMARY KEY AUTOINCREMENT
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
**Notes:**
|
|
133
|
+
- Only works with integer columns
|
|
134
|
+
- Changing autoincrement triggers a table rebuild
|
|
135
|
+
|
|
136
|
+
## .defaultNow() (date only)
|
|
137
|
+
|
|
138
|
+
Use `Date.now()` as the default when the value is omitted during insert.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
const createdAt = d.date().defaultNow();
|
|
142
|
+
// SQL: created_at INTEGER DEFAULT (unixepoch * 1000)
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Notes:**
|
|
146
|
+
- Only works with date columns
|
|
147
|
+
- Makes the column non-nullable in query results (guaranteed to have a value)
|
|
148
|
+
|
|
149
|
+
## .onUpdateTimestamp() (date only)
|
|
150
|
+
|
|
151
|
+
Always set to `Date.now()` on update, regardless of the provided value.
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
const updatedAt = d.date().onUpdateTimestamp();
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**Notes:**
|
|
158
|
+
- Only works with date columns
|
|
159
|
+
- The value is set automatically on every UPDATE operation
|
|
160
|
+
- Not reflected in SQL schema — handled by the ORM at runtime
|
|
161
|
+
|
|
162
|
+
## Migration Safety
|
|
163
|
+
|
|
164
|
+
| Change | Safe? | Notes |
|
|
165
|
+
|--------|-------|-------|
|
|
166
|
+
| Adding NOT NULL with default | ✅ | Safe |
|
|
167
|
+
| Adding NOT NULL without default | ❌ | Triggers rebuild |
|
|
168
|
+
| Removing NOT NULL | ❌ | Triggers rebuild |
|
|
169
|
+
| Adding UNIQUE | ❌ | Triggers rebuild |
|
|
170
|
+
| Removing UNIQUE | ❌ | Triggers rebuild |
|
|
171
|
+
| Adding/changing DEFAULT | ✅ | Safe |
|
|
172
|
+
| Removing DEFAULT | ❌ | SQLite has no DROP DEFAULT |
|
|
173
|
+
| Adding/changing FK | ❌ | Triggers rebuild |
|
|
174
|
+
| Removing FK | ❌ | Triggers rebuild |
|
|
175
|
+
| Changing FK actions | ❌ | Triggers rebuild |
|
|
176
|
+
| Changing autoincrement | ❌ | Triggers rebuild |
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: driver-selection
|
|
3
|
+
description: >
|
|
4
|
+
Choose the right driver for your environment: local file, in-memory,
|
|
5
|
+
serverless, or remote. Covers bun:sqlite, better-sqlite3, @libsql/client,
|
|
6
|
+
@libsql/client/web, @tursodatabase/database, @tursodatabase/sync. Load
|
|
7
|
+
when deciding which driver to use or when troubleshooting driver-specific
|
|
8
|
+
issues.
|
|
9
|
+
metadata:
|
|
10
|
+
type: core
|
|
11
|
+
library: flint-orm
|
|
12
|
+
library_version: 0.7.0
|
|
13
|
+
sources:
|
|
14
|
+
- 'kavenlabs/flint-orm:src/entries/bun-sqlite.ts'
|
|
15
|
+
- 'kavenlabs/flint-orm:src/entries/better-sqlite3.ts'
|
|
16
|
+
- 'kavenlabs/flint-orm:src/entries/libsql.ts'
|
|
17
|
+
- 'kavenlabs/flint-orm:src/entries/libsql-web.ts'
|
|
18
|
+
- 'kavenlabs/flint-orm:src/entries/turso.ts'
|
|
19
|
+
- 'kavenlabs/flint-orm:src/entries/turso-sync.ts'
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# flint-orm — Driver Selection
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
// bun:sqlite — Bun runtime only, local file
|
|
28
|
+
import { flint } from 'flint-orm/bun-sqlite';
|
|
29
|
+
const db = flint({ url: './app.db' });
|
|
30
|
+
|
|
31
|
+
// better-sqlite3 — Node.js, local file
|
|
32
|
+
import { flint } from 'flint-orm/better-sqlite3';
|
|
33
|
+
const db = flint({ url: './app.db' });
|
|
34
|
+
|
|
35
|
+
// libsql — local file or remote (Turso)
|
|
36
|
+
import { flint } from 'flint-orm/libsql';
|
|
37
|
+
const db = flint({ url: 'libsql://your-db.turso.io', authToken: '...' });
|
|
38
|
+
|
|
39
|
+
// libsql-web — serverless/edge, remote only (no native binary)
|
|
40
|
+
import { flint } from 'flint-orm/libsql-web';
|
|
41
|
+
const db = flint({ url: 'libsql://your-db.turso.io', authToken: '...' });
|
|
42
|
+
|
|
43
|
+
// turso — Turso database
|
|
44
|
+
import { flint } from 'flint-orm/turso';
|
|
45
|
+
const db = flint({ url: 'libsql://your-db.turso.io', authToken: '...' });
|
|
46
|
+
|
|
47
|
+
// turso-sync — Turso with sync support
|
|
48
|
+
import { flint } from 'flint-orm/turso-sync';
|
|
49
|
+
const db = flint({ url: 'libsql://your-db.turso.io', authToken: '...' });
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Core Patterns
|
|
53
|
+
|
|
54
|
+
### Local file database
|
|
55
|
+
|
|
56
|
+
Use bun:sqlite (Bun) or better-sqlite3 (Node.js) for local SQLite files:
|
|
57
|
+
|
|
58
|
+
```ts
|
|
59
|
+
// Bun runtime
|
|
60
|
+
import { flint } from 'flint-orm/bun-sqlite';
|
|
61
|
+
const db = flint({ url: './app.db' });
|
|
62
|
+
|
|
63
|
+
// Node.js
|
|
64
|
+
import { flint } from 'flint-orm/better-sqlite3';
|
|
65
|
+
const db = flint({ url: './app.db' });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Remote database (Turso)
|
|
69
|
+
|
|
70
|
+
Use libsql or turso for remote Turso databases:
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { flint } from 'flint-orm/libsql';
|
|
74
|
+
const db = flint({
|
|
75
|
+
url: 'libsql://your-db.turso.io',
|
|
76
|
+
authToken: process.env.TURSO_AUTH_TOKEN,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Serverless/edge environment
|
|
81
|
+
|
|
82
|
+
Use libsql-web for serverless or edge runtimes (no native binary):
|
|
83
|
+
|
|
84
|
+
```ts
|
|
85
|
+
import { flint } from 'flint-orm/libsql-web';
|
|
86
|
+
const db = flint({
|
|
87
|
+
url: 'libsql://your-db.turso.io',
|
|
88
|
+
authToken: process.env.TURSO_AUTH_TOKEN,
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### In-memory database
|
|
93
|
+
|
|
94
|
+
All drivers support in-memory databases via `:memory:`:
|
|
95
|
+
|
|
96
|
+
```ts
|
|
97
|
+
import { flint } from 'flint-orm/bun-sqlite';
|
|
98
|
+
const db = flint({ url: ':memory:' });
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Driver Comparison
|
|
102
|
+
|
|
103
|
+
| Driver | Runtime | File URLs | Remote URLs | Serverless | Auth Token |
|
|
104
|
+
|--------|---------|-----------|-------------|------------|------------|
|
|
105
|
+
| bun:sqlite | Bun only | ✅ | ❌ | ❌ | ❌ |
|
|
106
|
+
| better-sqlite3 | Node.js | ✅ | ❌ | ❌ | ❌ |
|
|
107
|
+
| libsql | Bun/Node | ✅ | ✅ | ✅ | ✅ |
|
|
108
|
+
| libsql-web | Any | ❌ | ✅ | ✅ | ✅ |
|
|
109
|
+
| turso | Bun/Node | ✅ | ✅ | ✅ | ✅ |
|
|
110
|
+
| turso-sync | Bun/Node | ✅ | ✅ | ✅ | ✅ |
|
|
111
|
+
|
|
112
|
+
## Common Mistakes
|
|
113
|
+
|
|
114
|
+
### HIGH libsql requires file: prefix for local paths
|
|
115
|
+
|
|
116
|
+
Wrong:
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { flint } from 'flint-orm/libsql';
|
|
120
|
+
const db = flint({ url: 'app.db' }); // ERROR: URL_INVALID
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Correct:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
import { flint } from 'flint-orm/libsql';
|
|
127
|
+
const db = flint({ url: 'file:./app.db' });
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Unlike bun:sqlite and better-sqlite3, the libsql driver requires the `file:` prefix for local file paths.
|
|
131
|
+
|
|
132
|
+
Source: maintainer interview
|
|
133
|
+
|
|
134
|
+
### HIGH Using libsql-web with file: URLs
|
|
135
|
+
|
|
136
|
+
Wrong:
|
|
137
|
+
|
|
138
|
+
```ts
|
|
139
|
+
import { flint } from 'flint-orm/libsql-web';
|
|
140
|
+
const db = flint({ url: 'file:./app.db' }); // ERROR
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Correct:
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
// For local files, use libsql or bun-sqlite
|
|
147
|
+
import { flint } from 'flint-orm/libsql';
|
|
148
|
+
const db = flint({ url: 'file:./app.db' });
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
libsql-web only supports HTTP/WebSocket URLs — it has no native binary and cannot access local files.
|
|
152
|
+
|
|
153
|
+
Source: src/entries/libsql-web.ts
|
|
154
|
+
|
|
155
|
+
### HIGH Missing authToken for remote databases
|
|
156
|
+
|
|
157
|
+
Wrong:
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
import { flint } from 'flint-orm/libsql';
|
|
161
|
+
const db = flint({ url: 'libsql://my-db.turso.io' }); // Missing authToken
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Correct:
|
|
165
|
+
|
|
166
|
+
```ts
|
|
167
|
+
import { flint } from 'flint-orm/libsql';
|
|
168
|
+
const db = flint({
|
|
169
|
+
url: 'libsql://my-db.turso.io',
|
|
170
|
+
authToken: process.env.TURSO_AUTH_TOKEN,
|
|
171
|
+
});
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Remote Turso/libsql databases require authentication.
|
|
175
|
+
|
|
176
|
+
Source: maintainer interview
|
|
177
|
+
|
|
178
|
+
## References
|
|
179
|
+
|
|
180
|
+
- [Full driver comparison](references/driver-comparison.md)
|