flint-orm 0.1.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/API.md +620 -0
- package/README.md +0 -0
- package/dist/entries/expressions.d.ts +5 -0
- package/dist/entries/expressions.js +1184 -0
- package/dist/entries/table.d.ts +4 -0
- package/dist/entries/table.js +302 -0
- package/dist/errors.d.ts +13 -0
- package/dist/flint.d.ts +128 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +1485 -0
- package/dist/query/aggregates.d.ts +47 -0
- package/dist/query/builder.d.ts +256 -0
- package/dist/query/conditions.d.ts +176 -0
- package/dist/schema/columns.d.ts +137 -0
- package/dist/schema/table.d.ts +44 -0
- package/dist/src/entries/expressions.d.ts +5 -0
- package/dist/src/entries/table.d.ts +4 -0
- package/dist/src/errors.d.ts +13 -0
- package/dist/src/flint.d.ts +128 -0
- package/dist/src/index.d.ts +9 -0
- package/dist/src/query/aggregates.d.ts +47 -0
- package/dist/src/query/builder.d.ts +256 -0
- package/dist/src/query/conditions.d.ts +176 -0
- package/dist/src/schema/columns.d.ts +137 -0
- package/dist/src/schema/table.d.ts +44 -0
- package/package.json +40 -0
package/API.md
ADDED
|
@@ -0,0 +1,620 @@
|
|
|
1
|
+
# Flint ORM — API Reference
|
|
2
|
+
|
|
3
|
+
A minimal, SQLite/libSQL-only query builder. Type-safe, immutable, parameterized.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
bun add flint-orm
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Quick Start
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { flint, table, text, integer, eq } from 'flint-orm';
|
|
15
|
+
|
|
16
|
+
// Define schema
|
|
17
|
+
const users = table('users', {
|
|
18
|
+
id: text('id').primaryKey(),
|
|
19
|
+
name: text('name').notNull(),
|
|
20
|
+
email: text('email').unique(),
|
|
21
|
+
age: integer('age'),
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// Connect
|
|
25
|
+
const db = flint({ url: 'app.db' });
|
|
26
|
+
|
|
27
|
+
// Query
|
|
28
|
+
const user = db.select().from(users).where(eq(users.id, 'u1')).single().execute();
|
|
29
|
+
// { id: "u1", name: "Alice", email: "alice@example.com", age: 30 }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Schema
|
|
35
|
+
|
|
36
|
+
### `table(name, columns)`
|
|
37
|
+
|
|
38
|
+
Define a table. Columns live as direct properties. SQL metadata is under `._`.
|
|
39
|
+
|
|
40
|
+
```ts
|
|
41
|
+
import { table, text, integer, boolean } from 'flint-orm';
|
|
42
|
+
|
|
43
|
+
const users = table('users', {
|
|
44
|
+
id: text('id').primaryKey(),
|
|
45
|
+
name: text('name').notNull(),
|
|
46
|
+
email: text('email').unique(),
|
|
47
|
+
active: boolean('active').default(true),
|
|
48
|
+
age: integer('age'),
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### `snakeCase.table(name, columns)`
|
|
53
|
+
|
|
54
|
+
Auto-converts camelCase keys to snake_case SQL names.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
import { snakeCase, text } from 'flint-orm';
|
|
58
|
+
|
|
59
|
+
const users = snakeCase.table('users', {
|
|
60
|
+
id: text().primaryKey(), // SQL: id
|
|
61
|
+
firstName: text().notNull(), // SQL: first_name
|
|
62
|
+
createdAt: text(), // SQL: created_at
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Column Types
|
|
67
|
+
|
|
68
|
+
| Function | TS Type | SQLite Storage | Notes |
|
|
69
|
+
| ----------- | --------- | ------------------ | --------------------------------------- |
|
|
70
|
+
| `text()` | `string` | TEXT | |
|
|
71
|
+
| `integer()` | `number` | INTEGER | Supports `.autoIncrement()` |
|
|
72
|
+
| `boolean()` | `boolean` | INTEGER (0/1) | Encodes/decodes automatically |
|
|
73
|
+
| `json<T>()` | `T` | TEXT (JSON) | Generic, encodes/decodes automatically |
|
|
74
|
+
| `real()` | `number` | REAL | |
|
|
75
|
+
| `date()` | `Date` | INTEGER (epoch ms) | Supports `.defaultNow()`, `.onUpdate()` |
|
|
76
|
+
|
|
77
|
+
### Column Modifiers
|
|
78
|
+
|
|
79
|
+
Every column supports chaining:
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
text('name')
|
|
83
|
+
.primaryKey() // PRIMARY KEY
|
|
84
|
+
.notNull() // NOT NULL
|
|
85
|
+
.unique() // UNIQUE
|
|
86
|
+
.default('hello') // DEFAULT 'hello'
|
|
87
|
+
.defaultFn(() => new Date()) // DEFAULT (computed at insert)
|
|
88
|
+
.references(otherColumn); // REFERENCES
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**Integer-only:**
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
integer('id').autoIncrement(); // AUTOINCREMENT
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Date-only:**
|
|
98
|
+
|
|
99
|
+
```ts
|
|
100
|
+
date('created_at')
|
|
101
|
+
.defaultNow() // DEFAULT (current epoch ms)
|
|
102
|
+
.onUpdate(); // Always set to now on UPDATE
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### `InferRow<T>`
|
|
106
|
+
|
|
107
|
+
Derives the row type from a table definition.
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
type UserRow = InferRow<typeof users>;
|
|
111
|
+
// { id: string; name: string; email: string | null; active: boolean; age: number | null }
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### `InsertRow<T>`
|
|
115
|
+
|
|
116
|
+
Row type for INSERT. Columns with defaults or autoIncrement are optional.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
type UserInsert = InsertRow<typeof users>;
|
|
120
|
+
// { id: string; name: string; email?: string; active?: boolean; age?: number }
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## Query Builder
|
|
126
|
+
|
|
127
|
+
### `db.select().from(table)`
|
|
128
|
+
|
|
129
|
+
Start a SELECT query. Two-phase: `.from()` is required before anything else.
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
db.select().from(users).execute();
|
|
133
|
+
// SELECT * FROM users
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### `.columns(keys)`
|
|
137
|
+
|
|
138
|
+
Narrow which columns appear in the result.
|
|
139
|
+
|
|
140
|
+
```ts
|
|
141
|
+
db.select().from(users).columns(['id', 'name']).execute();
|
|
142
|
+
// SELECT id, name FROM users
|
|
143
|
+
// Returns: { id: string; name: string }[]
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### `.where(condition)`
|
|
147
|
+
|
|
148
|
+
Filter rows.
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
db.select().from(users).where(eq(users.active, true)).execute();
|
|
152
|
+
// SELECT * FROM users WHERE active = 1
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### `.single()`
|
|
156
|
+
|
|
157
|
+
Return one row or null instead of an array. Adds `LIMIT 1`.
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
db.select().from(users).where(eq(users.id, 'u1')).single().execute();
|
|
161
|
+
// SELECT * FROM users WHERE id = ? LIMIT 1
|
|
162
|
+
// Returns: UserRow | null
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### `.orderBy(key, direction?)`
|
|
166
|
+
|
|
167
|
+
Sort results. Default direction is `"asc"`.
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
db.select().from(users).orderBy('name', 'desc').execute();
|
|
171
|
+
// SELECT * FROM users ORDER BY name DESC
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### `.limit(n)`
|
|
175
|
+
|
|
176
|
+
Limit the number of results.
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
db.select().from(users).limit(10).execute();
|
|
180
|
+
// SELECT * FROM users LIMIT 10
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### `.offset(n)`
|
|
184
|
+
|
|
185
|
+
Skip N rows.
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
db.select().from(users).limit(10).offset(20).execute();
|
|
189
|
+
// SELECT * FROM users LIMIT 10 OFFSET 20
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### `.distinct()`
|
|
193
|
+
|
|
194
|
+
Return unique rows.
|
|
195
|
+
|
|
196
|
+
```ts
|
|
197
|
+
db.select().from(users).columns(['name']).distinct().execute();
|
|
198
|
+
// SELECT DISTINCT name FROM users
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### Full Example
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
const results = db.select().from(users).columns(['id', 'name']).where(eq(users.active, true)).orderBy('name', 'asc').limit(10).offset(0).execute();
|
|
205
|
+
// Returns: { id: string; name: string }[]
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
---
|
|
209
|
+
|
|
210
|
+
### `db.insert(table).values(row)`
|
|
211
|
+
|
|
212
|
+
Insert one or more rows. Two-phase: `.values()` is required before `.execute()`.
|
|
213
|
+
|
|
214
|
+
```ts
|
|
215
|
+
// Single row
|
|
216
|
+
db.insert(users).values({ id: 'u1', name: 'Alice', email: 'alice@example.com' }).execute();
|
|
217
|
+
// INSERT INTO users (id, name, email) VALUES (?, ?, ?)
|
|
218
|
+
|
|
219
|
+
// Multiple rows (bulk insert)
|
|
220
|
+
db.insert(users)
|
|
221
|
+
.values([
|
|
222
|
+
{ id: 'u1', name: 'Alice' },
|
|
223
|
+
{ id: 'u2', name: 'Bob' },
|
|
224
|
+
])
|
|
225
|
+
.execute();
|
|
226
|
+
// INSERT INTO users (id, name) VALUES (?, ?), (?, ?)
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
Columns with defaults can be omitted:
|
|
230
|
+
|
|
231
|
+
```ts
|
|
232
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }).execute();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### `.returning()`
|
|
236
|
+
|
|
237
|
+
Return the inserted row(s) instead of void. Pass an array to narrow which columns are returned.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
// Return all columns
|
|
241
|
+
const user = db.insert(users).values({ id: 'u1', name: 'Alice' }).returning().execute();
|
|
242
|
+
// Returns: { id: string; name: string; ... }[]
|
|
243
|
+
|
|
244
|
+
// Return specific columns
|
|
245
|
+
const user = db.insert(users).values({ id: 'u1', name: 'Alice' }).returning(['id', 'name']).execute();
|
|
246
|
+
// Returns: { id: string; name: string }[]
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
### `.onConflictDoNothing()`
|
|
250
|
+
|
|
251
|
+
Skip the insert if a row with the same primary key already exists.
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
db.insert(users).values({ id: 'u1', name: 'Alice' }).onConflictDoNothing().execute();
|
|
255
|
+
// INSERT OR IGNORE INTO users ...
|
|
256
|
+
```
|
|
257
|
+
|
|
258
|
+
### `.onConflictDoUpdate()`
|
|
259
|
+
|
|
260
|
+
Update specific columns when a row with the same primary key already exists (upsert).
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
db.insert(users)
|
|
264
|
+
.values({ id: 'u1', name: 'Alice' })
|
|
265
|
+
.onConflictDoUpdate({
|
|
266
|
+
target: users.id,
|
|
267
|
+
set: { name: 'Alice Updated' },
|
|
268
|
+
})
|
|
269
|
+
.execute();
|
|
270
|
+
// INSERT INTO users ... ON CONFLICT (id) DO UPDATE SET name = excluded.name
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
---
|
|
274
|
+
|
|
275
|
+
### `db.update(table).set(partial).where(condition)`
|
|
276
|
+
|
|
277
|
+
Update rows. Two-phase: `.set()` is required before `.execute()`.
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
db.update(users).set({ name: 'Bob' }).where(eq(users.id, 'u1')).execute();
|
|
281
|
+
// UPDATE users SET name = ? WHERE id = ?
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
Multiple `.set()` calls merge:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
db.update(users).set({ name: 'Bob' }).set({ email: 'bob@example.com' }).where(eq(users.id, 'u1')).execute();
|
|
288
|
+
// UPDATE users SET name = ?, email = ? WHERE id = ?
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### `.returning()`
|
|
292
|
+
|
|
293
|
+
Return the updated row(s) instead of void. Pass an array to narrow which columns are returned.
|
|
294
|
+
|
|
295
|
+
```ts
|
|
296
|
+
// Return all columns
|
|
297
|
+
const updated = db.update(users).set({ name: 'Bob' }).where(eq(users.id, 'u1')).returning().execute();
|
|
298
|
+
// Returns: { id: string; name: string; ... }[]
|
|
299
|
+
|
|
300
|
+
// Return specific columns
|
|
301
|
+
const updated = db.update(users).set({ name: 'Bob' }).where(eq(users.id, 'u1')).returning(['id', 'name']).execute();
|
|
302
|
+
// Returns: { id: string; name: string }[]
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
---
|
|
306
|
+
|
|
307
|
+
### `db.delete(table).where(condition)`
|
|
308
|
+
|
|
309
|
+
Delete rows.
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
db.delete(users).where(eq(users.id, 'u1')).execute();
|
|
313
|
+
// DELETE FROM users WHERE id = ?
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### `.returning()`
|
|
317
|
+
|
|
318
|
+
Return the deleted row(s) instead of void. Pass an array to narrow which columns are returned.
|
|
319
|
+
|
|
320
|
+
```ts
|
|
321
|
+
// Return all columns
|
|
322
|
+
const deleted = db.delete(users).where(eq(users.id, 'u1')).returning().execute();
|
|
323
|
+
// Returns: { id: string; name: string; ... }[]
|
|
324
|
+
|
|
325
|
+
// Return specific columns
|
|
326
|
+
const deleted = db.delete(users).where(eq(users.id, 'u1')).returning(['id', 'name']).execute();
|
|
327
|
+
// Returns: { id: string; name: string }[]
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
---
|
|
331
|
+
|
|
332
|
+
## Joins
|
|
333
|
+
|
|
334
|
+
### `db.leftJoin(parent).on(child, condition?)`
|
|
335
|
+
|
|
336
|
+
LEFT JOIN. Returns all parent rows, with matching child data or null.
|
|
337
|
+
|
|
338
|
+
```ts
|
|
339
|
+
const orders = table('orders', {
|
|
340
|
+
id: text('id').primaryKey(),
|
|
341
|
+
userId: text('userId').notNull().references(users.id),
|
|
342
|
+
total: integer('total').notNull(),
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
// With explicit condition
|
|
346
|
+
db.leftJoin(orders).on(users, eq(orders.userId, users.id)).execute();
|
|
347
|
+
|
|
348
|
+
// Auto-join from foreign key (if .references() is defined)
|
|
349
|
+
db.leftJoin(orders).on(users).execute();
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
### `db.innerJoin(parent).on(child, condition?)`
|
|
353
|
+
|
|
354
|
+
INNER JOIN. Returns only rows where both tables match.
|
|
355
|
+
|
|
356
|
+
```ts
|
|
357
|
+
db.innerJoin(orders).on(users, eq(orders.userId, users.id)).execute();
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
### Join Result Shape
|
|
361
|
+
|
|
362
|
+
Joins return **nested** results, not flat-merged:
|
|
363
|
+
|
|
364
|
+
```ts
|
|
365
|
+
// One-to-many: one user with multiple orders
|
|
366
|
+
[
|
|
367
|
+
{
|
|
368
|
+
id: 'u1',
|
|
369
|
+
name: 'Alice',
|
|
370
|
+
orders: [
|
|
371
|
+
{ id: 'o1', userId: 'u1', total: 100 },
|
|
372
|
+
{ id: 'o2', userId: 'u1', total: 200 },
|
|
373
|
+
],
|
|
374
|
+
},
|
|
375
|
+
];
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
### Join + Columns
|
|
379
|
+
|
|
380
|
+
Narrow parent columns with `.columns()`:
|
|
381
|
+
|
|
382
|
+
```ts
|
|
383
|
+
db.leftJoin(orders).on(users, eq(orders.userId, users.id)).columns(['id', 'name']).execute();
|
|
384
|
+
// Returns: { id: string; name: string; orders: OrderRow[] }[]
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### `.single()` on Joins
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
db.leftJoin(orders).on(users, eq(orders.userId, users.id)).single().execute();
|
|
391
|
+
// Returns: { id: string; name: string; orders: OrderRow[] } | null
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
---
|
|
395
|
+
|
|
396
|
+
## Conditions
|
|
397
|
+
|
|
398
|
+
All conditions are imported from `flint-orm`.
|
|
399
|
+
|
|
400
|
+
### Comparison
|
|
401
|
+
|
|
402
|
+
```ts
|
|
403
|
+
eq(column, value); // column = value
|
|
404
|
+
eq(left, right); // left = right (column-to-column)
|
|
405
|
+
neq(column, value); // column != value
|
|
406
|
+
gt(column, value); // column > value
|
|
407
|
+
gte(column, value); // column >= value
|
|
408
|
+
lt(column, value); // column < value
|
|
409
|
+
lte(column, value); // column <= value
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
### Range
|
|
413
|
+
|
|
414
|
+
```ts
|
|
415
|
+
between(column, low, high); // column BETWEEN low AND high
|
|
416
|
+
```
|
|
417
|
+
|
|
418
|
+
### Null Checks
|
|
419
|
+
|
|
420
|
+
```ts
|
|
421
|
+
isNull(column); // column IS NULL
|
|
422
|
+
isNotNull(column); // column IS NOT NULL
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
### Array
|
|
426
|
+
|
|
427
|
+
```ts
|
|
428
|
+
isIn(column, values); // column IN (?, ?, ...)
|
|
429
|
+
isNotIn(column, values); // column NOT IN (?, ?, ...)
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
### Pattern Matching
|
|
433
|
+
|
|
434
|
+
```ts
|
|
435
|
+
like(column, pattern); // column LIKE ? (% and _ wildcards, case-insensitive)
|
|
436
|
+
glob(column, pattern); // column GLOB ? (* and ? wildcards, case-sensitive)
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Logical
|
|
440
|
+
|
|
441
|
+
```ts
|
|
442
|
+
and(...conditions); // cond1 AND cond2 AND ...
|
|
443
|
+
or(...conditions); // (cond1 OR cond2 OR ...)
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### Examples
|
|
447
|
+
|
|
448
|
+
```ts
|
|
449
|
+
import { eq, and, or, gt, isIn, like, between } from "flint-orm";
|
|
450
|
+
|
|
451
|
+
// Simple equality
|
|
452
|
+
.where(eq(users.name, "Alice"))
|
|
453
|
+
|
|
454
|
+
// Column-to-column
|
|
455
|
+
.where(eq(orders.userId, users.id))
|
|
456
|
+
|
|
457
|
+
// Multiple conditions
|
|
458
|
+
.where(and(eq(users.active, true), gt(users.age, 18)))
|
|
459
|
+
|
|
460
|
+
// OR
|
|
461
|
+
.where(or(eq(users.name, "Alice"), eq(users.name, "Bob")))
|
|
462
|
+
|
|
463
|
+
// IN
|
|
464
|
+
.where(isIn(users.status, ["active", "pending"]))
|
|
465
|
+
|
|
466
|
+
// LIKE
|
|
467
|
+
.where(like(users.email, "%@example.com"))
|
|
468
|
+
|
|
469
|
+
// BETWEEN
|
|
470
|
+
.where(between(users.age, 18, 65))
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
---
|
|
474
|
+
|
|
475
|
+
## Aggregates
|
|
476
|
+
|
|
477
|
+
Aggregate functions are methods on the `db` object. They execute immediately and return a value.
|
|
478
|
+
|
|
479
|
+
### `db.count(table, condition?)`
|
|
480
|
+
|
|
481
|
+
Count all rows.
|
|
482
|
+
|
|
483
|
+
```ts
|
|
484
|
+
db.count(users); // 150
|
|
485
|
+
db.count(users, eq(users.active, true)); // 120
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### `db.countColumn(table, column, condition?)`
|
|
489
|
+
|
|
490
|
+
Count non-null values in a column.
|
|
491
|
+
|
|
492
|
+
```ts
|
|
493
|
+
db.countColumn(users, users.email); // 145 (5 users have no email)
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
### `db.sum(table, column, condition?)`
|
|
497
|
+
|
|
498
|
+
Sum of values. Returns `null` if no rows match.
|
|
499
|
+
|
|
500
|
+
```ts
|
|
501
|
+
db.sum(orders, orders.total); // 45000
|
|
502
|
+
db.sum(orders, orders.total, eq(orders.userId, 'u1')); // 1500
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### `db.avg(table, column, condition?)`
|
|
506
|
+
|
|
507
|
+
Average of values. Returns `null` if no rows match.
|
|
508
|
+
|
|
509
|
+
```ts
|
|
510
|
+
db.avg(orders, orders.total); // 300
|
|
511
|
+
db.avg(orders, orders.total, eq(orders.userId, 'u1')); // 500
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
### `db.min(table, column, condition?)`
|
|
515
|
+
|
|
516
|
+
Minimum value. Returns `null` if no rows match.
|
|
517
|
+
|
|
518
|
+
```ts
|
|
519
|
+
db.min(orders, orders.total); // 10
|
|
520
|
+
db.min(orders, orders.total, eq(orders.userId, 'u1')); // 50
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### `db.max(table, column, condition?)`
|
|
524
|
+
|
|
525
|
+
Maximum value. Returns `null` if no rows match.
|
|
526
|
+
|
|
527
|
+
```ts
|
|
528
|
+
db.max(orders, orders.total); // 1000
|
|
529
|
+
db.max(orders, orders.total, eq(orders.userId, 'u1')); // 800
|
|
530
|
+
```
|
|
531
|
+
|
|
532
|
+
### Multiple Aggregates
|
|
533
|
+
|
|
534
|
+
Use `Promise.all` when you need multiple aggregates:
|
|
535
|
+
|
|
536
|
+
```ts
|
|
537
|
+
const [total, revenue, avgOrder] = await Promise.all([db.count(orders), db.sum(orders, orders.total), db.avg(orders, orders.total)]);
|
|
538
|
+
```
|
|
539
|
+
|
|
540
|
+
---
|
|
541
|
+
|
|
542
|
+
## Batch
|
|
543
|
+
|
|
544
|
+
Run multiple queries atomically in a single transaction.
|
|
545
|
+
|
|
546
|
+
```ts
|
|
547
|
+
import { flint, table, text, eq } from 'flint-orm';
|
|
548
|
+
|
|
549
|
+
const db = flint({ url: 'app.db' });
|
|
550
|
+
|
|
551
|
+
db.batch([db.insert(orders).values({ id: 'o1', userId: 'u1', total: 100 }), db.update(users).set({ totalOrders: 1 }).where(eq(users.id, 'u1'))]);
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
All queries succeed or all roll back.
|
|
555
|
+
|
|
556
|
+
---
|
|
557
|
+
|
|
558
|
+
## Raw SQL
|
|
559
|
+
|
|
560
|
+
Access the underlying `bun:sqlite` client directly for raw queries.
|
|
561
|
+
|
|
562
|
+
```ts
|
|
563
|
+
// Simple query
|
|
564
|
+
const users = db.$client.prepare('SELECT * FROM users WHERE id = ?').all('u1');
|
|
565
|
+
|
|
566
|
+
// With type annotation
|
|
567
|
+
const rows = db.$client.prepare('SELECT count(*) as cnt FROM users').all() as { cnt: number }[];
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
---
|
|
571
|
+
|
|
572
|
+
## Tagged Template SQL
|
|
573
|
+
|
|
574
|
+
Build parameterized SQL expressions with automatic placeholder handling.
|
|
575
|
+
|
|
576
|
+
```ts
|
|
577
|
+
import { sql } from 'flint-orm';
|
|
578
|
+
|
|
579
|
+
const expr = sql`SELECT * FROM users WHERE name = ${'Alice'} AND age > ${18}`;
|
|
580
|
+
// { sql: "SELECT * FROM users WHERE name = ? AND age > ?", params: ["Alice", 18] }
|
|
581
|
+
|
|
582
|
+
// Execute with db.$client
|
|
583
|
+
const rows = db.$client.prepare(expr.sql).all(...expr.params);
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
---
|
|
587
|
+
|
|
588
|
+
## Escape Hatch
|
|
589
|
+
|
|
590
|
+
Access the underlying `bun:sqlite` client directly.
|
|
591
|
+
|
|
592
|
+
```ts
|
|
593
|
+
db.$client; // Database instance
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
---
|
|
597
|
+
|
|
598
|
+
## Types
|
|
599
|
+
|
|
600
|
+
| Type | Description |
|
|
601
|
+
| ------------------- | ------------------------------------------------------------- |
|
|
602
|
+
| `TableDef<T>` | Table definition with hidden `._` metadata |
|
|
603
|
+
| `ColumnDef<T, S>` | Column definition with phantom type `T` and storage class `S` |
|
|
604
|
+
| `InferRow<T>` | Derives row type from table definition |
|
|
605
|
+
| `InsertRow<T>` | Derives insert type (defaults are optional) |
|
|
606
|
+
| `Condition` | Condition node for WHERE clauses |
|
|
607
|
+
| `Executable` | Anything with a `.toSQL()` method (for `batch()`) |
|
|
608
|
+
| `ConnectionDetails` | `{ url: string }` |
|
|
609
|
+
| `SQLExpression` | `{ sql: string; params: unknown[] }` |
|
|
610
|
+
|
|
611
|
+
---
|
|
612
|
+
|
|
613
|
+
## Error Classes
|
|
614
|
+
|
|
615
|
+
Prefixed with `Flint` to avoid collisions in consumer codebases.
|
|
616
|
+
|
|
617
|
+
| Class | When |
|
|
618
|
+
| ---------------------- | ----------------------------------------------------------------- |
|
|
619
|
+
| `FlintValidationError` | Invalid query construction (e.g., no primary key for `.single()`) |
|
|
620
|
+
| `FlintQueryError` | Runtime SQL execution failure |
|
package/README.md
ADDED
|
File without changes
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { eq, and, or, gt, gte, lt, lte, neq, isIn, isNotIn, isNull, isNotNull, like, glob, between } from '../query/conditions';
|
|
2
|
+
export type { Condition } from '../query/conditions';
|
|
3
|
+
export { sql } from '../flint';
|
|
4
|
+
export type { SQLExpression } from '../flint';
|
|
5
|
+
export { count, countColumn, sum, avg, min, max } from '../query/aggregates';
|