qubu 0.4.0 → 0.4.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.
- package/dist/{canonical-BbnqavJm.mjs → canonical-BXUguqfo.mjs} +23 -23
- package/dist/codegen.d.mts +25 -33
- package/dist/codegen.mjs +38 -45
- package/dist/column-hqKr7-1I.mjs +620 -0
- package/dist/{complete-D5Djh-zo.mjs → complete-WYyVozgK.mjs} +50 -50
- package/dist/{complete-types-BavBtv8J.d.mts → complete-types-IjEn5VPN.d.mts} +56 -56
- package/dist/core.d.mts +2 -2
- package/dist/core.mjs +4 -4
- package/dist/ddl.d.mts +12 -12
- package/dist/ddl.mjs +13 -14
- package/dist/{dialect-b2-Z6uBF.mjs → dialect-wUKrnPMB.mjs} +2 -2
- package/dist/diff.d.mts +1 -1
- package/dist/diff.mjs +6 -7
- package/dist/{index-DH0qV6aS.d.mts → index-1DpA3mUh.d.mts} +20 -20
- package/dist/{index-D3ZOwPT-.d.mts → index-CPvfEheG.d.mts} +13 -15
- package/dist/index.d.mts +1 -1
- package/dist/index.mjs +94 -95
- package/dist/introspection.d.mts +8 -8
- package/dist/introspection.mjs +29 -30
- package/dist/{json-CUZlv4HT.mjs → json-Db7XRD91.mjs} +2 -2
- package/dist/migration.d.mts +20 -21
- package/dist/migration.mjs +5 -6
- package/dist/{mysql-CKEKGnj4.mjs → mysql-B_cYzzX2.mjs} +237 -7
- package/dist/mysql.d.mts +2 -2
- package/dist/mysql.mjs +3 -3
- package/dist/{on-conflict-4MOBl51J.mjs → on-conflict-hfPW0KmQ.mjs} +3 -4
- package/dist/postgres.d.mts +5 -5
- package/dist/postgres.mjs +6 -6
- package/dist/{registry-CXV8u7Pt.mjs → registry-BRMLYwDp.mjs} +26 -29
- package/dist/{relational-DuQ9IHSb.mjs → relational-BZ3WDPzC.mjs} +2 -2
- package/dist/schema.d.mts +1 -1
- package/dist/schema.mjs +5 -6
- package/dist/{snapshot-DJpfmxhQ.mjs → snapshot-C-W65HEd.mjs} +5 -5
- package/dist/snapshot.d.mts +3 -3
- package/dist/snapshot.mjs +5 -7
- package/dist/{source-DGO3DRgg.mjs → source-BcS2AsIg.mjs} +6 -8
- package/dist/{serialize-CFtYYAdk.mjs → sqlite-Cg0nwYEH.mjs} +326 -12
- package/dist/sqlite.d.mts +1 -1
- package/dist/sqlite.mjs +2 -2
- package/dist/{standard-BTVYKh_F.mjs → standard-DfcZEVOj.mjs} +1 -1
- package/dist/{table-D6rcs8SB.mjs → table-Bp5irMSj.mjs} +2 -3
- package/dist/{types-Deo_q43Y.d.mts → types-BK1COGZe.d.mts} +1113 -1128
- package/dist/{types-g-vVvj1B.mjs → types-JM3FcAnX.mjs} +7 -8
- package/dist/{types-CSNJTYaM.d.mts → types-JSZHpUEj.d.mts} +83 -84
- package/dist/{value-b6OFZXVS.mjs → value-Bi71Agyf.mjs} +1 -1
- package/dist/vite/ambient.d.ts +309 -340
- package/dist/vite.d.mts +5 -6
- package/dist/vite.mjs +3 -4
- package/docs/dialects-and-execution.md +45 -55
- package/docs/getting-started.md +4 -4
- package/docs/guides/better-auth.md +57 -0
- package/docs/guides/compose-queries.md +24 -67
- package/docs/guides/drizzle.md +23 -23
- package/docs/guides/extensions/dialects.md +4 -4
- package/docs/guides/extensions/sources-and-clauses.md +15 -15
- package/docs/guides/extensions/typed-expressions.md +13 -17
- package/docs/guides/extensions/unsafe-syntax.md +3 -3
- package/docs/guides/json.md +7 -7
- package/docs/guides/mutations.md +14 -21
- package/docs/guides/select/conditions.md +6 -8
- package/docs/guides/select/grouping-and-windows.md +4 -15
- package/docs/guides/select/ordering-and-pagination.md +6 -19
- package/docs/guides/select/overview.md +12 -26
- package/docs/guides/sql-templates.md +20 -24
- package/docs/guides/vite-plugin.md +9 -13
- package/docs/index.md +5 -7
- package/docs/query-model/fragments.md +4 -15
- package/docs/query-model/result-shapes.md +11 -14
- package/docs/query-model/source-scope.md +31 -49
- package/docs/reference/mysql-snapshot.md +2 -5
- package/docs/reference/postgres-snapshot.md +3 -3
- package/docs/reference/sqlite-snapshot.md +2 -5
- package/docs/reference/supported-surface.md +26 -26
- package/docs/schema/catalog-model.md +2 -5
- package/docs/schema/code-generation.md +18 -18
- package/docs/schema/columns-and-writes.md +17 -25
- package/docs/schema/constraints-and-indexes.md +36 -52
- package/docs/schema/ddl-emission.md +6 -6
- package/docs/schema/diff.md +5 -5
- package/docs/schema/introspection.md +7 -7
- package/docs/schema/migration-plans.md +9 -9
- package/docs/schema/snapshots.md +3 -3
- package/docs/schema/storage-and-schema-sql.md +11 -11
- package/docs/schema/tables-and-names.md +11 -14
- package/docs/sql-semantic-types.md +10 -10
- package/docs/troubleshooting.md +10 -10
- package/package.json +21 -33
- package/skills/qubu/agents/openai.yaml +3 -3
- package/dist/column-D-8OqGuV.mjs +0 -328
- package/dist/column-LV7oGQde.mjs +0 -296
- package/dist/drizzle-mysql.d.mts +0 -24
- package/dist/drizzle-mysql.mjs +0 -72
- package/dist/drizzle-postgres.d.mts +0 -24
- package/dist/drizzle-postgres.mjs +0 -73
- package/dist/drizzle-sqlite.d.mts +0 -57
- package/dist/drizzle-sqlite.mjs +0 -108
- package/dist/drizzle.d.mts +0 -13
- package/dist/drizzle.mjs +0 -2
- package/dist/errors-BGCoLe_r.mjs +0 -14
- package/dist/postgres-BK2APkKP.mjs +0 -236
- package/dist/runtime-Cn_Xgzta.mjs +0 -193
- package/dist/sqlite-CrsK0Fza.mjs +0 -320
- package/dist/types-DZsueVoI.d.mts +0 -45
package/dist/vite.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
//#region src/vite/globals.d.ts
|
|
2
2
|
/**
|
|
3
|
-
* Public runtime names that the `"use qubu"` transform can auto-import.
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
* the layered entrypoints.
|
|
3
|
+
* Public runtime names that the `"use qubu"` transform can auto-import. Keep this catalog aligned
|
|
4
|
+
* with the ordinary authoring surface of `qubu`; fragment internals, dialect construction, and
|
|
5
|
+
* schema extensions belong to the layered entrypoints.
|
|
7
6
|
*/
|
|
8
7
|
declare const qubuGlobals: readonly ["add", "alias", "all", "allowAll", "and", "asc", "asValue", "avg", "bigint", "between", "binary", "boolean", "call", "caseWhen", "cast", "check", "coalesce", "column", "concat", "count", "countDistinct", "correlate", "crossJoin", "cte", "date", "denseRank", "defaultValues", "deleteFrom", "desc", "distinct", "divide", "eq", "except", "execute", "executeRows", "externalDefault", "externalGeneratedColumn", "exists", "fetchFirst", "fetchNext", "foreignKey", "from", "fullJoin", "generatedColumn", "gt", "gte", "groupBy", "having", "identityColumn", "inList", "inQuery", "index", "innerJoin", "insertInto", "insertSelect", "integer", "intersect", "isDistinctFrom", "isNotDistinctFrom", "isNotNull", "isNull", "isTrue", "json", "jsonBoolean", "jsonExists", "jsonNumber", "jsonPath", "jsonText", "lateral", "leftJoin", "like", "lower", "lt", "lte", "max", "min", "modulo", "multiply", "naturalJoin", "nativeColumn", "nativeStorage", "ne", "not", "notExists", "notIn", "notLike", "nullsFirst", "nullsLast", "numeric", "nullable", "offset", "omit", "or", "order", "orderBy", "over", "portableStorage", "primaryKey", "qubu", "references", "recursiveCte", "render", "returning", "rightJoin", "rank", "rowNumber", "scalar", "schema", "schemaCall", "select", "sql", "stream", "subtract", "sum", "table", "text", "timestamp", "union", "unionAll", "unique", "uniqueConstraint", "update", "upper", "value", "values", "where", "withCte", "uuid"];
|
|
9
8
|
type QubuGlobal = (typeof qubuGlobals)[number];
|
|
@@ -25,8 +24,8 @@ interface QubuViteTransformResult {
|
|
|
25
24
|
}
|
|
26
25
|
/** A Vite-compatible plugin without a runtime dependency on Vite itself. */
|
|
27
26
|
interface QubuVitePlugin {
|
|
28
|
-
readonly name:
|
|
29
|
-
readonly enforce:
|
|
27
|
+
readonly name: "qubu:compiler-hint";
|
|
28
|
+
readonly enforce: "pre";
|
|
30
29
|
transform(code: string, id: string): QubuViteTransformResult | null;
|
|
31
30
|
}
|
|
32
31
|
declare function qubu(options?: QubuVitePluginOptions): QubuVitePlugin;
|
package/dist/vite.mjs
CHANGED
|
@@ -70,10 +70,9 @@ function isWhitespace$1(character) {
|
|
|
70
70
|
//#endregion
|
|
71
71
|
//#region src/vite/globals.ts
|
|
72
72
|
/**
|
|
73
|
-
* Public runtime names that the `"use qubu"` transform can auto-import.
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
* the layered entrypoints.
|
|
73
|
+
* Public runtime names that the `"use qubu"` transform can auto-import. Keep this catalog aligned
|
|
74
|
+
* with the ordinary authoring surface of `qubu`; fragment internals, dialect construction, and
|
|
75
|
+
* schema extensions belong to the layered entrypoints.
|
|
77
76
|
*/
|
|
78
77
|
const qubuGlobals = [
|
|
79
78
|
"add",
|
|
@@ -20,8 +20,8 @@ Construct the query without choosing a driver, then render it with the policy
|
|
|
20
20
|
the adapter expects:
|
|
21
21
|
|
|
22
22
|
```ts
|
|
23
|
-
import { render } from
|
|
24
|
-
import { postgresDialect } from
|
|
23
|
+
import { render } from "qubu"
|
|
24
|
+
import { postgresDialect } from "qubu/postgres"
|
|
25
25
|
|
|
26
26
|
const standard = render(query)
|
|
27
27
|
const postgres = render(query, postgresDialect())
|
|
@@ -40,15 +40,11 @@ capability requirement to the rendering boundary. PostgreSQL's `ilike()` is
|
|
|
40
40
|
the first such feature:
|
|
41
41
|
|
|
42
42
|
```ts
|
|
43
|
-
import { from, like, render, select, where } from
|
|
44
|
-
import { ilike, postgresDialect } from
|
|
45
|
-
import { sqliteDialect } from
|
|
43
|
+
import { from, like, render, select, where } from "qubu"
|
|
44
|
+
import { ilike, postgresDialect } from "qubu/postgres"
|
|
45
|
+
import { sqliteDialect } from "qubu/sqlite"
|
|
46
46
|
|
|
47
|
-
const postgresQuery = select(
|
|
48
|
-
{ name: users.name },
|
|
49
|
-
from(users),
|
|
50
|
-
where(ilike(users.name, '%ada%'))
|
|
51
|
-
)
|
|
47
|
+
const postgresQuery = select({ name: users.name }, from(users), where(ilike(users.name, "%ada%")))
|
|
52
48
|
|
|
53
49
|
render(postgresQuery, postgresDialect()) // supported
|
|
54
50
|
render(postgresQuery, sqliteDialect()) // TypeScript error
|
|
@@ -59,14 +55,10 @@ received from an untyped integration. Use the portable operator when the
|
|
|
59
55
|
query must render across dialects:
|
|
60
56
|
|
|
61
57
|
```ts
|
|
62
|
-
import { from, like, render, select, where } from
|
|
63
|
-
import { sqliteDialect } from
|
|
58
|
+
import { from, like, render, select, where } from "qubu"
|
|
59
|
+
import { sqliteDialect } from "qubu/sqlite"
|
|
64
60
|
|
|
65
|
-
const portableQuery = select(
|
|
66
|
-
{ name: users.name },
|
|
67
|
-
from(users),
|
|
68
|
-
where(like(users.name, '%ada%'))
|
|
69
|
-
)
|
|
61
|
+
const portableQuery = select({ name: users.name }, from(users), where(like(users.name, "%ada%")))
|
|
70
62
|
|
|
71
63
|
render(portableQuery)
|
|
72
64
|
render(portableQuery, sqliteDialect())
|
|
@@ -88,15 +80,15 @@ to produce the typed `ExecutionResult`. A `TransactionalQueryAdapter` can also
|
|
|
88
80
|
pin one driver connection for a callback transaction:
|
|
89
81
|
|
|
90
82
|
```ts
|
|
91
|
-
import { qubu } from
|
|
92
|
-
import { postgresDialect } from
|
|
93
|
-
import type { ExecutionRequest, QueryAdapter } from
|
|
83
|
+
import { qubu } from "qubu"
|
|
84
|
+
import { postgresDialect } from "qubu/postgres"
|
|
85
|
+
import type { ExecutionRequest, QueryAdapter } from "qubu"
|
|
94
86
|
|
|
95
87
|
declare const driver: {
|
|
96
88
|
query<TRow extends object>(
|
|
97
89
|
text: string,
|
|
98
90
|
parameters: readonly unknown[],
|
|
99
|
-
options: { signal?: AbortSignal }
|
|
91
|
+
options: { signal?: AbortSignal },
|
|
100
92
|
): Promise<{ rows: readonly TRow[]; rowCount: number | null }>
|
|
101
93
|
}
|
|
102
94
|
|
|
@@ -107,13 +99,11 @@ const adapter: QueryAdapter = {
|
|
|
107
99
|
const result = await driver.query<Record<string, unknown>>(
|
|
108
100
|
statement.text,
|
|
109
101
|
statement.parameters,
|
|
110
|
-
{ signal }
|
|
102
|
+
{ signal },
|
|
111
103
|
)
|
|
112
104
|
return {
|
|
113
105
|
rows: result.rows,
|
|
114
|
-
...(queryKind !==
|
|
115
|
-
queryKind !== 'set' &&
|
|
116
|
-
result.rowCount !== null
|
|
106
|
+
...(queryKind !== "select" && queryKind !== "set" && result.rowCount !== null
|
|
117
107
|
? { affectedRows: result.rowCount }
|
|
118
108
|
: {}),
|
|
119
109
|
}
|
|
@@ -134,9 +124,9 @@ import {
|
|
|
134
124
|
dateResultDecoder,
|
|
135
125
|
jsonTextResultDecoder,
|
|
136
126
|
timestampResultDecoder,
|
|
137
|
-
} from
|
|
138
|
-
import type { AdapterExecutionResult, QueryAdapter, RenderedQuery } from
|
|
139
|
-
import { sqliteDialect } from
|
|
127
|
+
} from "qubu"
|
|
128
|
+
import type { AdapterExecutionResult, QueryAdapter, RenderedQuery } from "qubu"
|
|
129
|
+
import { sqliteDialect } from "qubu/sqlite"
|
|
140
130
|
|
|
141
131
|
declare const sqliteDriver: {
|
|
142
132
|
execute(statement: RenderedQuery): Promise<AdapterExecutionResult>
|
|
@@ -165,10 +155,10 @@ Use a column decoder for a custom stored type, or `mapResult()` for one
|
|
|
165
155
|
expression. Both override adapter policy for that field:
|
|
166
156
|
|
|
167
157
|
```ts
|
|
168
|
-
import { column, mapResult, value } from
|
|
158
|
+
import { column, mapResult, value } from "qubu"
|
|
169
159
|
|
|
170
|
-
const score = column<number>({ decode: value => Number(value) })
|
|
171
|
-
const decodedTotal = mapResult(value(
|
|
160
|
+
const score = column<number>({ decode: (value) => Number(value) })
|
|
161
|
+
const decodedTotal = mapResult(value("42"), (value) => Number(value))
|
|
172
162
|
```
|
|
173
163
|
|
|
174
164
|
## Stream read results
|
|
@@ -180,20 +170,20 @@ Mutations stay on `execute()` and `executeRows()`, including mutations with
|
|
|
180
170
|
`RETURNING`.
|
|
181
171
|
|
|
182
172
|
```ts
|
|
183
|
-
import { qubu } from
|
|
184
|
-
import type { ExecutionRequest, StreamingQueryAdapter } from
|
|
185
|
-
import { postgresDialect } from
|
|
173
|
+
import { qubu } from "qubu"
|
|
174
|
+
import type { ExecutionRequest, StreamingQueryAdapter } from "qubu"
|
|
175
|
+
import { postgresDialect } from "qubu/postgres"
|
|
186
176
|
|
|
187
177
|
declare const driver: {
|
|
188
178
|
query<TRow extends object>(
|
|
189
179
|
text: string,
|
|
190
180
|
parameters: readonly unknown[],
|
|
191
|
-
options: { signal?: AbortSignal }
|
|
181
|
+
options: { signal?: AbortSignal },
|
|
192
182
|
): Promise<{ rows: readonly TRow[]; rowCount: number | null }>
|
|
193
183
|
stream<TRow extends object>(
|
|
194
184
|
text: string,
|
|
195
185
|
parameters: readonly unknown[],
|
|
196
|
-
options: { signal?: AbortSignal }
|
|
186
|
+
options: { signal?: AbortSignal },
|
|
197
187
|
): AsyncIterable<TRow>
|
|
198
188
|
}
|
|
199
189
|
|
|
@@ -203,7 +193,7 @@ const adapter: StreamingQueryAdapter = {
|
|
|
203
193
|
const result = await driver.query<Record<string, unknown>>(
|
|
204
194
|
request.statement.text,
|
|
205
195
|
request.statement.parameters,
|
|
206
|
-
{ signal: request.signal }
|
|
196
|
+
{ signal: request.signal },
|
|
207
197
|
)
|
|
208
198
|
return {
|
|
209
199
|
rows: result.rows,
|
|
@@ -214,7 +204,7 @@ const adapter: StreamingQueryAdapter = {
|
|
|
214
204
|
return driver.stream<Record<string, unknown>>(
|
|
215
205
|
request.statement.text,
|
|
216
206
|
request.statement.parameters,
|
|
217
|
-
{ signal: request.signal }
|
|
207
|
+
{ signal: request.signal },
|
|
218
208
|
)
|
|
219
209
|
},
|
|
220
210
|
}
|
|
@@ -259,10 +249,10 @@ transaction callback receives a streaming client. The adapter must keep its
|
|
|
259
249
|
cursor and connection valid until the callback's streams finish or close:
|
|
260
250
|
|
|
261
251
|
```ts
|
|
262
|
-
declare const transactionalAdapter: import(
|
|
252
|
+
declare const transactionalAdapter: import("qubu").StreamingTransactionalQueryAdapter
|
|
263
253
|
const transactionalDb = qubu(transactionalAdapter)
|
|
264
254
|
|
|
265
|
-
await transactionalDb.transaction(async transaction => {
|
|
255
|
+
await transactionalDb.transaction(async (transaction) => {
|
|
266
256
|
for await (const row of transaction.stream(readQuery)) {
|
|
267
257
|
consume(row)
|
|
268
258
|
}
|
|
@@ -281,17 +271,17 @@ standalone `explain()` function and the bound `db.explain()` method render a
|
|
|
281
271
|
plan request without calling `execute()`:
|
|
282
272
|
|
|
283
273
|
```ts
|
|
284
|
-
import { explain, qubu } from
|
|
285
|
-
import type { ExplainableQueryAdapter } from
|
|
286
|
-
import { postgresDialect } from
|
|
274
|
+
import { explain, qubu } from "qubu"
|
|
275
|
+
import type { ExplainableQueryAdapter } from "qubu"
|
|
276
|
+
import { postgresDialect } from "qubu/postgres"
|
|
287
277
|
|
|
288
|
-
type PostgresPlanRow = {
|
|
278
|
+
type PostgresPlanRow = { "QUERY PLAN": string }
|
|
289
279
|
|
|
290
280
|
declare const driver: {
|
|
291
281
|
query<TRow extends object>(
|
|
292
282
|
text: string,
|
|
293
283
|
parameters: readonly unknown[],
|
|
294
|
-
options: { signal?: AbortSignal }
|
|
284
|
+
options: { signal?: AbortSignal },
|
|
295
285
|
): Promise<{ rows: readonly TRow[] }>
|
|
296
286
|
}
|
|
297
287
|
|
|
@@ -304,7 +294,7 @@ const adapter: ExplainableQueryAdapter<PostgresPlanRow> = {
|
|
|
304
294
|
const result = await driver.query<PostgresPlanRow>(
|
|
305
295
|
request.statement.text,
|
|
306
296
|
request.statement.parameters,
|
|
307
|
-
{ signal: request.signal }
|
|
297
|
+
{ signal: request.signal },
|
|
308
298
|
)
|
|
309
299
|
return { rows: result.rows }
|
|
310
300
|
},
|
|
@@ -369,13 +359,13 @@ Use a transactional adapter when several queries must share one commit or
|
|
|
369
359
|
rollback boundary:
|
|
370
360
|
|
|
371
361
|
```ts
|
|
372
|
-
import { qubu } from
|
|
373
|
-
import type { TransactionalQueryAdapter } from
|
|
362
|
+
import { qubu } from "qubu"
|
|
363
|
+
import type { TransactionalQueryAdapter } from "qubu"
|
|
374
364
|
|
|
375
365
|
declare const transactionalAdapter: TransactionalQueryAdapter
|
|
376
366
|
const transactionalDb = qubu(transactionalAdapter)
|
|
377
367
|
|
|
378
|
-
const result = await transactionalDb.transaction(async transaction => {
|
|
368
|
+
const result = await transactionalDb.transaction(async (transaction) => {
|
|
379
369
|
await transaction.execute(firstMutation)
|
|
380
370
|
await transaction.execute(secondMutation)
|
|
381
371
|
return transaction.rows(readQuery)
|
|
@@ -400,7 +390,7 @@ The standalone functions remain useful when the adapter varies by call or a
|
|
|
400
390
|
small module does not need a bound client:
|
|
401
391
|
|
|
402
392
|
```ts
|
|
403
|
-
import { execute, executeRows } from
|
|
393
|
+
import { execute, executeRows } from "qubu"
|
|
404
394
|
|
|
405
395
|
const result = await execute(query, adapter)
|
|
406
396
|
const rows = await executeRows(readQuery, adapter)
|
|
@@ -449,13 +439,13 @@ Use `createDialect()` when a driver needs a different policy but the query
|
|
|
449
439
|
syntax stays portable:
|
|
450
440
|
|
|
451
441
|
```ts
|
|
452
|
-
import { render } from
|
|
453
|
-
import { createDialect } from
|
|
442
|
+
import { render } from "qubu"
|
|
443
|
+
import { createDialect } from "qubu/core"
|
|
454
444
|
|
|
455
445
|
const namedParameters = createDialect({
|
|
456
|
-
name:
|
|
457
|
-
placeholder: position => `:p${position}`,
|
|
458
|
-
castTypes: { text:
|
|
446
|
+
name: "named-parameters",
|
|
447
|
+
placeholder: (position) => `:p${position}`,
|
|
448
|
+
castTypes: { text: "STRING" },
|
|
459
449
|
})
|
|
460
450
|
|
|
461
451
|
const statement = render(query, namedParameters)
|
package/docs/getting-started.md
CHANGED
|
@@ -24,9 +24,9 @@ Use `table()` once for each query-facing table. Column helpers describe the
|
|
|
24
24
|
application values that can be selected and, for mutations, written.
|
|
25
25
|
|
|
26
26
|
```ts
|
|
27
|
-
import { integer, table, text } from
|
|
27
|
+
import { integer, table, text } from "qubu"
|
|
28
28
|
|
|
29
|
-
const users = table(
|
|
29
|
+
const users = table("users", {
|
|
30
30
|
id: integer(),
|
|
31
31
|
name: text(),
|
|
32
32
|
email: text({ nullable: true }),
|
|
@@ -44,7 +44,7 @@ it, then renders the normalized statement in SQL order. The example uses the
|
|
|
44
44
|
`users` table from the previous section.
|
|
45
45
|
|
|
46
46
|
```ts
|
|
47
|
-
import { eq, from, render, select, where } from
|
|
47
|
+
import { eq, from, render, select, where } from "qubu"
|
|
48
48
|
|
|
49
49
|
const query = select(
|
|
50
50
|
{
|
|
@@ -52,7 +52,7 @@ const query = select(
|
|
|
52
52
|
displayName: users.name,
|
|
53
53
|
},
|
|
54
54
|
from(users),
|
|
55
|
-
where(eq(users.id, 7))
|
|
55
|
+
where(eq(users.id, 7)),
|
|
56
56
|
)
|
|
57
57
|
|
|
58
58
|
const statement = render(query)
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Better Auth
|
|
2
|
+
|
|
3
|
+
> Derive Qubu-owned auth tables and run Better Auth through a transactional Qubu client.
|
|
4
|
+
|
|
5
|
+
Install the integration next to Qubu and Better Auth:
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
pnpm add qubu @qubu/better-auth better-auth
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Define the Better Auth options once. The schema derivation reads Better Auth's
|
|
12
|
+
resolved public metadata, so core tables, renamed models and fields, additional
|
|
13
|
+
fields, plugin tables, references, unique constraints, and compound indexes all
|
|
14
|
+
participate.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { betterAuth } from "better-auth"
|
|
18
|
+
import type { BetterAuthOptions } from "better-auth/types"
|
|
19
|
+
import { twoFactor } from "better-auth/plugins"
|
|
20
|
+
import { betterAuthSchema, qubuAdapter } from "@qubu/better-auth"
|
|
21
|
+
import { qubu } from "qubu"
|
|
22
|
+
import { pgAdapter } from "@qubu/adapter-pg"
|
|
23
|
+
|
|
24
|
+
const options = {
|
|
25
|
+
user: {
|
|
26
|
+
modelName: "auth_users",
|
|
27
|
+
additionalFields: {
|
|
28
|
+
locale: { type: "string", required: false },
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
plugins: [twoFactor()],
|
|
32
|
+
} satisfies BetterAuthOptions
|
|
33
|
+
|
|
34
|
+
const authSchema = betterAuthSchema(options, "postgresql")
|
|
35
|
+
const db = qubu(pgAdapter(pool))
|
|
36
|
+
|
|
37
|
+
export const auth = betterAuth({
|
|
38
|
+
...options,
|
|
39
|
+
database: qubuAdapter(db, { schema: authSchema }),
|
|
40
|
+
})
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
`authSchema` is an ordinary Qubu `Schema`. Hand it to Qubu snapshot, diff,
|
|
44
|
+
migration-plan, and DDL workflows. The adapter's Better Auth `createSchema`
|
|
45
|
+
hook emits a TypeScript module that reconstructs the same Qubu-owned metadata.
|
|
46
|
+
|
|
47
|
+
The package never imports PostgreSQL, MySQL, or SQLite drivers. It executes
|
|
48
|
+
through Qubu's query and transaction boundaries. PostgreSQL and SQLite use one
|
|
49
|
+
limited mutation statement for atomic consume and guarded increment operations;
|
|
50
|
+
MySQL locks one selected row inside the Qubu-owned transaction. A client without
|
|
51
|
+
transaction support, or a dialect other than PostgreSQL, MySQL, or SQLite, is
|
|
52
|
+
rejected during adapter construction.
|
|
53
|
+
|
|
54
|
+
Better Auth enum metadata is currently rejected because Qubu cannot preserve
|
|
55
|
+
the closed value set as a portable column without adding a database constraint.
|
|
56
|
+
The error includes the model and field path instead of silently widening it to
|
|
57
|
+
text.
|
|
@@ -8,37 +8,19 @@
|
|
|
8
8
|
Attach it with `withCte()` and use the CTE source in `from()`:
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
from,
|
|
15
|
-
integer,
|
|
16
|
-
select,
|
|
17
|
-
table,
|
|
18
|
-
text,
|
|
19
|
-
where,
|
|
20
|
-
withCte,
|
|
21
|
-
} from 'qubu'
|
|
22
|
-
|
|
23
|
-
const users = table('users', {
|
|
11
|
+
import { cte, eq, from, integer, select, table, text, where, withCte } from "qubu"
|
|
12
|
+
|
|
13
|
+
const users = table("users", {
|
|
24
14
|
id: integer(),
|
|
25
15
|
name: text(),
|
|
26
16
|
})
|
|
27
17
|
|
|
28
18
|
const activeUsers = cte(
|
|
29
|
-
|
|
30
|
-
select(
|
|
31
|
-
{ id: users.id, name: users.name },
|
|
32
|
-
from(users),
|
|
33
|
-
where(eq(users.id, 7))
|
|
34
|
-
)
|
|
19
|
+
"active_users",
|
|
20
|
+
select({ id: users.id, name: users.name }, from(users), where(eq(users.id, 7))),
|
|
35
21
|
)
|
|
36
22
|
|
|
37
|
-
const report = select(
|
|
38
|
-
{ displayName: activeUsers.name },
|
|
39
|
-
withCte(activeUsers),
|
|
40
|
-
from(activeUsers)
|
|
41
|
-
)
|
|
23
|
+
const report = select({ displayName: activeUsers.name }, withCte(activeUsers), from(activeUsers))
|
|
42
24
|
```
|
|
43
25
|
|
|
44
26
|
`activeUsers.name` is a typed column derived from the first query's row shape.
|
|
@@ -53,24 +35,10 @@ member. The callback receives a typed self-reference; introduce it through
|
|
|
53
35
|
`from()` or a join before selecting its fields:
|
|
54
36
|
|
|
55
37
|
```ts
|
|
56
|
-
import {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
from,
|
|
60
|
-
integer,
|
|
61
|
-
lt,
|
|
62
|
-
recursiveCte,
|
|
63
|
-
select,
|
|
64
|
-
value,
|
|
65
|
-
where,
|
|
66
|
-
withCte,
|
|
67
|
-
} from 'qubu'
|
|
68
|
-
|
|
69
|
-
const numbers = recursiveCte(
|
|
70
|
-
'numbers',
|
|
71
|
-
select({ value: cast(value(1), integer()) }),
|
|
72
|
-
self =>
|
|
73
|
-
select({ value: add(self.value, 1) }, from(self), where(lt(self.value, 3)))
|
|
38
|
+
import { add, cast, from, integer, lt, recursiveCte, select, value, where, withCte } from "qubu"
|
|
39
|
+
|
|
40
|
+
const numbers = recursiveCte("numbers", select({ value: cast(value(1), integer()) }), (self) =>
|
|
41
|
+
select({ value: add(self.value, 1) }, from(self), where(lt(self.value, 3))),
|
|
74
42
|
)
|
|
75
43
|
|
|
76
44
|
const query = select({ value: numbers.value }, withCte(numbers), from(numbers))
|
|
@@ -89,11 +57,11 @@ recursive CTEs can share one `withCte()` clause.
|
|
|
89
57
|
Alias a query when it should be used as an inline source:
|
|
90
58
|
|
|
91
59
|
```ts
|
|
92
|
-
import { alias, from, lower, select } from
|
|
93
|
-
import type { SqlTypeOf } from
|
|
60
|
+
import { alias, from, lower, select } from "qubu"
|
|
61
|
+
import type { SqlTypeOf } from "qubu"
|
|
94
62
|
|
|
95
63
|
const names = select({ name: lower(users.name) }, from(users))
|
|
96
|
-
const namesSource = alias(names,
|
|
64
|
+
const namesSource = alias(names, "names")
|
|
97
65
|
|
|
98
66
|
const query = select({ name: namesSource.name }, from(namesSource))
|
|
99
67
|
type NameSqlDomain = SqlTypeOf<typeof namesSource.name>
|
|
@@ -112,7 +80,7 @@ so downstream text operations remain checked without redeclaring the field.
|
|
|
112
80
|
`scalar()` turns a query with exactly one selected field into an expression:
|
|
113
81
|
|
|
114
82
|
```ts
|
|
115
|
-
import { from, scalar, select, value } from
|
|
83
|
+
import { from, scalar, select, value } from "qubu"
|
|
116
84
|
|
|
117
85
|
const firstId = select({ id: users.id }, from(users))
|
|
118
86
|
const query = select(
|
|
@@ -120,7 +88,7 @@ const query = select(
|
|
|
120
88
|
name: users.name,
|
|
121
89
|
firstId: scalar(firstId),
|
|
122
90
|
},
|
|
123
|
-
from(users)
|
|
91
|
+
from(users),
|
|
124
92
|
)
|
|
125
93
|
```
|
|
126
94
|
|
|
@@ -139,7 +107,7 @@ Set operations preserve the left query's row shape. Both queries must select
|
|
|
139
107
|
compatible rows:
|
|
140
108
|
|
|
141
109
|
```ts
|
|
142
|
-
import { eq, from, select, unionAll, where } from
|
|
110
|
+
import { eq, from, select, unionAll, where } from "qubu"
|
|
143
111
|
|
|
144
112
|
const first = select({ id: users.id }, from(users))
|
|
145
113
|
const second = select({ id: users.id }, from(users), where(eq(users.id, 7)))
|
|
@@ -156,17 +124,12 @@ are collected in traversal order.
|
|
|
156
124
|
Build reusable pieces as ordinary values and pass them into the final query:
|
|
157
125
|
|
|
158
126
|
```ts
|
|
159
|
-
import { desc, eq, from, orderBy, select, where } from
|
|
127
|
+
import { desc, eq, from, orderBy, select, where } from "qubu"
|
|
160
128
|
|
|
161
129
|
const byId = where(eq(users.id, 7))
|
|
162
130
|
const newest = orderBy(desc(users.id))
|
|
163
131
|
|
|
164
|
-
const query = select(
|
|
165
|
-
{ id: users.id, name: users.name },
|
|
166
|
-
from(users),
|
|
167
|
-
byId,
|
|
168
|
-
newest
|
|
169
|
-
)
|
|
132
|
+
const query = select({ id: users.id, name: users.name }, from(users), byId, newest)
|
|
170
133
|
```
|
|
171
134
|
|
|
172
135
|
This makes it possible to share a predicate or projection without mutating a
|
|
@@ -185,13 +148,10 @@ generic function retains its exact source identity.
|
|
|
185
148
|
For an application-level requirement, describe the required JavaScript row:
|
|
186
149
|
|
|
187
150
|
```ts
|
|
188
|
-
import { eq, where } from
|
|
189
|
-
import type { TableLike } from
|
|
151
|
+
import { eq, where } from "qubu"
|
|
152
|
+
import type { TableLike } from "qubu"
|
|
190
153
|
|
|
191
|
-
function byStringId<TTable extends TableLike<{ id: string }>>(
|
|
192
|
-
table: TTable,
|
|
193
|
-
id: string
|
|
194
|
-
) {
|
|
154
|
+
function byStringId<TTable extends TableLike<{ id: string }>>(table: TTable, id: string) {
|
|
195
155
|
return where(eq(table.columns.id, id))
|
|
196
156
|
}
|
|
197
157
|
```
|
|
@@ -203,18 +163,15 @@ extra fields and rejects `string | null`, but it does not distinguish
|
|
|
203
163
|
Use `FieldLike` when the fragment depends on SQL semantics:
|
|
204
164
|
|
|
205
165
|
```ts
|
|
206
|
-
import { eq, where } from
|
|
207
|
-
import type { FieldLike, SourceLike, SqlTextLike } from
|
|
166
|
+
import { eq, where } from "qubu"
|
|
167
|
+
import type { FieldLike, SourceLike, SqlTextLike } from "qubu"
|
|
208
168
|
|
|
209
169
|
type NonNullTextId = FieldLike<{
|
|
210
170
|
sqlType: SqlTextLike
|
|
211
171
|
nullable: false
|
|
212
172
|
}>
|
|
213
173
|
|
|
214
|
-
function byTextId<TSource extends SourceLike<{ id: NonNullTextId }>>(
|
|
215
|
-
source: TSource,
|
|
216
|
-
id: string
|
|
217
|
-
) {
|
|
174
|
+
function byTextId<TSource extends SourceLike<{ id: NonNullTextId }>>(source: TSource, id: string) {
|
|
218
175
|
return where(eq(source.columns.id, id))
|
|
219
176
|
}
|
|
220
177
|
```
|
package/docs/guides/drizzle.md
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
|
|
6
6
|
## Install the optional integration
|
|
7
7
|
|
|
8
|
-
Install
|
|
9
|
-
|
|
8
|
+
Install the integration next to Qubu and Drizzle. The `@qubu/drizzle` package
|
|
9
|
+
owns schema conversion; the root `qubu` package does not depend on Drizzle.
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
pnpm add qubu drizzle-orm@rc
|
|
12
|
+
pnpm add qubu @qubu/drizzle drizzle-orm@rc
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
The converter supports PostgreSQL, MySQL, and SQLite with Drizzle 1.0.0-rc.4
|
|
@@ -21,29 +21,29 @@ Declare tables and the root registry with Qubu, then import the converter for
|
|
|
21
21
|
your database:
|
|
22
22
|
|
|
23
23
|
```ts
|
|
24
|
-
import { integer, schema, table, text } from
|
|
25
|
-
import { toPostgresDrizzleSchema } from
|
|
24
|
+
import { integer, schema, table, text } from "qubu"
|
|
25
|
+
import { toPostgresDrizzleSchema } from "@qubu/drizzle/postgres"
|
|
26
26
|
|
|
27
|
-
const users = table(
|
|
27
|
+
const users = table("user_records", {
|
|
28
28
|
id: integer({ generated: true }),
|
|
29
29
|
name: text(),
|
|
30
30
|
nickname: text({ nullable: true }),
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
const appSchema = schema({ users }, { namespace:
|
|
33
|
+
const appSchema = schema({ users }, { namespace: "app" })
|
|
34
34
|
const drizzleTables = toPostgresDrizzleSchema(appSchema)
|
|
35
35
|
```
|
|
36
36
|
|
|
37
37
|
The import path selects the dialect. Each module imports only its matching
|
|
38
38
|
Drizzle core package:
|
|
39
39
|
|
|
40
|
-
| Database | Import
|
|
41
|
-
| ---------- |
|
|
42
|
-
| PostgreSQL |
|
|
43
|
-
| MySQL |
|
|
44
|
-
| SQLite |
|
|
40
|
+
| Database | Import | Converter |
|
|
41
|
+
| ---------- | ------------------------ | --------------------------- |
|
|
42
|
+
| PostgreSQL | `@qubu/drizzle/postgres` | `toPostgresDrizzleSchema()` |
|
|
43
|
+
| MySQL | `@qubu/drizzle/mysql` | `toMysqlDrizzleSchema()` |
|
|
44
|
+
| SQLite | `@qubu/drizzle/sqlite` | `toSqliteDrizzleSchema()` |
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
`@qubu/drizzle` exports the shared conversion error and dialect types. It does
|
|
47
47
|
not import a dialect core or provide a universal runtime converter.
|
|
48
48
|
|
|
49
49
|
`drizzleTables.users` is a real Drizzle `PgTable`. The logical `users` key,
|
|
@@ -54,8 +54,8 @@ Use the converted tables in ordinary Drizzle queries. Table objects do not need
|
|
|
54
54
|
to be passed to `drizzle()`:
|
|
55
55
|
|
|
56
56
|
```ts
|
|
57
|
-
import { eq } from
|
|
58
|
-
import { drizzle } from
|
|
57
|
+
import { eq } from "drizzle-orm"
|
|
58
|
+
import { drizzle } from "drizzle-orm/node-postgres"
|
|
59
59
|
|
|
60
60
|
const db = drizzle(pool)
|
|
61
61
|
|
|
@@ -74,12 +74,12 @@ Use `sqliteTimestamp()` when an existing SQLite schema stores dates as integer
|
|
|
74
74
|
Unix timestamps and Drizzle must continue reading and writing `Date` values:
|
|
75
75
|
|
|
76
76
|
```ts
|
|
77
|
-
import { schema, table } from
|
|
78
|
-
import { sqliteTimestamp, toSqliteDrizzleSchema } from
|
|
77
|
+
import { schema, table } from "qubu"
|
|
78
|
+
import { sqliteTimestamp, toSqliteDrizzleSchema } from "@qubu/drizzle/sqlite"
|
|
79
79
|
|
|
80
|
-
const events = table(
|
|
80
|
+
const events = table("events", {
|
|
81
81
|
createdAt: sqliteTimestamp({
|
|
82
|
-
mode:
|
|
82
|
+
mode: "timestamp",
|
|
83
83
|
defaultFn: () => new Date(),
|
|
84
84
|
}),
|
|
85
85
|
})
|
|
@@ -127,11 +127,11 @@ unique constraints, checks, foreign keys, and indexes. Native storage must
|
|
|
127
127
|
belong to the selected dialect:
|
|
128
128
|
|
|
129
129
|
```ts
|
|
130
|
-
import { nativeColumn, schema, table } from
|
|
131
|
-
import { toPostgresDrizzleSchema } from
|
|
130
|
+
import { nativeColumn, schema, table } from "qubu"
|
|
131
|
+
import { toPostgresDrizzleSchema } from "@qubu/drizzle/postgres"
|
|
132
132
|
|
|
133
|
-
const records = table(
|
|
134
|
-
handle: nativeColumn(
|
|
133
|
+
const records = table("records", {
|
|
134
|
+
handle: nativeColumn("postgresql", "CITEXT"),
|
|
135
135
|
})
|
|
136
136
|
|
|
137
137
|
const tables = toPostgresDrizzleSchema(schema({ records }))
|
|
@@ -8,12 +8,12 @@ identifiers, placeholders, or pagination:
|
|
|
8
8
|
The examples assume a query has already been built.
|
|
9
9
|
|
|
10
10
|
```ts
|
|
11
|
-
import { render } from
|
|
12
|
-
import { createDialect } from
|
|
11
|
+
import { render } from "qubu"
|
|
12
|
+
import { createDialect } from "qubu/core"
|
|
13
13
|
|
|
14
14
|
const colonDialect = createDialect({
|
|
15
|
-
name:
|
|
16
|
-
placeholder: position =>
|
|
15
|
+
name: "colon",
|
|
16
|
+
placeholder: (position) => ":p" + position,
|
|
17
17
|
})
|
|
18
18
|
|
|
19
19
|
render(query, colonDialect)
|