rado 0.3.2 → 0.3.3

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.
Files changed (68) hide show
  1. package/README.md +277 -275
  2. package/dist/define/Expr.d.ts +2 -2
  3. package/dist/define/Expr.js +4 -1
  4. package/dist/define/Ops.d.ts +1 -6
  5. package/dist/define/Ops.js +10 -7
  6. package/dist/define/Query.d.ts +117 -2
  7. package/dist/define/Query.js +472 -17
  8. package/dist/define/Selection.d.ts +1 -1
  9. package/dist/define/Table.d.ts +1 -2
  10. package/dist/define/Table.js +1 -1
  11. package/dist/define/VirtualTable.d.ts +1 -1
  12. package/dist/driver/bun-sqlite.js +1 -2
  13. package/dist/index.d.ts +0 -8
  14. package/dist/index.js +0 -8
  15. package/dist/lib/Driver.d.ts +1 -2
  16. package/package.json +74 -74
  17. package/dist/define/Cursor.d.ts +0 -85
  18. package/dist/define/Cursor.js +0 -256
  19. package/dist/define/Update.d.ts +0 -4
  20. package/dist/define/Update.js +0 -0
  21. package/dist/define/query/Batch.d.ts +0 -7
  22. package/dist/define/query/Batch.js +0 -17
  23. package/dist/define/query/CreateTable.d.ts +0 -6
  24. package/dist/define/query/CreateTable.js +0 -11
  25. package/dist/define/query/Delete.d.ts +0 -13
  26. package/dist/define/query/Delete.js +0 -21
  27. package/dist/define/query/Insert.d.ts +0 -22
  28. package/dist/define/query/Insert.js +0 -47
  29. package/dist/define/query/Select.d.ts +0 -41
  30. package/dist/define/query/Select.js +0 -183
  31. package/dist/define/query/TableSelect.d.ts +0 -28
  32. package/dist/define/query/TableSelect.js +0 -79
  33. package/dist/define/query/Union.d.ts +0 -17
  34. package/dist/define/query/Union.js +0 -116
  35. package/dist/define/query/Update.d.ts +0 -12
  36. package/dist/define/query/Update.js +0 -18
  37. package/dist/lib/Column.d.ts +0 -57
  38. package/dist/lib/Column.js +0 -68
  39. package/dist/lib/Cursor.d.ts +0 -85
  40. package/dist/lib/Cursor.js +0 -256
  41. package/dist/lib/Expr.d.ts +0 -154
  42. package/dist/lib/Expr.js +0 -284
  43. package/dist/lib/Fields.d.ts +0 -13
  44. package/dist/lib/Fields.js +0 -0
  45. package/dist/lib/Functions.d.ts +0 -5
  46. package/dist/lib/Functions.js +0 -11
  47. package/dist/lib/Id.d.ts +0 -3
  48. package/dist/lib/Id.js +0 -0
  49. package/dist/lib/Index.d.ts +0 -17
  50. package/dist/lib/Index.js +0 -20
  51. package/dist/lib/Ops.d.ts +0 -7
  52. package/dist/lib/Ops.js +0 -36
  53. package/dist/lib/OrderBy.d.ts +0 -9
  54. package/dist/lib/OrderBy.js +0 -9
  55. package/dist/lib/Param.d.ts +0 -20
  56. package/dist/lib/Param.js +0 -27
  57. package/dist/lib/Query.d.ts +0 -109
  58. package/dist/lib/Query.js +0 -74
  59. package/dist/lib/Schema.d.ts +0 -18
  60. package/dist/lib/Schema.js +0 -67
  61. package/dist/lib/Selection.d.ts +0 -20
  62. package/dist/lib/Selection.js +0 -7
  63. package/dist/lib/Table.d.ts +0 -62
  64. package/dist/lib/Table.js +0 -130
  65. package/dist/lib/Target.d.ts +0 -38
  66. package/dist/lib/Target.js +0 -42
  67. package/dist/lib/Update.d.ts +0 -4
  68. package/dist/lib/Update.js +0 -0
