rado 0.3.0 → 0.3.2

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 (43) hide show
  1. package/README.md +275 -275
  2. package/dist/define/Column.d.ts +14 -1
  3. package/dist/define/Column.js +25 -1
  4. package/dist/define/Cursor.d.ts +85 -0
  5. package/dist/define/Cursor.js +256 -0
  6. package/dist/define/Schema.js +8 -3
  7. package/dist/define/Update.d.ts +4 -0
  8. package/dist/define/Update.js +0 -0
  9. package/dist/lib/Column.d.ts +57 -0
  10. package/dist/lib/Column.js +68 -0
  11. package/dist/lib/Cursor.d.ts +85 -0
  12. package/dist/lib/Cursor.js +256 -0
  13. package/dist/lib/Expr.d.ts +154 -0
  14. package/dist/lib/Expr.js +284 -0
  15. package/dist/lib/Fields.d.ts +13 -0
  16. package/dist/lib/Fields.js +0 -0
  17. package/dist/lib/Formatter.d.ts +1 -1
  18. package/dist/lib/Formatter.js +19 -4
  19. package/dist/lib/Functions.d.ts +5 -0
  20. package/dist/lib/Functions.js +11 -0
  21. package/dist/lib/Id.d.ts +3 -0
  22. package/dist/lib/Id.js +0 -0
  23. package/dist/lib/Index.d.ts +17 -0
  24. package/dist/lib/Index.js +20 -0
  25. package/dist/lib/Ops.d.ts +7 -0
  26. package/dist/lib/Ops.js +36 -0
  27. package/dist/lib/OrderBy.d.ts +9 -0
  28. package/dist/lib/OrderBy.js +9 -0
  29. package/dist/lib/Param.d.ts +20 -0
  30. package/dist/lib/Param.js +27 -0
  31. package/dist/lib/Query.d.ts +109 -0
  32. package/dist/lib/Query.js +74 -0
  33. package/dist/lib/Schema.d.ts +18 -0
  34. package/dist/lib/Schema.js +67 -0
  35. package/dist/lib/Selection.d.ts +20 -0
  36. package/dist/lib/Selection.js +7 -0
  37. package/dist/lib/Table.d.ts +62 -0
  38. package/dist/lib/Table.js +130 -0
  39. package/dist/lib/Target.d.ts +38 -0
  40. package/dist/lib/Target.js +42 -0
  41. package/dist/lib/Update.d.ts +4 -0
  42. package/dist/lib/Update.js +0 -0
  43. package/package.json +74 -74
package/README.md CHANGED
@@ -1,275 +1,275 @@
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
+ # 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
+ ```
@@ -17,6 +17,8 @@ export interface PartialColumnData {
17
17
  primaryKey?: boolean;
18
18
  unique?: boolean;
19
19
  references?: () => ExprData;
20
+ onUpdate?: Action;
21
+ onDelete?: Action;
20
22
  enumerable?: boolean;
21
23
  }
22
24
  export interface ColumnData extends PartialColumnData {
@@ -46,9 +48,20 @@ export declare class ValueColumn<T> extends Callable implements Column<T> {
46
48
  get unique(): ValueColumn<T>;
47
49
  get autoIncrement(): OptionalColumn<T>;
48
50
  primaryKey<K = string>(create?: () => EV<T>): PrimaryColumn<T, K>;
49
- references<X extends T>(column: Expr<X> | (() => Expr<X>)): ValueColumn<X>;
51
+ references<X extends T>(column: Expr<X> | (() => Expr<X>)): ForeignKey<X>;
50
52
  default(value: DefaultValue<T>): OptionalColumn<T>;
51
53
  }
54
+ export declare enum Action {
55
+ NoAction = "no action",
56
+ Restrict = "restrict",
57
+ SetNull = "set null",
58
+ SetDefault = "set default",
59
+ Cascade = "cascade"
60
+ }
61
+ export declare class ForeignKey<T> extends ValueColumn<T> {
62
+ onUpdate(value: `${Action}`): ForeignKey<unknown>;
63
+ onDelete(value: `${Action}`): ForeignKey<unknown>;
64
+ }
52
65
  export declare class OptionalColumn<T> extends ValueColumn<T> {
53
66
  [Column.IsOptional]: true;
54
67
  }
@@ -40,7 +40,7 @@ class ValueColumn extends Callable {
40
40
  });
41
41
  }
42
42
  references(column2) {
43
- return new ValueColumn({
43
+ return new ForeignKey({
44
44
  ...this[Column.Data],
45
45
  references() {
46
46
  return ExprData.create(Expr.isExpr(column2) ? column2 : column2());
@@ -57,6 +57,28 @@ class ValueColumn extends Callable {
57
57
  function createDefaultValue(value) {
58
58
  return typeof value === "function" && !Expr.isExpr(value) ? () => ExprData.create(value()) : ExprData.create(value);
59
59
  }
60
+ var Action = /* @__PURE__ */ ((Action2) => {
61
+ Action2["NoAction"] = "no action";
62
+ Action2["Restrict"] = "restrict";
63
+ Action2["SetNull"] = "set null";
64
+ Action2["SetDefault"] = "set default";
65
+ Action2["Cascade"] = "cascade";
66
+ return Action2;
67
+ })(Action || {});
68
+ class ForeignKey extends ValueColumn {
69
+ onUpdate(value) {
70
+ return new ForeignKey({
71
+ ...this[Column.Data],
72
+ onUpdate: value
73
+ });
74
+ }
75
+ onDelete(value) {
76
+ return new ForeignKey({
77
+ ...this[Column.Data],
78
+ onDelete: value
79
+ });
80
+ }
81
+ }
60
82
  class OptionalColumn extends ValueColumn {
61
83
  [Column.IsOptional];
62
84
  }
@@ -147,8 +169,10 @@ class UnTyped extends Callable {
147
169
  }
148
170
  const column = new UnTyped();
149
171
  export {
172
+ Action,
150
173
  Column,
151
174
  ColumnType,
175
+ ForeignKey,
152
176
  ObjectColumn,
153
177
  OptionalColumn,
154
178
  OptionalObjectColumn,