qubu 0.6.1 → 0.6.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 (114) hide show
  1. package/dist/{complete-DP7pliuY.mjs → canonical-B5_ouh-c.mjs} +466 -241
  2. package/dist/codegen.d.mts +2 -2
  3. package/dist/codegen.mjs +180 -90
  4. package/dist/column-Cyc2CMnG.mjs +116 -0
  5. package/dist/column-DDRvD7SF.mjs +721 -0
  6. package/dist/{constraints-DM_tarXc.mjs → constraints-CAmi18Uk.mjs} +8 -3
  7. package/dist/core.d.mts +3 -3
  8. package/dist/core.mjs +4 -4
  9. package/dist/diff.d.mts +9 -9
  10. package/dist/diff.mjs +152 -102
  11. package/dist/{explain-CkIK13L_.mjs → explain-BIqEmZha.mjs} +1 -1
  12. package/dist/{expressions-BCjc08zw.mjs → expressions-_6JF_J77.mjs} +2 -1
  13. package/dist/index-CaxrMD1A.d.mts +1 -0
  14. package/dist/index.d.mts +2 -2
  15. package/dist/index.mjs +368 -74
  16. package/dist/introspection/mysql.d.mts +1 -1
  17. package/dist/introspection/mysql.mjs +156 -23
  18. package/dist/introspection/postgres.d.mts +6 -3
  19. package/dist/introspection/postgres.mjs +388 -53
  20. package/dist/introspection/sqlite.d.mts +1 -1
  21. package/dist/introspection/sqlite.mjs +198 -12
  22. package/dist/introspection.d.mts +26 -12
  23. package/dist/introspection.mjs +2 -672
  24. package/dist/mysql.d.mts +3 -3
  25. package/dist/mysql.mjs +6 -5
  26. package/dist/{errors-Dxv73YJu.mjs → omit-VEr9Ydux.mjs} +5 -1
  27. package/dist/{on-conflict-CnaY5qso.mjs → on-conflict-B2rFyHGF.mjs} +6 -8
  28. package/dist/on-duplicate-key-update-Czsw1Yq-.mjs +95 -0
  29. package/dist/{postgres-Dey7QXPL.mjs → postgres-hFhd0I9n.mjs} +4 -5
  30. package/dist/postgres.d.mts +2 -2
  31. package/dist/postgres.mjs +2 -2
  32. package/dist/{registry-oWDiqD7i.mjs → registry-BXE_4M9P.mjs} +1 -1
  33. package/dist/{relational-DSAJ-l58.mjs → relational-CoPBETjI.mjs} +3 -2
  34. package/dist/schema.d.mts +2 -2
  35. package/dist/schema.mjs +8 -8
  36. package/dist/{serialize-CE-gw5_s.mjs → serialize-CyobNEx-.mjs} +174 -30
  37. package/dist/serialize-Du2UPZMt.d.mts +92 -0
  38. package/dist/snapshot/mysql.d.mts +5 -5
  39. package/dist/snapshot/mysql.mjs +8 -8
  40. package/dist/snapshot/postgres.d.mts +3 -3
  41. package/dist/snapshot/postgres.mjs +9 -9
  42. package/dist/snapshot/sqlite.d.mts +3 -3
  43. package/dist/snapshot/sqlite.mjs +7 -7
  44. package/dist/snapshot-mIb-Zzb5.mjs +1489 -0
  45. package/dist/snapshot.d.mts +4 -5
  46. package/dist/snapshot.mjs +3 -4
  47. package/dist/{source-BDuUXmAk.mjs → source-DYSUqzvb.mjs} +2 -2
  48. package/dist/sqlite.d.mts +2 -2
  49. package/dist/sqlite.mjs +6 -6
  50. package/dist/{table-C1QGNe4P.mjs → table-B8zEq0az.mjs} +4 -4
  51. package/dist/{types-BLNRatG_.mjs → types-CYHpSPwj.mjs} +10 -5
  52. package/dist/{types-BEn0N_al.d.mts → types-CiMvKi5V.d.mts} +14 -4
  53. package/dist/{types-DUe6eeI0.d.mts → types-Dqr4o2I1.d.mts} +590 -168
  54. package/dist/value-CpaUFtjw.mjs +45 -0
  55. package/dist/vite/ambient.d.ts +2 -0
  56. package/dist/vite.d.mts +1 -1
  57. package/dist/vite.mjs +2 -0
  58. package/docs/dialects-and-execution.md +105 -26
  59. package/docs/getting-started.md +10 -10
  60. package/docs/guides/better-auth.md +16 -5
  61. package/docs/guides/compose-queries.md +21 -9
  62. package/docs/guides/drizzle.md +8 -3
  63. package/docs/guides/extensions/dialects.md +1 -1
  64. package/docs/guides/extensions/overview.md +1 -1
  65. package/docs/guides/extensions/sources-and-clauses.md +7 -3
  66. package/docs/guides/extensions/typed-expressions.md +26 -12
  67. package/docs/guides/extensions/unsafe-syntax.md +10 -6
  68. package/docs/guides/json.md +126 -8
  69. package/docs/guides/mutations.md +51 -6
  70. package/docs/guides/select/conditions.md +18 -11
  71. package/docs/guides/select/grouping-and-windows.md +5 -2
  72. package/docs/guides/select/ordering-and-pagination.md +5 -3
  73. package/docs/guides/select/overview.md +6 -3
  74. package/docs/guides/sql-templates.md +11 -5
  75. package/docs/guides/valtio-sync.md +11 -5
  76. package/docs/guides/vite-plugin.md +2 -2
  77. package/docs/index.md +25 -18
  78. package/docs/migrations/adapters.md +92 -27
  79. package/docs/migrations/artifacts-and-policy.md +49 -20
  80. package/docs/migrations/index.md +15 -8
  81. package/docs/migrations/lotta-adoption.md +16 -5
  82. package/docs/migrations/operations.md +29 -15
  83. package/docs/migrations/recovery.md +40 -17
  84. package/docs/query-model/fragments.md +33 -5
  85. package/docs/query-model/result-shapes.md +2 -2
  86. package/docs/query-model/source-scope.md +5 -3
  87. package/docs/reference/introspection-support.md +42 -37
  88. package/docs/reference/mysql-snapshot.md +19 -4
  89. package/docs/reference/postgres-snapshot.md +17 -4
  90. package/docs/reference/sqlite-snapshot.md +19 -2
  91. package/docs/reference/supported-surface.md +221 -84
  92. package/docs/schema/catalog-model.md +44 -14
  93. package/docs/schema/code-generation.md +40 -21
  94. package/docs/schema/columns-and-writes.md +21 -11
  95. package/docs/schema/constraints-and-indexes.md +12 -5
  96. package/docs/schema/ddl-emission.md +16 -5
  97. package/docs/schema/diff.md +13 -5
  98. package/docs/schema/introspection.md +64 -31
  99. package/docs/schema/migration-plans.md +18 -10
  100. package/docs/schema/snapshots.md +68 -30
  101. package/docs/schema/storage-and-schema-sql.md +16 -5
  102. package/docs/schema/tables-and-names.md +1 -1
  103. package/docs/sql-semantic-types.md +11 -8
  104. package/docs/troubleshooting.md +14 -6
  105. package/package.json +2 -1
  106. package/dist/canonical-DMvR9yBe.mjs +0 -972
  107. package/dist/column-BzN8KFJa.mjs +0 -364
  108. package/dist/column-CFvSbil0.mjs +0 -309
  109. package/dist/complete-types-CNMWBWap.d.mts +0 -371
  110. package/dist/index-CGui70hi.d.mts +0 -32
  111. package/dist/json-Db7XRD91.mjs +0 -169
  112. package/dist/omit-OxV58AwX.mjs +0 -5
  113. package/dist/serialize-OvXCLzjm.d.mts +0 -66
  114. package/dist/snapshot-DgsOhf_8.mjs +0 -354
