rado 1.2.1 → 1.3.0-preview.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +163 -257
  2. package/dist/compat.d.ts +3 -0
  3. package/dist/core/Builder.d.ts +16 -9
  4. package/dist/core/Builder.js +52 -4
  5. package/dist/core/Column.d.ts +25 -21
  6. package/dist/core/Column.js +39 -23
  7. package/dist/core/Constraint.d.ts +2 -1
  8. package/dist/core/Constraint.js +2 -1
  9. package/dist/core/Database.d.ts +5 -4
  10. package/dist/core/Database.js +14 -13
  11. package/dist/core/Emitter.d.ts +2 -2
  12. package/dist/core/Index.d.ts +1 -1
  13. package/dist/core/Internal.d.ts +19 -2
  14. package/dist/core/Internal.js +29 -11
  15. package/dist/core/Param.js +2 -0
  16. package/dist/core/Queries.d.ts +1 -0
  17. package/dist/core/Queries.js +11 -1
  18. package/dist/core/Resolver.js +2 -2
  19. package/dist/core/Schema.d.ts +1 -3
  20. package/dist/core/Selection.d.ts +9 -4
  21. package/dist/core/Selection.js +94 -5
  22. package/dist/core/Sql.d.ts +4 -3
  23. package/dist/core/Sql.js +8 -2
  24. package/dist/core/Table.d.ts +18 -11
  25. package/dist/core/Table.js +45 -25
  26. package/dist/core/View.d.ts +34 -0
  27. package/dist/core/View.js +153 -0
  28. package/dist/core/Virtual.d.ts +7 -2
  29. package/dist/core/Virtual.js +14 -10
  30. package/dist/core/expr/Conditions.d.ts +7 -7
  31. package/dist/core/expr/Include.d.ts +4 -4
  32. package/dist/core/expr/Include.js +2 -2
  33. package/dist/core/expr/Input.d.ts +1 -1
  34. package/dist/core/expr/Input.js +5 -1
  35. package/dist/core/query/CTE.d.ts +3 -5
  36. package/dist/core/query/CTE.js +1 -13
  37. package/dist/core/query/Delete.d.ts +10 -10
  38. package/dist/core/query/Delete.js +2 -2
  39. package/dist/core/query/Insert.d.ts +6 -5
  40. package/dist/core/query/Insert.js +22 -9
  41. package/dist/core/query/Query.d.ts +26 -11
  42. package/dist/core/query/Select.d.ts +41 -8
  43. package/dist/core/query/Select.js +235 -25
  44. package/dist/core/query/Shared.d.ts +2 -1
  45. package/dist/core/query/Shared.js +8 -5
  46. package/dist/core/query/Update.d.ts +9 -9
  47. package/dist/core/query/Update.js +24 -12
  48. package/dist/driver/better-sqlite3.js +4 -0
  49. package/dist/driver/bun-sqlite.js +3 -0
  50. package/dist/driver/d1.js +3 -0
  51. package/dist/driver/libsql.js +4 -0
  52. package/dist/driver/mysql2.js +5 -0
  53. package/dist/driver/pg.d.ts +17 -1
  54. package/dist/driver/pg.js +10 -1
  55. package/dist/driver/pglite.js +21 -6
  56. package/dist/driver/sql.js.js +4 -0
  57. package/dist/index.d.ts +1 -0
  58. package/dist/index.js +1 -0
  59. package/dist/mysql/columns.d.ts +30 -30
  60. package/dist/mysql/columns.js +25 -24
  61. package/dist/mysql/dialect.js +6 -2
  62. package/dist/mysql/diff.js +1 -1
  63. package/dist/mysql.d.ts +1 -0
  64. package/dist/mysql.js +2 -0
  65. package/dist/postgres/columns.d.ts +103 -33
  66. package/dist/postgres/columns.js +317 -35
  67. package/dist/postgres/diff.js +3 -3
  68. package/dist/postgres/enum.d.ts +17 -0
  69. package/dist/postgres/enum.js +64 -0
  70. package/dist/postgres/schema.d.ts +19 -0
  71. package/dist/postgres/schema.js +47 -0
  72. package/dist/postgres.d.ts +3 -1
  73. package/dist/postgres.js +9 -2
  74. package/dist/sqlite/columns.d.ts +29 -14
  75. package/dist/sqlite/columns.js +41 -9
  76. package/dist/sqlite/dialect.js +3 -1
  77. package/dist/sqlite/diff.js +1 -1
  78. package/dist/sqlite/functions.d.ts +1 -1
  79. package/dist/sqlite/functions.js +1 -1
  80. package/dist/sqlite.d.ts +1 -0
  81. package/dist/sqlite.js +3 -6
  82. package/dist/universal/columns.d.ts +8 -8
  83. package/dist/universal/columns.js +10 -9
  84. package/dist/universal/functions.d.ts +1 -1
  85. package/dist/universal/functions.js +1 -1
  86. package/package.json +57 -38