package/README.md CHANGED
@@ -1,275 +1,277 @@
1
- # rado
2
-
3
- Fully typed, lightweight TypeScript query builder.
4
- Currently focused on SQLite.
5
-
6
- - Definition via TypeScript types
7
- - Composable queries
8
- - Aggregate rows in selects
9
- - First class JSON columns
10
- - No code generation step
11
- - No dependencies
12
-
13
- <pre>npm install <a href="https://www.npmjs.com/package/rado">rado</a></pre>
14
-
15
- ## Queries
16
-
17
- #### Where
18
-
19
- Conditions can be created by accessing the fields of the table
20
- instances and using the operators of `Expr`:
21
-
22
- ```ts
23
- User.id // This is an Expr<number> which has an `is` method to compare
24
- User.id.is(1) // Compare with a value, results in an Expr<boolean>
25
- User.id.is(Post.userId) // Compare to fields on other tables
26
- ```
27
-
28
- Rows can be selected by passing the condition in the where method:
29
-
30
- ```ts
31
- // Call a table instance to start a query selecting from that table
32
- User().where(User.id.is(1))
33
- // Conditions can be as complex as you want
34
- User().where(User.id.isGreater(1).and(User.id.isLess(5)))
35
- // `And` conditions can also be comma separated in `where` to improve readability
36
- User().where(User.id.isGreater(1), User.id.isLess(5))
37
- ```
38
-
39
- There's a shortcut for comparing columns of a table directly in its call.
40
- The first comparison above can be simplified to:
41
-
42
- ```ts
43
- User({id: 1}) // Is the same as User().where(User.id.is(1))
44
- ```
45
-
46
- This comes in handy when joining tables too:
47
-
48
- ```ts
49
- User({id: 1}).innerJoin(Post({userId: User.id}))
50
- // Is equal to:
51
- User().innerJoin(Post, Post.userId.is(User.id)).where(User.id.is(1))
52
- ```
53
-
54
- #### Select
55
-
56
- Retrieve a single field
57
-
58
- ```ts
59
- User().select(User.name)
60
- ```
61
-
62
- Or an object of data
63
-
64
- ```ts
65
- User().select({
66
- ...User, // Get all user fields: id, username
67
- posts: Post({userId: User.id}) // Aggregate posts of this user
68
- })
69
- ```
70
-
71
- You can call the `posts` helper method defined in the example schema below
72
- to achieve the same:
73
-
74
- ```ts
75
- User().select({
76
- ...User,
77
- posts: User.posts()
78
- })
79
- ```
80
-
81
- Retrieve all posts with their author and tags and count the tag usage in other
82
- posts:
83
-
84
- ```ts
85
- import {count} from 'rado/sqlite'
86
- // Alias tables using `as`
87
- const pt = PostTags().as('pt')
88
- Post().select({
89
- ...Post,
90
- author: Post.author().select(User.username),
91
- tags: Post.tags().select({
92
- count: pt({tagId: Tag.id}).select(count()).first(),
93
- name: Tag.name
94
- })
95
- })
96
- ```
97
-
98
- Join other tables and select fields from either table:
99
-
100
- ```ts
101
- User({id: 1}).innerJoin(Post({userId: User.id})).select({
102
- username: User.username
103
- post: Post
104
- })
105
- ```
106
-
107
- Create expressions to select complex values:
108
-
109
- ```ts
110
- import {iif} from 'rado/sqlite'
111
- Person().select({
112
- name: Person.firstName.concat(' ').concat(Person.lastName),
113
- isMario: Person.firstName.is('Mario'),
114
- isActor: Person.id.isIn(Actor().select(Actor.personId)),
115
- email: iif(Person.email.isNotNull(), Person.email, 'its.me@mario'),
116
- twitterHandle: Person.socialMedia
117
- .filter(media => media.type.is('twitter'))
118
- .map(media => media.handle)
119
- .maybeFirst()
120
- })
121
- ```
122
-
123
- Order, group, limit queries:
124
-
125
- ```ts
126
- User()
127
- .orderBy(User.username.asc())
128
- .groupBy(User.id, User.username)
129
- .skip(20)
130
- .take(10)
131
- ```
132
-
133
- #### Insert
134
-
135
- Insert a single row and return the result:
136
-
137
- ```ts
138
- User().insertOne({username: 'Mario'})
139
- ```
140
-
141
- Insert multiple rows:
142
-
143
- ```ts
144
- User().insertAll([{username: 'Mario'}, {username: 'Luigi'}])
145
- ```
146
-
147
- #### Update
148
-
149
- Set values
150
-
151
- ```ts
152
- User({id: 1}).set({username: 'Bowser'})
153
- ```
154
-
155
- Use expressions to update complex values:
156
-
157
- ```ts
158
- User()
159
- .set({username: User.username.concat(' [removed]')})
160
- .where(User.deleted)
161
- ```
162
-
163
- #### Delete
164
-
165
- Delete rows
166
-
167
- ```ts
168
- User({id: 1}).delete()
169
- ```
170
-
171
- ## Schema definition
172
-
173
- ```ts
174
- import {table, column} from 'rado'
175
-
176
- const User = table({
177
- // Pass a definition under a key with the actual name of the table in database
178
- user: class {
179
- id = column.integer.primaryKey()
180
- username = column.string
181
-
182
- // Define helper methods directly on the model
183
- posts() {
184
- return Post({userId: this.id})
185
- }
186
- }
187
- })
188
-
189
- const Post = table({
190
- post: class {
191
- id = column.integer.primaryKey()
192
- userId = column.integer.references(() => User.id)
193
- content = column.string
194
-
195
- author() {
196
- return User({id: this.userId}).first()
197
- }
198
-
199
- tags() {
200
- return Tag({id: PostTags.tagId}).innerJoin(PostTags({postId: this.id}))
201
- }
202
- }
203
- })
204
-
205
- const Tag = table({
206
- tag: class {
207
- id = column.integer.primaryKey()
208
- name = column.string
209
- }
210
- })
211
-
212
- const PostTags = table({
213
- post_tag: class {
214
- postId = column.integer.references(() => Post.id)
215
- tagId = column.integer.references(() => Tag.id)
216
- }
217
- })
218
- ```
219
-
220
- ## Connections
221
-
222
- Currently supported SQLite drivers:
223
- `better-sqlite3`, `sql.js`, `sqlite3`, `bun:sqlite`
224
-
225
- Pass an instance of the database to the `connect` function to get started:
226
-
227
- ```ts
228
- import Database from 'better-sqlite3'
229
- import {connect} from 'rado/driver/better-sqlite3'
230
-
231
- const db = connect(new Database('foobar.db'))
232
- ```
233
-
234
- Currently all drivers are synchronous except for the `sqlite3` driver which is
235
- async.
236
-
237
- #### Run queries
238
-
239
- Call the connection with a query to retrieve its results:
240
-
241
- ```ts
242
- const posts = db(Post())
243
- // For async drivers this will be a Promise:
244
- const posts = await db(Post())
245
- ```
246
-
247
- #### Iterate results
248
-
249
- Iterate over query results:
250
-
251
- ```ts
252
- const allPosts = Post()
253
- for (const row of db.iterate(allPosts)) {
254
- console.log(row)
255
- }
256
- // For async drivers:
257
- for await (const row of db.iterate(allPosts)) {
258
- console.log(row)
259
- }
260
- ```
261
-
262
- #### Transactions
263
-
264
- Run transactions within the `transaction` method which will isolate a connection
265
- and can optionally return a result:
266
-
267
- ```ts
268
- const firstUserId = db.transaction(tx => {
269
- const createTable = User().create()
270
- tx(createTable)
271
- const insertUser = User().insertOne({username: 'Mario'})
272
- tx(insertUser)
273
- return insertUser.id
274
- })
275
- ```
1
+ [![npm](https://img.shields.io/npm/v/rado.svg)](https://npmjs.org/package/rado)
2
+
3
+ # rado
4
+
5
+ Fully typed, lightweight TypeScript query builder.
6
+ Currently focused on SQLite.
7
+
8
+ - Definition via TypeScript types
9
+ - Composable queries
10
+ - Aggregate rows in selects
11
+ - First class JSON columns
12
+ - No code generation step
13
+ - No dependencies
14
+
15
+ <pre>npm install <a href="https://www.npmjs.com/package/rado">rado</a></pre>
16
+
17
+ ## Queries
18
+
19
+ #### Where
20
+
21
+ Conditions can be created by accessing the fields of the table
22
+ instances and using the operators of `Expr`:
23
+
24
+ ```ts
25
+ User.id // This is an Expr<number> which has an `is` method to compare
26
+ User.id.is(1) // Compare with a value, results in an Expr<boolean>
27
+ User.id.is(Post.userId) // Compare to fields on other tables
28
+ ```
29
+
30
+ Rows can be selected by passing the condition in the where method:
31
+
32
+ ```ts
33
+ // Call a table instance to start a query selecting from that table
34
+ User().where(User.id.is(1))
35
+ // Conditions can be as complex as you want
36
+ User().where(User.id.isGreater(1).and(User.id.isLess(5)))
37
+ // `And` conditions can also be comma separated in `where` to improve readability
38
+ User().where(User.id.isGreater(1), User.id.isLess(5))
39
+ ```
40
+
41
+ There's a shortcut for comparing columns of a table directly in its call.
42
+ The first comparison above can be simplified to:
43
+
44
+ ```ts
45
+ User({id: 1}) // Is the same as User().where(User.id.is(1))
46
+ ```
47
+
48
+ This comes in handy when joining tables too:
49
+
50
+ ```ts
51
+ User({id: 1}).innerJoin(Post({userId: User.id}))
52
+ // Is equal to:
53
+ User().innerJoin(Post, Post.userId.is(User.id)).where(User.id.is(1))
54
+ ```
55
+
56
+ #### Select
57
+
58
+ Retrieve a single field
59
+
60
+ ```ts
61
+ User().select(User.name)
62
+ ```
63
+
64
+ Or an object of data
65
+
66
+ ```ts
67
+ User().select({
68
+ ...User, // Get all user fields: id, username
69
+ posts: Post({userId: User.id}) // Aggregate posts of this user
70
+ })
71
+ ```
72
+
73
+ You can call the `posts` helper method defined in the example schema below
74
+ to achieve the same:
75
+
76
+ ```ts
77
+ User().select({
78
+ ...User,
79
+ posts: User.posts()
80
+ })
81
+ ```
82
+
83
+ Retrieve all posts with their author and tags and count the tag usage in other
84
+ posts:
85
+
86
+ ```ts
87
+ import {count} from 'rado/sqlite'
88
+ // Alias tables using `as`
89
+ const pt = PostTags().as('pt')
90
+ Post().select({
91
+ ...Post,
92
+ author: Post.author().select(User.username),
93
+ tags: Post.tags().select({
94
+ count: pt({tagId: Tag.id}).select(count()).first(),
95
+ name: Tag.name
96
+ })
97
+ })
98
+ ```
99
+
100
+ Join other tables and select fields from either table:
101
+
102
+ ```ts
103
+ User({id: 1}).innerJoin(Post({userId: User.id})).select({
104
+ username: User.username
105
+ post: Post
106
+ })
107
+ ```
108
+
109
+ Create expressions to select complex values:
110
+
111
+ ```ts
112
+ import {iif} from 'rado/sqlite'
113
+ Person().select({
114
+ name: Person.firstName.concat(' ').concat(Person.lastName),
115
+ isMario: Person.firstName.is('Mario'),
116
+ isActor: Person.id.isIn(Actor().select(Actor.personId)),
117
+ email: iif(Person.email.isNotNull(), Person.email, 'its.me@mario'),
118
+ twitterHandle: Person.socialMedia
119
+ .filter(media => media.type.is('twitter'))
120
+ .map(media => media.handle)
121
+ .maybeFirst()
122
+ })
123
+ ```
124
+
125
+ Order, group, limit queries:
126
+
127
+ ```ts
128
+ User()
129
+ .orderBy(User.username.asc())
130
+ .groupBy(User.id, User.username)
131
+ .skip(20)
132
+ .take(10)
133
+ ```
134
+
135
+ #### Insert
136
+
137
+ Insert a single row and return the result:
138
+
139
+ ```ts
140
+ User().insertOne({username: 'Mario'})
141
+ ```
142
+
143
+ Insert multiple rows:
144
+
145
+ ```ts
146
+ User().insertAll([{username: 'Mario'}, {username: 'Luigi'}])
147
+ ```
148
+
149
+ #### Update
150
+
151
+ Set values
152
+
153
+ ```ts
154
+ User({id: 1}).set({username: 'Bowser'})
155
+ ```
156
+
157
+ Use expressions to update complex values:
158
+
159
+ ```ts
160
+ User()
161
+ .set({username: User.username.concat(' [removed]')})
162
+ .where(User.deleted)
163
+ ```
164
+
165
+ #### Delete
166
+
167
+ Delete rows
168
+
169
+ ```ts
170
+ User({id: 1}).delete()
171
+ ```
172
+
173
+ ## Schema definition
174
+
175
+ ```ts
176
+ import {table, column} from 'rado'
177
+
178
+ const User = table({
179
+ // Pass a definition under a key with the actual name of the table in database
180
+ user: class {
181
+ id = column.integer.primaryKey()
182
+ username = column.string
183
+
184
+ // Define helper methods directly on the model
185
+ posts() {
186
+ return Post({userId: this.id})
187
+ }
188
+ }
189
+ })
190
+
191
+ const Post = table({
192
+ post: class {
193
+ id = column.integer.primaryKey()
194
+ userId = column.integer.references(() => User.id)
195
+ content = column.string
196
+
197
+ author() {
198
+ return User({id: this.userId}).first()
199
+ }
200
+
201
+ tags() {
202
+ return Tag({id: PostTags.tagId}).innerJoin(PostTags({postId: this.id}))
203
+ }
204
+ }
205
+ })
206
+
207
+ const Tag = table({
208
+ tag: class {
209
+ id = column.integer.primaryKey()
210
+ name = column.string
211
+ }
212
+ })
213
+
214
+ const PostTags = table({
215
+ post_tag: class {
216
+ postId = column.integer.references(() => Post.id)
217
+ tagId = column.integer.references(() => Tag.id)
218
+ }
219
+ })
220
+ ```
221
+
222
+ ## Connections
223
+
224
+ Currently supported SQLite drivers:
225
+ `better-sqlite3`, `sql.js`, `sqlite3`, `bun:sqlite`
226
+
227
+ Pass an instance of the database to the `connect` function to get started:
228
+
229
+ ```ts
230
+ import Database from 'better-sqlite3'
231
+ import {connect} from 'rado/driver/better-sqlite3'
232
+
233
+ const db = connect(new Database('foobar.db'))
234
+ ```
235
+
236
+ Currently all drivers are synchronous except for the `sqlite3` driver which is
237
+ async.
238
+
239
+ #### Run queries
240
+
241
+ Call the connection with a query to retrieve its results:
242
+
243
+ ```ts
244
+ const posts = db(Post())
245
+ // For async drivers this will be a Promise:
246
+ const posts = await db(Post())
247
+ ```
248
+
249
+ #### Iterate results
250
+
251
+ Iterate over query results:
252
+
253
+ ```ts
254
+ const allPosts = Post()
255
+ for (const row of db.iterate(allPosts)) {
256
+ console.log(row)
257
+ }
258
+ // For async drivers:
259
+ for await (const row of db.iterate(allPosts)) {
260
+ console.log(row)
261
+ }
262
+ ```
263
+
264
+ #### Transactions
265
+
266
+ Run transactions within the `transaction` method which will isolate a connection
267
+ and can optionally return a result:
268
+
269
+ ```ts
270
+ const firstUserId = db.transaction(tx => {
271
+ const createTable = User().create()
272
+ tx(createTable)
273
+ const insertUser = User().insertOne({username: 'Mario'})
274
+ tx(insertUser)
275
+ return insertUser.id
276
+ })
277
+ ```
@@ -1,10 +1,9 @@
1
1
  import { Fields } from './Fields.js';
2
2
  import { OrderBy } from './OrderBy.js';
3
3
  import { ParamData } from './Param.js';
4
- import { QueryData } from './Query.js';
4
+ import { QueryData, Select, SelectFirst } from './Query.js';
5
5
  import { Selection } from './Selection.js';
6
6
  import { Target } from './Target.js';
7
- import { Select, SelectFirst } from './query/Select.js';
8
7
  export declare enum UnOpType {
9
8
  Not = "Not",
10
9
  IsNull = "IsNull"
@@ -161,6 +160,7 @@ export declare class Expr<T> {
161
160
  map<T, X extends Selection>(this: Expr<Array<T>>, fn: (cursor: Fields<T>) => X): Expr<Array<Selection.Infer<X>>>;
162
161
  sure(): Expr<NonNullable<T>>;
163
162
  get<T>(name: string): Expr<T>;
163
+ static [Symbol.hasInstance](instance: any): any;
164
164
  }
165
165
  export interface ObjectExpr {
166
166
  [Expr.Data]: ExprData;
@@ -386,6 +386,9 @@ class Expr {
386
386
  get(name) {
387
387
  return new Expr(new ExprData.Field(this[Expr.Data], name));
388
388
  }
389
+ static [Symbol.hasInstance](instance) {
390
+ return instance?.[Expr.IsExpr];
391
+ }
389
392
  }
390
393
  ((Expr2) => {
391
394
  Expr2.Data = Symbol("Expr.Data");
@@ -419,7 +422,7 @@ class Expr {
419
422
  }
420
423
  Expr2.hasExpr = hasExpr;
421
424
  function isExpr(input) {
422
- return input !== null && (typeof input === "object" || typeof input === "function") && input[Expr2.IsExpr];
425
+ return input instanceof Expr2;
423
426
  }
424
427
  Expr2.isExpr = isExpr;
425
428
  })(Expr || (Expr = {}));
@@ -1,11 +1,6 @@
1
+ import { Batch, Delete, InsertInto, RecursiveUnion, Select, Update } from './Query.js';
1
2
  import { Selection } from './Selection.js';
2
3
  import { Table } from './Table.js';
3
- import { Batch } from './query/Batch.js';
4
- import { Delete } from './query/Delete.js';
5
- import { InsertInto } from './query/Insert.js';
6
- import { Select } from './query/Select.js';
7
- import { RecursiveUnion } from './query/Union.js';
8
- import { Update } from './query/Update.js';
9
4
  export declare function withRecursive<Row>(initialSelect: Select<Row>): RecursiveUnion<Row>;
10
5
  export declare function alias<T extends Table<{}>>(table: T): Record<string, T>;
11
6
  export declare function select<X extends Selection>(selection: X): Select<Selection.Infer<X>>;
@@ -1,14 +1,17 @@
1
1
  import { ExprData } from "./Expr.js";
2
- import { Query, QueryData } from "./Query.js";
2
+ import {
3
+ Batch,
4
+ Delete,
5
+ InsertInto,
6
+ Query,
7
+ QueryData,
8
+ RecursiveUnion,
9
+ Select,
10
+ Update
11
+ } from "./Query.js";
3
12
  import { Schema } from "./Schema.js";
4
13
  import { Table, createTable } from "./Table.js";
5
14
  import { Target } from "./Target.js";
6
- import { Batch } from "./query/Batch.js";
7
- import { Delete } from "./query/Delete.js";
8
- import { InsertInto } from "./query/Insert.js";
9
- import { Select } from "./query/Select.js";
10
- import { RecursiveUnion } from "./query/Union.js";
11
- import { Update } from "./query/Update.js";
12
15
  function withRecursive(initialSelect) {
13
16
  return new RecursiveUnion(initialSelect[Query.Data]);
14
17
  }