@@ -0,0 +1,45 @@
1
+ import { A as fragment, E as makeSchemaExpression, U as resultValue, Y as standardJson, Z as createDialect } from "./column-DDRvD7SF.mjs";
2
+ //#region src/dialects/standard.ts
3
+ /** SQL:2008-style rendering defaults used by the core builder. */
4
+ function standardDialect() {
5
+ return createDialect({
6
+ name: "standard-sql",
7
+ placeholder: () => "?",
8
+ json: standardJson
9
+ });
10
+ }
11
+ //#endregion
12
+ //#region src/core/primitives/parameter.ts
13
+ function parameter(_value, sqlType) {
14
+ return fragment((context) => context.parameter(_value, sqlType));
15
+ }
16
+ //#endregion
17
+ //#region src/expressions/value.ts
18
+ /** Build a parameterized value expression with an optional runtime SQL domain for the adapter. */
19
+ function value(input, sqlType) {
20
+ const expression = makeSchemaExpression("value", (context) => context.render(parameter(input, sqlType)), resultValue(void 0, void 0, sqlType));
21
+ return Object.freeze({
22
+ ...expression,
23
+ value: input
24
+ });
25
+ }
26
+ /**
27
+ * Bind a value while declaring its compile-time and runtime SQL semantic domain.
28
+ *
29
+ * @remarks
30
+ * The domain is a binding hint; it does not select a JavaScript result decoder.
31
+ */
32
+ function typedValue(input, sqlType) {
33
+ return value(input, sqlType);
34
+ }
35
+ function isExpressionValue(valueToCheck) {
36
+ return typeof valueToCheck === "object" && valueToCheck !== null && "expressionKind" in valueToCheck && "render" in valueToCheck && typeof valueToCheck.render === "function";
37
+ }
38
+ function isValueExpression(valueToCheck) {
39
+ return isExpressionValue(valueToCheck) && valueToCheck.expressionKind === "value" && "value" in valueToCheck;
40
+ }
41
+ function asValue(input, sqlType) {
42
+ return isExpressionValue(input) ? input : value(input, sqlType);
43
+ }
44
+ //#endregion
45
+ export { parameter as a, value as i, isValueExpression as n, standardDialect as o, typedValue as r, asValue as t };
@@ -68,6 +68,8 @@ declare global {
68
68
  const isNull: typeof import("qubu").isNull
69
69
  const isTrue: typeof import("qubu").isTrue
70
70
  const json: typeof import("qubu").json
71
+ const jsonArrayFrom: typeof import("qubu").jsonArrayFrom
72
+ const jsonObjectFrom: typeof import("qubu").jsonObjectFrom
71
73
  const jsonBoolean: typeof import("qubu").jsonBoolean
72
74
  const jsonExists: typeof import("qubu").jsonExists
73
75
  const jsonNumber: typeof import("qubu").jsonNumber
package/dist/vite.d.mts CHANGED
@@ -4,7 +4,7 @@
4
4
  * with the ordinary authoring surface of `qubu`; fragment internals, dialect construction, and
5
5
  * schema extensions belong to the layered entrypoints.
6
6
  */
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"];
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", "jsonArrayFrom", "jsonObjectFrom", "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"];
8
8
  type QubuGlobal = (typeof qubuGlobals)[number];
9
9
  //#endregion
10
10
  //#region src/vite/index.d.ts
package/dist/vite.mjs CHANGED
@@ -138,6 +138,8 @@ const qubuGlobals = [
138
138
  "isNull",
139
139
  "isTrue",
140
140
  "json",
141
+ "jsonArrayFrom",
142
+ "jsonObjectFrom",
141
143
  "jsonBoolean",
142
144
  "jsonExists",
143
145
  "jsonNumber",
@@ -1,6 +1,6 @@
1
1
  # Dialects and execution
2
2
 
3
- > Keep portable query construction separate from placeholder, identifier, pagination, cast-target, and driver decisions at the rendering boundary.
3
+ > Choose a SQL dialect and connect Qubu queries to your database driver.
4
4
 
5
5
  ## Render once, choose a policy at the boundary
6
6
 
@@ -77,7 +77,12 @@ Qubu does not open connections or bind values for a particular client. An
77
77
  adapter receives an `ExecutionRequest` and returns driver-normalized object
78
78
  rows. Qubu then uses the query's result shape and the adapter's decoder policy
79
79
  to produce the typed `ExecutionResult`. A `TransactionalQueryAdapter` can also
80
- pin one driver connection for a callback transaction:
80
+ pin one driver connection for a callback transaction.
81
+
82
+ `request.statement.parameterSqlTypes`, when present, lists SQL domains in
83
+ the same order as `statement.parameters`. Adapters can pass each domain to their
84
+ value encoder or driver binding layer when a client distinguishes values such
85
+ as `DATE`, `TIMESTAMP`, `UUID`, and `DECIMAL`.
81
86
 
82
87
  ```ts
83
88
  import { qubu } from "qubu"
@@ -138,19 +143,23 @@ per request, binds Qubu's ordered parameters, copies object rows, and finalizes
138
143
  the statement in a `finally` block. SQLite's change count and generated row ID
139
144
  are returned as mutation metadata when the request is a mutation.
140
145
 
141
- The package does not create or terminate workers. Initialize the official
142
- SQLite module inside a dedicated worker, construct the adapter around its
143
- `sqlite3.oo1.DB`, and close the adapter before terminating that worker. The
144
- browser build must serve the package's `sqlite3.wasm` asset beside the bundled
145
- worker module; the combo runner's verified browser scenario demonstrates this
146
- lifecycle.
146
+ Your application manages the worker:
147
+
148
+ 1. Initialize the official SQLite module inside a dedicated worker.
149
+ 2. Construct the adapter around its `sqlite3.oo1.DB`.
150
+ 3. Close the adapter before terminating the worker.
151
+
152
+ Serve the package’s `sqlite3.wasm` asset beside the bundled worker module. The
153
+ combo runner’s verified browser scenario demonstrates this setup.
147
154
 
148
155
  ### Decode schema-aware result values
149
156
 
150
- Portable boolean, date, timestamp, and JSON columns retain their logical
151
- result domains through projection aliases, derived queries, CTEs, set
152
- operations, and mutation `RETURNING`. Register only the conversions required
153
- by the selected driver configuration:
157
+ Portable boolean, date, timestamp, JSON, and bigint columns retain their
158
+ logical result domains through projection aliases, derived queries, CTEs, set
159
+ operations, and mutation `RETURNING`. The result field exposes that domain as
160
+ `sqlType` before execution. Register only the conversions required by the
161
+ selected driver configuration. `sqlType` identifies the SQL domain; `type` and
162
+ the selected decoder determine whether Qubu converts the returned value:
154
163
 
155
164
  ```ts
156
165
  import {
@@ -180,6 +189,11 @@ const adapter: QueryAdapter = {
180
189
  }
181
190
  ```
182
191
 
192
+ Qubu can decode bigint values exactly when a driver returns a bigint, a safe
193
+ integer, or an integer string. For arbitrary-precision `DECIMAL` values, keep
194
+ the driver's exact representation (usually a string or decimal object) rather
195
+ than converting it to a JavaScript number.
196
+
183
197
  Do not register `jsonTextResultDecoder` when the driver already returns parsed
184
198
  JSON. A JSON string is otherwise ambiguous: it may be serialized JSON or an
185
199
  already-decoded JSON string scalar. With no registered decoder, Qubu preserves
@@ -422,12 +436,16 @@ queries identify their parent transaction operation. Hooks are synchronous,
422
436
  and their failures are sent to `onHookError` without changing the database
423
437
  operation's result.
424
438
 
439
+ ### What observations include
440
+
425
441
  Hook metadata accepts only strings, numbers, and booleans. Observations include
426
442
  rendered SQL and parameter count, but never parameter values, result rows,
427
443
  decoded values, or insert identifiers. Rendered SQL can still contain literals
428
444
  introduced by unsafe SQL helpers, so treat it according to the application's
429
445
  logging policy.
430
446
 
447
+ ### Stream observation timing
448
+
431
449
  Streaming adapters are still called eagerly. A consumed stream completes its
432
450
  observation when it is exhausted, closed early, or fails. A stream created but
433
451
  never consumed has no completion observation. Hooks are available only on
@@ -453,19 +471,74 @@ const result = await transactionalDb.transaction(async (transaction) => {
453
471
  })
454
472
  ```
455
473
 
456
- The adapter owns the driver lifecycle. It acquires and pins one connection,
457
- begins the transaction, invokes the callback, commits after it resolves, rolls
458
- back after it rejects, and releases the connection in every case. Qubu only
459
- creates the scoped client and passes the callback result through. It never
460
- emits `BEGIN`, `COMMIT`, or `ROLLBACK` itself.
474
+ The adapter manages the transaction:
475
+
476
+ 1. Acquire and pin one connection.
477
+ 2. Begin the transaction.
478
+ 3. Run the callback.
479
+ 4. Commit if the callback resolves, or roll back if it rejects.
480
+ 5. Release the connection in either case.
461
481
 
462
- The transaction client exposes `execute()` and `rows()` but no public
463
- `transaction()` method, so nested transactions are not part of this contract.
464
- When the adapter also implements `StreamingQueryAdapter`, the scoped client
465
- also exposes `stream()` and its streams follow the cleanup rule above. Use
466
- adapter-specific savepoints when a driver needs nested partial rollback.
467
- `TransactionOptions.signal` is passed to the adapter. Isolation levels and
468
- other driver-specific settings remain adapter-specific.
482
+ Qubu creates the scoped client and returns the callback result. The adapter
483
+ emits `BEGIN`, `COMMIT`, and `ROLLBACK`.
484
+
485
+ A scoped client's methods follow its adapter's capabilities: `execute()` and
486
+ `rows()` are always available; EXPLAIN and streaming require their respective
487
+ capabilities. `TransactionOptions.signal` is passed to the adapter. Isolation
488
+ levels and other driver-specific settings remain adapter-specific.
489
+
490
+ ### Roll back part of a transaction
491
+
492
+ The pg, mysql2, and node:sqlite adapters expose `transaction()` on scoped
493
+ clients through the shared `NestedTransactionalQueryAdapter` capability.
494
+ Other adapters retain their existing transaction surface. For example, with a
495
+ bound pg client, catch a nested failure to keep earlier work:
496
+
497
+ ```ts
498
+ await db.transaction(async (outer) => {
499
+ await outer.execute(firstMutation)
500
+ try {
501
+ await outer.transaction(async (inner) => {
502
+ await inner.execute(optionalMutation)
503
+ })
504
+ } catch (error) {
505
+ // The nested work was rolled back; the outer transaction can continue.
506
+ }
507
+ await outer.execute(secondMutation)
508
+ })
509
+ ```
510
+
511
+ Each nested callback uses a uniquely named savepoint on the same connection.
512
+ Success releases it; failure rolls back to it and releases it. Letting the
513
+ failure escape also rolls back the outer transaction. Failed savepoint creation
514
+ or recovery makes the entire transaction unsafe to commit, even if the callback
515
+ catches the error. Primary and cleanup failures are retained in `AggregateError`.
516
+
517
+ #### Finish work before leaving a scope
518
+
519
+ Await every query and nested transaction before returning. These three adapters
520
+ reject:
521
+
522
+ - Calls on a finished scoped client.
523
+ - Overlapping sibling scopes.
524
+ - A child scope started while its parent has pending queries.
525
+ - Parent queries while a child is active.
526
+
527
+ If a callback finishes with work pending, the adapter waits for that work and
528
+ rolls back.
529
+
530
+ Use the active scoped client for all work on a directly supplied connection.
531
+ Its root client rejects unrelated operations during the transaction. A pg pool
532
+ can still run independent queries on other connections.
533
+
534
+ Your application remains responsible for raw driver calls and separately
535
+ constructed adapters.
536
+
537
+ EXPLAIN and result decoding remain available at every depth. Nested transaction
538
+ hooks identify their enclosing transaction with `parentId`; queries identify
539
+ their immediate scope. Cancellation does not interrupt savepoint recovery.
540
+
541
+ ## Execute without a bound client
469
542
 
470
543
  The standalone functions remain useful when the adapter varies by call or a
471
544
  small module does not need a bound client:
@@ -477,6 +550,8 @@ const result = await execute(query, adapter)
477
550
  const rows = await executeRows(readQuery, adapter)
478
551
  ```
479
552
 
553
+ ### Result fields
554
+
480
555
  | Result field | Adapter type | Contract |
481
556
  | -------------- | ------------------------------------ | ------------------------------------------------------------------------------------- |
482
557
  | `rows` | `readonly Record<string, unknown>[]` | Key by rendered aliases; Qubu returns the decoded `readonly TRow[]` |
@@ -489,11 +564,15 @@ The last three fields are optional. For example, an adapter can map PostgreSQL
489
564
  `changes` and `lastInsertRowid`. Omit a fact that the selected driver cannot
490
565
  report accurately. Qubu does not derive mutation metadata from returned rows.
491
566
 
567
+ ### Dialect overrides and errors
568
+
492
569
  The adapter's `dialect` becomes the default for standalone and bound execution.
493
570
  A `dialect` in the execution options overrides that rendering policy. Qubu
494
571
  passes `signal`, `queryKind`, and `resultShape` to the adapter without changing
495
- them. The adapter decides whether and how its driver supports cancellation.
496
- Driver errors pass through unchanged. Decoder failures become a
572
+ them.
573
+
574
+ The adapter decides whether and how its driver supports cancellation. Driver
575
+ errors pass through unchanged. Decoder failures become a
497
576
  `ResultDecodingError` that identifies the row and field without exposing the
498
577
  raw value.
499
578
 
@@ -1,6 +1,6 @@
1
1
  # Getting started
2
2
 
3
- > Define a typed table, build one parameterized query, and inspect the exact SQL before connecting a driver.
3
+ > Define a table and inspect your first query’s SQL and parameters.
4
4
 
5
5
  ## Install Qubu
6
6
 
@@ -13,10 +13,9 @@ pnpm add qubu
13
13
  Import query-building functions from the package root. Qubu does not need a
14
14
  database connection to construct or render a query.
15
15
 
16
- The examples use the same order as the rendered statement: projection, `FROM`,
17
- then `WHERE`, ordering, grouping, and pagination. `select()` still accepts
18
- independent clauses in any order, which lets reusable values be composed, but
19
- keeping the final call in SQL order makes the query easy to scan and repair.
16
+ The examples write clauses in SQL order so the query is easy to scan.
17
+ `select()` also accepts independent clauses in any order and puts them in SQL
18
+ order when rendering.
20
19
 
21
20
  ## Define a table
22
21
 
@@ -38,10 +37,9 @@ nullable email column is inferred as `string | null` when selected.
38
37
 
39
38
  ## Build and render a query
40
39
 
41
- Pass a named projection and the final clauses to `select()` in SQL order. Qubu
42
- also accepts independent clause values in another order when composition needs
43
- it, then renders the normalized statement in SQL order. The example uses the
44
- `users` table from the previous section.
40
+ Pass the fields you want to return as a named object, called the projection.
41
+ Then add the clauses. This example uses the `users` table from the previous
42
+ section.
45
43
 
46
44
  ```ts
47
45
  import { eq, from, render, select, where } from "qubu"
@@ -69,6 +67,8 @@ statement.parameters
69
67
  // [7]
70
68
  ```
71
69
 
70
+ ### Inspect the result type
71
+
72
72
  The selected row type is available on the query value:
73
73
 
74
74
  ```ts
@@ -91,6 +91,6 @@ type UserRow = typeof query.row
91
91
  inputs.
92
92
  - [Choose a database dialect](dialects-and-execution.md) when the
93
93
  driver expects different identifier, placeholder, or pagination syntax.
94
- - [Read JSON scalars](guides/json.md) from a JSON column.
94
+ - [Query nested JSON](guides/json.md) or read scalars from a JSON column.
95
95
  - [Use the Vite compiler hint](guides/vite-plugin.md) for directive-based
96
96
  imports.
@@ -1,6 +1,8 @@
1
1
  # Better Auth
2
2
 
3
- > Derive Qubu-owned auth tables and run Better Auth through a transactional Qubu client.
3
+ > Define auth tables with Qubu and connect Better Auth to a transactional Qubu client.
4
+
5
+ ## Install the integration
4
6
 
5
7
  Install the integration next to Qubu and Better Auth:
6
8
 
@@ -8,10 +10,15 @@ Install the integration next to Qubu and Better Auth:
8
10
  pnpm add qubu @qubu/better-auth better-auth
9
11
  ```
10
12
 
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.
13
+ ## Define the auth schema
14
+
15
+ Define the Better Auth options once. Qubu derives its schema from Better Auth’s
16
+ resolved public metadata, including:
17
+
18
+ - Core tables and renamed models or fields.
19
+ - Additional fields and plugin tables.
20
+ - References and unique constraints.
21
+ - Compound indexes.
15
22
 
16
23
  ```ts
17
24
  import { betterAuth } from "better-auth"
@@ -44,6 +51,8 @@ export const auth = betterAuth({
44
51
  migration-plan, and DDL workflows. The adapter's Better Auth `createSchema`
45
52
  hook emits a TypeScript module that reconstructs the same Qubu-owned metadata.
46
53
 
54
+ ## Database requirements
55
+
47
56
  The package never imports PostgreSQL, MySQL, or SQLite drivers. It executes
48
57
  through Qubu's query and transaction boundaries. PostgreSQL and SQLite use one
49
58
  limited mutation statement for atomic consume and guarded increment operations;
@@ -51,6 +60,8 @@ MySQL locks one selected row inside the Qubu-owned transaction. A client without
51
60
  transaction support, or a dialect other than PostgreSQL, MySQL, or SQLite, is
52
61
  rejected during adapter construction.
53
62
 
63
+ ## Enum limitation
64
+
54
65
  Better Auth enum metadata is currently rejected because Qubu cannot preserve
55
66
  the closed value set as a portable column without adding a database constraint.
56
67
  The error includes the model and field path instead of silently widening it to
@@ -1,6 +1,6 @@
1
1
  # Compose queries
2
2
 
3
- > Reuse a query's inferred row shape as a typed source for CTEs, derived tables, subqueries, and set operations.
3
+ > Reuse a query as a CTE, a derived table, or a subquery, and combine query results.
4
4
 
5
5
  ## Turn a query into a CTE
6
6
 
@@ -28,6 +28,8 @@ The rendered statement includes the `WITH` clause before `SELECT`. Selected
28
28
  camelCase keys use snake_case while they belong to the CTE relation; the outer
29
29
  result projection aliases them back to camelCase for the returned row.
30
30
 
31
+ ### Use a CTE in a mutation
32
+
31
33
  Attach the same clause to an insert, update, or delete when the mutation reads
32
34
  through the CTE. For example, an insert can consume a filtered CTE through
33
35
  `insertSelect()`:
@@ -66,11 +68,20 @@ const numbers = recursiveCte("numbers", select({ value: cast(value(1), integer()
66
68
  const query = select({ value: numbers.value }, withCte(numbers), from(numbers))
67
69
  ```
68
70
 
69
- The anchor names the fields, application types, nullability, and SQL domains
70
- that the returned source exposes. The member must project those same fields
71
- with compatible types. Give bound anchor values an explicit SQL type with
71
+ The anchor defines the fields the CTE returns:
72
+
73
+ - Field names.
74
+ - Application types.
75
+ - Nullability.
76
+ - SQL domains.
77
+
78
+ The recursive member must select the same fields with compatible types.
79
+
80
+ Give bound anchor values an explicit SQL type with
72
81
  `cast()` when the database cannot infer it from surrounding columns; PostgreSQL
73
- requires this for recursive CTE anchors. Qubu renders `WITH RECURSIVE`, an
82
+ requires this for recursive CTE anchors.
83
+
84
+ Qubu renders `WITH RECURSIVE`, an
74
85
  explicit relation column list, and `anchor UNION ALL member`; ordinary and
75
86
  recursive CTEs can share one `withCte()` clause.
76
87
 
@@ -117,7 +128,9 @@ const query = select(
117
128
  `scalar()` throws at runtime when the query selects more than one field. Its
118
129
  type is the selected field's value type, widened with `null` when the query may
119
130
  return no rows. An ordinary select and `fetchFirst(1)` are both nullable: the
120
- limit proves at most one row, not that a row exists. A source-free select such
131
+ limit proves at most one row, not that a row exists.
132
+
133
+ A source-free select such
121
134
  as `select({ value: value(42) })` is known to produce exactly one row.
122
135
 
123
136
  Qubu does not treat an arbitrary predicate as proof of exactness. Use
@@ -163,9 +176,8 @@ code.
163
176
  ## Constrain a reusable fragment by required fields
164
177
 
165
178
  Use `TableLike` when a fragment requires a physical table and `SourceLike`
166
- when aliases, CTEs, derived tables, or custom sources are also valid. Both are
167
- lower-bound constraints: the source may contain additional fields, and the
168
- generic function retains its exact source identity.
179
+ when aliases, CTEs, derived tables, or custom sources are also valid. Both allow the source to contain additional fields. The generic function
180
+ retains the source’s exact identity.
169
181
 
170
182
  For an application-level requirement, describe the required JavaScript row:
171
183
 
@@ -123,9 +123,14 @@ override.
123
123
  ## Runtime metadata
124
124
 
125
125
  Each dialect adapter maps Qubu storage descriptors to its own Drizzle builders.
126
- It also transfers concrete defaults, generated expressions, common primary and
127
- unique constraints, checks, foreign keys, and indexes. Native storage must
128
- belong to the selected dialect:
126
+ It also transfers:
127
+
128
+ - Concrete defaults and generated expressions.
129
+ - Common primary and unique constraints.
130
+ - Checks and foreign keys.
131
+ - Indexes.
132
+
133
+ Native storage must belong to the selected dialect:
129
134
 
130
135
  ```ts
131
136
  import { nativeColumn, schema, table } from "qubu"
@@ -1,6 +1,6 @@
1
1
  # Add a dialect policy
2
2
 
3
- > Change identifiers, placeholders, pagination, or cast targets at the rendering boundary without changing portable query construction.
3
+ > Customize how Qubu renders SQL for your driver.
4
4
 
5
5
  Use `createDialect()` when the query is portable but the driver changes
6
6
  identifiers, placeholders, or pagination:
@@ -1,6 +1,6 @@
1
1
  # Extend Qubu
2
2
 
3
- > Choose an extension boundary when the built-in API does not cover a driver-specific or uncommon SQL feature.
3
+ > Add SQL features that Qubu’s built-in helpers do not cover.
4
4
 
5
5
  Qubu extensions are values that render SQL and carry the metadata later
6
6
  composition needs. Choose the page that matches the thing you are adding:
@@ -1,6 +1,6 @@
1
1
  # Add sources and clauses
2
2
 
3
- > Publish a custom SQL clause or relation while preserving parameter order and source-scope checks.
3
+ > Add a SQL clause or table-like source that works with Qubu’s query checks.
4
4
 
5
5
  ## Add a custom clause
6
6
 
@@ -67,9 +67,13 @@ render(query)
67
67
  ```
68
68
 
69
69
  `identity` is the source-scope key; `reference` is the SQL qualifier used by
70
- the generated columns. A nullable column remains nullable intrinsically, and a
70
+ the generated columns.
71
+
72
+ A nullable column remains nullable, and a
71
73
  `leftJoin(rows, ...)` adds outer-join nullability to every selected row
72
- column. Render the complete relation in the producer and bind values with
74
+ column.
75
+
76
+ Render the complete relation in the producer and bind values with
73
77
  `context.parameter()`; the normal renderer preserves parameter order.
74
78
 
75
79
  ## Read next
@@ -1,6 +1,6 @@
1
1
  # Add typed expressions
2
2
 
3
- > Extend Qubu with expressions that retain source, nullability, result, and SQL-domain metadata.
3
+ > Build custom expressions that preserve result types and query checks.
4
4
 
5
5
  ## Build expressions from public helpers
6
6
 
@@ -47,30 +47,44 @@ const nameAsCitext = cast(users.name, citext)
47
47
  ```
48
48
 
49
49
  The first three `column` type arguments are output, insert, and update values;
50
- the fourth is the SQL domain. The `text` equality and ordering groups make the
51
- custom domain compatible with `SqlText`. Use a distinct group when cross-type
52
- comparison is not portable. `castType` also makes this definition a cast
53
- target; its SQL text is emitted verbatim, so keep it in trusted extension code.
50
+ the fourth is the SQL domain.
51
+
52
+ The `text` equality and ordering groups make the custom domain compatible with `SqlText`. Use a distinct group when cross-type
53
+ comparison is not portable.
54
+
55
+ ### Use the definition as a cast target
56
+
57
+ `castType` also makes this definition a cast target. Its SQL text is emitted
58
+ unchanged, so keep it in trusted extension code.
59
+
54
60
  Definitions with schema flags are not accepted as cast targets because cast
55
61
  nullability comes from the operand and write flags have no cast meaning.
56
62
 
63
+ ### Type individual expressions
64
+
57
65
  Declare result domains at other extension boundaries too:
58
66
 
59
67
  ```ts
60
68
  import { typedCall, typedCast, typedValue, unsafeExpression } from "qubu/core"
61
69
  import type { SqlText, SqlUuid } from "qubu"
62
70
 
63
- const id = typedValue<SqlUuid, string>("108cb836-20d2-41b2-8c23-f0c94700aa7e")
71
+ const id = typedValue<SqlUuid, string>("108cb836-20d2-41b2-8c23-f0c94700aa7e", "uuid")
64
72
  const normalized = typedCall<SqlText, string>()("custom_text", users.name)
65
73
  const rawNameAsText = typedCast<string, SqlText>()(users.name, "TEXT")
66
74
  const generated = unsafeExpression<string, SqlText>("custom_text()")
67
75
  ```
68
76
 
69
- `typedCall()` preserves source requirements from its arguments. `typedCast()`
70
- is the fallback when no reusable definition describes the target. It preserves
71
- operand nullability and source metadata while emitting its supplied type name
72
- verbatim. `typedValue()` binds a parameter. `unsafeExpression()` emits its
73
- string unchanged and should remain a last resort.
77
+ Choose the helper for the operation:
78
+
79
+ - `typedCall()` preserves source requirements from its arguments.
80
+ - `typedCast()` supplies a cast target when no reusable definition describes
81
+ it. It preserves operand nullability and source metadata, and emits the
82
+ supplied type name unchanged.
83
+ - `typedValue()` binds a parameter and declares its runtime SQL domain for
84
+ the adapter. It does not choose a JavaScript result decoder; schema columns
85
+ carry decoder metadata separately.
86
+ - `unsafeExpression()` emits its string unchanged. Use it only when the other
87
+ helpers cannot express the syntax.
74
88
 
75
89
  The lower-level forms also expose the SQL domain in their generic lists:
76
90
  `call<Output, Name, Arguments, NullableFrom, SqlType>()` and
@@ -78,7 +92,7 @@ The lower-level forms also expose the SQL domain in their generic lists:
78
92
  argument or nullability types in its own generic signature.
79
93
 
80
94
  Untyped `column()`, `value()`, `call()`, and custom expressions use
81
- `SqlUnknown`, which stays permissive for backward compatibility. Declaring a
95
+ `SqlUnknown`, which allows composition without SQL-domain checks. Declaring a
82
96
  known domain opts the extension into incompatible-operation errors. See
83
97
  [SQL semantic types](../../sql-semantic-types.md) for the capability model and
84
98
  its limits.
@@ -14,12 +14,16 @@ const query = select({
14
14
  })
15
15
  ```
16
16
 
17
- Keep raw identifiers and values out of the string. Prefer a typed custom
18
- fragment when the syntax will be reused. Use the [`sql` template
19
- tag](../sql-templates.md) when fixed trusted syntax needs bound runtime values
20
- or existing Qubu fragments. Keep dynamic SQL text on `unsafeExpression()` and
21
- runtime identifiers on `identifier()` or `qualifiedIdentifier()` from
22
- `qubu/core`.
17
+ ## Choose the right helper
18
+
19
+ Keep raw identifiers and values out of the string:
20
+
21
+ - Use a typed custom fragment for syntax you will reuse.
22
+ - Use the [`sql` template tag](../sql-templates.md) for fixed trusted syntax
23
+ with bound values or existing Qubu fragments.
24
+ - Use `unsafeExpression()` for trusted dynamic SQL text.
25
+ - Use `identifier()` or `qualifiedIdentifier()` from `qubu/core` for runtime
26
+ identifiers.
23
27
 
24
28
  Read [Dialects and execution](../../dialects-and-execution.md) for the boundary
25
29
  between rendering and driver behavior. Read [Add typed