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,267 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: write-queries
|
|
3
|
+
description: >
|
|
4
|
+
Read, write, and modify data with type-safe SELECT, INSERT, UPDATE, DELETE,
|
|
5
|
+
joins, and aggregates. Covers selectFrom, insert, update, delete, leftJoin,
|
|
6
|
+
innerJoin, where, columns, single, orderBy, limit, offset, distinct,
|
|
7
|
+
returning, onConflictDoNothing, onConflictDoUpdate, count, sum, avg, min,
|
|
8
|
+
max, and raw SQL. Load when building queries or troubleshooting query issues.
|
|
9
|
+
metadata:
|
|
10
|
+
type: core
|
|
11
|
+
library: flint-orm
|
|
12
|
+
library_version: 0.7.0
|
|
13
|
+
sources:
|
|
14
|
+
- 'kavenlabs/flint-orm:src/query/builder.ts'
|
|
15
|
+
- 'kavenlabs/flint-orm:src/query/conditions.ts'
|
|
16
|
+
- 'kavenlabs/flint-orm:src/query/aggregates.ts'
|
|
17
|
+
- 'kavenlabs/flint-orm:src/flint.ts'
|
|
18
|
+
- 'kavenlabs/flint-orm:README.md'
|
|
19
|
+
- 'kavenlabs/flint-orm:API.md'
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
# flint-orm — Queries
|
|
23
|
+
|
|
24
|
+
## Setup
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { flint } from 'flint-orm/bun-sqlite';
|
|
28
|
+
import { table, text, integer } from 'flint-orm/table';
|
|
29
|
+
import { eq, and } from 'flint-orm/expressions';
|
|
30
|
+
|
|
31
|
+
const users = table('users', {
|
|
32
|
+
id: text('id').primaryKey(),
|
|
33
|
+
name: text('name').notNull(),
|
|
34
|
+
age: integer('age'),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const db = flint({ url: './app.db' });
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Core Patterns
|
|
41
|
+
|
|
42
|
+
### SELECT with conditions
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
const adults = await db
|
|
46
|
+
.selectFrom(users)
|
|
47
|
+
.where(eq(users.age, 18))
|
|
48
|
+
.execute();
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### SELECT specific columns
|
|
52
|
+
|
|
53
|
+
```ts
|
|
54
|
+
const names = await db
|
|
55
|
+
.selectFrom(users)
|
|
56
|
+
.columns(['id', 'name'])
|
|
57
|
+
.execute();
|
|
58
|
+
// Returns: { id: string; name: string }[]
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Get a single row
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
const user = await db
|
|
65
|
+
.selectFrom(users)
|
|
66
|
+
.where(eq(users.id, 'u1'))
|
|
67
|
+
.single()
|
|
68
|
+
.execute();
|
|
69
|
+
// Returns: User | null
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### INSERT with returning
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// Return all columns
|
|
76
|
+
const inserted = await db
|
|
77
|
+
.insert(users)
|
|
78
|
+
.values({ id: 'u1', name: 'Alice', age: 30 })
|
|
79
|
+
.returning()
|
|
80
|
+
.execute();
|
|
81
|
+
|
|
82
|
+
// Return specific columns
|
|
83
|
+
const inserted = await db
|
|
84
|
+
.insert(users)
|
|
85
|
+
.values({ id: 'u1', name: 'Alice', age: 30 })
|
|
86
|
+
.returning(['id', 'name'])
|
|
87
|
+
.execute();
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### UPSERT
|
|
91
|
+
|
|
92
|
+
```ts
|
|
93
|
+
await db
|
|
94
|
+
.insert(users)
|
|
95
|
+
.values({ id: 'u1', name: 'Alice' })
|
|
96
|
+
.onConflictDoUpdate({
|
|
97
|
+
target: users.id,
|
|
98
|
+
set: { name: 'Alice Updated' },
|
|
99
|
+
})
|
|
100
|
+
.execute();
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### UPDATE with conditions
|
|
104
|
+
|
|
105
|
+
```ts
|
|
106
|
+
// Update without returning
|
|
107
|
+
await db
|
|
108
|
+
.update(users)
|
|
109
|
+
.set({ name: 'Bob' })
|
|
110
|
+
.where(eq(users.id, 'u1'))
|
|
111
|
+
.execute();
|
|
112
|
+
|
|
113
|
+
// Update with returning
|
|
114
|
+
const updated = await db
|
|
115
|
+
.update(users)
|
|
116
|
+
.set({ name: 'Bob' })
|
|
117
|
+
.where(eq(users.id, 'u1'))
|
|
118
|
+
.returning(['id', 'name'])
|
|
119
|
+
.execute();
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### DELETE with conditions
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
// Delete without returning
|
|
126
|
+
await db
|
|
127
|
+
.delete(users)
|
|
128
|
+
.where(eq(users.id, 'u1'))
|
|
129
|
+
.execute();
|
|
130
|
+
|
|
131
|
+
// Delete with returning
|
|
132
|
+
const deleted = await db
|
|
133
|
+
.delete(users)
|
|
134
|
+
.where(eq(users.id, 'u1'))
|
|
135
|
+
.returning()
|
|
136
|
+
.execute();
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### LEFT JOIN
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const posts = table('posts', {
|
|
143
|
+
id: text('id').primaryKey(),
|
|
144
|
+
userId: text('userId').notNull().references(users.id),
|
|
145
|
+
title: text('title').notNull(),
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
const postsWithAuthors = await db
|
|
149
|
+
.leftJoin(users)
|
|
150
|
+
.on(posts)
|
|
151
|
+
.execute();
|
|
152
|
+
// Returns: { id: string; name: string; posts: Post[] }[]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Aggregate functions
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
const total = await db.count(users);
|
|
159
|
+
const activeCount = await db.count(users, eq(users.active, true));
|
|
160
|
+
const totalViews = await db.sum(posts, posts.views);
|
|
161
|
+
const avgAge = await db.avg(users, users.age);
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Building Queries Conditionally
|
|
165
|
+
|
|
166
|
+
The query builder is immutable — you can compose queries conditionally:
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
let query = db.selectFrom(users);
|
|
170
|
+
|
|
171
|
+
if (filter.name) {
|
|
172
|
+
query = query.where(eq(users.name, filter.name));
|
|
173
|
+
}
|
|
174
|
+
if (filter.minAge) {
|
|
175
|
+
query = query.where(gte(users.age, filter.minAge));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const results = await query.execute();
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
Call `.execute()` at the terminal state when ready to run the query.
|
|
182
|
+
|
|
183
|
+
## Common Mistakes
|
|
184
|
+
|
|
185
|
+
### CRITICAL Not calling .execute() at terminal state
|
|
186
|
+
|
|
187
|
+
Wrong:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
const query = db.selectFrom(users).where(eq(users.id, 'u1')); // Builder, not executed
|
|
191
|
+
const data = query; // Missing .execute()
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Correct:
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
// Building queries conditionally is fine:
|
|
198
|
+
let query = db.selectFrom(users);
|
|
199
|
+
if (filter) query = query.where(eq(users.active, true));
|
|
200
|
+
// But call .execute() at the terminal state:
|
|
201
|
+
const data = await query.execute();
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
Queries are builders — you can compose them conditionally, but `.execute()` must be called when ready to run the query.
|
|
205
|
+
|
|
206
|
+
Source: maintainer interview
|
|
207
|
+
|
|
208
|
+
### CRITICAL Not awaiting .execute()
|
|
209
|
+
|
|
210
|
+
Wrong:
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
const users = db.selectFrom(users).where(eq(users.id, 'u1')).execute(); // Promise, not data
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Correct:
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
const users = await db.selectFrom(users).where(eq(users.id, 'u1')).execute();
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
`.execute()` returns a Promise — without `await`, you get the Promise object, not the data.
|
|
223
|
+
|
|
224
|
+
Source: maintainer interview
|
|
225
|
+
|
|
226
|
+
### HIGH Mixing Kysely/Drizzle/Supabase API patterns
|
|
227
|
+
|
|
228
|
+
Wrong:
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
// Supabase-style (wrong)
|
|
232
|
+
const users = await db.from('users').select('id, name');
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
Correct:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
// Flint-style
|
|
239
|
+
const users = await db.selectFrom(users).columns(['id', 'name']).execute();
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
flint-orm has its own API — `selectFrom().columns([...]).single()` is specific to this library.
|
|
243
|
+
|
|
244
|
+
Source: maintainer interview
|
|
245
|
+
|
|
246
|
+
### HIGH Using WHERE conditions with columns from wrong table
|
|
247
|
+
|
|
248
|
+
Wrong:
|
|
249
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// Using posts column in users query
|
|
252
|
+
await db.selectFrom(users).where(eq(posts.userId, 'u1')).execute(); // ERROR
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Correct:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
await db.selectFrom(users).where(eq(users.id, 'u1')).execute();
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The builder validates column ownership — columns in WHERE must belong to the queried table(s).
|
|
262
|
+
|
|
263
|
+
Source: src/query/builder.ts
|
|
264
|
+
|
|
265
|
+
## References
|
|
266
|
+
|
|
267
|
+
- [Full condition operators reference](references/condition-operators.md)
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
# Condition Operators Reference
|
|
2
|
+
|
|
3
|
+
Detailed reference for all condition operators in flint-orm.
|
|
4
|
+
|
|
5
|
+
All conditions are imported from `flint-orm/expressions`.
|
|
6
|
+
|
|
7
|
+
## Comparison Operators
|
|
8
|
+
|
|
9
|
+
### eq(column, value)
|
|
10
|
+
|
|
11
|
+
Equal comparison.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { eq } from 'flint-orm/expressions';
|
|
15
|
+
|
|
16
|
+
// Value comparison
|
|
17
|
+
eq(users.name, 'Alice') // name = 'Alice'
|
|
18
|
+
|
|
19
|
+
// Column-to-column comparison
|
|
20
|
+
eq(posts.userId, users.id) // posts.userId = users.id
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### neq(column, value)
|
|
24
|
+
|
|
25
|
+
Not equal comparison.
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { neq } from 'flint-orm/expressions';
|
|
29
|
+
|
|
30
|
+
neq(users.id, 'excluded') // id != 'excluded'
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### gt(column, value)
|
|
34
|
+
|
|
35
|
+
Greater than.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { gt } from 'flint-orm/expressions';
|
|
39
|
+
|
|
40
|
+
gt(users.age, 18) // age > 18
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### gte(column, value)
|
|
44
|
+
|
|
45
|
+
Greater than or equal.
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import { gte } from 'flint-orm/expressions';
|
|
49
|
+
|
|
50
|
+
gte(users.age, 18) // age >= 18
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### lt(column, value)
|
|
54
|
+
|
|
55
|
+
Less than.
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { lt } from 'flint-orm/expressions';
|
|
59
|
+
|
|
60
|
+
lt(users.age, 65) // age < 65
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### lte(column, value)
|
|
64
|
+
|
|
65
|
+
Less than or equal.
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { lte } from 'flint-orm/expressions';
|
|
69
|
+
|
|
70
|
+
lte(users.age, 65) // age <= 65
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Range Operator
|
|
74
|
+
|
|
75
|
+
### between(column, low, high)
|
|
76
|
+
|
|
77
|
+
Between two values (inclusive).
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
import { between } from 'flint-orm/expressions';
|
|
81
|
+
|
|
82
|
+
between(users.age, 18, 65) // age BETWEEN 18 AND 65
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Null Check Operators
|
|
86
|
+
|
|
87
|
+
### isNull(column)
|
|
88
|
+
|
|
89
|
+
Check for NULL.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { isNull } from 'flint-orm/expressions';
|
|
93
|
+
|
|
94
|
+
isNull(users.deletedAt) // deletedAt IS NULL
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### isNotNull(column)
|
|
98
|
+
|
|
99
|
+
Check for NOT NULL.
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
import { isNotNull } from 'flint-orm/expressions';
|
|
103
|
+
|
|
104
|
+
isNotNull(users.email) // email IS NOT NULL
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Array Operators
|
|
108
|
+
|
|
109
|
+
### isIn(column, values)
|
|
110
|
+
|
|
111
|
+
Check if value is in array.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { isIn } from 'flint-orm/expressions';
|
|
115
|
+
|
|
116
|
+
isIn(users.id, ['u1', 'u2', 'u3']) // id IN ('u1', 'u2', 'u3')
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### isNotIn(column, values)
|
|
120
|
+
|
|
121
|
+
Check if value is not in array.
|
|
122
|
+
|
|
123
|
+
```ts
|
|
124
|
+
import { isNotIn } from 'flint-orm/expressions';
|
|
125
|
+
|
|
126
|
+
isNotIn(users.id, ['excluded']) // id NOT IN ('excluded')
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Pattern Matching Operators
|
|
130
|
+
|
|
131
|
+
### like(column, pattern)
|
|
132
|
+
|
|
133
|
+
SQL LIKE pattern matching (% = any characters, _ = single char, case-insensitive).
|
|
134
|
+
|
|
135
|
+
```ts
|
|
136
|
+
import { like } from 'flint-orm/expressions';
|
|
137
|
+
|
|
138
|
+
like(users.name, 'A%') // name LIKE 'A%'
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### glob(column, pattern)
|
|
142
|
+
|
|
143
|
+
SQL GLOB pattern matching (* = any characters, ? = single char, case-sensitive).
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
import { glob } from 'flint-orm/expressions';
|
|
147
|
+
|
|
148
|
+
glob(users.name, 'A*') // name GLOB 'A*'
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Logical Operators
|
|
152
|
+
|
|
153
|
+
### and(...conditions)
|
|
154
|
+
|
|
155
|
+
Logical AND.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { and, eq, gt } from 'flint-orm/expressions';
|
|
159
|
+
|
|
160
|
+
and(eq(users.active, true), gt(users.age, 18))
|
|
161
|
+
// active = 1 AND age > 18
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
### or(...conditions)
|
|
165
|
+
|
|
166
|
+
Logical OR (wraps in parentheses).
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
import { or, eq } from 'flint-orm/expressions';
|
|
170
|
+
|
|
171
|
+
or(eq(users.role, 'admin'), eq(users.role, 'moderator'))
|
|
172
|
+
// (role = 'admin' OR role = 'moderator')
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Composing Conditions
|
|
176
|
+
|
|
177
|
+
Conditions can be nested and combined:
|
|
178
|
+
|
|
179
|
+
```ts
|
|
180
|
+
import { and, or, eq, gt, lte, isIn } from 'flint-orm/expressions';
|
|
181
|
+
|
|
182
|
+
// Complex condition
|
|
183
|
+
and(
|
|
184
|
+
or(eq(users.role, 'admin'), eq(users.role, 'moderator')),
|
|
185
|
+
gt(users.age, 18),
|
|
186
|
+
isIn(users.status, ['active', 'pending'])
|
|
187
|
+
)
|
|
188
|
+
// ((role = 'admin' OR role = 'moderator') AND age > 18 AND status IN ('active', 'pending'))
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
## Column-to-Column Comparisons
|
|
192
|
+
|
|
193
|
+
`eq` supports comparing two columns:
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { eq } from 'flint-orm/expressions';
|
|
197
|
+
|
|
198
|
+
// Compare columns from different tables (useful for joins)
|
|
199
|
+
eq(posts.userId, users.id)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
## Operator Summary
|
|
203
|
+
|
|
204
|
+
| Operator | SQL | Notes |
|
|
205
|
+
|----------|-----|-------|
|
|
206
|
+
| `eq` | `=` | Supports column-to-column |
|
|
207
|
+
| `neq` | `!=` | |
|
|
208
|
+
| `gt` | `>` | |
|
|
209
|
+
| `gte` | `>=` | |
|
|
210
|
+
| `lt` | `<` | |
|
|
211
|
+
| `lte` | `<=` | |
|
|
212
|
+
| `between` | `BETWEEN` | Inclusive range |
|
|
213
|
+
| `isNull` | `IS NULL` | |
|
|
214
|
+
| `isNotNull` | `IS NOT NULL` | |
|
|
215
|
+
| `isIn` | `IN` | Array of values |
|
|
216
|
+
| `isNotIn` | `NOT IN` | Array of values |
|
|
217
|
+
| `like` | `LIKE` | % and _ wildcards, case-insensitive |
|
|
218
|
+
| `glob` | `GLOB` | * and ? wildcards, case-sensitive |
|
|
219
|
+
| `and` | `AND` | Composable |
|
|
220
|
+
| `or` | `OR` | Wraps in parentheses |
|