rado 1.0.9 → 1.0.11
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 +261 -123
- package/dist/core/Dialect.d.ts +3 -1
- package/dist/core/Dialect.js +5 -3
- package/dist/core/Driver.d.ts +1 -0
- package/dist/core/Emitter.d.ts +5 -4
- package/dist/core/Emitter.js +15 -2
- package/dist/core/Query.js +7 -3
- package/dist/core/expr/Include.js +4 -2
- package/dist/core/query/Insert.d.ts +8 -4
- package/dist/core/query/Insert.js +15 -6
- package/dist/driver/better-sqlite3.js +3 -1
- package/dist/driver/bun-sqlite.js +1 -0
- package/dist/driver/d1.d.ts +26 -0
- package/dist/driver/d1.js +58 -0
- package/dist/driver/mysql2.d.ts +1 -0
- package/dist/driver/mysql2.js +1 -0
- package/dist/driver/pg.d.ts +1 -0
- package/dist/driver/pg.js +1 -0
- package/dist/driver/pglite.d.ts +2 -1
- package/dist/driver/pglite.js +2 -1
- package/dist/driver/sql.js.js +1 -0
- package/dist/driver.d.ts +7 -0
- package/dist/driver.js +23 -0
- package/dist/mysql/dialect.js +1 -0
- package/dist/postgres/dialect.js +1 -0
- package/dist/sqlite/dialect.js +1 -1
- package/dist/universal/columns.d.ts +1 -1
- package/dist/universal/columns.js +7 -1
- package/package.json +13 -10
package/README.md
CHANGED
|
@@ -5,191 +5,329 @@
|
|
|
5
5
|
|
|
6
6
|
Fully typed, lightweight TypeScript query builder.
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
8
|
+
## Features
|
|
9
|
+
|
|
10
|
+
- Fully typed queries using TypeScript
|
|
11
|
+
- Composable and reusable query structures
|
|
12
|
+
- First-class support for JSON columns
|
|
13
|
+
- No code generation step required
|
|
14
|
+
- Zero dependencies
|
|
15
|
+
- Universal query support for multiple database engines
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
13
18
|
|
|
14
19
|
<pre>npm install <a href="https://www.npmjs.com/package/rado">rado</a></pre>
|
|
15
20
|
|
|
16
|
-
##
|
|
21
|
+
## Quick Start
|
|
22
|
+
|
|
23
|
+
```typescript
|
|
24
|
+
import {table} from 'rado'
|
|
25
|
+
import {text, integer} from 'rado/postgres'
|
|
26
|
+
import {connect} from 'rado/driver/pg'
|
|
27
|
+
import {Pool} from 'pg'
|
|
17
28
|
|
|
18
|
-
|
|
29
|
+
// Define your schema
|
|
30
|
+
const User = table({
|
|
31
|
+
id: integer().primaryKey(),
|
|
32
|
+
name: text().notNull(),
|
|
33
|
+
email: text().unique()
|
|
34
|
+
})
|
|
19
35
|
|
|
20
|
-
|
|
36
|
+
// Connect to the database
|
|
37
|
+
const db = connect(new Pool({
|
|
38
|
+
host: 'localhost',
|
|
39
|
+
database: 'my_database'
|
|
40
|
+
}))
|
|
21
41
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
eq(User.id, Post.userId) // Compare to fields on other tables
|
|
42
|
+
// Perform a query
|
|
43
|
+
const users = await db.select().from(User)
|
|
44
|
+
console.log(users)
|
|
26
45
|
```
|
|
27
46
|
|
|
28
|
-
|
|
47
|
+
## Supported Databases
|
|
48
|
+
|
|
49
|
+
Currently supported drivers:
|
|
50
|
+
|
|
51
|
+
| `PostgreSQL ` | `import ` |
|
|
52
|
+
| -------------------------- | ------------------------------ |
|
|
53
|
+
| `pg` | `'rado/driver/pg'` |
|
|
54
|
+
| `@electric-sql/pglite` | `'rado/driver/pglite'` |
|
|
55
|
+
| `@neondatabase/serverless` | `'rado/driver/pg'` |
|
|
56
|
+
| `@vercel/postgres` | `'rado/driver/pg'` |
|
|
57
|
+
|
|
58
|
+
| `SQLite ` | `import ` |
|
|
59
|
+
| -------------------------- | ------------------------------ |
|
|
60
|
+
| `better-sqlite3` | `'rado/driver/better-sqlite3'` |
|
|
61
|
+
| `bun:sqlite` | `'rado/driver/bun-sqlite'` |
|
|
62
|
+
| `sql.js` | `'rado/driver/sql.js'` |
|
|
63
|
+
| `Cloudflare D1` | `'rado/driver/d1'` |
|
|
64
|
+
|
|
65
|
+
| `MySQL ` | `import ` |
|
|
66
|
+
| -------------------------- | ------------------------------ |
|
|
67
|
+
| `mysql2` | `'rado/driver/mysql2'` |
|
|
68
|
+
|
|
69
|
+
Pass an instance of the database to the `connect` function to get started:
|
|
29
70
|
|
|
30
71
|
```ts
|
|
31
|
-
import
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
db
|
|
72
|
+
import Database from 'better-sqlite3'
|
|
73
|
+
import {connect} from 'rado/driver/better-sqlite3'
|
|
74
|
+
|
|
75
|
+
const db = connect(new Database('foobar.db'))
|
|
35
76
|
```
|
|
36
77
|
|
|
37
|
-
|
|
78
|
+
## Querying
|
|
38
79
|
|
|
39
|
-
|
|
80
|
+
### Select
|
|
40
81
|
|
|
41
|
-
|
|
42
|
-
|
|
82
|
+
Select operations allow you to retrieve data from your database.
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
// Basic select
|
|
86
|
+
const allUsers = await db.select().from(User)
|
|
87
|
+
|
|
88
|
+
// Select specific columns
|
|
89
|
+
const userNames = await db.select({id: User.id, name: User.name}).from(User)
|
|
43
90
|
```
|
|
44
91
|
|
|
45
|
-
|
|
92
|
+
### Where conditions
|
|
46
93
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
94
|
+
Where conditions help you filter your query results.
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
import {eq, and, or, gt, isNull} from 'rado'
|
|
98
|
+
|
|
99
|
+
// Simple equality
|
|
100
|
+
const john = await db.select().from(User).where(eq(User.name, 'John'))
|
|
101
|
+
|
|
102
|
+
// Complex conditions
|
|
103
|
+
const users = await db.select().from(User)
|
|
104
|
+
.where(
|
|
105
|
+
gt(User.age, 18),
|
|
106
|
+
or(
|
|
107
|
+
eq(User.name, 'Alice'),
|
|
108
|
+
isNull(User.email)
|
|
109
|
+
)
|
|
110
|
+
)
|
|
55
111
|
```
|
|
56
112
|
|
|
57
|
-
|
|
113
|
+
### Joins
|
|
58
114
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
115
|
+
Joins allow you to combine data from multiple tables.
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const usersWithPosts = await db.select({
|
|
119
|
+
userName: User.name,
|
|
120
|
+
postTitle: Post.title,
|
|
121
|
+
})
|
|
122
|
+
.from(User)
|
|
123
|
+
.leftJoin(Post, eq(User.id, Post.authorId))
|
|
68
124
|
```
|
|
69
125
|
|
|
70
|
-
|
|
126
|
+
### Ordering and Grouping
|
|
71
127
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
128
|
+
You can order and group your query results:
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
import {desc, asc, count} from 'rado'
|
|
132
|
+
|
|
133
|
+
const orderedUsers = await db.select()
|
|
134
|
+
.from(User)
|
|
135
|
+
.orderBy(asc(User.name))
|
|
136
|
+
|
|
137
|
+
const userPostCounts = await db.select({
|
|
138
|
+
userName: User.name,
|
|
139
|
+
postCount: count(Post.id)
|
|
140
|
+
})
|
|
141
|
+
.from(User)
|
|
142
|
+
.leftJoin(Post, eq(User.id, Post.authorId))
|
|
143
|
+
.groupBy(User.name)
|
|
78
144
|
```
|
|
79
145
|
|
|
80
|
-
|
|
146
|
+
### Pagination
|
|
81
147
|
|
|
82
|
-
|
|
148
|
+
Pagination helps you manage large datasets by retrieving results in smaller chunks:
|
|
83
149
|
|
|
84
|
-
```
|
|
85
|
-
|
|
150
|
+
```typescript
|
|
151
|
+
const pageSize = 10
|
|
152
|
+
const page = 2
|
|
153
|
+
|
|
154
|
+
const paginatedUsers = await db.select()
|
|
155
|
+
.from(User)
|
|
156
|
+
.limit(pageSize)
|
|
157
|
+
.offset((page - 1) * pageSize)
|
|
86
158
|
```
|
|
87
159
|
|
|
88
|
-
|
|
160
|
+
### JSON columns
|
|
89
161
|
|
|
90
|
-
|
|
91
|
-
|
|
162
|
+
Rado provides first-class support for JSON columns:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import {pgTable, serial, text, jsonb} from 'rado/postgres'
|
|
166
|
+
|
|
167
|
+
const User = pgTable({
|
|
168
|
+
id: serial().primaryKey(),
|
|
169
|
+
name: text(),
|
|
170
|
+
metadata: jsonb<{subscribed: boolean}>()
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
const subscribedUsers = await db.select()
|
|
174
|
+
.from(User)
|
|
175
|
+
.where(eq(User.metadata.subscribed, true))
|
|
92
176
|
```
|
|
93
177
|
|
|
94
|
-
|
|
178
|
+
### Subqueries
|
|
95
179
|
|
|
96
|
-
|
|
180
|
+
```typescript
|
|
181
|
+
const subquery = db.select({authorId: Post.authorId})
|
|
182
|
+
.from(Post)
|
|
183
|
+
.groupBy(Post.authorId)
|
|
184
|
+
.having(gt(count(Post.id), 5))
|
|
185
|
+
.as('authorIds')
|
|
97
186
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.
|
|
101
|
-
.where(eq(User.id, 1))
|
|
187
|
+
const prolificAuthors = await db.select()
|
|
188
|
+
.from(User)
|
|
189
|
+
.where(inArray(User.id, subquery))
|
|
102
190
|
```
|
|
103
191
|
|
|
104
|
-
|
|
192
|
+
### Include
|
|
105
193
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
194
|
+
Aggregate rows using the `include` function:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import {include} from 'rado'
|
|
198
|
+
|
|
199
|
+
const usersWithPosts = await db.select({
|
|
200
|
+
...User,
|
|
201
|
+
posts: include(
|
|
202
|
+
db.select().from(Post).where(eq(Post.authorId, User.id))
|
|
203
|
+
)
|
|
204
|
+
}).from(User)
|
|
205
|
+
|
|
206
|
+
// Use include.one for a single related record
|
|
207
|
+
const usersWithLatestPost = await db.select({
|
|
208
|
+
...User,
|
|
209
|
+
latestPost: include.one(
|
|
210
|
+
db.select()
|
|
211
|
+
.from(Post)
|
|
212
|
+
.where(eq(Post.authorId, User.id))
|
|
213
|
+
.orderBy(desc(Post.createdAt))
|
|
214
|
+
.limit(1)
|
|
215
|
+
)
|
|
216
|
+
}).from(User)
|
|
111
217
|
```
|
|
112
218
|
|
|
113
|
-
|
|
219
|
+
### SQL Operator
|
|
114
220
|
|
|
115
|
-
|
|
221
|
+
The `sql` operator allows you to write raw SQL and interpolate values safely:
|
|
116
222
|
|
|
117
|
-
```
|
|
118
|
-
|
|
223
|
+
```typescript
|
|
224
|
+
import {sql} from 'rado'
|
|
225
|
+
|
|
226
|
+
const minAge = 18
|
|
227
|
+
const adultUsers = await db.select()
|
|
228
|
+
.from(User)
|
|
229
|
+
.where(sql`${User.age} >= ${minAge}`)
|
|
119
230
|
```
|
|
120
231
|
|
|
121
|
-
##
|
|
232
|
+
## Modifying Data
|
|
122
233
|
|
|
123
|
-
|
|
124
|
-
import * as sqlite from 'rado/sqlite'
|
|
125
|
-
import {sqliteTable as table} from 'rado/sqlite'
|
|
234
|
+
### Insert
|
|
126
235
|
|
|
127
|
-
|
|
128
|
-
id: sqlite.integer().primaryKey(),
|
|
129
|
-
userName: sqlite.text('user') // Column names are optional
|
|
130
|
-
})
|
|
236
|
+
Insert operations allow you to add new records to your database:
|
|
131
237
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
238
|
+
```typescript
|
|
239
|
+
// Single insert
|
|
240
|
+
const newUser = await db.insert(User)
|
|
241
|
+
.values({name: 'Alice', email: 'alice@example.com'})
|
|
242
|
+
.returning()
|
|
137
243
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
244
|
+
// Bulk insert
|
|
245
|
+
const newUsers = await db.insert(User)
|
|
246
|
+
.values([
|
|
247
|
+
{name: 'Bob', email: 'bob@example.com'},
|
|
248
|
+
{name: 'Charlie', email: 'charlie@example.com'},
|
|
249
|
+
])
|
|
250
|
+
.returning()
|
|
251
|
+
```
|
|
142
252
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
253
|
+
### Update
|
|
254
|
+
|
|
255
|
+
Update operations modify existing records:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const updatedCount = await db.update(User)
|
|
259
|
+
.set({name: 'Johnny'})
|
|
260
|
+
.where(eq(User.name, 'John'))
|
|
147
261
|
```
|
|
148
262
|
|
|
149
|
-
|
|
263
|
+
### Delete
|
|
150
264
|
|
|
151
|
-
|
|
265
|
+
Delete operations remove records from your database:
|
|
152
266
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
| `bun-sqlite` | `'rado/driver/bun-sqlite'` |
|
|
157
|
-
| `mysql2` | `'rado/driver/mysql2'` |
|
|
158
|
-
| `pg` | `'rado/driver/pg'` |
|
|
159
|
-
| `pglite` | `'rado/driver/pglite'` |
|
|
160
|
-
| `sql.js` | `'rado/driver/sql.js'` |
|
|
267
|
+
```typescript
|
|
268
|
+
await db.delete(User).where(eq(User.name, 'John'))
|
|
269
|
+
```
|
|
161
270
|
|
|
162
|
-
|
|
271
|
+
### Transactions
|
|
163
272
|
|
|
164
|
-
|
|
165
|
-
import Database from 'better-sqlite3'
|
|
166
|
-
import {connect} from 'rado/driver/better-sqlite3'
|
|
273
|
+
Transactions allow you to group multiple operations into a single atomic unit.
|
|
167
274
|
|
|
168
|
-
|
|
275
|
+
```typescript
|
|
276
|
+
const result = await db.transaction(async (tx) => {
|
|
277
|
+
const user = await tx.insert(User)
|
|
278
|
+
.values({ name: 'Alice', email: 'alice@example.com' })
|
|
279
|
+
.returning()
|
|
280
|
+
const post = await tx.insert(Post)
|
|
281
|
+
.values({ title: 'My First Post', authorId: user.id })
|
|
282
|
+
.returning()
|
|
283
|
+
return {user, post}
|
|
284
|
+
})
|
|
169
285
|
```
|
|
170
286
|
|
|
287
|
+
## Universal Queries
|
|
171
288
|
|
|
172
|
-
|
|
289
|
+
Rado provides a universal query builder that works across different database
|
|
290
|
+
engines, whether they run synchronously or asynchronously. This is useful for
|
|
291
|
+
writing database-agnostic code.
|
|
173
292
|
|
|
174
|
-
|
|
293
|
+
```typescript
|
|
294
|
+
import {table} from 'rado'
|
|
295
|
+
import {id, text} from 'rado/universal'
|
|
175
296
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
297
|
+
const User = table('user', {
|
|
298
|
+
id: id(),
|
|
299
|
+
name: text()
|
|
300
|
+
})
|
|
301
|
+
|
|
302
|
+
const db = process.env.SQLITE ? sqliteDb : postgresDb
|
|
303
|
+
|
|
304
|
+
const userNames = await db.select(User.name).from(User)
|
|
181
305
|
```
|
|
182
306
|
|
|
183
|
-
|
|
307
|
+
## Custom Column Types
|
|
184
308
|
|
|
185
|
-
|
|
186
|
-
|
|
309
|
+
You can define custom column types, such as a boolean column that is stored as a
|
|
310
|
+
tinyint in the database:
|
|
187
311
|
|
|
188
|
-
```
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
312
|
+
```typescript
|
|
313
|
+
import {Column, column, table, sql} from 'rado'
|
|
314
|
+
|
|
315
|
+
export function bool(name?: string): Column<boolean | null> {
|
|
316
|
+
return column({
|
|
317
|
+
name,
|
|
318
|
+
type: sql`tinyint(1)`,
|
|
319
|
+
mapFromDriverValue(value: number): boolean {
|
|
320
|
+
return value === 1
|
|
321
|
+
},
|
|
322
|
+
mapToDriverValue(value: boolean): number {
|
|
323
|
+
return value ? 1 : 0
|
|
324
|
+
}
|
|
325
|
+
})
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
// Usage
|
|
329
|
+
const User = table('user', {
|
|
330
|
+
// ...
|
|
331
|
+
isActive: bool()
|
|
194
332
|
})
|
|
195
|
-
```
|
|
333
|
+
```
|
package/dist/core/Dialect.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { Emitter } from './Emitter.js';
|
|
2
2
|
import { type HasQuery, type HasSql } from './Internal.js';
|
|
3
|
+
import type { Runtime } from './MetaData.js';
|
|
3
4
|
export declare class Dialect {
|
|
4
5
|
#private;
|
|
5
|
-
|
|
6
|
+
runtime: Runtime;
|
|
7
|
+
constructor(runtime: Runtime, createEmitter: new (runtime: Runtime) => Emitter);
|
|
6
8
|
emit: (input: HasSql | HasQuery) => Emitter;
|
|
7
9
|
inline: (input: HasSql | HasQuery) => string;
|
|
8
10
|
}
|
package/dist/core/Dialect.js
CHANGED
|
@@ -5,19 +5,21 @@ import {
|
|
|
5
5
|
hasQuery
|
|
6
6
|
} from "./Internal.js";
|
|
7
7
|
var Dialect = class {
|
|
8
|
+
runtime;
|
|
8
9
|
#createEmitter;
|
|
9
|
-
constructor(createEmitter) {
|
|
10
|
+
constructor(runtime, createEmitter) {
|
|
11
|
+
this.runtime = runtime;
|
|
10
12
|
this.#createEmitter = createEmitter;
|
|
11
13
|
}
|
|
12
14
|
emit = (input) => {
|
|
13
15
|
const sql = hasQuery(input) ? getQuery(input) : getSql(input);
|
|
14
|
-
const emitter = new this.#createEmitter();
|
|
16
|
+
const emitter = new this.#createEmitter(this.runtime);
|
|
15
17
|
sql.emitTo(emitter);
|
|
16
18
|
return emitter;
|
|
17
19
|
};
|
|
18
20
|
inline = (input) => {
|
|
19
21
|
const sql = hasQuery(input) ? getQuery(input) : getSql(input);
|
|
20
|
-
const emitter = new this.#createEmitter();
|
|
22
|
+
const emitter = new this.#createEmitter(this.runtime);
|
|
21
23
|
sql.inlineValues().emitTo(emitter);
|
|
22
24
|
return emitter.sql;
|
|
23
25
|
};
|
package/dist/core/Driver.d.ts
CHANGED
package/dist/core/Emitter.d.ts
CHANGED
|
@@ -13,17 +13,18 @@ import type { SelectData } from './query/Select.js';
|
|
|
13
13
|
import type { UnionData } from './query/Union.js';
|
|
14
14
|
import type { Update } from './query/Update.js';
|
|
15
15
|
export declare abstract class Emitter {
|
|
16
|
+
#private;
|
|
16
17
|
sql: string;
|
|
17
18
|
protected params: Array<Param>;
|
|
18
|
-
get hasParams(): boolean;
|
|
19
|
-
bind(inputs?: Record<string, unknown>): Array<unknown>;
|
|
20
|
-
processValue(value: unknown): unknown;
|
|
21
|
-
abstract runtime: Runtime;
|
|
22
19
|
abstract emitIdentifier(value: string): void;
|
|
23
20
|
abstract emitValue(value: unknown): void;
|
|
24
21
|
abstract emitInline(value: unknown): void;
|
|
25
22
|
abstract emitJsonPath(path: JsonPath): void;
|
|
26
23
|
abstract emitPlaceholder(value: string): void;
|
|
24
|
+
constructor(runtime: Runtime);
|
|
25
|
+
get hasParams(): boolean;
|
|
26
|
+
bind(inputs?: Record<string, unknown>): Array<unknown>;
|
|
27
|
+
processValue(value: unknown): unknown;
|
|
27
28
|
emitIdentifierOrSelf(value: string): void;
|
|
28
29
|
selfName?: string;
|
|
29
30
|
emitSelf({ name, inner }: {
|
package/dist/core/Emitter.js
CHANGED
|
@@ -10,8 +10,12 @@ import { Sql, sql } from "./Sql.js";
|
|
|
10
10
|
import { callFunction } from "./expr/Functions.js";
|
|
11
11
|
import { jsonAggregateArray, jsonArray } from "./expr/Json.js";
|
|
12
12
|
var Emitter = class {
|
|
13
|
+
#runtime;
|
|
13
14
|
sql = "";
|
|
14
15
|
params = [];
|
|
16
|
+
constructor(runtime) {
|
|
17
|
+
this.#runtime = runtime;
|
|
18
|
+
}
|
|
15
19
|
get hasParams() {
|
|
16
20
|
return this.params.length > 0;
|
|
17
21
|
}
|
|
@@ -85,7 +89,15 @@ var Emitter = class {
|
|
|
85
89
|
}).emitTo(this);
|
|
86
90
|
}
|
|
87
91
|
emitInsert(insert) {
|
|
88
|
-
const {
|
|
92
|
+
const {
|
|
93
|
+
cte,
|
|
94
|
+
into,
|
|
95
|
+
values,
|
|
96
|
+
select,
|
|
97
|
+
onConflict,
|
|
98
|
+
onDuplicateKeyUpdate,
|
|
99
|
+
returning
|
|
100
|
+
} = getData(insert);
|
|
89
101
|
if (cte) this.emitWith(cte);
|
|
90
102
|
const table = getTable(into);
|
|
91
103
|
const tableName = sql.identifier(table.name);
|
|
@@ -93,6 +105,7 @@ var Emitter = class {
|
|
|
93
105
|
insertInto: sql`${tableName}(${table.listColumns()})`,
|
|
94
106
|
...values ? { values } : { "": select },
|
|
95
107
|
onConflict,
|
|
108
|
+
onDuplicateKeyUpdate,
|
|
96
109
|
returning
|
|
97
110
|
}).inlineFields(false).emitTo(this);
|
|
98
111
|
}
|
|
@@ -160,7 +173,7 @@ var Emitter = class {
|
|
|
160
173
|
sql`(select ${data.first ? subject : jsonAggregateArray(subject)} from (${inner}) as _)`.emitTo(this);
|
|
161
174
|
}
|
|
162
175
|
emitUniversal(runtimes) {
|
|
163
|
-
const sql2 = runtimes[this
|
|
176
|
+
const sql2 = runtimes[this.#runtime] ?? runtimes.default;
|
|
164
177
|
if (!sql2) throw new Error("Unsupported runtime");
|
|
165
178
|
sql2.emitTo(this);
|
|
166
179
|
}
|
package/dist/core/Query.js
CHANGED
|
@@ -26,9 +26,13 @@ var Executable = class {
|
|
|
26
26
|
return this.#execute();
|
|
27
27
|
}
|
|
28
28
|
// biome-ignore lint/suspicious/noThenProperty:
|
|
29
|
-
then(onfulfilled, onrejected) {
|
|
30
|
-
|
|
31
|
-
|
|
29
|
+
async then(onfulfilled, onrejected) {
|
|
30
|
+
try {
|
|
31
|
+
const result = await this.#execute();
|
|
32
|
+
return onfulfilled ? onfulfilled(result) : result;
|
|
33
|
+
} catch (error) {
|
|
34
|
+
return onrejected ? onrejected(error) : Promise.reject(error);
|
|
35
|
+
}
|
|
32
36
|
}
|
|
33
37
|
catch(onrejected) {
|
|
34
38
|
return this.then().catch(onrejected);
|
|
@@ -13,8 +13,10 @@ var Include = class {
|
|
|
13
13
|
#mapFromDriverValue = (value, specs) => {
|
|
14
14
|
const { select, first } = getData(this);
|
|
15
15
|
const parsed = specs.parsesJson ? value : JSON.parse(value);
|
|
16
|
-
if (first)
|
|
17
|
-
|
|
16
|
+
if (first) {
|
|
17
|
+
const result = parsed ? select.mapRow({ values: parsed, index: 0, specs }) : null;
|
|
18
|
+
return result;
|
|
19
|
+
}
|
|
18
20
|
if (!parsed) return [];
|
|
19
21
|
const rows = parsed;
|
|
20
22
|
const ctx = {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type HasSql, type HasTable, internalData, internalQuery, internalSelection } from '../Internal.js';
|
|
2
|
-
import type { IsPostgres, IsSqlite, QueryMeta } from '../MetaData.js';
|
|
2
|
+
import type { IsMysql, IsPostgres, IsSqlite, QueryMeta } from '../MetaData.js';
|
|
3
3
|
import { Query, type QueryData } from '../Query.js';
|
|
4
4
|
import { type Selection, type SelectionInput, type SelectionRow } from '../Selection.js';
|
|
5
5
|
import { type Sql } from '../Sql.js';
|
|
@@ -12,6 +12,7 @@ interface InsertIntoData<Meta extends QueryMeta> extends QueryData<Meta> {
|
|
|
12
12
|
export interface InsertData<Meta extends QueryMeta> extends InsertIntoData<Meta> {
|
|
13
13
|
returning?: Selection;
|
|
14
14
|
onConflict?: HasSql;
|
|
15
|
+
onDuplicateKeyUpdate?: HasSql;
|
|
15
16
|
}
|
|
16
17
|
export declare class Insert<Result, Meta extends QueryMeta = QueryMeta> extends Query<Result, Meta> {
|
|
17
18
|
readonly [internalData]: InsertData<Meta>;
|
|
@@ -27,14 +28,17 @@ export interface OnConflict {
|
|
|
27
28
|
target: HasSql | Array<HasSql>;
|
|
28
29
|
targetWhere?: HasSql<boolean>;
|
|
29
30
|
}
|
|
30
|
-
export interface
|
|
31
|
+
export interface OnConflictSet<Definition extends TableDefinition> {
|
|
31
32
|
set: TableUpdate<Definition>;
|
|
33
|
+
}
|
|
34
|
+
export interface OnConflictUpdate<Definition extends TableDefinition> extends OnConflict, OnConflictSet<Definition> {
|
|
32
35
|
setWhere?: HasSql<boolean>;
|
|
33
36
|
}
|
|
34
37
|
declare class InsertCanConflict<Definition extends TableDefinition, Meta extends QueryMeta> extends InsertCanReturn<Definition, Meta> {
|
|
35
38
|
#private;
|
|
36
|
-
onConflictDoNothing(this: InsertCanConflict<Definition, IsPostgres>, onConflict?: OnConflict): InsertCanReturn<Definition, Meta>;
|
|
37
|
-
onConflictDoUpdate(this: InsertCanConflict<Definition, IsPostgres>, onConflict: OnConflictUpdate<Definition>): InsertCanReturn<Definition, Meta>;
|
|
39
|
+
onConflictDoNothing(this: InsertCanConflict<Definition, IsPostgres | IsSqlite>, onConflict?: OnConflict): InsertCanReturn<Definition, Meta>;
|
|
40
|
+
onConflictDoUpdate(this: InsertCanConflict<Definition, IsPostgres | IsSqlite>, onConflict: OnConflictUpdate<Definition>): InsertCanReturn<Definition, Meta>;
|
|
41
|
+
onDuplicateKeyUpdate(this: InsertCanConflict<Definition, IsMysql>, update: OnConflictSet<Definition>): InsertCanReturn<Definition, Meta>;
|
|
38
42
|
}
|
|
39
43
|
export declare class InsertInto<Definition extends TableDefinition, Meta extends QueryMeta> {
|
|
40
44
|
[internalData]: InsertData<Meta>;
|
|
@@ -40,18 +40,27 @@ var InsertCanConflict = class extends InsertCanReturn {
|
|
|
40
40
|
onConflictDoUpdate(onConflict) {
|
|
41
41
|
return this.#onConflict(onConflict);
|
|
42
42
|
}
|
|
43
|
+
onDuplicateKeyUpdate(update) {
|
|
44
|
+
return new InsertCanReturn({
|
|
45
|
+
...getData(this),
|
|
46
|
+
onDuplicateKeyUpdate: this.#updateFields(update.set)
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
#updateFields(update) {
|
|
50
|
+
return sql.join(
|
|
51
|
+
Object.entries(update).map(
|
|
52
|
+
([key, value]) => sql`${sql.identifier(key)} = ${input(value)}`
|
|
53
|
+
),
|
|
54
|
+
sql`, `
|
|
55
|
+
);
|
|
56
|
+
}
|
|
43
57
|
#onConflict({
|
|
44
58
|
target,
|
|
45
59
|
targetWhere,
|
|
46
60
|
set,
|
|
47
61
|
setWhere
|
|
48
62
|
}) {
|
|
49
|
-
const update = set &&
|
|
50
|
-
Object.entries(set).map(
|
|
51
|
-
([key, value]) => sql`${sql.identifier(key)} = ${input(value)}`
|
|
52
|
-
),
|
|
53
|
-
sql`, `
|
|
54
|
-
);
|
|
63
|
+
const update = set && this.#updateFields(set);
|
|
55
64
|
return new InsertCanReturn({
|
|
56
65
|
...getData(this),
|
|
57
66
|
onConflict: sql.join([
|
|
@@ -18,7 +18,8 @@ var PreparedStatement = class {
|
|
|
18
18
|
return this.stmt.get(...params);
|
|
19
19
|
}
|
|
20
20
|
values(params) {
|
|
21
|
-
if (this.isSelection)
|
|
21
|
+
if (this.isSelection)
|
|
22
|
+
return this.stmt.raw(true).all(...params);
|
|
22
23
|
this.stmt.run(...params);
|
|
23
24
|
return [];
|
|
24
25
|
}
|
|
@@ -31,6 +32,7 @@ var BetterSqlite3Driver = class _BetterSqlite3Driver {
|
|
|
31
32
|
this.depth = depth;
|
|
32
33
|
}
|
|
33
34
|
parsesJson = false;
|
|
35
|
+
supportsTransactions = true;
|
|
34
36
|
exec(query) {
|
|
35
37
|
this.client.exec(query);
|
|
36
38
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { AsyncDatabase } from '../core/Database.js';
|
|
2
|
+
import type { AsyncDriver, AsyncStatement, BatchQuery, PrepareOptions } from '../core/Driver.js';
|
|
3
|
+
type Client = D1Database;
|
|
4
|
+
declare class PreparedStatement implements AsyncStatement {
|
|
5
|
+
private stmt;
|
|
6
|
+
private isSelection;
|
|
7
|
+
constructor(stmt: D1PreparedStatement, isSelection: boolean);
|
|
8
|
+
all(params: Array<unknown>): Promise<Array<object>>;
|
|
9
|
+
run(params: Array<unknown>): Promise<void>;
|
|
10
|
+
get(params: Array<unknown>): Promise<object | null>;
|
|
11
|
+
values(params: Array<unknown>): Promise<Array<Array<unknown>>>;
|
|
12
|
+
free(): void;
|
|
13
|
+
}
|
|
14
|
+
export declare class D1Driver implements AsyncDriver {
|
|
15
|
+
private client;
|
|
16
|
+
parsesJson: boolean;
|
|
17
|
+
supportsTransactions: boolean;
|
|
18
|
+
constructor(client: Client);
|
|
19
|
+
exec(query: string): Promise<void>;
|
|
20
|
+
prepare(sql: string, options: PrepareOptions): PreparedStatement;
|
|
21
|
+
close(): Promise<void>;
|
|
22
|
+
batch(queries: Array<BatchQuery>): Promise<Array<Array<unknown>>>;
|
|
23
|
+
transaction<T>(): Promise<T>;
|
|
24
|
+
}
|
|
25
|
+
export declare function connect(client: Client): AsyncDatabase<'sqlite'>;
|
|
26
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// src/driver/d1.ts
|
|
2
|
+
import { AsyncDatabase } from "../core/Database.js";
|
|
3
|
+
import { sqliteDialect } from "../sqlite.js";
|
|
4
|
+
import { sqliteDiff } from "../sqlite/diff.js";
|
|
5
|
+
var PreparedStatement = class {
|
|
6
|
+
constructor(stmt, isSelection) {
|
|
7
|
+
this.stmt = stmt;
|
|
8
|
+
this.isSelection = isSelection;
|
|
9
|
+
}
|
|
10
|
+
async all(params) {
|
|
11
|
+
return this.stmt.bind(...params).all().then(({ results }) => results);
|
|
12
|
+
}
|
|
13
|
+
async run(params) {
|
|
14
|
+
const results = await this.stmt.bind(...params).run();
|
|
15
|
+
}
|
|
16
|
+
async get(params) {
|
|
17
|
+
return this.stmt.bind(...params).first();
|
|
18
|
+
}
|
|
19
|
+
async values(params) {
|
|
20
|
+
if (this.isSelection) return this.stmt.bind(...params).raw();
|
|
21
|
+
await this.stmt.bind(...params).run();
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
free() {
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
var D1Driver = class {
|
|
28
|
+
constructor(client) {
|
|
29
|
+
this.client = client;
|
|
30
|
+
}
|
|
31
|
+
parsesJson = false;
|
|
32
|
+
supportsTransactions = false;
|
|
33
|
+
async exec(query) {
|
|
34
|
+
await this.client.exec(query);
|
|
35
|
+
}
|
|
36
|
+
prepare(sql, options) {
|
|
37
|
+
return new PreparedStatement(this.client.prepare(sql), options.isSelection);
|
|
38
|
+
}
|
|
39
|
+
async close() {
|
|
40
|
+
}
|
|
41
|
+
async batch(queries) {
|
|
42
|
+
const stmts = queries.map(
|
|
43
|
+
({ sql, params }) => this.client.prepare(sql).bind(...params)
|
|
44
|
+
);
|
|
45
|
+
const rows = await this.client.batch(stmts);
|
|
46
|
+
return rows.map((row) => row.results);
|
|
47
|
+
}
|
|
48
|
+
async transaction() {
|
|
49
|
+
throw new Error("Transactions are not supported in D1");
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
function connect(client) {
|
|
53
|
+
return new AsyncDatabase(new D1Driver(client), sqliteDialect, sqliteDiff);
|
|
54
|
+
}
|
|
55
|
+
export {
|
|
56
|
+
D1Driver,
|
|
57
|
+
connect
|
|
58
|
+
};
|
package/dist/driver/mysql2.d.ts
CHANGED
|
@@ -19,6 +19,7 @@ export declare class Mysql2Driver implements AsyncDriver {
|
|
|
19
19
|
private client;
|
|
20
20
|
private depth;
|
|
21
21
|
parsesJson: boolean;
|
|
22
|
+
supportsTransactions: boolean;
|
|
22
23
|
constructor(client: Queryable, depth?: number);
|
|
23
24
|
exec(query: string): Promise<void>;
|
|
24
25
|
prepare(sql: string, options?: PrepareOptions): PreparedStatement;
|
package/dist/driver/mysql2.js
CHANGED
package/dist/driver/pg.d.ts
CHANGED
|
@@ -17,6 +17,7 @@ export declare class PgDriver implements AsyncDriver {
|
|
|
17
17
|
private client;
|
|
18
18
|
private depth;
|
|
19
19
|
parsesJson: boolean;
|
|
20
|
+
supportsTransactions: boolean;
|
|
20
21
|
constructor(client: Queryable, depth?: number);
|
|
21
22
|
exec(query: string): Promise<void>;
|
|
22
23
|
prepare(sql: string, options?: PrepareOptions): PreparedStatement;
|
package/dist/driver/pg.js
CHANGED
package/dist/driver/pglite.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { PGlite, Transaction } from '@electric-sql/pglite';
|
|
2
|
+
import { AsyncDatabase, type TransactionOptions } from '../core/Database.js';
|
|
2
3
|
import type { AsyncDriver, AsyncStatement, BatchQuery } from '../core/Driver.js';
|
|
3
|
-
import { AsyncDatabase, type TransactionOptions } from '../index.js';
|
|
4
4
|
type Queryable = PGlite | Transaction;
|
|
5
5
|
declare class PreparedStatement implements AsyncStatement {
|
|
6
6
|
private client;
|
|
@@ -16,6 +16,7 @@ export declare class PGliteDriver implements AsyncDriver {
|
|
|
16
16
|
private client;
|
|
17
17
|
private depth;
|
|
18
18
|
parsesJson: boolean;
|
|
19
|
+
supportsTransactions: boolean;
|
|
19
20
|
constructor(client: Queryable, depth?: number);
|
|
20
21
|
exec(query: string): Promise<void>;
|
|
21
22
|
close(): Promise<void>;
|
package/dist/driver/pglite.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// src/driver/pglite.ts
|
|
2
|
-
import { AsyncDatabase } from "../
|
|
2
|
+
import { AsyncDatabase } from "../core/Database.js";
|
|
3
3
|
import { postgresDialect } from "../postgres/dialect.js";
|
|
4
4
|
import { postgresDiff } from "../postgres/diff.js";
|
|
5
5
|
import { setTransaction } from "../postgres/transactions.js";
|
|
@@ -35,6 +35,7 @@ var PGliteDriver = class _PGliteDriver {
|
|
|
35
35
|
this.depth = depth;
|
|
36
36
|
}
|
|
37
37
|
parsesJson = true;
|
|
38
|
+
supportsTransactions = true;
|
|
38
39
|
async exec(query) {
|
|
39
40
|
await this.client.exec(query);
|
|
40
41
|
}
|
package/dist/driver/sql.js.js
CHANGED
package/dist/driver.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { connect as 'better-sqlite3' } from './driver/better-sqlite3.js';
|
|
2
|
+
export { connect as 'bun:sqlite' } from './driver/bun-sqlite.js';
|
|
3
|
+
export { connect as 'd1' } from './driver/d1.js';
|
|
4
|
+
export { connect as 'mysql2' } from './driver/mysql2.js';
|
|
5
|
+
export { connect as '@neondatabase/serverless', connect as '@vercel/postgres', connect as 'pg' } from './driver/pg.js';
|
|
6
|
+
export { connect as '@electric-sql/pglite' } from './driver/pglite.js';
|
|
7
|
+
export { connect as 'sql.js' } from './driver/sql.js.js';
|
package/dist/driver.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
// src/driver.ts
|
|
2
|
+
import { connect } from "./driver/better-sqlite3.js";
|
|
3
|
+
import { connect as connect2 } from "./driver/bun-sqlite.js";
|
|
4
|
+
import { connect as connect3 } from "./driver/d1.js";
|
|
5
|
+
import { connect as connect4 } from "./driver/mysql2.js";
|
|
6
|
+
import {
|
|
7
|
+
connect as connect5,
|
|
8
|
+
connect as connect6,
|
|
9
|
+
connect as connect7
|
|
10
|
+
} from "./driver/pg.js";
|
|
11
|
+
import { connect as connect8 } from "./driver/pglite.js";
|
|
12
|
+
import { connect as connect9 } from "./driver/sql.js.js";
|
|
13
|
+
export {
|
|
14
|
+
connect8 as "@electric-sql/pglite",
|
|
15
|
+
connect5 as "@neondatabase/serverless",
|
|
16
|
+
connect6 as "@vercel/postgres",
|
|
17
|
+
connect as "better-sqlite3",
|
|
18
|
+
connect2 as "bun:sqlite",
|
|
19
|
+
connect3 as d1,
|
|
20
|
+
connect4 as mysql2,
|
|
21
|
+
connect7 as pg,
|
|
22
|
+
connect9 as "sql.js"
|
|
23
|
+
};
|
package/dist/mysql/dialect.js
CHANGED
package/dist/postgres/dialect.js
CHANGED
package/dist/sqlite/dialect.js
CHANGED
|
@@ -9,8 +9,8 @@ var SINGLE_QUOTE = "'";
|
|
|
9
9
|
var ESCAPE_SINGLE_QUOTE = "''";
|
|
10
10
|
var MATCH_SINGLE_QUOTE = /'/g;
|
|
11
11
|
var sqliteDialect = new Dialect(
|
|
12
|
+
"sqlite",
|
|
12
13
|
class extends Emitter {
|
|
13
|
-
runtime = "sqlite";
|
|
14
14
|
processValue(value) {
|
|
15
15
|
return typeof value === "boolean" ? value ? 1 : 0 : value;
|
|
16
16
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { type Column, JsonColumn } from '../core/Column.js';
|
|
2
2
|
export declare function id(name?: string): Column<number>;
|
|
3
3
|
export declare function text(name?: string): Column<string | null>;
|
|
4
4
|
export declare function varchar(name?: string, options?: {
|
|
@@ -69,7 +69,13 @@ function jsonb(name) {
|
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
function blob(name) {
|
|
72
|
-
return column({
|
|
72
|
+
return column({
|
|
73
|
+
name,
|
|
74
|
+
type: blobType,
|
|
75
|
+
mapFromDriverValue(value) {
|
|
76
|
+
return new Uint8Array(value);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
73
79
|
}
|
|
74
80
|
export {
|
|
75
81
|
blob,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rado",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.11",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"scripts": {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"cycles": "madge --circular src/index.ts src/sqlite.ts src/mysql.ts src/postgres.ts",
|
|
12
12
|
"test:bun": "bun test",
|
|
13
13
|
"test:node": "node --test-force-exit --test-concurrency=1 --import tsx --test \"**/*.test.ts\"",
|
|
14
|
-
"test:deno": "deno test --no-check --
|
|
14
|
+
"test:deno": "deno test --no-check -A --unstable-ffi"
|
|
15
15
|
},
|
|
16
16
|
"sideEffects": false,
|
|
17
17
|
"exports": {
|
|
@@ -26,16 +26,20 @@
|
|
|
26
26
|
},
|
|
27
27
|
"files": ["dist"],
|
|
28
28
|
"devDependencies": {
|
|
29
|
+
"@alinea/suite": "^0.4.0",
|
|
29
30
|
"@biomejs/biome": "^1.8.3",
|
|
30
31
|
"@cloudflare/workers-types": "^4.20230628.0",
|
|
31
32
|
"@electric-sql/pglite": "^0.1.5",
|
|
33
|
+
"@miniflare/d1": "^2.14.2",
|
|
34
|
+
"@miniflare/shared": "^2.14.2",
|
|
32
35
|
"@sqlite.org/sqlite-wasm": "^3.42.0-build4",
|
|
33
|
-
"@types/better-sqlite3": "^
|
|
34
|
-
"@types/bun": "^1.1.
|
|
35
|
-
"@types/glob": "^8.
|
|
36
|
-
"@types/pg": "^8.11.
|
|
37
|
-
"@types/sql.js": "^1.4.
|
|
38
|
-
"
|
|
36
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
37
|
+
"@types/bun": "^1.1.8",
|
|
38
|
+
"@types/glob": "^8.1.0",
|
|
39
|
+
"@types/pg": "^8.11.8",
|
|
40
|
+
"@types/sql.js": "^1.4.9",
|
|
41
|
+
"@vercel/postgres": "^0.10.0",
|
|
42
|
+
"better-sqlite3": "^11.3.0",
|
|
39
43
|
"esbuild": "^0.23.1",
|
|
40
44
|
"glob": "^11.0.0",
|
|
41
45
|
"madge": "^8.0.0",
|
|
@@ -45,7 +49,6 @@
|
|
|
45
49
|
"sql.js": "^1.11.0",
|
|
46
50
|
"sqlite3": "^5.1.7",
|
|
47
51
|
"tsx": "^4.19.0",
|
|
48
|
-
"typescript": "^5.
|
|
49
|
-
"@alinea/suite": "^0.4.0"
|
|
52
|
+
"typescript": "^5.6.2"
|
|
50
53
|
}
|
|
51
54
|
}
|