package/README.md CHANGED
@@ -3,332 +3,238 @@
3
3
 
4
4
  # rado
5
5
 
6
- Fully typed, lightweight TypeScript query builder.
6
+ A fully typed, lightweight TypeScript query builder for SQLite, PostgreSQL and
7
+ MySQL.
8
+
9
+ ```ts
10
+ import {eq} from 'rado'
11
+
12
+ const greatPosts = await db.select().from(Post).where(eq(Post.rating, 5))
13
+ ```
14
+
15
+ If you've used [Drizzle ORM](https://orm.drizzle.team), the code above looks
16
+ familiar. That's intentional. Rado deliberately aligns its query building
17
+ syntax with Drizzle, while making a few different choices under the hood:
18
+ simpler types, immutable queries, a much smaller footprint and queries that can
19
+ run on any of the supported databases. Read about the project's history in
20
+ [Taking the Drizzle challenge](https://ben.mk/notes/taking-the-drizzle-challenge).
7
21
 
8
22
  ## Features
9
23
 
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
24
+ - **Fully typed**: schema definitions drive query and result types, with
25
+ plain, readable TypeScript types
26
+ - **Immutable queries**: every method returns a new query, so you can branch
27
+ and reuse query fragments without surprises
28
+ - **Universal queries**: write a query once, run it on SQLite, PostgreSQL or
29
+ MySQL, chosen at runtime
30
+ - **First-class JSON columns**: select and filter on typed JSON fields with
31
+ plain property access
32
+ - **`include` instead of an ORM**: fetch related rows as nested arrays or
33
+ objects inside the query builder, no relations setup required
34
+ - **Auto-migrations**: diff your schema against the database and update it
35
+ anywhere, including the browser
36
+ - **Zero dependencies, small bundle**: `rado` + the PostgreSQL utilities
37
+ bundle to roughly 8.5 kB gzipped
38
+ - **No code generation step**: your schema is just TypeScript
16
39
 
17
40
  ## Installation
18
41
 
19
- <pre>npm install <a href="https://www.npmjs.com/package/rado">rado</a></pre>
42
+ ```sh
43
+ npm install rado
44
+ ```
20
45
 
21
- ## Quick Start
46
+ Rado is also published on JSR as [@rado/rado](https://jsr.io/@rado/rado) for
47
+ Deno users.
22
48
 
23
- ```typescript
24
- import {table} from 'rado'
25
- import {text, integer} from 'rado/postgres'
49
+ ## Quick start
50
+
51
+ ```ts
52
+ import {eq} from 'rado'
53
+ import {integer, pgTable, text} from 'rado/postgres'
26
54
  import {connect} from 'rado/driver/pg'
27
55
  import {Pool} from 'pg'
28
56
 
29
- // Define your schema
30
- const User = table("user", {
31
- id: integer().primaryKey(),
57
+ // Define a schema
58
+ const User = pgTable('user', {
59
+ id: integer().primaryKey().generatedAlwaysAsIdentity(),
32
60
  name: text().notNull(),
33
61
  email: text().unique()
34
62
  })
35
63
 
36
- // Connect to the database
37
- const db = connect(new Pool({
38
- host: 'localhost',
39
- database: 'my_database'
40
- }))
64
+ // Connect a database
65
+ const db = connect(new Pool({connectionString: process.env.DATABASE_URL}))
41
66
 
42
- // Perform a query
43
- const users = await db.select().from(User)
44
- console.log(users)
45
- ```
67
+ // Create the table and start querying
68
+ await db.create(User)
69
+
70
+ await db.insert(User).values({name: 'Ada', email: 'ada@example.com'})
46
71
 
47
- ## Supported Databases
72
+ const users = await db.select().from(User).where(eq(User.name, 'Ada'))
73
+ ```
48
74
 
49
- Currently supported drivers:
75
+ ## Documentation
50
76
 
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 |
77
+ The full documentation lives in [docs](docs/README.md). A map of the territory:
57
78
 
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
- | @libsql/client | rado/driver/libsql |
64
- | Cloudflare D1 | rado/driver/d1 |
79
+ | Section | What's inside |
80
+ | ------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
81
+ | [Getting started](docs/getting-started.md) | Install, connect, define a schema, run your first queries |
82
+ | [Drivers](docs/drivers.md) | Every supported driver and how to connect it |
83
+ | [Tables](docs/schema/tables.md) | Defining tables, column modifiers, defaults, references |
84
+ | [Column types](docs/schema/columns-sqlite.md) | Per dialect: [SQLite](docs/schema/columns-sqlite.md), [PostgreSQL](docs/schema/columns-postgres.md), [MySQL](docs/schema/columns-mysql.md), [universal](docs/schema/columns-universal.md) |
85
+ | [Indexes & constraints](docs/schema/indexes-and-constraints.md) | Primary keys, unique constraints, foreign keys, indexes |
86
+ | [Views](docs/schema/views.md) | Views and PostgreSQL materialized views |
87
+ | [Schemas & enums](docs/schema/postgres-schemas-and-enums.md) | `pgSchema` and `pgEnum` |
88
+ | [Custom column types](docs/schema/custom-types.md) | Mapping your own types to and from the database |
89
+ | [Select](docs/queries/select.md) | Selections, filtering, ordering, grouping, pagination, locking |
90
+ | [Joins](docs/queries/joins.md) | Inner/left/right/full/cross joins and lateral variants |
91
+ | [Insert](docs/queries/insert.md) | Values, bulk inserts, upserts, insert-from-select |
92
+ | [Update](docs/queries/update.md) / [Delete](docs/queries/delete.md) | Modifying and removing rows, `returning` |
93
+ | [Filter operators](docs/queries/operators.md) | `eq`, `and`, `or`, `inArray`, `like`, `between` and friends |
94
+ | [Aggregates](docs/queries/aggregates.md) | `count`, `sum`, `avg`, `min`, `max` and grouping |
95
+ | [Include](docs/queries/include.md) | Nested results without an ORM |
96
+ | [JSON](docs/queries/json.md) | Typed JSON columns, selecting and filtering nested fields |
97
+ | [Set operations](docs/queries/set-operations.md) | `union`, `intersect`, `except` |
98
+ | [Subqueries & CTEs](docs/queries/subqueries-and-ctes.md) | `.as()`, `$with`, recursive CTEs |
99
+ | [The sql tag](docs/queries/sql.md) | Raw SQL escape hatches, safely |
100
+ | [Transactions & batch](docs/runtime/transactions-and-batch.md) | Atomic operations, rollbacks, batching |
101
+ | [Prepared statements](docs/runtime/prepared-statements.md) | Placeholders, `prepare`, inspecting generated SQL |
102
+ | [Migrations](docs/runtime/migrations.md) | `db.create`, `db.migrate` and how diffing works |
103
+ | [Universal queries](docs/runtime/universal-queries.md) | One query, three databases |
104
+ | [Coming from Drizzle](docs/drizzle.md) | Side-by-side comparison and migration notes |
65
105
 
66
- | `MySQL ` | `import ` |
67
- | -------------------------- | ------------------------------ |
68
- | mysql2 | rado/driver/mysql2 |
106
+ ## Supported drivers
69
107
 
70
- Pass an instance of the database to the `connect` function to get started:
108
+ Pass a database client instance to the matching `connect` function:
71
109
 
72
110
  ```ts
73
111
  import Database from 'better-sqlite3'
74
112
  import {connect} from 'rado/driver/better-sqlite3'
75
113
 
76
- const db = connect(new Database('foobar.db'))
77
- ```
78
-
79
- ## Querying
80
-
81
- ### Select
82
-
83
- Select operations allow you to retrieve data from your database.
84
-
85
- ```typescript
86
- // Basic select
87
- const allUsers = await db.select().from(User)
88
-
89
- // Select specific columns
90
- const userNames = await db.select({id: User.id, name: User.name}).from(User)
114
+ const db = connect(new Database('app.db'))
91
115
  ```
92
116
 
93
- ### Where conditions
117
+ | Database | Package | Import | Sync/async |
118
+ | ---------- | ---------------------------------------------------------------------------------- | ---------------------------- | ---------- |
119
+ | PostgreSQL | [pg](https://www.npmjs.com/package/pg) | `rado/driver/pg` | async |
120
+ | PostgreSQL | [@electric-sql/pglite](https://www.npmjs.com/package/@electric-sql/pglite) | `rado/driver/pglite` | async |
121
+ | PostgreSQL | [@neondatabase/serverless](https://www.npmjs.com/package/@neondatabase/serverless) | `rado/driver/pg` | async |
122
+ | PostgreSQL | [@vercel/postgres](https://www.npmjs.com/package/@vercel/postgres) | `rado/driver/pg` | async |
123
+ | SQLite | [better-sqlite3](https://www.npmjs.com/package/better-sqlite3) | `rado/driver/better-sqlite3` | sync |
124
+ | SQLite | `bun:sqlite` | `rado/driver/bun-sqlite` | sync |
125
+ | SQLite | [sql.js](https://www.npmjs.com/package/sql.js) | `rado/driver/sql.js` | sync |
126
+ | SQLite | [@libsql/client](https://www.npmjs.com/package/@libsql/client) | `rado/driver/libsql` | async |
127
+ | SQLite | Cloudflare D1 | `rado/driver/d1` | async |
128
+ | MySQL | [mysql2](https://www.npmjs.com/package/mysql2) | `rado/driver/mysql2` | async |
94
129
 
95
- Where conditions help you filter your query results.
130
+ Synchronous drivers give you a fully synchronous database. No `await`
131
+ required, although awaiting queries still works. See
132
+ [Drivers](docs/drivers.md) for details.
96
133
 
97
- ```typescript
98
- import {eq, and, or, gt, isNull} from 'rado'
134
+ ## A taste of the API
99
135
 
100
- // Simple equality
101
- const john = await db.select().from(User).where(eq(User.name, 'John'))
136
+ ### Immutable queries
102
137
 
103
- // Complex conditions
104
- const users = await db.select().from(User)
105
- .where(
106
- gt(User.age, 18),
107
- or(
108
- eq(User.name, 'Alice'),
109
- isNull(User.email)
110
- )
111
- )
112
- ```
113
-
114
- ### Joins
115
-
116
- Joins allow you to combine data from multiple tables.
138
+ Queries are values. Branch them, reuse them, store them in variables. Nothing
139
+ mutates:
117
140
 
118
- ```typescript
119
- const usersWithPosts = await db.select({
120
- userName: User.name,
121
- postTitle: Post.title,
122
- })
123
- .from(User)
124
- .leftJoin(Post, eq(User.id, Post.authorId))
125
- ```
126
-
127
- ### Ordering and Grouping
128
-
129
- You can order and group your query results:
130
-
131
- ```typescript
132
- import {desc, asc, count} from 'rado'
141
+ ```ts
142
+ import {count, gt} from 'rado'
133
143
 
134
- const orderedUsers = await db.select()
135
- .from(User)
136
- .orderBy(asc(User.name))
144
+ const allUsers = db.select(count()).from(User)
145
+ const filtered = allUsers.where(gt(User.id, 1))
137
146
 
138
- const userPostCounts = await db.select({
139
- userName: User.name,
140
- postCount: count(Post.id)
141
- })
142
- .from(User)
143
- .leftJoin(Post, eq(User.id, Post.authorId))
144
- .groupBy(User.name)
147
+ const [total] = await allUsers // unaffected by the where above
148
+ const [matching] = await filtered
145
149
  ```
146
150
 
147
- ### Pagination
151
+ ### Nested results with `include`
148
152
 
149
- Pagination helps you manage large datasets by retrieving results in smaller chunks:
153
+ No relations file, no separate query API. Declare the relationship inline:
150
154
 
151
- ```typescript
152
- const pageSize = 10
153
- const page = 2
155
+ ```ts
156
+ import {eq, include} from 'rado'
154
157
 
155
- const paginatedUsers = await db.select()
158
+ const usersWithPosts = await db
159
+ .select({
160
+ ...User,
161
+ posts: include(db.select().from(Post).where(eq(Post.authorId, User.id)))
162
+ })
156
163
  .from(User)
157
- .limit(pageSize)
158
- .offset((page - 1) * pageSize)
159
164
  ```
160
165
 
161
- ### JSON columns
166
+ Each row comes back with a typed `posts` array. Use `include.one` for a single
167
+ related row. More in [Include](docs/queries/include.md).
162
168
 
163
- Rado provides first-class support for JSON columns:
169
+ ### Typed JSON columns
164
170
 
165
- ```typescript
166
- import {pgTable, serial, text, jsonb} from 'rado/postgres'
171
+ ```ts
172
+ import {eq} from 'rado'
173
+ import {jsonb, pgTable, serial, text} from 'rado/postgres'
167
174
 
168
- const User = pgTable("user", {
175
+ const User = pgTable('user', {
169
176
  id: serial().primaryKey(),
170
177
  name: text(),
171
- metadata: jsonb<{subscribed: boolean}>()
178
+ settings: jsonb<{theme: 'light' | 'dark'; notifications: boolean}>()
172
179
  })
173
180
 
174
- const subscribedUsers = await db.select()
181
+ const darkSide = await db
182
+ .select(User.name)
175
183
  .from(User)
176
- .where(eq(User.metadata.subscribed, true))
184
+ .where(eq(User.settings.theme, 'dark'))
177
185
  ```
178
186
 
179
- ### Subqueries
187
+ That `User.settings.theme` is a real, typed expression that compiles to the
188
+ correct JSON access syntax for your database. More in
189
+ [JSON](docs/queries/json.md).
180
190
 
181
- ```typescript
182
- const subquery = db.select({authorId: Post.authorId})
183
- .from(Post)
184
- .groupBy(Post.authorId)
185
- .having(gt(count(Post.id), 5))
186
- .as('authorIds')
191
+ ### Universal queries
187
192
 
188
- const prolificAuthors = await db.select()
189
- .from(User)
190
- .where(inArray(User.id, subquery))
191
- ```
192
-
193
- ### Include
194
-
195
- Aggregate rows using the `include` function:
196
-
197
- ```typescript
198
- import {include} from 'rado'
199
-
200
- const usersWithPosts = await db.select({
201
- ...User,
202
- posts: include(
203
- db.select().from(Post).where(eq(Post.authorId, User.id))
204
- )
205
- }).from(User)
206
-
207
- // Use include.one for a single related record
208
- const usersWithLatestPost = await db.select({
209
- ...User,
210
- latestPost: include.one(
211
- db.select()
212
- .from(Post)
213
- .where(eq(Post.authorId, User.id))
214
- .orderBy(desc(Post.createdAt))
215
- .limit(1)
216
- )
217
- }).from(User)
218
- ```
193
+ Define your schema with `rado/universal` column types and pick the database at
194
+ runtime:
219
195
 
220
- ### SQL Operator
221
-
222
- The `sql` operator allows you to write raw SQL and interpolate values safely:
223
-
224
- ```typescript
225
- import {sql} from 'rado'
226
-
227
- const minAge = 18
228
- const adultUsers = await db.select()
229
- .from(User)
230
- .where(sql`${User.age} >= ${minAge}`)
231
- ```
232
-
233
- ## Modifying Data
234
-
235
- ### Insert
236
-
237
- Insert operations allow you to add new records to your database:
238
-
239
- ```typescript
240
- // Single insert
241
- const newUser = await db.insert(User)
242
- .values({name: 'Alice', email: 'alice@example.com'})
243
- .returning()
244
-
245
- // Bulk insert
246
- const newUsers = await db.insert(User)
247
- .values([
248
- {name: 'Bob', email: 'bob@example.com'},
249
- {name: 'Charlie', email: 'charlie@example.com'},
250
- ])
251
- .returning()
252
- ```
253
-
254
- ### Update
255
-
256
- Update operations modify existing records:
257
-
258
- ```typescript
259
- const updatedCount = await db.update(User)
260
- .set({name: 'Johnny'})
261
- .where(eq(User.name, 'John'))
262
- ```
263
-
264
- ### Delete
196
+ ```ts
197
+ import {table} from 'rado'
198
+ import {id, text} from 'rado/universal'
265
199
 
266
- Delete operations remove records from your database:
200
+ const User = table('user', {
201
+ id: id(), // auto-incrementing primary key on every dialect
202
+ name: text()
203
+ })
267
204
 
268
- ```typescript
269
- await db.delete(User).where(eq(User.name, 'John'))
205
+ const db = useSqlite ? sqliteDb : postgresDb
206
+ const names = await db.select(User.name).from(User)
270
207
  ```
271
208
 
272
- ### Transactions
209
+ More in [Universal queries](docs/runtime/universal-queries.md).
273
210
 
274
- Transactions allow you to group multiple operations into a single atomic unit.
211
+ ### Auto-migrations
275
212
 
276
- ```typescript
277
- const result = await db.transaction(async (tx) => {
278
- const user = await tx.insert(User)
279
- .values({ name: 'Alice', email: 'alice@example.com' })
280
- .returning()
281
- const post = await tx.insert(Post)
282
- .values({ title: 'My First Post', authorId: user.id })
283
- .returning()
284
- return {user, post}
285
- })
213
+ ```ts
214
+ // Compares the defined schema to the actual database and applies the diff
215
+ await db.migrate(User, Post)
286
216
  ```
287
217
 
288
- ## Universal Queries
218
+ Works in Node.js, Bun, Deno and the browser. It is powerful and a little
219
+ direct, so read [Migrations](docs/runtime/migrations.md) before pointing it at
220
+ production.
289
221
 
290
- Rado provides a universal query builder that works across different database
291
- engines, whether they run synchronously or asynchronously. This is useful for
292
- writing database-agnostic code.
222
+ ## Coming from Drizzle?
293
223
 
294
- ```typescript
295
- import {table} from 'rado'
296
- import {id, text} from 'rado/universal'
224
+ Most queries port by changing imports:
297
225
 
298
- const User = table('user', {
299
- id: id(),
300
- name: text()
301
- })
302
-
303
- const db = process.env.SQLITE ? sqliteDb : postgresDb
304
-
305
- const userNames = await db.select(User.name).from(User)
226
+ ```ts
227
+ import {sql, eq, and, or} from 'rado' // was 'drizzle-orm'
228
+ import {pgTable, integer, text} from 'rado/postgres' // was 'drizzle-orm/pg-core'
229
+ import {sqliteTable} from 'rado/sqlite' // was 'drizzle-orm/sqlite-core'
230
+ import {mysqlTable} from 'rado/mysql' // was 'drizzle-orm/mysql-core'
306
231
  ```
307
232
 
308
- ## Custom Column Types
309
-
310
- You can define custom column types, such as a boolean column that is stored as a
311
- tinyint in the database:
233
+ Rado also exposes compatibility helpers such as `InferSelectModel`,
234
+ `InferInsertModel`, `getTableColumns` and `TransactionRollbackError`. See
235
+ [Coming from Drizzle](docs/drizzle.md) for a full side-by-side comparison of
236
+ what's the same, what's better and what's missing.
312
237
 
313
- ```typescript
314
- import {Column, column, table, sql} from 'rado'
238
+ ## License
315
239
 
316
- export function bool(name?: string): Column<boolean | null> {
317
- return column({
318
- name,
319
- type: sql`tinyint(1)`,
320
- mapFromDriverValue(value: number): boolean {
321
- return value === 1
322
- },
323
- mapToDriverValue(value: boolean): number {
324
- return value ? 1 : 0
325
- }
326
- })
327
- }
328
-
329
- // Usage
330
- const User = table('user', {
331
- // ...
332
- isActive: bool()
333
- })
334
- ```
240
+ MIT
package/dist/compat.d.ts CHANGED
@@ -3,6 +3,9 @@ import type { Sql } from './core/Sql.js';
3
3
  import type { Table, TableDefinition, TableFields } from './core/Table.js';
4
4
  export type { InsertRow as InferInsertModel, SelectRow as InferSelectModel } from './core/Table.js';
5
5
  export type SQL<T = unknown> = Sql<T>;
6
+ export declare namespace SQL {
7
+ type Aliased<T = unknown> = Sql<T>;
8
+ }
6
9
  export type SQLWrapper<T = unknown> = HasSql<T>;
7
10
  export declare function getTableColumns<Definition extends TableDefinition>(table: Table<Definition>): TableFields<Definition>;
8
11
  export { Rollback as TransactionRollbackError } from './core/Database.js';
@@ -1,14 +1,15 @@
1
1
  import { type HasSql, internalData } from './Internal.js';
2
2
  import type { IsPostgres, QueryMeta } from './MetaData.js';
3
3
  import type { QueryData, SingleQuery } from './Queries.js';
4
- import type { SelectionInput, SelectionRow } from './Selection.js';
5
- import type { Table, TableDefinition } from './Table.js';
6
- import { type CTE } from './query/CTE.js';
7
- import { DeleteFrom } from './query/Delete.js';
8
- import { InsertInto } from './query/Insert.js';
4
+ import type { CTE } from './query/CTE.js';
5
+ import { Delete, DeleteFrom } from './query/Delete.js';
6
+ import { Insert, InsertInto } from './query/Insert.js';
9
7
  import type { DeleteQuery, FromGuard, FromQuery, FromRow, InsertQuery, QueryBase, SelectionQuery, UpdateQuery } from './query/Query.js';
10
8
  import type { UnionBase, WithSelection, WithoutSelection } from './query/Select.js';
11
- import { UpdateTable } from './query/Update.js';
9
+ import { Update, UpdateTable } from './query/Update.js';
10
+ import { type SelectionInput, type SelectionRow } from './Selection.js';
11
+ import { Sql } from './Sql.js';
12
+ import type { Table, TableDefinition } from './Table.js';
12
13
  declare class BuilderBase<Meta extends QueryMeta> {
13
14
  readonly [internalData]: QueryData<Meta> & QueryBase;
14
15
  constructor(data?: QueryData<Meta> & QueryBase);
@@ -21,15 +22,21 @@ declare class BuilderBase<Meta extends QueryMeta> {
21
22
  select<Input extends SelectionInput>(select: Input): WithSelection<Input, Meta>;
22
23
  selectDistinct(): WithoutSelection<Meta>;
23
24
  selectDistinct<Input extends SelectionInput>(selection: Input): WithSelection<Input, Meta>;
24
- selectDistinctOn(this: Builder<IsPostgres>, columns: Array<HasSql>): WithoutSelection<Meta>;
25
- selectDistinctOn<Input extends SelectionInput>(this: Builder<IsPostgres>, columns: Array<HasSql>, selection: Input): WithSelection<Input, Meta>;
25
+ selectDistinctOn(this: BuilderBase<IsPostgres>, columns: Array<HasSql>): WithoutSelection<Meta>;
26
+ selectDistinctOn<Input extends SelectionInput>(this: BuilderBase<IsPostgres>, columns: Array<HasSql>, selection: Input): WithSelection<Input, Meta>;
26
27
  update<Definition extends TableDefinition>(table: Table<Definition>): UpdateTable<Definition, Meta>;
27
28
  insert<Definition extends TableDefinition>(into: Table<Definition>): InsertInto<Definition, Meta>;
28
29
  delete<Definition extends TableDefinition>(from: Table<Definition>): DeleteFrom<Definition, Meta>;
29
30
  }
30
31
  export declare class Builder<Meta extends QueryMeta> extends BuilderBase<Meta> {
32
+ $with<Input extends SelectionInput>(cteName: string, columns: Input): {
33
+ as(query: Sql): CTE<Input>;
34
+ };
31
35
  $with(cteName: string): {
32
- as<Input extends SelectionInput>(query: UnionBase<Input, Meta>): CTE<Input>;
36
+ as<Input>(query: UnionBase<Input, Meta>): CTE<Input>;
37
+ as<Input>(query: Delete<Input, Meta>): CTE<Input>;
38
+ as<Input>(query: Update<Input, Meta>): CTE<Input>;
39
+ as<Input>(query: Insert<Input, Meta>): CTE<Input>;
33
40
  };
34
41
  with(...definitions: Array<CTE>): BuilderBase<Meta>;
35
42
  withRecursive(...definitions: Array<CTE>): BuilderBase<Meta>;
@@ -1,10 +1,47 @@
1
1
  // src/core/Builder.ts
2
- import { getData, internalData } from "./Internal.js";
3
- import { createCTE } from "./query/CTE.js";
2
+ import {
3
+ getData,
4
+ getQuery,
5
+ getSelection,
6
+ getSql,
7
+ hasData,
8
+ hasQuery,
9
+ hasSql,
10
+ hasSelection,
11
+ internalData,
12
+ internalSelection,
13
+ internalQuery
14
+ } from "./Internal.js";
4
15
  import { Delete, DeleteFrom } from "./query/Delete.js";
5
16
  import { Insert, InsertInto } from "./query/Insert.js";
6
17
  import { Select } from "./query/Select.js";
7
18
  import { Update, UpdateTable } from "./query/Update.js";
19
+ import {
20
+ StarSelection
21
+ } from "./Selection.js";
22
+ import { Sql } from "./Sql.js";
23
+ import { virtualTarget } from "./Virtual.js";
24
+ function aliasSql(input, name) {
25
+ const expr = getSql(input);
26
+ if (expr.alias) return expr;
27
+ const aliased = new Sql((emitter) => expr.emit(emitter));
28
+ aliased.alias = name;
29
+ aliased.mapFromDriverValue = expr.mapFromDriverValue;
30
+ return aliased;
31
+ }
32
+ function aliasUnnamedFields(input, name) {
33
+ if (hasSql(input)) {
34
+ if (!name) return input;
35
+ return aliasSql(input, name);
36
+ }
37
+ if (!input || typeof input !== "object") return input;
38
+ return Object.fromEntries(
39
+ Object.entries(input).map(([key, value]) => [
40
+ key,
41
+ aliasUnnamedFields(value, key)
42
+ ])
43
+ );
44
+ }
8
45
  var BuilderBase = class {
9
46
  [internalData];
10
47
  constructor(data = {}) {
@@ -48,10 +85,21 @@ var BuilderBase = class {
48
85
  }
49
86
  };
50
87
  var Builder = class extends BuilderBase {
51
- $with(cteName) {
88
+ $with(cteName, columns) {
52
89
  return {
53
90
  as(query) {
54
- return createCTE(cteName, query);
91
+ let input = hasQuery(query) ? hasSelection(query) ? getSelection(query).input : columns : columns;
92
+ const data = hasQuery(query) && hasData(query) ? getData(query) : void 0;
93
+ if (input && data?.compound?.length > 1)
94
+ input = aliasUnnamedFields(input);
95
+ const target = virtualTarget(cteName, input ?? {});
96
+ return {
97
+ ...target,
98
+ [internalSelection]: new StarSelection(target),
99
+ get [internalQuery]() {
100
+ return hasQuery(query) ? getQuery(query).nameSelf(cteName) : getSql(query);
101
+ }
102
+ };
55
103
  }
56
104
  };
57
105
  }